From 8dd588b1fb51bb9178bf34a6be9f35de84e95045 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Tue, 2 Mar 2021 20:31:36 +0000 Subject: [PATCH] CC Mode: Fix analysis of brace lists, particularly in C++ Mode Fix some alignment functionality in cc-align.el. * lisp/progmodes/cc-align.el (c-lineup-arglist-intro-after-paren): Align the next line under the previous entry rather than one to the right of the paren. (c-lineup-2nd-brace-entry-in-arglist): Take the anchor point from the brace-list-entry element, not the brace-list-intro one. * lisp/progmodes/cc-engine.el (c-looking-at-decl-block): Use c-looking-at-statement-block to test whether "struct A {" begins a brace list or a struct declaration. (c-looking-at-or-maybe-in-bracelist): Several detailed amendments, correctly to recognize brace lists. (c-looking-at-statement-block): No longer search for commas, as they are not reliable indicators of a brace list. Search now for a restricted set of keywords, since some can appear in brace lists in C++ mode. * lisp/progmodes/cc-langs.el (c-stmt-block-only-keywords) (c-stmt-block-only-keywords-regexp): New lang consts/vars. (c-pre-id-bracelist-kwds): New lang const. (c-pre-id-bracelist-key): Derive now from the above. (c-pre-brace-non-bracelist-key): New lang const/var. --- lisp/progmodes/cc-align.el | 7 +- lisp/progmodes/cc-engine.el | 125 ++++++++++++++++++++---------------- lisp/progmodes/cc-langs.el | 50 +++++++++++++-- 3 files changed, 122 insertions(+), 60 deletions(-) diff --git a/lisp/progmodes/cc-align.el b/lisp/progmodes/cc-align.el index d14ef1744af..51d51deef71 100644 --- a/lisp/progmodes/cc-align.el +++ b/lisp/progmodes/cc-align.el @@ -274,8 +274,10 @@ statement-block-intro, statement-case-intro, arglist-intro." (save-excursion (beginning-of-line) (backward-up-list 1) + (forward-char) (skip-chars-forward " \t" (c-point 'eol)) - (vector (1+ (current-column))))) + (if (eolp) (skip-chars-backward " \t")) + (vector (current-column)))) (defun c-lineup-arglist-close-under-paren (langelem) "Line up a line under the enclosing open paren. @@ -1145,7 +1147,8 @@ Works with brace-list-intro." ; the line. (save-excursion ; "{" earlier on the line (goto-char (c-langelem-pos - (assq 'brace-list-intro c-syntactic-context))) + (assq 'brace-list-entry + c-syntactic-context))) (and (eq (c-backward-token-2 1 nil diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 4cf7af843b7..079985a6bc5 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -11410,7 +11410,9 @@ comment at the start of cc-engine.el for more info." ;; also might be part of a declarator expression. Currently ;; there's no such language. (not (or (looking-at c-symbol-start) - (looking-at c-type-decl-prefix-key)))))) + (looking-at c-type-decl-prefix-key) + (and (eq (char-after) ?{) + (not (c-looking-at-statement-block)))))))) ;; In Pike a list of modifiers may be followed by a brace ;; to make them apply to many identifiers. Note that the @@ -11817,15 +11819,17 @@ comment at the start of cc-engine.el for more info." ;; POINT, or nil if there is no such position, or we do not know it. LIM is ;; a backward search limit. ;; - ;; The determination of whether the brace starts a brace list is solely by - ;; the context of the brace, not by its contents. + ;; The determination of whether the brace starts a brace list is mainly by + ;; the context of the brace, not by its contents. In exceptional + ;; circumstances (e.g. "struct A {" in C++ Mode), the contents are examined, + ;; too. ;; ;; Here, "brace list" does not include the body of an enum. (save-excursion (let ((start (point)) (braceassignp 'dontknow) inexpr-brace-list bufpos macro-start res pos after-type-id-pos - in-paren parens-before-brace + pos2 in-paren parens-before-brace paren-state paren-pos) (setq res (c-backward-token-2 1 t lim)) @@ -11841,12 +11845,16 @@ comment at the start of cc-engine.el for more info." (goto-char paren-pos) (setq braceassignp 'c++-noassign in-paren 'in-paren)) - ((looking-at c-pre-id-bracelist-key) + ((looking-at c-pre-brace-non-bracelist-key) (setq braceassignp nil)) ((looking-at c-return-key)) ((and (looking-at c-symbol-start) (not (looking-at c-keywords-regexp))) - (setq after-type-id-pos (point))) + (if (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))) + (setq braceassignp 'c++-noassign) + (setq after-type-id-pos (point)))) ((eq (char-after) ?\() (setq parens-before-brace t) nil) @@ -11860,8 +11868,13 @@ comment at the start of cc-engine.el for more info." (eq (char-after paren-pos) ?\() (setq in-paren 'in-paren) (goto-char paren-pos))) - ((looking-at c-pre-id-bracelist-key)) + ((looking-at c-pre-brace-non-bracelist-key)) ((looking-at c-return-key)) + ((and (looking-at c-symbol-start) + (not (looking-at c-keywords-regexp)) + (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))))) (t (setq after-type-id-pos (point)) nil)))) (setq braceassignp 'c++-noassign)) @@ -11946,8 +11959,12 @@ comment at the start of cc-engine.el for more info." (cond (braceassignp ;; We've hit the beginning of the aggregate list. - (c-beginning-of-statement-1 containing-sexp) - (cons (point) (or in-paren inexpr-brace-list))) + (setq pos2 (point)) + (cons + (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) + (point) + pos2) + (or in-paren inexpr-brace-list))) ((and after-type-id-pos (save-excursion (when (eq (char-after) ?\;) @@ -11959,34 +11976,36 @@ comment at the start of cc-engine.el for more info." (c-get-char-property (point) 'syntax-table)) (c-go-list-forward nil after-type-id-pos) (c-forward-syntactic-ws))) - (and - (or (not (looking-at c-class-key)) - (save-excursion - (goto-char (match-end 1)) - (c-forward-syntactic-ws) - (not (eq (point) after-type-id-pos)))) - (progn - (setq res - (c-forward-decl-or-cast-1 - (save-excursion (c-backward-syntactic-ws) (point)) - nil nil)) - (and (consp res) - (cond - ((eq (car res) after-type-id-pos)) - ((> (car res) after-type-id-pos) nil) - (t - (catch 'find-decl - (save-excursion - (goto-char (car res)) - (c-do-declarators - (point-max) t nil nil - (lambda (id-start _id-end _tok _not-top _func _init) - (cond - ((> id-start after-type-id-pos) - (throw 'find-decl nil)) - ((eq id-start after-type-id-pos) - (throw 'find-decl t))))) - nil))))))))) + (if (and (not (eq (point) after-type-id-pos)) + (or (not (looking-at c-class-key)) + (save-excursion + (goto-char (match-end 1)) + (c-forward-syntactic-ws) + (not (eq (point) after-type-id-pos))))) + (progn + (setq res + (c-forward-decl-or-cast-1 (c-point 'bosws) + nil nil)) + (and (consp res) + (cond + ((eq (car res) after-type-id-pos)) + ((> (car res) after-type-id-pos) nil) + (t + (catch 'find-decl + (save-excursion + (goto-char (car res)) + (c-do-declarators + (point-max) t nil nil + (lambda (id-start _id-end _tok _not-top _func _init) + (cond + ((> id-start after-type-id-pos) + (throw 'find-decl nil)) + ((eq id-start after-type-id-pos) + (throw 'find-decl t))))) + nil)))))) + (save-excursion + (goto-char start) + (not (c-looking-at-statement-block)))))) (cons bufpos (or in-paren inexpr-brace-list))) ((or (eq (char-after) ?\;) ;; Brace lists can't contain a semicolon, so we're done. @@ -12136,33 +12155,31 @@ comment at the start of cc-engine.el for more info." (defun c-looking-at-statement-block () ;; Point is at an opening brace. If this is a statement block (i.e. the ;; elements in the block are terminated by semicolons, or the block is - ;; empty, or the block contains a keyword) return non-nil. Otherwise, - ;; return nil. + ;; empty, or the block contains a characteristic keyword, or there is a + ;; nested statement block) return non-nil. Otherwise, return nil. (let ((here (point))) (prog1 (if (c-go-list-forward) (let ((there (point))) (backward-char) - (c-syntactic-skip-backward "^;," here t) + (c-syntactic-skip-backward "^;" here t) (cond - ((eq (char-before) ?\;) t) - ((eq (char-before) ?,) nil) - (t ; We're at (1+ here). - (cond - ((progn (c-forward-syntactic-ws) - (eq (point) (1- there)))) - ((c-syntactic-re-search-forward c-keywords-regexp there t)) - ((c-syntactic-re-search-forward "{" there t t) - (backward-char) - (c-looking-at-statement-block)) - (t nil))))) + ((eq (char-before) ?\;)) + ((progn (c-forward-syntactic-ws) + (eq (point) (1- there)))) + ((c-syntactic-re-search-forward + c-stmt-block-only-keywords-regexp there t)) + ((c-syntactic-re-search-forward "{" there t t) + (backward-char) + (c-looking-at-statement-block)) + (t nil))) (forward-char) (cond - ((c-syntactic-re-search-forward "[;,]" nil t t) - (eq (char-before) ?\;)) + ((c-syntactic-re-search-forward ";" nil t t)) ((progn (c-forward-syntactic-ws) (eobp))) - ((c-syntactic-re-search-forward c-keywords-regexp nil t t)) + ((c-syntactic-re-search-forward c-stmt-block-only-keywords-regexp + nil t t)) ((c-syntactic-re-search-forward "{" nil t t) (backward-char) (c-looking-at-statement-block)) diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 07479389c62..fa4e73087ef 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -3098,6 +3098,36 @@ Note that Java specific rules are currently applied to tell this from t (c-make-keywords-re t (c-lang-const c-keywords))) (c-lang-defvar c-keywords-regexp (c-lang-const c-keywords-regexp)) +(c-lang-defconst c-stmt-block-only-keywords + "All keywords which unambiguously signify a statement block (as opposed to + a brace list) when occurring inside braces." + t (c--set-difference + (c-lang-const c-keywords) + (append (c-lang-const c-primary-expr-kwds) + (c-lang-const c-constant-kwds) + `(,@(when (c-major-mode-is 'c++-mode) + '("typeid" "dynamic_cast" "static_cast" "const_cast" + "reinterpret_cast" "alignof"))) + (c-lang-const c-type-modifier-prefix-kwds) + (c-lang-const c-overloadable-operators) + (c-lang-const c-template-typename-kwds) + `(,@(when (c-major-mode-is 'c++-mode) + '("reflexpr"))) + `(,@(when (c-major-mode-is '(c-mode c++-mode)) + '("sizeof"))) + (c-lang-const c-pre-lambda-tokens) + (c-lang-const c-block-decls-with-vars) + (c-lang-const c-primitive-type-kwds)) + :test 'string-equal)) + +(c-lang-defconst c-stmt-block-only-keywords-regexp + ;; A regexp matching a keyword in `c-stmt-block-only-keywords'. Such a + ;; match can start and end only at token boundaries. + t (concat "\\(^\\|\\=\\|[^" (c-lang-const c-symbol-chars) "]\\)" + (c-make-keywords-re t (c-lang-const c-stmt-block-only-keywords)))) +(c-lang-defvar c-stmt-block-only-keywords-regexp + (c-lang-const c-stmt-block-only-keywords-regexp)) + (c-lang-defconst c-keyword-member-alist ;; An alist with all the keywords in the cars. The cdr for each ;; keyword is a list of the symbols for the `*-kwds' lists that @@ -3650,13 +3680,25 @@ list." c t) (c-lang-defvar c-recognize-knr-p (c-lang-const c-recognize-knr-p)) +(c-lang-defconst c-pre-id-bracelist-kwds + "Keywords which, preceding an identifier and brace, signify a bracelist. +This is only used in c++-mode." + t nil + c++ '("new" "throw")) + (c-lang-defconst c-pre-id-bracelist-key - "A regexp matching tokens which, preceding an identifier, signify a bracelist. -" - t regexp-unmatchable - c++ "new\\([^[:alnum:]_$]\\|$\\)\\|&&?\\(\\S.\\|$\\)") + ;; A regexp matching keywords which, preceding an identifier and brace, + ;; signify a bracelist. Only used in c++-mode. + t (c-make-keywords-re t (c-lang-const c-pre-id-bracelist-kwds))) (c-lang-defvar c-pre-id-bracelist-key (c-lang-const c-pre-id-bracelist-key)) +(c-lang-defconst c-pre-brace-non-bracelist-key + "A regexp matching tokens which, preceding a brace, make it a non-bracelist." + t regexp-unmatchable + c++ "&&?\\(\\S.\\|$\\)") +(c-lang-defvar c-pre-brace-non-bracelist-key + (c-lang-const c-pre-brace-non-bracelist-key)) + (c-lang-defconst c-recognize-typeless-decls "Non-nil means function declarations without return type should be recognized. That can introduce an ambiguity with parenthesized macro -- 2.39.2