]> git.eshelyaron.com Git - emacs.git/commitdiff
scope.el: Optimize and improve documentation.
authorEshel Yaron <me@eshelyaron.com>
Sun, 11 Aug 2024 15:03:43 +0000 (17:03 +0200)
committerEshel Yaron <me@eshelyaron.com>
Sun, 11 Aug 2024 15:03:59 +0000 (17:03 +0200)
lisp/emacs-lisp/scope.el

index 680963faa375ee75dcc2d6eaf7f1fa7bba53dd40..af46836920575e19bedcf718ecc43f3b3cf373f9 100644 (file)
 
 (eval-when-compile (require 'cl-lib))
 
+(defsubst scope-local-get (sym local)
+  "Get binding position of symbol SYM in local context LOCAL."
+  (alist-get sym local))
+
+(defsubst scope-local-new (sym pos &optional local)
+  "Return new local context with SYM bound at POS.
+
+Optional argument LOCAL is a local context to extend."
+  (cons (cons sym pos) local))
+
 (defun scope-s (local sym)
   (let* ((beg (symbol-with-pos-pos sym))
          (bare (bare-symbol sym))
          (len (length (symbol-name bare))))
     (unless (or (booleanp bare) (keywordp bare))
-      (list (list beg len (alist-get bare local))))))
+      (list (list beg len (scope-local-get bare local))))))
 
 (defun scope-let (local bindings body)
   (nconc
                       (len (length (symbol-name bare))))
                  (list (list beg len beg)))))
            bindings)
-   (scope-n
-    (append (mapcar
-             (lambda (binding)
-               (let ((sym (if (consp binding) (car binding) binding)))
-                 (cons (bare-symbol sym) (symbol-with-pos-pos sym))))
-             bindings)
-            local)
-    body)))
+   (let ((l local))
+     (dolist (binding bindings)
+       (let ((sym (if (consp binding) (car binding) binding)))
+         (setq l (scope-local-new (bare-symbol sym) (symbol-with-pos-pos sym) l))))
+     (scope-n l body))))
 
 (defun scope-let* (local bindings body)
   (if bindings
@@ -75,9 +82,8 @@
                   (bare (bare-symbol sym)))
              (list (list beg (length (symbol-name bare)) beg))))
          (scope-let*
-          (cons (let ((sym (if (consp binding) (car binding) binding)))
-                  (cons (bare-symbol sym) (symbol-with-pos-pos sym)))
-                local)
+          (let ((sym (if (consp binding) (car binding) binding)))
+            (scope-local-new (bare-symbol sym) (symbol-with-pos-pos sym) local))
           (cdr bindings)
           body)))
     (scope-n local body)))
                   (cons
                    (list beg (length (symbol-name bare)) beg)
                    (nconc (scope-1 local (cadr binding))
-                          (scope-if-let* (cons (cons bare beg) local)
+                          (scope-if-let* (scope-local-new bare beg local)
                                          (cdr bindings) body))))
               ;; BINDING is (VALUEFORM).
               (nconc (scope-1 local (car binding))
                  (bare (bare-symbol sym)))
             (cons
              (list beg (length (symbol-name bare)) beg)
-             (scope-if-let* (cons (cons bare beg) local)
+             (scope-if-let* (scope-local-new bare beg local)
                             (cdr bindings) body)))))
     (scope-n local body)))
 
                args)
      (scope-1 local doc-form)
      (scope-1 local int-spec)
-     (scope-n (append
-               (seq-keep (lambda (arg)
-                           (and (symbol-with-pos-p arg)
-                                (not (memq (bare-symbol arg) '(&optional &rest)))
-                                (cons (bare-symbol arg) (symbol-with-pos-pos arg))))
-                         args)
-               local)
-              body))))
+     (let ((l local))
+       (dolist (arg args)
+         (when (and (symbol-with-pos-p arg) (not (memq (bare-symbol arg) '(&optional &rest))))
+           (setq l (scope-local-new (bare-symbol arg) (symbol-with-pos-pos arg) l))))
+       (scope-n l body)))))
 
 (defun scope-defmethod-1 (local0 local name args body)
   (if args
                            (eq 'eql (bare-symbol head))
                            (not (or (symbolp form) (symbol-with-pos-p form)))
                            (scope-1 local0 form)))))
