master 8c8caa30885 1/2: elisp-scope.el: Support associating analyzers with multiple symbols
Eshel Yaron via Mailing list for Emacs changes <[email protected]> Sat, 4 Jul 2026 15:50:45 -0400 (EDT)
| Newsgroups | gmane.emacs.diffs |
|---|---|
| Message-ID | <[email protected]> |
branch: master commit 8c8caa308857b89e22b2d2f9d4be2241cf0a6f67 Author: Eshel Yaron <[email protected]> Commit: Eshel Yaron <[email protected]> elisp-scope.el: Support associating analyzers with multiple symbols Many analyzers (those defined with 'elisp-scope-define-analyzer' and friends) are appropriate for more than one function/macro. Before this commit, our way of reusing an analyzer was to do this: ;; Define analyzer for `foo' (elisp-scope-define-function-analyzer foo (...) ...) ;; Reuse it for `bar' (put 'bar 'elisp-scope-analyzer #'elisp-scope--analyze-foo) This isn't a good solution for users/packages that want to reuse their custom analyzers, because it relies on too many brittle internal implementation details. This commit provides a nicer solution: we generalize the analyzer-defining macros so they can take a list of symbols and directly associated the defined analyzer with all of the given symbols. So the above example is simplified to: (elisp-scope-define-function-analyzer (foo bar) (...) ...) * lisp/emacs-lisp/elisp-scope.el (elisp-scope-define-analyzer): Accept either a symbol or a list of symbols, and register the analyzer function for each symbol in the list (bug#81355). (elisp-scope--define-function-analyzer) (elisp-scope-define-function-analyzer) (elisp-scope-define-macro-analyzer) (elisp-scope-define-special-form-analyzer): Likewise. Convert all call sites that used to register extra symbols via separate 'put' calls to use this new feature instead. * etc/NEWS: Announce it. --- etc/NEWS | 9 ++ lisp/emacs-lisp/elisp-scope.el | 272 +++++++++++++++++++---------------------- 2 files changed, 133 insertions(+), 148 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index d39ede2b346..7733c5e8eba 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -237,6 +237,15 @@ The HANDLER argument of 'dbus-call-method-asynchronously' can be a cons cell '(HANDLER . ERROR-HANDLER)'. ERROR-HANDLER is invoked if the method call returns with a D-Bus error; the error is passed as argument. +** elisp-scope.el + +*** Custom analyzers can be associated with multiple functions. +'elisp-scope' macros that define custom analyzer functions, such as +'elisp-scope-define-function-analyzer' and +'elisp-scope-define-macro-analyzer', can now take a list of symbols to +which the defined analyzer should apply (or a single symbol, as before). +This makes it easy to specify the same analyzer for multiple functions. + * Changes in Emacs 32.1 on Non-Free Operating Systems diff --git a/lisp/emacs-lisp/elisp-scope.el b/lisp/emacs-lisp/elisp-scope.el index 55e82001df9..4d3c39850b4 100644 --- a/lisp/emacs-lisp/elisp-scope.el +++ b/lisp/emacs-lisp/elisp-scope.el @@ -1576,16 +1576,23 @@ property, or if the current buffer is trusted (see `trusted-content-p')." (defmacro elisp-scope-define-analyzer (fsym args &rest body) "Define an analyzer function for function/macro FSYM. +FSYM is a symbol, or a list of symbols that all share the same analyzer. ARGS is the arguments list of the analyzer function, and BODY is its body." (declare (indent defun)) - (let ((analyzer (intern (concat "elisp-scope--analyze-" (symbol-name fsym))))) + (let* ((syms (ensure-list fsym)) + (fsym (car syms)) + (analyzer (intern (concat "elisp-scope--analyze-" (symbol-name fsym))))) `(progn (defun ,analyzer ,args ,@body) - (put ',fsym 'elisp-scope-analyzer #',analyzer)))) + . ,(mapcar (lambda (sym) + `(put ',sym 'elisp-scope-analyzer #',analyzer)) + syms)))) (defmacro elisp-scope--define-function-analyzer (fsym args role &rest body) (declare (indent defun)) - (let ((helper (intern (concat "elisp-scope--analyze-" (symbol-name fsym) "-1")))) + (let ((helper (intern (concat "elisp-scope--analyze-" + (symbol-name (car (ensure-list fsym))) + "-1")))) `(progn (defun ,helper ,args ,@body) (elisp-scope-define-analyzer ,fsym (f &rest args) @@ -1594,6 +1601,7 @@ ARGS is the arguments list of the analyzer function, and BODY is its body." (defmacro elisp-scope-define-function-analyzer (fsym args &rest body) "Define an analyzer function for function FSYM. +FSYM is a symbol, or a list of symbols that all share the same analyzer. The analyzer function analyzes occurrences of FSYM as a function call, and it analyzes the arguments in calls to FSYM by executing BODY with ARGS bound to the analyzed arguments." @@ -1602,11 +1610,14 @@ ARGS bound to the analyzed arguments." (defmacro elisp-scope-define-macro-analyzer (fsym args &rest body) "Define an analyzer function for macro FSYM. +FSYM is a symbol, or a list of symbols that all share the same analyzer. The analyzer function analyzes occurrences of FSYM as a macro call, and it analyzes the arguments in calls to FSYM by executing BODY with ARGS bound to the analyzed arguments." (declare (indent defun)) - (let ((helper (intern (concat "elisp-scope--analyze-" (symbol-name fsym) "-1")))) + (let ((helper (intern (concat "elisp-scope--analyze-" + (symbol-name (car (ensure-list fsym))) + "-1")))) `(progn (defun ,helper ,args ,@body) (elisp-scope-define-analyzer ,fsym (f &rest args) @@ -1615,11 +1626,14 @@ bound to the analyzed arguments." (defmacro elisp-scope-define-special-form-analyzer (fsym args &rest body) "Define an analyzer function for special form FSYM. +FSYM is a symbol, or a list of symbols that all share the same analyzer. The analyzer function analyzes occurrences of FSYM as a special form, and it analyzes the arguments in calls to FSYM by executing BODY with ARGS bound to the analyzed arguments." (declare (indent defun)) - (let ((helper (intern (concat "elisp-scope--analyze-" (symbol-name fsym) "-1")))) + (let ((helper (intern (concat "elisp-scope--analyze-" + (symbol-name (car (ensure-list fsym))) + "-1")))) `(progn (defun ,helper ,args ,@body) (elisp-scope-define-analyzer ,fsym (f &rest args) @@ -1647,12 +1661,10 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 (or (elisp-scope--unquote form) form)) (elisp-scope-1 lexical)) -(elisp-scope-define-function-analyzer funcall (&optional f &rest args) +(elisp-scope-define-function-analyzer (funcall apply) (&optional f &rest args) (elisp-scope-1 f '(symbol . function)) (elisp-scope-n args)) -(put 'apply 'elisp-scope-analyzer #'elisp-scope--analyze-funcall) - (elisp-scope-define-function-analyzer defalias (&optional sym def docstring) (elisp-scope-1 sym '(symbol . defun)) (elisp-scope-1 def '(symbol . defun)) @@ -1687,22 +1699,17 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 alias '(symbol . defcharset)) (elisp-scope-1 charset '(symbol . charset))) -(elisp-scope-define-function-analyzer charset-chars +(elisp-scope-define-function-analyzer + (charset-chars + charset-description charset-info charset-iso-final-char + charset-long-name charset-plist charset-short-name + get-charset-property put-charset-property list-charset-chars + set-charset-plist set-charset-priority unify-charset + locale-charset-to-coding-system) (&optional charset &rest rest) (elisp-scope-1 charset '(symbol . charset)) (elisp-scope-n rest)) -(dolist (sym '(charset-description charset-info charset-iso-final-char - charset-long-name charset-plist - charset-short-name - get-charset-property put-charset-property - list-charset-chars - set-charset-plist - set-charset-priority - unify-charset - locale-charset-to-coding-system)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-charset-chars)) - (elisp-scope-define-function-analyzer define-coding-system (&optional name &rest rest) (elisp-scope-1 name '(symbol . defcoding)) @@ -1713,75 +1720,68 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 alias '(symbol . defcoding)) (elisp-scope-1 coding-system '(symbol . coding))) -(elisp-scope-define-function-analyzer decode-coding-region +(elisp-scope-define-function-analyzer + (decode-coding-region encode-coding-region) (&optional start end coding-system &rest rest) (elisp-scope-1 start) (elisp-scope-1 end) (elisp-scope-1 coding-system '(symbol . coding)) (elisp-scope-n rest)) -(put 'encode-coding-region 'elisp-scope-analyzer #'elisp-scope--analyze-decode-coding-region) - -(elisp-scope-define-function-analyzer decode-coding-string +(elisp-scope-define-function-analyzer + (decode-coding-string encode-coding-char encode-coding-string) (&optional string coding-system &rest rest) (elisp-scope-1 string) (elisp-scope-1 coding-system '(symbol . coding)) (elisp-scope-n rest)) -(dolist (sym '(encode-coding-char encode-coding-string)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-decode-coding-string)) - -(elisp-scope-define-function-analyzer coding-system-mnemonic +(elisp-scope-define-function-analyzer + (coding-system-mnemonic + add-to-coding-system-list + check-coding-system + coding-system-aliases + coding-system-base + coding-system-category + coding-system-change-eol-conversion + coding-system-change-text-conversion + coding-system-charset-list + coding-system-doc-string + coding-system-eol-type + coding-system-eol-type-mnemonic + coding-system-get + coding-system-plist + coding-system-post-read-conversion + coding-system-pre-write-conversion + coding-system-put + coding-system-translation-table-for-decode + coding-system-translation-table-for-encode + coding-system-type + describe-coding-system + prefer-coding-system + print-coding-system + print-coding-system-briefly + revert-buffer-with-coding-system + set-buffer-file-coding-system + set-clipboard-coding-system + set-coding-system-priority + set-default-coding-systems + set-file-name-coding-system + set-keyboard-coding-system + set-next-selection-coding-system + set-selection-coding-system + set-terminal-coding-system + universal-coding-system-argument) (&optional coding-system &rest rest) (elisp-scope-1 coding-system '(symbol . coding)) (elisp-scope-n rest)) -(dolist (sym '(add-to-coding-system-list - check-coding-system - coding-system-aliases - coding-system-base - coding-system-category - coding-system-change-eol-conversion - coding-system-change-text-conversion - coding-system-charset-list - coding-system-doc-string - coding-system-eol-type - coding-system-eol-type-mnemonic - coding-system-get - coding-system-plist - coding-system-post-read-conversion - coding-system-pre-write-conversion - coding-system-put - coding-system-translation-table-for-decode - coding-system-translation-table-for-encode - coding-system-type - describe-coding-system - prefer-coding-system - print-coding-system - print-coding-system-briefly - revert-buffer-with-coding-system - set-buffer-file-coding-system - set-clipboard-coding-system - set-coding-system-priority - set-default-coding-systems - set-file-name-coding-system - set-keyboard-coding-system - set-next-selection-coding-system - set-selection-coding-system - set-terminal-coding-system - universal-coding-system-argument)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-coding-system-mnemonic)) - -(elisp-scope-define-function-analyzer thing-at-point (&optional thing no-props) +(elisp-scope-define-function-analyzer + (thing-at-point + forward-thing beginning-of-thing end-of-thing bounds-of-thing-at-point) + (&optional thing no-props) (elisp-scope-1 thing '(symbol . thing)) (elisp-scope-1 no-props)) -(dolist (sym '( forward-thing - beginning-of-thing - end-of-thing - bounds-of-thing-at-point)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-thing-at-point)) - (elisp-scope-define-function-analyzer bounds-of-thing-at-mouse (&optional event thing) (elisp-scope-1 event) (elisp-scope-1 thing '(symbol . thing))) @@ -1847,14 +1847,14 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 error-symbol '(symbol . condition)) (elisp-scope-1 data)) -(elisp-scope--define-function-analyzer kill-emacs (&rest rest) non-local-exit +(elisp-scope--define-function-analyzer + (kill-emacs + abort-recursive-edit top-level exit-recursive-edit + tty-frame-restack error user-error + minibuffer-quit-recursive-edit exit-minibuffer) + (&rest rest) non-local-exit (elisp-scope-n rest)) -(dolist (sym '( abort-recursive-edit top-level exit-recursive-edit - tty-frame-restack error user-error - minibuffer-quit-recursive-edit exit-minibuffer)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-kill-emacs)) - (elisp-scope-define-function-analyzer run-hooks (&rest hooks) (dolist (hook hooks) (elisp-scope-1 hook '(symbol . free-variable)))) @@ -1876,35 +1876,35 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 face 'face) (elisp-scope-n rest)) -(elisp-scope-define-function-analyzer facep (&optional face &rest rest) +(elisp-scope-define-function-analyzer + (facep + check-face face-id face-differs-from-default-p + face-name face-all-attributes face-attribute + face-foreground face-background face-stipple + face-underline-p face-inverse-video-p face-bold-p + face-italic-p face-extend-p face-documentation + set-face-documentation set-face-attribute + set-face-font set-face-background set-face-foreground + set-face-stipple set-face-underline set-face-inverse-video + set-face-bold set-face-italic set-face-extend) + (&optional face &rest rest) (elisp-scope-1 face '(symbol . face)) (elisp-scope-n rest)) -(dolist (sym '( check-face face-id face-differs-from-default-p - face-name face-all-attributes face-attribute - face-foreground face-background face-stipple - face-underline-p face-inverse-video-p face-bold-p - face-italic-p face-extend-p face-documentation - set-face-documentation set-face-attribute - set-face-font set-face-background set-face-foreground - set-face-stipple set-face-underline set-face-inverse-video - set-face-bold set-face-italic set-face-extend)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-facep)) - -(elisp-scope-define-function-analyzer boundp (&optional var &rest rest) +(elisp-scope-define-function-analyzer + (boundp + set symbol-value define-abbrev-table + special-variable-p local-variable-p + local-variable-if-set-p add-variable-watcher + get-variable-watchers remove-variable-watcher + default-value set-default make-local-variable + buffer-local-value add-to-list add-to-history find-buffer + customize-set-variable set-variable + add-hook remove-hook run-hook-with-args run-hook-wrapped) + (&optional var &rest rest) (elisp-scope-1 var '(symbol . free-variable)) (elisp-scope-n rest)) -(dolist (sym '( set symbol-value define-abbrev-table - special-variable-p local-variable-p - local-variable-if-set-p add-variable-watcher - get-variable-watchers remove-variable-watcher - default-value set-default make-local-variable - buffer-local-value add-to-list add-to-history find-buffer - customize-set-variable set-variable - add-hook remove-hook run-hook-with-args run-hook-wrapped)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-boundp)) - (elisp-scope-define-function-analyzer defvaralias (new base &optional docstring) (elisp-scope-1 new '(symbol . defvar)) (elisp-scope-1 base '(symbol . free-variable)) @@ -1916,17 +1916,16 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 parent '(or (symbol . condition) (repeat . (symbol . condition))))) -(elisp-scope-define-function-analyzer featurep (feature &rest rest) +(elisp-scope-define-function-analyzer (featurep require) (feature &rest rest) (elisp-scope-1 feature '(symbol . feature)) (elisp-scope-n rest)) -(put 'require 'elisp-scope-analyzer #'elisp-scope--analyze-featurep) - (elisp-scope-define-function-analyzer provide (feature &rest rest) (elisp-scope-1 feature '(symbol . deffeature)) (elisp-scope-n rest)) -(elisp-scope-define-function-analyzer put-text-property (&optional beg end prop val obj) +(elisp-scope-define-function-analyzer (put-text-property remove-overlays) + (&optional beg end prop val obj) (elisp-scope-1 beg) (elisp-scope-1 end) (elisp-scope-1 prop) @@ -1937,8 +1936,6 @@ ARGS bound to the analyzed arguments." 'face))) (elisp-scope-1 obj)) -(put 'remove-overlays 'elisp-scope-analyzer #'elisp-scope--analyze-put-text-property) - (elisp-scope-define-function-analyzer propertize (string &rest props) (elisp-scope-1 string) (while props @@ -1998,13 +1995,13 @@ ARGS bound to the analyzed arguments." (setq args (cddr args))) (when args (elisp-scope-n args))) -(elisp-scope-define-function-analyzer provide-theme (name &rest rest) +(elisp-scope-define-function-analyzer + (provide-theme + enable-theme disable-theme load-theme custom-theme-p) + (name &rest rest) (elisp-scope-1 name '(symbol . theme)) (elisp-scope-n rest)) -(dolist (sym '(enable-theme disable-theme load-theme custom-theme-p)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-provide-theme)) - (elisp-scope-define-function-analyzer custom-theme-set-variables (theme &rest args) (elisp-scope-1 theme '(symbol . theme)) (dolist (arg args) @@ -2021,20 +2018,19 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 name '(symbol . deftheme)) (elisp-scope-n rest)) -(elisp-scope-define-function-analyzer eieio-oref (obj slot) +(elisp-scope-define-function-analyzer + (eieio-oref + slot-boundp slot-makeunbound slot-exists-p eieio-oref-default) + (obj slot) (elisp-scope-1 obj) (elisp-scope-1 slot '(symbol . slot))) -(dolist (fun '(slot-boundp slot-makeunbound slot-exists-p eieio-oref-default)) - (put fun 'elisp-scope-analyzer #'elisp-scope--analyze-eieio-oref)) - -(elisp-scope-define-function-analyzer eieio-oset (obj slot value) +(elisp-scope-define-function-analyzer (eieio-oset eieio-oset-default) + (obj slot value) (elisp-scope-1 obj) (elisp-scope-1 slot '(symbol . slot)) (elisp-scope-1 value)) -(put 'eieio-oset-default 'elisp-scope-analyzer #'elisp-scope--analyze-eieio-oset) - (elisp-scope-define-function-analyzer derived-mode-p (modes &rest rest) (elisp-scope-1 modes '(or (repeat . (symbol . major-mode)) (symbol . major-mode))) @@ -2332,25 +2328,19 @@ ARGS bound to the analyzed arguments." (elisp-scope-define-macro-analyzer define-minor-mode (&optional mode doc &rest body) (when mode (elisp-scope-define-minor-mode mode doc body))) -(elisp-scope-define-macro-analyzer setq-local (&rest args) +(elisp-scope-define-macro-analyzer (setq-local setq-default) (&rest args) (elisp-scope-setq args)) -(put 'setq-default 'elisp-scope-analyzer #'elisp-scope--analyze-setq-local) - -(elisp-scope-define-macro-analyzer cl-defun (name arglist &rest body) +(elisp-scope-define-macro-analyzer (cl-defun cl-defmacro) (name arglist &rest body) (elisp-scope-cl-defun name arglist body)) -(put 'cl-defmacro 'elisp-scope-analyzer #'elisp-scope--analyze-cl-defun) - -(elisp-scope-define-macro-analyzer defun (&optional name arglist &rest body) +(elisp-scope-define-macro-analyzer (defun ert-deftest) (&optional name arglist &rest body) (when name (elisp-scope-defun name arglist body))) (elisp-scope-define-macro-analyzer defmacro (&optional name arglist &rest body) (elisp-scope-report-s name 'defmacro) (elisp-scope-lambda arglist body)) -(put 'ert-deftest 'elisp-scope-analyzer #'elisp-scope--analyze-defun) - (elisp-scope-define-macro-analyzer elisp-scope-define-symbol-role (&optional name parents &rest props) (elisp-scope-report-s name 'symbol-role-definition) (dolist (parent parents) (elisp-scope-report-s parent 'symbol-role)) @@ -2386,34 +2376,25 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 place) (elisp-scope-n body elisp-scope-output-spec)) -(elisp-scope-define-macro-analyzer cl-pushnew (&rest args) +(elisp-scope-define-macro-analyzer (cl-pushnew incf decf) (&rest args) (mapc #'elisp-scope-1 args)) -(dolist (sym '(incf decf)) - (put sym 'elisp-scope-analyzer #'elisp-scope--analyze-cl-pushnew)) - (elisp-scope-define-macro-analyzer static-if (&optional test then &rest else) (elisp-scope-1 test) (elisp-scope-1 then elisp-scope-output-spec) (elisp-scope-n else elisp-scope-output-spec)) -(elisp-scope-define-macro-analyzer static-when (&optional test &rest body) +(elisp-scope-define-macro-analyzer (static-when static-unless) (&optional test &rest body) (elisp-scope-1 test) (elisp-scope-n body elisp-scope-output-spec)) -(put 'static-unless 'elisp-scope-analyzer #'elisp-scope--analyze-static-when) - -(elisp-scope-define-macro-analyzer eval-when-compile (&rest body) +(elisp-scope-define-macro-analyzer (eval-when-compile eval-and-compile) (&rest body) (elisp-scope-n body elisp-scope-output-spec)) -(put 'eval-and-compile 'elisp-scope-analyzer #'elisp-scope--analyze-eval-when-compile) - -(elisp-scope-define-macro-analyzer cl-callf (&rest args) +(elisp-scope-define-macro-analyzer (cl-callf cl-callf2) (&rest args) (elisp-scope-sharpquote (car args)) (elisp-scope-n (cdr args))) -(put 'cl-callf2 'elisp-scope-analyzer #'elisp-scope--analyze-cl-callf) - (elisp-scope-define-macro-analyzer seq-let (args sequence &rest body) (elisp-scope-1 sequence) (let ((l elisp-scope-local-bindings)) @@ -2696,23 +2677,18 @@ ARGS bound to the analyzed arguments." (elisp-scope-1 tag '(symbol . throw-tag)) (elisp-scope-n body elisp-scope-output-spec)) -(elisp-scope-define-special-form-analyzer progn (&rest body) +(elisp-scope-define-special-form-analyzer + (progn inline save-current-buffer save-excursion save-restriction) + (&rest body) (elisp-scope-n body elisp-scope-output-spec)) -(put 'inline 'elisp-scope-analyzer #'elisp-scope--analyze-progn) -(put 'save-current-buffer 'elisp-scope-analyzer #'elisp-scope--analyze-progn) -(put 'save-excursion 'elisp-scope-analyzer #'elisp-scope--analyze-progn) -(put 'save-restriction 'elisp-scope-analyzer #'elisp-scope--analyze-progn) - (elisp-scope-define-special-form-analyzer while (&rest rest) (mapc #'elisp-scope-1 rest)) -(elisp-scope-define-special-form-analyzer prog1 (&rest body) +(elisp-scope-define-special-form-analyzer (prog1 unwind-protect) (&rest body) (when (consp body) (elisp-scope-1 (pop body) elisp-scope-output-spec)) (elisp-scope-n body)) -(put 'unwind-protect 'elisp-scope-analyzer #'elisp-scope--analyze-prog1) - ;;; Specifications for some common variables: (put 'coding-system-for-read 'elisp-scope-variable-spec '(symbol . coding)) (put 'coding-system-for-write 'elisp-scope-variable-spec '(symbol . coding))