12.10.5 Converting to Lexical Binding

Converting an Emacs Lisp program to lexical binding is easy. First, add a file-local variable setting of lexical-binding to t in the header line of the Emacs Lisp source file (see File Local Variables). Second, check that every variable in the program which needs to be dynamically bound has a variable definition, so that it is not inadvertently bound lexically.

A simple way to find out which variables need a variable definition is to byte-compile the source file. See Byte Compilation. If a non-special variable is used outside of a let form, the byte-compiler will warn about reference or assignment to a free variable. If a non-special variable is bound but not used within a let form, the byte-compiler will warn about an unused lexical variable. The byte-compiler will also issue a warning if you use a special variable as a function argument.

A warning about a reference or an assignment to a free variable is usually a clear sign that that variable should be marked as dynamically scoped, so you need to add an appropriate defvar before the first use of that variable.

A warning about an unused variable may be a good hint that the variable was intended to be dynamically scoped (because it is actually used, but in another function), but it may also be an indication that the variable is simply really not used and could simply be removed. So you need to find out which case it is, and based on that, either add a defvar or remove the variable altogether. If removal is not possible or not desirable (typically because it is a formal argument and that we cannot or don’t want to change all the callers), you can also add a leading underscore to the variable’s name to indicate to the compiler that this is a variable known not to be used.)

Cross-file variable checking

Caution: This is an experimental feature that may change or disappear without prior notice.

The byte-compiler can also warn about lexical variables that are special in other Emacs Lisp files, often indicating a missing defvar declaration. This useful but somewhat specialized check requires three steps:

  1. Byte-compile all files whose special variable declarations may be of interest, with the environment variable EMACS_GENERATE_DYNVARS set to a nonempty string. These are typically all the files in the same package or related packages or Emacs subsystems. The process will generate a file whose name ends in .dynvars for each compiled Emacs Lisp file.
  2. Concatenate the .dynvars files into a single file.
  3. Byte-compile the files that need to be checked, this time with the environment variable EMACS_DYNVARS_FILE set to the name of the aggregated file created in step 2.

Here is an example illustrating how this could be done, assuming that a Unix shell and make are used for byte-compilation:

$ rm *.elc                                # force recompilation
$ EMACS_GENERATE_DYNVARS=1 make           # generate .dynvars
$ cat *.dynvars > ~/my-dynvars            # combine .dynvars
$ rm *.elc                                # force recompilation
$ EMACS_DYNVARS_FILE=~/my-dynvars make    # perform checks