-                  (scope-defmethod-1 local0 (cons (cons bare beg) local) name (cdr args) body)))))
+                  (scope-defmethod-1 local0 (scope-local-new bare beg local) name (cdr args) body)))))
              ((consp var)
               ;; VAR is (&key (VAR INIT SVAR)) or (&key VAR).
               (let ((var (cadr var)))
                                (eq 'eql (bare-symbol head))
                                (not (or (symbolp form) (symbol-with-pos-p form)))
                                (scope-1 local0 form)))))
-                      (scope-defmethod-1 local0 (cons (cons bare beg) local) name (cdr args) body)))))
+                      (scope-defmethod-1 local0 (scope-local-new bare beg local) name (cdr args) body)))))
                  ((consp var)
                   (let* ((init (cadr var))
                          (svar (caddr var))
                         (let ((sbeg (symbol-with-pos-pos svar)))
                           (list (list sbeg (length (symbol-name (bare-symbol svar)))
                                       sbeg))))
-                      (scope-defmethod-1 local0 (cons (cons bare beg)
-                                                      (append
-                                                       (when svar
-                                                         (list (cons (bare-symbol svar)
-                                                                     (symbol-with-pos-pos svar))))
-                                                       local))
-                                         name (cdr args) body)))))))))))
+                      (scope-defmethod-1
+                       local0
+                       (scope-local-new bare beg
+                                        (if svar
+                                            (scope-local-new (bare-symbol svar)
+                                                             (symbol-with-pos-pos svar)
+                                                             local)
+                                          local))
+                       name (cdr args) body)))))))))))
          ((symbol-with-pos-p arg)
           (cond
            ((memq (bare-symbol arg) '(&optional &rest &body _))
                    (len (length (symbol-name bare))))
               (cons
                (list beg len beg)
-               (scope-defmethod-1 local0 (cons (cons bare beg) local)
+               (scope-defmethod-1 local0 (scope-local-new bare beg local)
                                   name (cdr args) body))))))))
     (scope-n local body)))
 
        (list (list beg (length (symbol-name bare)) beg))))
    (scope-1 local bodyform)
    (mapcan
-    (let ((l (if var (cons (cons (bare-symbol var) (symbol-with-pos-pos var)) local) local)))
+    (let ((l (if var (scope-local-new (bare-symbol var) (symbol-with-pos-pos var) local) local)))
       (lambda (handler) (scope-n l (cdr handler))))
     handlers)))
 
    (nconc
     (scope-1 local lst)
     (scope-1 local res)
-    (let ((l (cons (cons (bare-symbol var) (symbol-with-pos-pos var)) local)))
+    (let ((l (scope-local-new (bare-symbol var) (symbol-with-pos-pos var) local)))
       (scope-n l body)))))
 
 (defun scope-pcase-qpat (local qpat)
     (let ((bare (bare-symbol pattern)))
       (if (eq bare '_) (list local)
         (let* ((beg (symbol-with-pos-pos pattern)))
-          (cons (cons (cons bare beg) local)
+          (cons (scope-local-new bare beg local)
                 (list (list beg (length (symbol-name bare)) beg)))))))
    ((consp pattern)
     (cond
                     fun))
    (scope-n local body)))
 
