From: Eshel Yaron Date: Sat, 15 Feb 2025 16:37:42 +0000 (+0100) Subject: Fix bug#76305 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=d8e9d8151d6b3b266966e5ad868bb81e6efea595;p=emacs.git Fix bug#76305 --- diff --git a/lisp/emacs-lisp/scope.el b/lisp/emacs-lisp/scope.el index 687a2163433..eb00cd17859 100644 --- a/lisp/emacs-lisp/scope.el +++ b/lisp/emacs-lisp/scope.el @@ -30,6 +30,8 @@ (defvar scope-counter nil) +(defvar scope--local nil) + (defvar scope-callback #'ignore) (defvar scope-current-let-alist-form nil) @@ -57,7 +59,8 @@ Optional argument LOCAL is a local context to extend." (let* ((beg (scope-sym-pos sym)) (bare (bare-symbol sym)) (name (symbol-name bare)) - (len (length name))) + (len (length name)) + (scope--local local)) (when (and beg (not (booleanp bare))) (cond ((keywordp bare) (scope-report 'constant beg len)) diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 56002e475ef..6c9c0f829ec 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -673,154 +673,20 @@ be used instead. ;;; Completion at point for Elisp -(defun elisp--local-variables-1 (vars sexp) - "Return VARS locally bound around the witness, or nil if not found." - (let (res) - (while - (unless - (setq res - (pcase sexp - (`(,(or 'let 'let*) ,bindings) - (let ((vars vars)) - (when (eq 'let* (car sexp)) - (dolist (binding (cdr (reverse bindings))) - (push (or (car-safe binding) binding) vars))) - (elisp--local-variables-1 - vars (car (cdr-safe (car (last bindings))))))) - (`(,(or 'let 'let*) ,bindings . ,body) - (let ((vars vars)) - (dolist (binding bindings) - (push (or (car-safe binding) binding) vars)) - (elisp--local-variables-1 vars (car (last body))))) - (`(lambda ,_args) - ;; FIXME: Look for the witness inside `args'. - (setq sexp nil)) - (`(lambda ,args . ,body) - (elisp--local-variables-1 - (let ((args (if (listp args) args))) - ;; FIXME: Exit the loop if witness is in args. - (append (remq '&optional (remq '&rest args)) vars)) - (car (last body)))) - (`(condition-case ,_ ,e) (elisp--local-variables-1 vars e)) - (`(condition-case ,v ,_ . ,catches) - (elisp--local-variables-1 - (cons v vars) (cdr (car (last catches))))) - (`(quote . ,_) - ;; FIXME: Look for the witness inside sexp. - (setq sexp nil)) - ;; FIXME: Handle `cond'. - (`(,_ . ,_) - (elisp--local-variables-1 vars (car (last sexp)))) - ('elisp--witness--lisp (or vars '(nil))) - (_ nil))) - ;; We didn't find the witness in the last element so we try to - ;; backtrack to the last-but-one. - (setq sexp (ignore-errors (butlast sexp))))) - res)) - -(defvar warning-minimum-log-level) - -(defvar elisp--local-macroenv - `((cl-eval-when . ,(lambda (&rest args) `(progn . ,(cdr args)))) - (eval-when-compile . ,(lambda (&rest args) `(progn . ,args))) - (eval-and-compile . ,(lambda (&rest args) `(progn . ,args)))) - "Environment to use while tentatively expanding macros. -This is used to try and avoid the most egregious problems linked to the -use of `macroexpand-all' as a way to find the \"underlying raw code\".") +(defvar scope--local) -(defvar elisp--macroexpand-untrusted-warning t) - -(defun elisp--safe-macroexpand-all (sexp) - (if (not (trusted-content-p)) - ;; FIXME: We should try and do better here, either using a notion - ;; of "safe" macros, or with `bwrap', or ... - (progn - (when elisp--macroexpand-untrusted-warning - (setq-local elisp--macroexpand-untrusted-warning nil) ;Don't spam! - (let ((inhibit-message t)) ;Only log. - (message "Completion of local vars is disabled in %s (untrusted content)" - (buffer-name)))) - sexp) - (let ((macroexpand-advice - (lambda (expander form &rest args) - (condition-case err - (apply expander form args) - (error - (message "Ignoring macroexpansion error: %S" err) form))))) - (unwind-protect - ;; Silence any macro expansion errors when - ;; attempting completion at point (bug#58148). - (let ((inhibit-message t) - (macroexp-inhibit-compiler-macros t) - (warning-minimum-log-level :emergency)) - (advice-add 'macroexpand-1 :around macroexpand-advice) - (macroexpand-all sexp elisp--local-macroenv)) - (advice-remove 'macroexpand-1 macroexpand-advice))))) - -(defun elisp--local-variables () - "Return a list of locally let-bound variables at point." - (save-excursion - (skip-syntax-backward "w_") - (let* ((ppss (syntax-ppss)) - (txt (buffer-substring-no-properties (or (car (nth 9 ppss)) (point)) - (or (nth 8 ppss) (point)))) - (closer ())) - (dolist (p (nth 9 ppss)) - (push (cdr (syntax-after p)) closer)) - (setq closer (apply #'string closer)) - (let* ((sexp (condition-case nil - (car (read-from-string - (concat txt "elisp--witness--lisp" closer))) - ((invalid-read-syntax end-of-file) nil))) - (vars (elisp--local-variables-1 - nil (elisp--safe-macroexpand-all sexp)))) - (delq nil - (mapcar (lambda (var) - (and (symbolp var) - (not (string-match (symbol-name var) "\\`[&_]")) - ;; Eliminate uninterned vars. - (intern-soft var) - var)) - vars)))))) - -(defun elisp--expect-function-p (pos) - "Return non-nil if the symbol at position POS is expected to be a function." - (or - (and (eq (char-before pos) ?') - (eq (char-before (1- pos)) ?#)) - (save-excursion - (let ((parent (nth 1 (syntax-ppss pos)))) - (when parent - (goto-char parent) - (and - (looking-at (concat "(\\(cl-\\)?" - (regexp-opt '("declare-function" - "function" "defadvice" - "callf" "callf2" - "defsetf")) - "[ \t\r\n]+")) - (eq (match-end 0) pos))))))) - -(defun elisp--form-quoted-p (pos) - "Return non-nil if the form at POS is not evaluated. -It can be quoted, or be inside a quoted form." - ;; FIXME: Do some macro expansion maybe. - (save-excursion - (let ((state (syntax-ppss pos))) - (or (nth 8 state) ; Code inside strings usually isn't evaluated. - (let ((nesting (cons (point) (reverse (nth 9 state)))) - res) - (while (and nesting (not res)) - (goto-char (pop nesting)) - (cond - ((or (eq (char-after) ?\[) - (progn - (skip-chars-backward " ") - (memq (char-before) '(?' ?` ?‘)))) - (setq res t)) - ((eq (char-before) ?,) - (setq nesting nil)))) - res))))) +(defun elisp-local-variables (&optional pos) + "Return list of local variable names in scope at POS." + (let (all res) + (save-excursion + (if pos (goto-char pos) (setq pos (point))) + (beginning-of-defun) + (scope (lambda (_type beg len _id &optional _def) + (when (<= beg pos (+ beg len)) + (setq all (nconc (mapcar #'car scope--local) all)))))) + (dolist (sym all) + (let* ((var sym)) (unless (memq var res) (setq res (cons var res))))) + res)) ;; FIXME: Support for Company brings in features which straddle eldoc. ;; We should consolidate this, so that major modes can provide all that @@ -845,7 +711,7 @@ It can be quoted, or be inside a quoted form." (cond ((fboundp symbol) (describe-function symbol)) ((boundp symbol) (describe-variable symbol)) - ((featurep symbol) (describe-package symbol)) + ((featurep symbol) (describe-library symbol)) ((facep symbol) (describe-face symbol)) (t (signal 'user-error nil))) (help-buffer)))))) @@ -934,7 +800,7 @@ in `completion-at-point-functions' (which see)." (when (<= beg pos (+ beg len)) (throw 'sym-type type)))) nil)) - ((variable constant) (let ((local-vars (elisp--local-variables))) + ((variable constant) (let ((local-vars (elisp-local-variables))) (lambda (sym) (or (elisp--shorthand-aware-boundp sym) (memq sym local-vars))))) ((function macro special-form top-level) #'elisp--shorthand-aware-fboundp) @@ -1496,10 +1362,7 @@ confidence." (substring file 3)) file))) -(defun elisp-load-path-roots () - (if (boundp 'package-user-dir) - (cons package-user-dir load-path) - load-path)) +(defun elisp-load-path-roots () load-path) ;;; Elisp Interaction mode diff --git a/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el index b601fbc37ad..c457b531052 100644 --- a/lisp/use-package/use-package-core.el +++ b/lisp/use-package/use-package-core.el @@ -673,15 +673,6 @@ extending any keys already present." (let* ((name-symbol (if (stringp name) (intern name) name)) (name-string (symbol-name name-symbol))) - ;; The function `elisp--local-variables' inserts this unbound variable into - ;; macro forms to determine the locally bound variables for - ;; `elisp-completion-at-point'. It ends up throwing a lot of errors since it - ;; can occupy the position of a keyword (or look like a second argument to a - ;; keyword that takes one). Deleting it when it's at the top level should be - ;; harmless since there should be no locally bound variables to discover - ;; here anyway. - (setq args (delq 'elisp--witness--lisp args)) - ;; Reduce the set of keywords down to its most fundamental expression. (setq args (use-package-unalias-keywords name-symbol args))