master 2a2675fd397 2/2: New macro 'elisp-scope-define-function-spec' (bug#81355)

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 2a2675fd3977b872d8c6b3efb8cc1d93c9e6b4fa
Author: Eshel Yaron <[email protected]>
Commit: Eshel Yaron <[email protected]>

    New macro 'elisp-scope-define-function-spec' (bug#81355)
    
    * lisp/emacs-lisp/elisp-scope.el
    (elisp-scope--analyze-function-args): New helper function.
    (elisp-scope-define-function-spec): New macro.
    Convert simple function analyzers to use it.
    
    * etc/NEWS: Announce it.
---
 etc/NEWS                       |   7 +
 lisp/emacs-lisp/elisp-scope.el | 486 ++++++++++++++++++-----------------------
 2 files changed, 220 insertions(+), 273 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 7733c5e8eba..ce53ea51f44 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -246,6 +246,13 @@ method call returns with a D-Bus error; the error is passed as argument.
 which the defined analyzer should apply (or a single symbol, as before).
 This makes it easy to specify the same analyzer for multiple functions.
 
+*** New macro 'elisp-scope-define-function-spec'.
+This macro in a declarative alternative to
+'elisp-scope-define-function-analyzer'.  It lets you tell 'elisp-scope'
+how to analyze the arguments of a function by declaring the
+specification of each argument, rather than implementing an analyzer
+function as you would with 'elisp-scope-define-function-analyzer'.
+
 
 * 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 4d3c39850b4..a81a7dfacaf 100644
--- a/lisp/emacs-lisp/elisp-scope.el
+++ b/lisp/emacs-lisp/elisp-scope.el
@@ -108,7 +108,7 @@
 ;;
 ;; When analyzer functions invoke `elisp-scope-1/n' to analyze some
 ;; sub-forms, they specify the OUTSPEC argument to convey information
-;; but the expected value of the evaluated sub-form(s), so
+;; about the expected value of the evaluated sub-form(s), so
 ;; `elisp-scope-1/n' will know what to do with a sub-form that is just
 ;; (quoted) data.  For example, the analyzer function for
 ;; `face-attribute' calls `elisp-scope-1' to analyze its first argument
@@ -130,6 +130,10 @@
 ;;
 ;; See also the docstring of `elisp-scope-1' for details about the
 ;; format of the `outspec' argument.
+;;
+;; To define custom analyzers, you can use the macros
+;; `elisp-scope-define-function-spec' and
+;; `elisp-scope-define-macro-analyzer', which see.
 
 ;;; Code:
 
@@ -1608,6 +1612,31 @@ ARGS bound to the analyzed arguments."
   (declare (indent defun))
   `(elisp-scope--define-function-analyzer ,fsym ,args function ,@body))
 
+(defun elisp-scope--analyze-function-args (args specs &optional rest-spec)
+  (while (and args specs) (elisp-scope-1 (pop args) (pop specs)))
+  (dolist (arg args) (elisp-scope-1 arg rest-spec)))
+
+(defmacro elisp-scope-define-function-spec (fsym arg-specs &optional rest-spec)
+  "Specify how to analyze arguments of FSYM.
+FSYM is a function symbol or a list of function symbols, and ARG-SPECS
+is a list of output specs corresponding to the arguments of FSYM.
+Optional argument REST-SPEC is the output spec of any remaining
+arguments after those specified by ARG-SPECS.
+
+For example, the following form says that the functions `foo' and `bar'
+take a face name as their second argument, and that all arguments after
+the second are macro names.  The first argument remains unspecified.
+
+  (elisp-scope-define-function-spec (foo bar)
+   (nil (symbol . face))
+   (symbol . macro))"
+  (declare (indent 1))
+  `(elisp-scope-define-function-analyzer ,fsym (&rest args)
+     (elisp-scope--analyze-function-args args ',arg-specs ',rest-spec)))
+
+(elisp-scope-define-function-spec elisp-scope--analyze-function-args
+  (nil (repeat . spec) spec))
+
 (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.
@@ -1661,14 +1690,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 apply) (&optional f &rest args)
-  (elisp-scope-1 f '(symbol . function))
-  (elisp-scope-n args))
+(elisp-scope-define-function-spec (funcall apply) ((symbol . function)))
 
-(elisp-scope-define-function-analyzer defalias (&optional sym def docstring)
-  (elisp-scope-1 sym '(symbol . defun))
-  (elisp-scope-1 def '(symbol . defun))
-  (elisp-scope-1 docstring))
+(elisp-scope-define-function-spec defalias
+  ((symbol . defun) (symbol . defun)))
 
 (elisp-scope-define-function-analyzer oclosure--define
   (&optional name docstring parent-names slots &rest props)
@@ -1688,108 +1713,81 @@ ARGS bound to the analyzed arguments."
     (setq props (cddr props)))
   (when props (elisp-scope-n props)))
 
-(elisp-scope-define-function-analyzer define-charset
-  (&optional name docstring &rest props)
-  (elisp-scope-1 name '(symbol . defcharset))
-  (elisp-scope-1 docstring)
-  (elisp-scope-n props))
-
-(elisp-scope-define-function-analyzer define-charset-alias
-  (&optional alias charset)
-  (elisp-scope-1 alias '(symbol . defcharset))
-  (elisp-scope-1 charset '(symbol . charset)))
-
-(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))
-
-(elisp-scope-define-function-analyzer define-coding-system
-  (&optional name &rest rest)
-  (elisp-scope-1 name '(symbol . defcoding))
-  (mapc #'elisp-scope-1 rest))
-
-(elisp-scope-define-function-analyzer define-coding-system-alias
-  (&optional alias coding-system)
-  (elisp-scope-1 alias '(symbol . defcoding))
-  (elisp-scope-1 coding-system '(symbol . coding)))
-
-(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))
-
-(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))
-
-(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))
-
-(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))
-
-(elisp-scope-define-function-analyzer bounds-of-thing-at-mouse (&optional event thing)
-  (elisp-scope-1 event)
-  (elisp-scope-1 thing '(symbol . thing)))
-
-(elisp-scope-define-function-analyzer thing-at-mouse (&optional event thing no-props)
-  (elisp-scope-1 event)
-  (elisp-scope-1 thing '(symbol . thing))
-  (elisp-scope-1 no-props))
+(elisp-scope-define-function-spec define-charset ((symbol . defcharset)))
+
+(elisp-scope-define-function-spec define-charset-alias
+  ((symbol . defcharset) (symbol . charset)))
+
+(elisp-scope-define-function-spec
+    (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)
+  ((symbol . charset)))
+
+(elisp-scope-define-function-spec define-coding-system ((symbol . defcoding)))
+
+(elisp-scope-define-function-spec define-coding-system-alias
+  ((symbol . defcoding) (symbol . coding)))
+
+(elisp-scope-define-function-spec
+    (decode-coding-region encode-coding-region)
+  (nil nil (symbol . coding)))
+
+(elisp-scope-define-function-spec
+    (decode-coding-string encode-coding-char encode-coding-string)
+  (nil (symbol . coding)))
+
+(elisp-scope-define-function-spec
+    (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)
+  ((symbol . coding)))
+
+(elisp-scope-define-function-spec
+    (thing-at-point
+     forward-thing beginning-of-thing end-of-thing bounds-of-thing-at-point)
+  ((symbol . thing)))
+
+(elisp-scope-define-function-spec bounds-of-thing-at-mouse
+  (nil (symbol . thing)))
+
+(elisp-scope-define-function-spec thing-at-mouse
+  (nil (symbol . thing)))
 
 (elisp-scope-define-function-analyzer custom-declare-variable (sym default doc &rest args)
   (elisp-scope-1 sym '(symbol . defvar))
@@ -1830,14 +1828,10 @@ ARGS bound to the analyzed arguments."
     (setq args (cddr args)))
   (when args (elisp-scope-n args)))
 
-(elisp-scope-define-function-analyzer cl-typep (val type)
-  (elisp-scope-1 val)
-  (elisp-scope-1 type 'cl-type))
+(elisp-scope-define-function-spec cl-typep (nil cl-type))
 
-(elisp-scope-define-function-analyzer pulse-momentary-highlight-region (start end &optional face)
-  (elisp-scope-1 start)
-  (elisp-scope-1 end)
-  (elisp-scope-1 face '(symbol . face)))
+(elisp-scope-define-function-spec pulse-momentary-highlight-region
+  (nil nil (symbol . face)))
 
 (elisp-scope--define-function-analyzer throw (&optional tag val) non-local-exit
   (elisp-scope-1 tag '(symbol . throw-tag))
@@ -1855,11 +1849,9 @@ ARGS bound to the analyzed arguments."
   (&rest rest) non-local-exit
   (elisp-scope-n rest))
 
-(elisp-scope-define-function-analyzer run-hooks (&rest hooks)
-  (dolist (hook hooks) (elisp-scope-1 hook '(symbol . free-variable))))
+(elisp-scope-define-function-spec run-hooks () (symbol . free-variable))
 
-(elisp-scope-define-function-analyzer fboundp (&optional symbol)
-  (elisp-scope-1 symbol '(symbol . function)))
+(elisp-scope-define-function-spec fboundp ((symbol . function)))
 
 (elisp-scope-define-function-analyzer overlay-put (&optional ov prop val)
   (elisp-scope-1 ov)
@@ -1870,59 +1862,44 @@ ARGS bound to the analyzed arguments."
      (when (memq (elisp-scope--sym-bare q) '(face mouse-face))
        'face))))
 
-(elisp-scope-define-function-analyzer add-face-text-property (&optional start end face &rest rest)
-  (elisp-scope-1 start)
-  (elisp-scope-1 end)
-  (elisp-scope-1 face 'face)
-  (elisp-scope-n 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))
-
-(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))
-
-(elisp-scope-define-function-analyzer defvaralias (new base &optional docstring)
-  (elisp-scope-1 new '(symbol . defvar))
-  (elisp-scope-1 base '(symbol . free-variable))
-  (elisp-scope-1 docstring))
-
-(elisp-scope-define-function-analyzer define-error (&optional name message parent)
-  (elisp-scope-1 name '(symbol . defcondition))
-  (elisp-scope-1 message)
-  (elisp-scope-1 parent '(or (symbol . condition)
-                             (repeat . (symbol . condition)))))
-
-(elisp-scope-define-function-analyzer (featurep require) (feature &rest rest)
-  (elisp-scope-1 feature '(symbol . feature))
-  (elisp-scope-n rest))
-
-(elisp-scope-define-function-analyzer provide (feature &rest rest)
-  (elisp-scope-1 feature '(symbol . deffeature))
-  (elisp-scope-n rest))
+(elisp-scope-define-function-spec add-face-text-property (nil nil face))
+
+(elisp-scope-define-function-spec
+    (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)
+  ((symbol . face)))
+
+(elisp-scope-define-function-spec
+    (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)
+  ((symbol . free-variable)))
+
+(elisp-scope-define-function-spec defvaralias
+  ((symbol . defvar) (symbol . free-variable)))
+
+(elisp-scope-define-function-spec define-error
+  ((symbol . defcondition) nil
+   (or (symbol . condition)
+       (repeat . (symbol . condition)))))
+
+(elisp-scope-define-function-spec (featurep require) ((symbol . feature)))
+
+(elisp-scope-define-function-spec provide ((symbol . deffeature)))
 
 (elisp-scope-define-function-analyzer (put-text-property remove-overlays)
   (&optional beg end prop val obj)
@@ -1948,37 +1925,26 @@ ARGS bound to the analyzed arguments."
     (setq props (cddr props)))
   (when props (elisp-scope-n props)))
 
-(elisp-scope-define-function-analyzer eieio-defclass-internal
-  (&optional name superclasses slots options)
-  (elisp-scope-1 name '(symbol . deftype))
-  (elisp-scope-1 superclasses '(repeat . (symbol . type)))
-  (elisp-scope-1 slots
-                 '(repeat
-                   cons
-                   (symbol . slot)
-                   plist
-                   (:initform   . code)
-                   (:initarg    . (symbol . constant))
-                   (:accessor   . (symbol . defun))
-                   (:allocation . code)
-                   (:writer     . (symbol . function))
-                   (:reader     . (symbol . function))
-                   (:type       . cl-type)
-                   ;; TODO: add (:custom  . custom-type)
-                   ))
-  (elisp-scope-1 options))
-
-(elisp-scope-define-function-analyzer cl-struct-define
-  (&optional name doc parent type named slots children tag print)
-  (elisp-scope-1 name '(symbol . deftype))
-  (elisp-scope-1 doc)
-  (elisp-scope-1 parent '(symbol . type))
-  (elisp-scope-1 type)
-  (elisp-scope-1 named)
-  (elisp-scope-1 slots)           ;TODO: Specify type of `slots'.
-  (elisp-scope-1 children)
-  (elisp-scope-1 tag)
-  (elisp-scope-1 print))
+(elisp-scope-define-function-spec eieio-defclass-internal
+  ((symbol . deftype)
+   (repeat . (symbol . type))
+   (repeat
+    cons
+    (symbol . slot)
+    plist
+    (:initform   . code)
+    (:initarg    . (symbol . constant))
+    (:accessor   . (symbol . defun))
+    (:allocation . code)
+    (:writer     . (symbol . function))
+    (:reader     . (symbol . function))
+    (:type       . cl-type)
+    ;; TODO: add (:custom  . custom-type)
+    )))
+
+;; TODO: Specify spec of the `slots' argument.
+(elisp-scope-define-function-spec cl-struct-define
+  ((symbol . deftype) nil (symbol . type)))
 
 (elisp-scope-define-function-analyzer define-widget (name class doc &rest args)
   (elisp-scope-1 name '(symbol . widget-type-definition))
@@ -1995,62 +1961,43 @@ ARGS bound to the analyzed arguments."
     (setq args (cddr args)))
   (when args (elisp-scope-n args)))
 
-(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))
+(elisp-scope-define-function-spec
+    (provide-theme enable-theme disable-theme load-theme custom-theme-p)
+  ((symbol . theme)))
 
-(elisp-scope-define-function-analyzer custom-theme-set-variables (theme &rest args)
-  (elisp-scope-1 theme '(symbol . theme))
-  (dolist (arg args)
-    (elisp-scope-1
-     arg
-     '(cons (symbol . free-variable) .
-            (cons code .
-                  (or (cons t .
-                            (cons (repeat . (symbol . feature)) .
-                                  t))
-                      t))))))
-
-(elisp-scope-define-function-analyzer custom-declare-theme (name &rest rest)
-  (elisp-scope-1 name '(symbol . deftheme))
-  (elisp-scope-n rest))
+(elisp-scope-define-function-spec custom-theme-set-variables
+  ((symbol . theme))
+  (cons (symbol . free-variable) .
+        (cons code .
+              (or (cons t .
+                        (cons (repeat . (symbol . feature)) .
+                              t))
+                  t))))
 
-(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)))
-
-(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))
-
-(elisp-scope-define-function-analyzer derived-mode-p (modes &rest rest)
-  (elisp-scope-1 modes '(or (repeat . (symbol . major-mode))
-                            (symbol . major-mode)))
-  (dolist (mode rest) (elisp-scope-1 mode '(symbol . major-mode))))
-
-(elisp-scope-define-function-analyzer derived-mode-set-parent (&optional mode parent)
-  (elisp-scope-1 mode '(symbol . major-mode))
-  (elisp-scope-1 parent '(symbol . major-mode)))
-
-(elisp-scope-define-function-analyzer elisp-scope-report (role &rest args)
-  (elisp-scope-1 role '(symbol . symbol-role))
-  (mapc #'elisp-scope-1 args))
+(elisp-scope-define-function-spec custom-declare-theme ((symbol . deftheme)))
 
-(elisp-scope-define-function-analyzer elisp-scope-report-s (&optional sym role)
-  (elisp-scope-1 sym)
-  (elisp-scope-1 role '(symbol . symbol-role)))
+(elisp-scope-define-function-spec
+    (eieio-oref slot-boundp slot-makeunbound slot-exists-p eieio-oref-default)
+  (nil (symbol . slot)))
 
-(elisp-scope-define-function-analyzer elisp-scope-1 (&optional form outspec)
-  (elisp-scope-1 form)
-  (elisp-scope-1 outspec 'spec))
+(elisp-scope-define-function-spec (eieio-oset eieio-oset-default)
+  (nil (symbol . slot)))
+
+(elisp-scope-define-function-spec derived-mode-p
+  ((or (repeat . (symbol . major-mode))
+       (symbol . major-mode)))
+  (symbol . major-mode))
+
+(elisp-scope-define-function-spec derived-mode-set-parent
+  ((symbol . major-mode) (symbol . major-mode)))
+
+(elisp-scope-define-function-spec elisp-scope-report
+  ((symbol . symbol-role)))
+
+(elisp-scope-define-function-spec elisp-scope-report-s
+  (nil (symbol . symbol-role)))
+
+(elisp-scope-define-function-spec elisp-scope-1 (nil spec))
 
 (elisp-scope-define-function-analyzer icons--register (&optional name parent spec doc kws)
   (elisp-scope-1 name '(symbol . deficon))
@@ -2072,21 +2019,15 @@ ARGS bound to the analyzed arguments."
   (elisp-scope-1 var '(symbol . free-variable))
   (elisp-scope-1 val elisp-scope-output-spec))
 
-(elisp-scope-define-function-analyzer autoload (&optional func file doc int type)
-  (elisp-scope-1 func '(symbol . function))
-  (elisp-scope-1 file)
-  (elisp-scope-1 doc)
-  (elisp-scope-1 int '(repeat . (symbol . major-mode)))
-  (elisp-scope-1 type))
+(elisp-scope-define-function-spec autoload
+  ((symbol . function) nil nil (repeat . (symbol . major-mode))))
 
-(elisp-scope-define-function-analyzer define-completion-category (&optional name parents &rest rest)
-  (elisp-scope-1 name '(symbol . completion-category-definition))
-  (elisp-scope-1 parents '(repeat . (symbol . completion-category)))
-  (elisp-scope-n rest))
+(elisp-scope-define-function-spec define-completion-category
+  ((symbol . completion-category-definition)
+   (repeat . (symbol . completion-category))))
 
-(elisp-scope-define-function-analyzer completion-table-with-category (&optional category table)
-  (elisp-scope-1 category '(symbol . completion-category))
-  (elisp-scope-1 table))
+(elisp-scope-define-function-spec completion-table-with-category
+  ((symbol . completion-category)))
 
 (defun elisp-scope--easy-menu-do-define-menu (menu)
   (let ((items (cdr menu)))
@@ -2145,9 +2086,8 @@ ARGS bound to the analyzed arguments."
     (elisp-scope-1 def))
   (elisp-scope-1 remove))
 
-(elisp-scope-define-function-analyzer eval-after-load (&optional file form)
-  (elisp-scope-1 file '(symbol . feature))
-  (elisp-scope-1 form 'code))
+(elisp-scope-define-function-spec eval-after-load
+  ((symbol . feature) code))
 
 ;; We use a bespoke analyzer for `if-let*' instead of letting
 ;; `elisp-scope-1' expand it because `if-let*' expands to a form that