-(defun scope-backquote (depth local elements)
+(defun scope--backquote (local elements depth)
   (cond
    ((zerop depth) (scope-n local elements))
    ((consp elements)
     (cond
      ((memq (car elements) '(\, \,@))
-      (scope-backquote (1- depth) local (cdr elements)))
+      (scope--backquote local (cdr elements) (1- depth)))
      ((eq (car elements) '\`)
-      (scope-backquote (1+ depth) local (cdr elements)))
-     (t (nconc (scope-backquote depth local (car elements))
-               (scope-backquote depth local (cdr elements))))))
+      (scope--backquote local (cdr elements) (1+ depth)))
+     (t (nconc (scope--backquote local (car elements) depth)
+               (scope--backquote local (cdr elements) depth)))))
    ((vectorp elements)
-    (scope-backquote depth local (append elements nil)))))
+    (scope--backquote local (append elements nil) depth))))
+
+(defun scope-backquote (local elements &optional depth)
+  (scope--backquote local elements (or depth 1)))
 
 (defvar scope-flet-list nil)
 
             (scope-1 local (car exps)))
           (let ((scope-flet-list (cons (bare-symbol func) scope-flet-list)))
             (scope-flet
-             (cons (cons (bare-symbol func) (symbol-with-pos-pos func))
-                   local)
+             (scope-local-new (bare-symbol func) (symbol-with-pos-pos func)
+                              local)
              (cdr defs) body)))))
     (scope-n local body)))
 
          (list (symbol-with-pos-pos func) (length (symbol-name (bare-symbol func)))
                (symbol-with-pos-pos func))
          (let ((scope-flet-list (cons (bare-symbol func) scope-flet-list))
-               (l (cons (cons (bare-symbol func) (symbol-with-pos-pos func)) local)))
+               (l (scope-local-new (bare-symbol func) (symbol-with-pos-pos func) local)))
            (nconc
             (scope-defun l nil args body)
             (scope-flet l (cdr defs) forms)))))
     (scope-n local forms)))
 
-(defun scope-function (local arg)
+(defun scope-sharpquote (local arg)
   (and (or (and (symbol-with-pos-p arg) (memq (bare-symbol arg) scope-flet-list))
            (consp arg))
        (scope-1 local arg)))
                  (len (length (symbol-name bare))))
             (cons
              (list beg len beg)
-             (scope-cl-defun-aux (cons (cons bare beg) local)
+             (scope-cl-defun-aux (scope-local-new bare beg local)
                                  name (cdr args) body))))
          ((consp arg)
           (let* ((var (car arg))
              (list beg len beg)
              (nconc
               (scope-1 local init)
-              (scope-cl-defun-aux (cons (cons bare beg) local)
+              (scope-cl-defun-aux (scope-local-new bare beg local)
                                   name (cdr args) body)))))))
     (scope-n local body)))
 
                      (len (length (symbol-name bare))))
                 (cons
                  (list beg len beg)
-                 (scope-cl-defun-key (cons (cons bare beg) local)
+                 (scope-cl-defun-key (scope-local-new bare beg local)
                                      name (cdr args) body))))))
          ((consp arg)
           (let* ((var (car arg))
                 (let ((sbeg (symbol-with-pos-pos svar)))
                   (list (list sbeg (length (symbol-name (bare-symbol svar)))
                               sbeg))))
-              (scope-cl-defun-key (cons (cons bare beg)
-                                        (append
-                                         (when svar
-                                           (list (cons (bare-symbol svar)
-                                                       (symbol-with-pos-pos svar))))
-                                         local))
-                                  name (cdr args) body)))))))
+              (scope-cl-defun-key
+               (scope-local-new bare beg
+                                (if svar
+                                    (scope-local-new (bare-symbol svar)
+                                                     (symbol-with-pos-pos svar)
+                                                     local)
+                                  local))
+               name (cdr args) body)))))))
     (scope-n local body)))
 
 (defun scope-cl-defun-rest (local name args body)
          (beg (symbol-with-pos-pos var))
          (bare (bare-symbol var))
          (len (length (symbol-name bare)))
-         (l (cons (cons bare beg) local)))
+         (l (scope-local-new bare beg local)))
     (cons
      (list beg len beg)
      (if (cdr args)
                      (len (length (symbol-name bare))))
                 (cons
                  (list beg len beg)
-                 (scope-cl-defun-optional (cons (cons bare beg) local)
+                 (scope-cl-defun-optional (scope-local-new bare beg local)
                                           name (cdr args) body))))))
          ((consp arg)
           (let* ((var (car arg))
                 (let ((sbeg (symbol-with-pos-pos svar)))
                   (list (list sbeg (length (symbol-name (bare-symbol svar)))
                               sbeg))))
-              (scope-cl-defun-optional (cons (cons bare beg)
-                                             (append
-                                              (when svar
-                                                (list (cons (bare-symbol svar)
-                                                            (symbol-with-pos-pos svar))))
-                                              local))
-                                       name (cdr args) body)))))))
+              (scope-cl-defun-optional
+               (scope-local-new bare beg
+                                (if svar
+                                    (scope-local-new (bare-symbol svar)
+                                                     (symbol-with-pos-pos svar)
+                                                     local)
+                                  local))
+               name (cdr args) body)))))))
     (scope-n local body)))
 
 (defun scope-cl-defun-1 (local name args body)
                    (len (length (symbol-name bare))))
               (cons
                (list beg len beg)
-               (scope-cl-defun-1 (cons (cons (bare-symbol arg)
-                                             (symbol-with-pos-pos arg))
+               (scope-cl-defun-1 (scope-local-new (bare-symbol arg)
+                                                  (symbol-with-pos-pos arg)
                                        local)
                                  name (cdr args) body))))))
     (scope-n local body)))
                     (len (length (symbol-name bare))))
                (list beg len beg)))
            args)
