Multiple Emacs Lisp config files lead to "free variable" and "function not known to be defined" warnings

Viewed 80

I've been looking for emacs config files for a while, so that I learn how to structure my Emacs config in the most optimal way. Now i'm looking at purcell's config files which look very tidy and simple to read.

However, for every config file I've found there's always the same problem: "free variable" and "function not known to be defined" warnings on all the included files. It is quite frustrating to see all the red underlines from flymake everytime, and it also makes me question if this really is the best way to write the config file.

Here's a file from the repository mentioned above:

;;; init-python.el --- Python editing -*- lexical-binding: t -*-

(setq auto-mode-alist
      (append '(("SConstruct\\'" . python-mode)
                ("SConscript\\'" . python-mode))
              auto-mode-alist))

;;WARNING: assignment to free variable ‘python-shell-interpreter’
(setq python-shell-interpreter "python3") 

(require-package 'pip-requirements)

(when (maybe-require-package 'toml-mode)
  (add-to-list 'auto-mode-alist '("poetry\\.lock\\'" . toml-mode)))

;;WARNING: reference to free variable ‘black’
(when (maybe-require-package 'reformatter)
  (reformatter-define black :program "black" :args '("-")))

(provide 'init-python)
;;WARNING: the following functions are not known to be defined: require-package, maybe-require-package, reformatter-define
;;; init-python.el ends here

The python-shell-interpreter is a variable defined from the python-mode package. The require-package and maybe-require-package are defined in a custom local file called init-elpa.el which is requires'd by the init.el file and thus they're not directly recognized by this file. The black variable warning is the mostr "intricate": I guess it's caused by the fact that the reformatter package isn't included because maybe-require-package isn't included as well.

This gives me a headache. If I see it correctly, the root problem is the fact that the included files (init-python.el et al.) depend on stuff from the including file (init.el) without it being explicitly stated.

Is there a better way to structure an Emacs Lisp config among multiple files?

1 Answers

The python-shell-interpreter is a variable defined from the python-mode package.

Add (require 'python-mode) to the top of the file (or simply above the code which depends on that).

The require-package and maybe-require-package are defined in a custom local file called init-elpa.el

Add (require 'init-elpa) to the file (I'm assuming it provides the init-elpa feature).

which is require'd by the init.el file

This is fine -- require does nothing if the library in question has already been loaded. Even when there's an overarching init file loading other files, it's sensible for each file to require the things it needs (unless there are circular dependencies).

The black variable warning is the most "intricate": I guess it's caused by the fact that the reformatter package isn't included because maybe-require-package isn't included as well.

I'm guessing that reformatter-define is a macro and that the symbol black is not actually used as a variable. When only macros are required, one would typically (eval-when-compile (require 'reformatter)) but judging by the name maybe-require-package it's intended that reformatter might not be loadable at all. If that's all accurate, you can use this wrapper to cope with the false-positive:

(with-suppressed-warnings ((free-vars black))
  ...)

See C-hig (elisp)Compiler Errors for further information on handling byte-compilation warnings and errors.

Related