You are missing Unevaluated
:
SetAttributes[f, HoldFirst]
f[x_] := {SymbolName[Unevaluated@x], x}
because SymbolName
does not hold its arguments, so you have to prevent evaluation also there.
Generally, if you are passing some argument via a chain of function calls, and want to keep it unevaluated, you have to prevent it's evaluation at each stage (function call). If the chain is long, it may be easier (and more robust) to wrap the argument in Hold
, for the passing purposes, and unwrap in the function that actually needs it.
Note by the way that it is better to set attributes before you give definitions to a function, to avoid some surprises, unless you know precisely what you do and why.