All you need is compiler diagnostics about variable shadowing to make this academic issue entirely go away:
1> (defmacro mystery-macro (form) ^(let ((x 73)) ,form))
mystery-macro
2> (with-compile-opts ()
(compile-toplevel '(let ((x 42)) (mystery-macro x))))
** expr-2:2: warning: let: variable x unused
#<sys:vm-desc: a0db560>
3> (with-compile-opts (:warn shadow-var)
(compile-toplevel '(let ((x 42)) (mystery-macro x))))
** expr-3:2: warning: let: variable x shadows local variable
** expr-3:2: warning: let: variable x unused
#<sys:vm-desc: a0fd0f0>
4> (with-compile-opts (:error shadow-var)
(compile-toplevel '(let ((x 42)) (mystery-macro x))))
** expr-4:2: let: variable x shadows local variable
** during evaluation of form (compile-toplevel '(let ((x 42))
(mystery-macro
x)))
** ... an expansion of (progn (compile-toplevel '(let ((x 42))
(mystery-macro
x))))
** ... an expansion of (with-compile-opts
(compile-toplevel '(let ((x 42))
(mystery-macro
x))))
** which is located at expr-4:1
Moreover, this is a problem that mainly affects densely nested code. If you write small functions that are not nested beyond a couple of lexical levels or so, referentially opaque smells have nowhere to hide.
Example with macro generating code that unhygienically relies on list, which is redefined around the macro:
1> (defmacro mac (x) ^(list ,x))
mac
2> (with-compile-opts (:warn shadow-fun)
(compile-toplevel '(labels ((list ())) (mac 1))))
** expr-2:2: warning: sys:lbind: redefining list, which is a built-in defun
** expr-2:2: warning: sys:lbind: function list shadows global function
** expr-2:2: warning: list: too many arguments: max 0, given 1
#<sys:vm-desc: 9d0e4a0>
3> (with-compile-opts (:error shadow-fun)
(compile-toplevel '(labels ((list ())) (mac 1))))
** expr-3:2: warning: sys:lbind: redefining list, which is a built-in defun
** expr-3:2: sys:lbind: function list shadows global function
** during evaluation of form (compile-toplevel '(labels ((list ()))
(mac 1)))
** ... an expansion of (progn (compile-toplevel '(labels ((list ()))
(mac 1))))
** ... an expansion of (with-compile-opts
(compile-toplevel '(labels ((list ()))
(mac 1))))
** which is located at expr-3:1
The concept of referential transparency is meaningless if there is no shadowing. If an expression which refers to a certain binding is moved around in the program, it either continues to refer to that same binding, or at worst becomes a free reference (easily diagnosable). If there is no shadowing, constructs cannot surreptitiously intercept bindings.