-   (scope-n (append
-             (mapcar (lambda (arg)
-                       (cons (bare-symbol arg) (symbol-with-pos-pos arg)))
-                     args)
-             local)
-            body)))
+   (let ((l local))
+     (dolist (arg args)
+       (when (and (symbol-with-pos-p arg) (not (memq (bare-symbol arg) '(&rest))))
+         (setq l (scope-local-new (bare-symbol arg) (symbol-with-pos-pos arg) l))))
+     (scope-n l body))))
 
 (defun scope-pcase-lambda (local lambda-list body)
   (if lambda-list
              (sym (car binder))
              (bare (bare-symbol sym))
              (beg (symbol-with-pos-pos sym))
-             (l (cons (cons bare beg) local))
+             (l (scope-local-new bare beg local))
              (form (cadr binder)))
         (cons
          (list beg (length (symbol-name bare)) beg)
                 (scope-letrec l (cdr binders) body))))
     (scope-n local body)))
 
-(defun scope-f (local f)
-  "Return function that scope-analyzes arguments of F in context LOCAL."
-  (cond
-   ((symbol-with-pos-p f)
-    (let ((bare (bare-symbol f)))
-      (cond
-       ((functionp bare) (apply-partially #'scope-n local))
-       ((macrop bare)
-        (cond
-         ((eq (get bare 'edebug-form-spec) t)
-          (apply-partially #'scope-n local))
-         ((memq bare '( setf with-memoization cl-assert cl-incf cl-decf
-                        eval-when-compile eval-and-compile with-eval-after-load
-                        ;; We could recognize contant symbols bindings
-                        ;; in `cl-progv', but it is not really worth the
-                        ;; trouble since this macro is specifically
-                        ;; intended for computing bindings at run time.
-                        cl-progv))
-          (apply-partially #'scope-n local))
-         ((memq bare '( defun defmacro defsubst define-inline))
-          (lambda (forms) (scope-defun local (car forms) (cadr forms) (cddr forms))))
-         ((memq bare '( cl-defgeneric))
-          (lambda (forms) (scope-defgeneric local (car forms) (cadr forms) (cddr forms))))
-         ((memq bare '(cl-case))
-          (lambda (forms) (scope-case local (car forms) (cdr forms))))
-         ((memq bare '( cl-defun))
-          (lambda (forms) (scope-cl-defun local (car forms) (cadr forms) (cddr forms))))
-         ((memq bare '( cl-defmethod))
-          (lambda (forms) (scope-defmethod local (car forms) (cdr forms))))
-         ((memq bare '(lambda))
-          (lambda (forms) (scope-defun local nil (car forms) (cdr forms))))
-         ((memq bare '(declare-function))
-          (lambda (forms) (scope-declare-function local (car forms) (cadr forms)
-                                                  (caddr forms) (cadddr forms))))
-         ((memq bare '(if-let when-let and-let))
-          (lambda (forms) (scope-if-let local (car forms) (cdr forms))))
-         ((memq bare '(if-let* when-let* and-let* while-let))
-          (lambda (forms) (scope-if-let* local (car forms) (cdr forms))))
-         ((memq bare '( defvar-local defcustom))
-          (lambda (forms) (scope-defvar local (car forms) (cadr forms))))
-         ((memq bare '(dolist dotimes))
-          (lambda (forms) (scope-dotimes local (caar forms) (cadar forms) (caddar forms) (cdr forms))))
-         ((memq bare '(pcase pcase-exhaustive))
-          (lambda (forms) (scope-pcase local (car forms) (cdr forms))))
-         ((memq bare '(pcase-lambda))
-          (lambda (forms) (scope-pcase-lambda local (car forms) (cdr forms))))
-         ((memq bare '(pcase-dolist))
-          (lambda (forms) (scope-pcase-dolist local (caar forms) (cadar forms) (cdr forms))))
-         ((memq bare '(pcase-let))
-          (lambda (forms) (scope-pcase-let local (car forms) (cdr forms))))
-         ((memq bare '(pcase-let*))
-          (lambda (forms) (scope-pcase-let* local (car forms) (cdr forms))))
-         ((memq bare '(setq-local setq-default))
-          (apply-partially #'scope-setq local))
-         ((memq bare '(push))
-          (lambda (forms) (scope-push local (car forms) (cadr forms))))
-         ((memq bare '(pop oref))
-          (lambda (forms) (scope-1 local (car forms))))
-         ((memq bare '(letrec))
-          (lambda (forms) (scope-letrec local (car forms) (cdr forms))))
-         ((memq bare '(cl-flet))
-          (lambda (forms) (scope-flet local (car forms) (cdr forms))))
-         ((memq bare '(cl-labels))
-          (lambda (forms) (scope-labels local (car forms) (cdr forms))))
-         ((memq bare '(minibuffer-with-setup-hook))
-          (lambda (forms) (scope-minibuffer-with-setup-hook local (car forms) (cdr forms))))
-         ((memq bare '(condition-case-unless-debug))
-          (lambda (forms) (scope-condition-case local (car forms) (cadr forms) (cddr forms))))
-         ((memq bare '(seq-let))
-          (lambda (forms) (scope-seq-let local (car forms) (cadr forms) (cddr forms))))
-         ((memq bare '( define-derived-mode))
-          (lambda (forms)
-            (scope-define-derived local (car forms) (cadr forms) (caddr forms) (cdddr forms))))
-         ((memq bare '( define-minor-mode))
-          (lambda (forms) (scope-define-minor local (car forms) (cadr forms) (cddr forms))))
-         ((memq bare '(inline-quote))
-          (lambda (forms) (scope-backquote 1 local (car forms))))
-         ((memq bare '(inline-letevals))
-          (lambda (forms) (scope-let local (car forms) (cdr forms))))
-         ((memq bare '(with-suppressed-warnings))
-          (lambda (forms) (scope-n local (cdr forms))))
-         ((get bare 'scope-function)    ;For custom extensions.
-          (apply-partially (get bare 'scope-function) local))
-         (t #'ignore)))
-       ((special-form-p bare)
-        (cond
-         ((memq bare '( if and or while
-                        save-excursion save-restriction save-current-buffer
-                        catch unwind-protect
-                        progn prog1))
-          (apply-partially #'scope-n local))
-         ((eq bare 'let)
-          (lambda (forms) (scope-let local (car forms) (cdr forms))))
-         ((eq bare 'let*)
-          (lambda (forms) (scope-let* local (car forms) (cdr forms))))
-         ((eq bare 'cond) (apply-partially #'scope-cond local))
-         ((eq bare 'setq) (apply-partially #'scope-setq local))
-         ((memq bare '( defconst defvar))
-          (lambda (forms) (scope-defvar local (car forms) (cadr forms))))
-         ((eq bare 'condition-case)
-          (lambda (forms) (scope-condition-case local (car forms) (cadr forms) (cddr forms))))
-         (t #'ignore)))
-       ((memq bare scope-flet-list)
-        (lambda (forms) (nconc (scope-s local f)
-                               (scope-n local forms))))
-       ;; FIXME: Assume unknown symbols refer to functions, unless at
-       ;; top level.
-       (t #'ignore))))
-   ;; Symbol without position, a quotation marker that the reader
-   ;; expands into a symbol but does not annotate with a position.
-   ((symbolp f)
-    (cond
-     ((eq f '\`) (lambda (forms) (scope-backquote 1 local (car forms))))
-     ((eq f 'function) (lambda (forms) (scope-function local (car forms))))
-     (t #'ignore)))
-   (t #'ignore)))
+(defvar scope-assume-func-p nil)
 
-(defun scope-1 (local form)
+(defun scope-1 (local form &optional top-level)
   (cond
    ((consp form)
-    (funcall (scope-f local (car form)) (cdr form)))
+    (let ((f (car form))
+          (forms (cdr form)))
+      (cond
+       ((symbol-with-pos-p f)
+        (let ((bare (bare-symbol f)))
+          (cond
+           ((or (functionp bare)
+                (memq bare '( if and or while
+                              save-excursion save-restriction save-current-buffer
+                              catch unwind-protect
+                              progn prog1 eval-when-compile eval-and-compile with-eval-after-load
+                              with-memoization cl-assert cl-incf cl-decf setf
+                              ;; We could recognize contant symbols bindings
+                              ;; in `cl-progv', but it is not really worth the
+                              ;; trouble since this macro is specifically
+                              ;; intended for computing bindings at run time.
+                              cl-progv))
+                (eq (get bare 'edebug-form-spec) t))
+            (scope-n local forms))
+           ((macrop bare)
+            (cond
+             ((memq bare '( defun defmacro defsubst define-inline))
+              (scope-defun local (car forms) (cadr forms) (cddr forms)))
+             ((memq bare '( cl-defgeneric))
+              (scope-defgeneric local (car forms) (cadr forms) (cddr forms)))
+             ((memq bare '(cl-case))
+              (scope-case local (car forms) (cdr forms)))
+             ((memq bare '( cl-defun))
+              (scope-cl-defun local (car forms) (cadr forms) (cddr forms)))
+             ((memq bare '( cl-defmethod))
+              (scope-defmethod local (car forms) (cdr forms)))
+             ((memq bare '(lambda))
+              (scope-defun local nil (car forms) (cdr forms)))
+             ((memq bare '(declare-function))
+              (scope-declare-function local (car forms) (cadr forms)
+                                      (caddr forms) (cadddr forms)))
+             ((memq bare '(if-let when-let and-let))
+              (scope-if-let local (car forms) (cdr forms)))
+             ((memq bare '(if-let* when-let* and-let* while-let))
+              (scope-if-let* local (car forms) (cdr forms)))
+             ((memq bare '( defvar-local defcustom))
+              (scope-defvar local (car forms) (cadr forms)))
+             ((memq bare '(dolist dotimes))
+              (scope-dotimes local (caar forms) (cadar forms) (caddar forms) (cdr forms)))
+             ((memq bare '(pcase pcase-exhaustive))
+              (scope-pcase local (car forms) (cdr forms)))
+             ((memq bare '(pcase-lambda))
+              (scope-pcase-lambda local (car forms) (cdr forms)))
+             ((memq bare '(pcase-dolist))
+              (scope-pcase-dolist local (caar forms) (cadar forms) (cdr forms)))
+             ((memq bare '(pcase-let))
+              (scope-pcase-let local (car forms) (cdr forms)))
+             ((memq bare '(pcase-let*))
+              (scope-pcase-let* local (car forms) (cdr forms)))
+             ((memq bare '(setq-local setq-default))
+              (scope-setq local forms))
+             ((memq bare '(push))
+              (scope-push local (car forms) (cadr forms)))
+             ((memq bare '(pop oref))
+              (scope-1 local (car forms)))
+             ((memq bare '(letrec))
+              (scope-letrec local (car forms) (cdr forms)))
+             ((memq bare '(cl-flet))
+              (scope-flet local (car forms) (cdr forms)))
+             ((memq bare '(cl-labels))
+              (scope-labels local (car forms) (cdr forms)))
+             ((memq bare '(minibuffer-with-setup-hook))
+              (scope-minibuffer-with-setup-hook local (car forms) (cdr forms)))
+             ((memq bare '(condition-case-unless-debug))
+              (scope-condition-case local (car forms) (cadr forms) (cddr forms)))
+             ((memq bare '(seq-let))
+              (scope-seq-let local (car forms) (cadr forms) (cddr forms)))
+             ((memq bare '( define-derived-mode))
+              (scope-define-derived local (car forms) (cadr forms) (caddr forms) (cdddr forms)))
+             ((memq bare '( define-minor-mode))
+              (scope-define-minor local (car forms) (cadr forms) (cddr forms)))
+             ((memq bare '(inline-quote))
+              (scope-backquote local (car forms)))
+             ((memq bare '(inline-letevals))
+              (scope-let local (car forms) (cdr forms)))
+             ((memq bare '(with-suppressed-warnings))
+              (scope-n local (cdr forms)))
+             ((get bare 'scope-function) ;For custom extensions.
+              (funcall (get bare 'scope-function) local forms))))
+           ((special-form-p bare)
+            (cond
+             ((eq bare 'let)
+              (scope-let local (car forms) (cdr forms)))
+             ((eq bare 'let*)
+              (scope-let* local (car forms) (cdr forms)))
+             ((eq bare 'cond) (scope-cond local forms))
+             ((eq bare 'setq) (scope-setq local forms))
+             ((memq bare '( defconst defvar))
+              (scope-defvar local (car forms) (cadr forms)))
+             ((eq bare 'condition-case)
+              (scope-condition-case local (car forms) (cadr forms) (cddr forms)))
+             ((get bare 'scope-function)
+              (funcall (get bare 'scope-function) local forms))))
+           ((memq bare scope-flet-list)
+            (nconc (scope-s local f) (scope-n local forms)))
+           ((get bare 'scope-function)
+            (funcall (get bare 'scope-function) local forms))
+           ;; Assume nothing about unknown top-level forms.
+           (top-level nil)
+           (scope-assume-func-p (scope-n local forms)))))
+       ;; Symbol without position, a quotation marker that the reader
+       ;; expands into a symbol but does not annotate with a position.
+       ((symbolp f)
+        (cond
+         ((eq f '\`) (scope-backquote local (car forms)))
+         ((eq f 'function) (scope-sharpquote local (car forms))))))))
    ((symbol-with-pos-p form)
     (scope-s local form))))
 
@@ -818,8 +822,35 @@ list of elements (OCCURRENCE LEN BINDING) where OCCURRENCE is a buffer
 position where a symbol of length LEN occurs, which is bound by another
 occurrence of the same symbol that starts at position BINDING.  If
 OCCURRENCE is itself a binding occurrence, then BINDING and OCCURRENCE
-are equal.  If OCCURRENCE is not lexically bound, then BINDING is nil."
-  (scope-1 nil (read-positioning-symbols stream)))
+are equal.  If OCCURRENCE is not lexically bound, then BINDING is nil.
+
+This function recursively analyzes Lisp forms (HEAD . TAIL), usually
+starting with a top-level form, by inspecting HEAD at each level:
+
+- If HEAD satisfies `functionp', which means it is a function in the
+  running Emacs session, analzye the form as a function call.
+
+- Standard macros and special forms, such as `defun', `if', `let',
+  `pcase', quotes, backquotes and more, are handled specially according
+  to their particular semantics.
+
+- If HEAD has the property symbol `scope-function', the value of this
+  property is used to analyze TAIL.  It should be a function that takes
+  two arguments, LOCAL and TAIL, and returns a bindings graph for TAIL.
+  LOCAL represents the local context around the current form, the
+  `scope-function' can pass LOCAL to functions such as `scope-1' and
+  `scope-n' to obtain bindings graphs for sub-forms.  See also
+  `scope-local-new' for extending LOCAL with local bindings in TAIL.
+
+- If within the code under analysis HEAD is a `cl-letf'-bound local
+  function name, analyze the form as a function call.
+
+- Otherwise, HEAD is unknown.  If the HEAD of the top-level form that
+  this function reads from STREAM is unknown, this function ignores it
+  and returns nil.  If an unknown HEAD occurs in a nested form, by
+  default it is similarly ignored, but if you set `scope-assume-func-p'
+  to non-nil, then this function assumes that such HEADs are functions."
+  (scope-1 nil (read-positioning-symbols stream) t))
 
 (provide 'scope)
 ;;; scope.el ends here