From a18918df0bd0ddd9c8b05b99c302e26b226af5d3 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Thu, 29 Sep 2022 15:22:42 +0000 Subject: [PATCH] CC Mode: Refactor c-forward-delarator and callers c-forward-declarator now moves over a complete declarator, including any arglist expression, but possibly not including certain C++20 constructs. * lisp/progmodes/cc-engine.el (c-forward-decl-arglist): New function, extracted from c-do-declarators. (c-forward-declarator): Extend the result list by one element, denoting whether the function parsed an argument list. Insert special handling for a C++ operator construct. If there is a parenthesis expression, and it is an argument list, move forward over it. (c-do-declarators): Append an optional parameter, cdd-anon-accepted instructing the function to handle declarators without identifiers. Extract c-forward-decl-arglist. * lisp/progmodes/cc-fonts.el (c-font-lock-declarators): New &optional parameter accept-anon, as above. Analyze and use the identifier location returned by c-do-declarators, rather than using c-last-identifier-range. (c-font-lock-single-decl): Determine accept-anon from the context, passing it as argument to c-font-lock-declarators. * lisp/progmodes/cc-langs.el (c-opt-identifier-prefix-key): Make a c-lang-defvar out of this existing c-lang-defconst. * lisp/progmodes/cc-mode.el (c-fl-decl-end): Adapt for the new c-forward-declarator which now moves over arglists. Amend better to handle certain syntactically invalid constructs in C++. --- lisp/progmodes/cc-engine.el | 203 ++++++++++++++++++++++-------------- lisp/progmodes/cc-fonts.el | 57 +++++----- lisp/progmodes/cc-langs.el | 2 + lisp/progmodes/cc-mode.el | 65 ++++++------ 4 files changed, 192 insertions(+), 135 deletions(-) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index e0aef2b2ee6..fc005183604 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -9590,14 +9590,71 @@ point unchanged and return nil." (goto-char here) nil))) -(defun c-forward-declarator (&optional limit accept-anon) +(defun c-forward-decl-arglist (not-top id-in-parens &optional limit) + ;; Point is at an open parenthesis, assumed to be the arglist of a function + ;; declaration. Move over this arglist and following syntactic whitespace, + ;; and return non-nil. If the construct isn't such an arglist, leave point + ;; unmoved and return nil. + ;; + ;; Note that point is assumed to be at a place where an arglist is expected. + ;; Only for C++, where there are other possibilities, is any actual + ;; processing done. Otherwise, t is simply returned. + (let ((here (point)) got-type) + (if (or + (not (c-major-mode-is 'c++-mode)) + (and + (or (not not-top) + id-in-parens ; Id is in parens, etc. + (save-excursion + (forward-char) + (c-forward-syntactic-ws limit) + (looking-at "[*&]"))) + (when + (save-excursion + (let (c-last-identifier-range) + (forward-char) + (c-forward-syntactic-ws limit) + (catch 'is-function + (while + ;; Go forward one argument at each iteration. + (progn + (while + (cond + ((looking-at c-decl-hangon-key) + (c-forward-keyword-clause 1)) + ((looking-at + c-noise-macro-with-parens-name-re) + (c-forward-noise-clause)))) + (when (eq (char-after) ?\)) + (forward-char) + (c-forward-syntactic-ws limit) + (throw 'is-function t)) + (setq got-type (c-forward-type)) + (cond + ((null got-type) + (throw 'is-function nil)) + ((not (eq got-type 'maybe)) + (throw 'is-function t))) + (c-forward-declarator limit t t) + (eq (char-after) ?,)) + (forward-char) + (c-forward-syntactic-ws)) + t))) + (and (c-go-list-forward (point) limit) + (progn (c-forward-syntactic-ws limit) t))))) + t + (goto-char here) + nil))) + +(defun c-forward-declarator (&optional limit accept-anon not-top) ;; Assuming point is at the start of a declarator, move forward over it, ;; leaving point at the next token after it (e.g. a ) or a ; or a ,), or at ;; end of buffer if there is no such token. ;; - ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT DECORATED), - ;; where ID-START and ID-END are the bounds of the declarator's identifier, - ;; and BRACKETS-AFTER-ID is non-nil if a [...] pair is present after the id. + ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT DECORATED + ;; ARGLIST), where ID-START and ID-END are the bounds of the declarator's + ;; identifier, BRACKETS-AFTER-ID is non-nil if a [...] pair is present after + ;; the id, and ARGLIST is non-nil if an arglist has been moved over. ;; GOT-INIT is non-nil when the declarator is followed by "=" or "(", ;; DECORATED is non-nil when the identifier is embellished by an operator, ;; like "*x", or "(*x)". @@ -9619,7 +9676,8 @@ point unchanged and return nil." ;; array/struct initialization) or "=" or terminating delimiter ;; (e.g. "," or ";" or "}"). (let ((here (point)) - id-start id-end brackets-after-id paren-depth decorated) + id-start id-end brackets-after-id paren-depth decorated + got-init arglist) (or limit (setq limit (point-max))) (if (and (< (point) limit) @@ -9640,6 +9698,18 @@ point unchanged and return nil." ((and c-opt-cpp-prefix (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)) + ;; Special handling for operator. + ((and c-opt-op-identifier-prefix + (looking-at c-opt-op-identifier-prefix)) + (goto-char (match-end 1)) + (c-forward-syntactic-ws limit) + (setq id-start (point)) + (if (looking-at c-overloadable-operators-regexp) + (progn + (goto-char (match-end 0)) + (setq got-identifier t) + nil) + t)) ((and (looking-at c-type-decl-prefix-key) (if (and (c-major-mode-is 'c++-mode) (match-beginning 4)) ; Was 3 - 2021-01-01 @@ -9648,17 +9718,17 @@ point unchanged and return nil." ;; prefix only if it specifies a member pointer. (progn (setq id-start (point)) - (c-forward-name) - (if (save-match-data - (looking-at "\\(::\\)")) - ;; We only check for a trailing "::" and - ;; let the "*" that should follow be - ;; matched in the next round. - t - ;; It turned out to be the real identifier, - ;; so flag that and stop. - (setq got-identifier t) - nil)) + (when (c-forward-name) + (if (save-match-data + (looking-at "\\(::\\)")) + ;; We only check for a trailing "::" and + ;; let the "*" that should follow be + ;; matched in the next round. + t + ;; It turned out to be the real identifier, + ;; so flag that and stop. + (setq got-identifier t) + nil))) t)) (if (save-match-data (looking-at c-type-decl-operator-prefix-key)) @@ -9684,7 +9754,7 @@ point unchanged and return nil." (accept-anon (setq id-start nil id-end nil) t) - (t (/= (point) here)))) + (t nil))) ;; Skip out of the parens surrounding the identifier. If closing ;; parens are missing, this form returns nil. @@ -9725,10 +9795,13 @@ point unchanged and return nil." (while (and (< (point) limit) - (setq found - (c-syntactic-re-search-forward - "[;:,]\\|\\s)\\|\\(=\\|\\s(\\)" - limit t t)) + (prog1 + (setq found + (c-syntactic-re-search-forward + "[;:,]\\|\\s)\\|\\(=\\|\\s(\\)" + limit t t)) + (setq got-init + (and found (match-beginning 1)))) (eq (char-before) ?:) (if (looking-at c-:-op-cont-regexp) (progn (goto-char (match-end 0)) t) @@ -9742,18 +9815,30 @@ point unchanged and return nil." (c-simple-skip-symbol-backward)) (looking-at c-paren-stmt-key)))))))) found) - (eq (char-before) ?\[) - (c-go-up-list-forward)) - (setq brackets-after-id t)) - (when found (backward-char)) + (cond ((eq (char-before) ?\[) + (setq brackets-after-id t) + (prog1 (c-go-up-list-forward) + (c-forward-syntactic-ws))) + ((and (not brackets-after-id) + (eq (char-before) ?\()) + (backward-char) + (if (c-forward-decl-arglist not-top decorated limit) + (setq arglist t + got-init nil) + (forward-char)) + nil)))) ; To end the loop. + (when (and found + (memq (char-before) '(?\; ?\: ?, ?= ?\( ?\[ ?{))) + (backward-char)) (<= (point) limit))) - (list id-start id-end brackets-after-id (match-beginning 1) decorated) + (list id-start id-end brackets-after-id got-init decorated arglist) (goto-char here) nil))) (defun c-do-declarators - (cdd-limit cdd-list cdd-not-top cdd-comma-prop cdd-function) + (cdd-limit cdd-list cdd-not-top cdd-comma-prop cdd-function + &optional cdd-accept-anon) "Assuming point is at the start of a comma separated list of declarators, apply CDD-FUNCTION to each declarator (when CDD-LIST is non-nil) or just the first declarator (when CDD-LIST is nil). When CDD-FUNCTION is nil, no @@ -9778,6 +9863,9 @@ Stop at or before CDD-LIMIT (which may NOT be nil). If CDD-NOT-TOP is non-nil, we are not at the top-level (\"top-level\" includes being directly inside a class or namespace, etc.). +If CDD-ACCEPT-ANON is non-nil, we also process declarators without names, +e.g. \"int (*)(int)\" in a function prototype. + Return non-nil if we've reached the token after the last declarator (often a semicolon, or a comma when CDD-LIST is nil); otherwise (when we hit CDD-LIMIT, or fail otherwise) return nil, leaving point at the beginning of the putative @@ -9789,67 +9877,25 @@ This function might do hidden buffer changes." ;; CDD-FUNCTION. (let ((cdd-pos (point)) cdd-next-pos cdd-id-start cdd-id-end - cdd-decl-res cdd-got-func cdd-got-type cdd-got-init + cdd-decl-res cdd-got-func cdd-got-init c-last-identifier-range cdd-exhausted cdd-after-block) ;; The following `while' applies `cdd-function' to a single declarator id ;; each time round. It loops only when CDD-LIST is non-nil. (while (and (not cdd-exhausted) - (setq cdd-decl-res (c-forward-declarator cdd-limit))) + (setq cdd-decl-res (c-forward-declarator + cdd-limit cdd-accept-anon cdd-not-top))) + (setq cdd-next-pos (point) cdd-id-start (car cdd-decl-res) cdd-id-end (cadr cdd-decl-res) - cdd-got-func (and (eq (char-after) ?\() - (or (not (c-major-mode-is 'c++-mode)) - (not cdd-not-top) - (car (cddr (cddr cdd-decl-res))) ; Id is in - ; parens, etc. - (save-excursion - (forward-char) - (c-forward-syntactic-ws) - (looking-at "[*&]"))) - (not (car (cddr cdd-decl-res))) - (or (not (c-major-mode-is 'c++-mode)) - (save-excursion - (let (c-last-identifier-range) - (forward-char) - (c-forward-syntactic-ws) - (catch 'is-function - (while - (progn - (while - (cond - ((looking-at c-decl-hangon-key) - (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) - (c-forward-noise-clause)))) - (if (eq (char-after) ?\)) - (throw 'is-function t)) - (setq cdd-got-type (c-forward-type)) - (cond - ((null cdd-got-type) - (throw 'is-function nil)) - ((not (eq cdd-got-type 'maybe)) - (throw 'is-function t))) - (c-forward-declarator nil t) - (eq (char-after) ?,)) - (forward-char) - (c-forward-syntactic-ws)) - t))))) - cdd-got-init (and (cadr (cddr cdd-decl-res)) - (char-after))) + cdd-got-func (cadr (cddr (cddr cdd-decl-res))) + cdd-got-init (and (cadr (cddr cdd-decl-res)) (char-after))) ;; Jump past any initializer or function prototype to see if ;; there's a ',' to continue at. - (cond (cdd-got-func - ;; Skip a parenthesized initializer (C++) or a function - ;; prototype. - (if (c-go-list-forward (point) cdd-limit) ; over the parameter list. - (c-forward-syntactic-ws cdd-limit) - (setq cdd-exhausted t))) ; unbalanced parens - - (cdd-got-init ; "=" sign OR opening "(", "[", or "(" + (cond (cdd-got-init ; "=" sign OR opening "(", "[", or "(" ;; Skip an initializer expression in braces, whether or not (in ;; C++ Mode) preceded by an "=". Be careful that the brace list ;; isn't a code block or a struct (etc.) block. @@ -9872,8 +9918,9 @@ This function might do hidden buffer changes." (t (c-forward-syntactic-ws cdd-limit))) (if cdd-function - (funcall cdd-function cdd-id-start cdd-id-end cdd-next-pos - cdd-not-top cdd-got-func cdd-got-init)) + (save-excursion + (funcall cdd-function cdd-id-start cdd-id-end cdd-next-pos + cdd-not-top cdd-got-func cdd-got-init))) ;; If a ',' is found we set cdd-pos to the next declarator and iterate. (if (and cdd-list (< (point) cdd-limit) (looking-at ",")) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index d2af53e837c..8f930c54377 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1092,7 +1092,7 @@ casts and declarations are fontified. Used on level 2 and higher." nil) (defun c-font-lock-declarators (limit list types not-top - &optional template-class) + &optional template-class accept-anon) ;; Assuming the point is at the start of a declarator in a declaration, ;; fontify the identifier it declares. (If TYPES is t, it does this via the ;; macro `c-fontify-types-and-refs'.) @@ -1112,6 +1112,8 @@ casts and declarations are fontified. Used on level 2 and higher." ;; a default (introduced by "="), it will be fontified as a type. ;; E.g. "". ;; + ;; ACCEPT-ANON is non-nil when we accept anonymous declarators. + ;; ;; Nil is always returned. The function leaves point at the delimiter after ;; the last declarator it processes. ;; @@ -1124,37 +1126,35 @@ casts and declarations are fontified. Used on level 2 and higher." limit list not-top (cond ((eq types t) 'c-decl-type-start) ((null types) 'c-decl-id-start)) - (lambda (id-start _id-end end-pos _not-top is-function init-char) + (lambda (id-start id-end end-pos _not-top is-function init-char) (if (eq types t) - ;; Register and fontify the identifier as a type. - (let ((c-promote-possible-types t)) - (goto-char id-start) - (c-forward-type)) - ;; The following doesn't work properly (yet, 2018-09-22). - ;; (c-put-font-lock-face id-start id-end - ;; (if is-function - ;; 'font-lock-function-name-face - ;; 'font-lock-variable-name-face)) - (when (and c-last-identifier-range - (not (get-text-property (car c-last-identifier-range) - 'face))) - ;; We use `c-last-identifier-range' rather than `id-start' and - ;; `id-end', since the latter two can be erroneous. E.g. in - ;; "~Foo", `id-start' is at the tilde. This is a bug in - ;; `c-forward-declarator'. - (c-put-font-lock-face (car c-last-identifier-range) - (cdr c-last-identifier-range) - (cond - ((not (memq types '(nil t))) types) - (is-function 'font-lock-function-name-face) - (t 'font-lock-variable-name-face))))) + (when id-start + ;; Register and fontify the identifier as a type. + (let ((c-promote-possible-types t)) + (goto-char id-start) + (c-forward-type))) + (when id-start + (goto-char id-start) + (when c-opt-identifier-prefix-key + (unless (and (looking-at c-opt-identifier-prefix-key) ; For operator~ + (eq (match-end 1) id-end)) + (while (and (< (point) id-end) + (re-search-forward c-opt-identifier-prefix-key id-end t)) + (c-forward-syntactic-ws limit)))) + (when (not (get-text-property (point) 'face)) + (c-put-font-lock-face (point) id-end + (cond + ((not (memq types '(nil t))) types) + (is-function 'font-lock-function-name-face) + (t 'font-lock-variable-name-face)))))) (and template-class (eq init-char ?=) ; C++ ""? (progn (goto-char end-pos) (c-forward-token-2 1 nil limit) ; Over "=" (let ((c-promote-possible-types t)) - (c-forward-type t)))))) + (c-forward-type t))))) + accept-anon) ; Last argument to c-do-declarators. nil)) (defun c-get-fontification-context (match-pos not-front-decl &optional toplev) @@ -1384,9 +1384,12 @@ casts and declarations are fontified. Used on level 2 and higher." 'c-decl-type-start 'c-decl-id-start))))) (c-font-lock-declarators - (min limit (point-max)) decl-list + (min limit (point-max)) + decl-list (not (null (cadr decl-or-cast))) - (not toplev) template-class)) + (not toplev) + template-class + (memq context '(decl <>)))) ;; A declaration has been successfully identified, so do all the ;; fontification of types and refs that've been recorded. diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 85f43d6a26b..3b008fd6dc7 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -934,6 +934,8 @@ This value is by default merged into `c-operators'." t))) (when ops (c-make-keywords-re 'appendable ops)))) +(c-lang-defvar c-opt-identifier-prefix-key + (c-lang-const c-opt-identifier-prefix-key)) (c-lang-defconst c-after-id-concat-ops "Operators that can occur after a binary operator on `c-identifier-ops' diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 9dd5ddb4656..732b2b10549 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -2471,38 +2471,43 @@ with // and /*, not more generic line and block comments." (c-backward-syntactic-ws lim)) (when (setq pos1 (c-on-identifier)) (goto-char pos1) - (let ((lim (save-excursion - (and (c-beginning-of-macro) - (progn (c-end-of-macro) (point)))))) - (and (c-forward-declarator lim) - (if (and (eq (char-after) ?\() - (c-go-list-forward nil lim)) + (let* ((lim (save-excursion + (and (c-beginning-of-macro) + (progn (c-end-of-macro) (point))))) + (decl-res (c-forward-declarator lim))) + (if (or (cadr (cddr (cddr decl-res))) ; We scanned an arglist. + (and (eq (char-after) ?\() ; Move over a non arglist (...). + (prog1 (c-go-list-forward nil lim) + (c-forward-syntactic-ws lim)))) + (if (looking-at c-symbol-char-key) + ;; Deal with baz (foo((bar)) type var), where `pos' + ;; was inside foo, but foo((bar)) is not semantically + ;; valid. The result must be after var). (and - (progn (c-forward-syntactic-ws lim) - (not (eobp))) + (goto-char pos) + (setq pos1 (c-on-identifier)) + (goto-char pos1) (progn - (if (looking-at c-symbol-char-key) - ;; Deal with baz (foo((bar)) type var), where - ;; foo((bar)) is not semantically valid. The result - ;; must be after var). - (and - (goto-char pos) - (setq pos1 (c-on-identifier)) - (goto-char pos1) - (progn - (c-backward-syntactic-ws lim) - (eq (char-before) ?\()) - (c-fl-decl-end (1- (point)))) - (c-backward-syntactic-ws lim) - (point)))) - (if (progn (c-forward-syntactic-ws lim) - (not (eobp))) - (c-forward-over-token) - (let ((lit-start (c-literal-start))) - (when lit-start - (goto-char lit-start)) - (c-backward-syntactic-ws))) - (and (>= (point) pos) (point)))))))) + (c-backward-syntactic-ws lim) + (eq (char-before) ?\()) + (c-fl-decl-end (1- (point)))) + (c-backward-syntactic-ws lim) + (point)) + (if (progn (c-forward-syntactic-ws lim) + (not (eobp))) + (progn + (c-forward-over-token) + ;; Cope with having POS withing a syntactically invalid + ;; (...), by moving backward out of the parens and trying + ;; again. + (when (and (eq (char-before) ?\)) + (c-go-list-backward (point) lim)) + (c-fl-decl-end (point)))) + (let ((lit-start (c-literal-start))) + (when lit-start + (goto-char lit-start)) + (c-backward-syntactic-ws))) + (and (>= (point) pos) (point))))))) (defun c-change-expand-fl-region (_beg _end _old-len) ;; Expand the region (c-new-BEG c-new-END) to an after-change font-lock -- 2.39.2