13.16 告知编译器某个函数已被定义

对文件进行字节编译时,经常会产生关于编译器未知函数的警告(see 编译器错误)。有时这确实代表一个问题,但更多时候,相关函数是定义在其他文件中,代码运行时会被加载。例如,编译 simple.el 时曾经会出现这样的警告:

simple.el:8727:1:Warning: the function ‘shell-mode’ is not known to be
    defined.

实际上,shell-mode 仅在一个函数中被使用,该函数会在调用 shell-mode 之前执行 (require 'shell),因此 shell-mode 在运行时会被正确定义。当你确定这类警告并非真正的问题时,最好将其抑制 —— 这能让那些可能代表真实问题的新警告更容易被发现。你可以通过 declare-function 来实现这一点。

只需在相关函数的第一次使用之前添加一条 declare-function 语句即可:

(declare-function shell-mode "shell" ())

该语句表明 shell-mode 定义在 shell.el 文件中(后缀 ‘.el’ 可省略)。编译器会默认该文件确实定义了这个函数,而不会进行检查。

可选的第三个参数用于指定 shell-mode 的参数列表。本例中,该函数不接受任何参数(注意 nil 与不指定值是不同的)。在其他情况下,参数列表可能形如 (file &optional overwrite)。你并非必须指定参数列表,但如果指定了,字节编译器就能检查函数调用是否与声明的参数列表匹配。

Macro: declare-function function file &optional arglist fileonly

告知字节编译器,假定 function 定义在文件 file 中。可选的第三个参数 arglist 有两种取值: 若为 t,表示未指定该函数的参数列表; 若为列表,则是与 defun 风格一致的形式参数列表(需包含外层括号)。 若省略 arglist,其默认值为 t 而非 nil—— 这是省略参数时的特殊行为,意味着如果要传入第四个参数但不传入第三个,必须将第三个参数占位符指定为 t(而非通常使用的 nil)。可选的第四个参数 fileonly 若为非 nil 值,则仅检查 file 文件是否存在,不验证该文件中是否实际定义了 function

若要验证这些函数是否确实声明在 declare-function 所指定的位置,可使用 check-declare-file 检查单个源码文件中所有 declare-function 调用的有效性,或使用 check-declare-directory 检查指定目录及其子目录下所有文件的相关声明。

这些命令会通过 locate-library 查找理应包含函数定义的文件;若该函数未找到对应文件,则会以包含 declare-function 调用的文件所在目录为基准,展开函数定义文件的路径。

你也可以通过指定后缀为 ‘.c’ 或 ‘.m’ 的文件名,声明某个函数为原生函数(primitive)。这种用法仅适用于调用那些仅在特定系统中定义的原生函数场景 —— 大多数原生函数是全局定义的,因此不会触发此类警告。

有时某个文件会可选地使用来自外部包的函数。如果你在 declare-function 语句中的文件名前加上前缀 ‘ext:’,那么工具会在文件存在时进行检查,若不存在则直接跳过,不报错。

有一些函数定义是 ‘check-declare’ 无法识别的(例如 defstruct 以及其他一些宏)。在这种情况下,你可以给 declare-function 传入一个非 nilfileonly 参数,表示只检查文件是否存在,不验证它是否真的定义了该函数。注意,如果你想这样做又不必指定参数列表,应当把 arglist 参数设为 t(因为 nil 表示空参数列表,而非 “未指定”)。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike