I'm writing an emacs major mode, which uses buffer-local variables to store some state:
(defun foo-mode ()
"My nice major mode"
(interactive)
(kill-all-local-variables)
(setq mode-name "foo")
(setq major-mode 'foo-mode)
(set (make-local-variable 'foo-state) "bar"))
(defun foo-change-state ()
(setq foo-state "baz"))
This works very well and has the property that in any buffer not using my major mode, the foo-state variable is not bound (which is a good thing in my opinion, since it avoids cluttering the symbols table).
However, byte-compiling such a piece of code produces the following warning:
Warning: assignment to free variable `foo-state'
Using defvar gets rid of the warning, but has the side-effect that foo-state is now bound everywhere, which is undesirable in my opinion.
Is there a way to get rid of the warnings while still not binding the mode-specific variables in every buffer? Or am I mistaken when I think these variables should not be declared globally?
The official way to do what you want is (defvar foo-state). Note the absence of a second argument. Note also that such a declaration only applies to the file where it is found (or to the scope in which it is found, if it's used inside a function).
Declare the variable with defvar. There is no other way to remove the warning, and it is really considered good practice.
Your intention to keep the symbol table uncluttered is worthy, but you are not actually doing so. I think you have misunderstood the semantics of variable bindings in Emacs Lisp, since you seem to believe that by not declaring it foo-state will be unbound in any buffer not using foo-mode. That is not the case.
In Emacs Lisp names (aka symbols) are global. As soon as foo-state is evaluated the first time, the runtime creates a new symbol object for foo-state and puts this into the global symbol table (aka obarray). There are no local symbol tables, so it does not matter where foo-state is evaluated and how, foo-state refers to the same symbol object at any place (see Creating Symbols).
Each symbol object consists of components (aka cells), one of which is the variable cell (see Symbol components). setq modifies the current binding of the system, on top-level without lexical binding this effectively changes the variable cell of the symbol object, thus the global value of the variable. Again, it does not matter where setq is evaluated. Actually if some bar-mode evaluated (setq foo-state "bar"), foo-state would be bound to "bar" in foo-mode too, and vice versa.
Thus the only effect of (defvar) over (setq) it that documents the intention of using a symbol as global variable, so telling others to not modify this variable unless manipulation of the behavior of foo-mode is intended. You can attach documentation to the variable, and mark as being defined in your buffer (C-h v foo-state will provide a link to jump to the definition).
Since Emacs Lisp lacks namespaces and is – by default – dynamically scoped, documentation is fundamentally important to avoid conflicts between modules. If I wrote a bar-mode using your foo-mode I might accidentally bind to foo-state, call foo-change-state and then see my mode misbehaving because a variable was unintentionally overwritten. Declaring foo-state doesn't make this impossible, but it at least allows me to catch the error, because C-h v foo-state will reveal that this variable is used by another mode, so I'd better not use it unless I really intend to manipulate that mode.
As a last word: In all of the aforementioned text “mode” can be replaced with Emacs Lisp files. modes are nothing special with regards to symbols. All of the above also holds for Emacs Lisp that do not declare modes, but just contain a bunch of functions.
来源:https://stackoverflow.com/questions/12432093/get-rid-of-reference-to-free-variable-byte-compilation-warnings