From 622724e95d6bcf858c52922e7d85423c8337bc06 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie <acm@muc.de> Date: Fri, 28 Oct 2022 17:50:33 +0000 Subject: [PATCH] CC Mode: Fontify cast types without adding them to c-found-types * lisp/progmodes/cc-engine.el (c-forward-type): Test for the special new value `just-one' of c-promote-possible-types, and if found, fontify the type, but don't add it to c-found-types. (c-forward-decl-or-cast-1): Add the new &optional parameter inside-macro. Whilst checking for a cast construct, analyze the text following the closing paren more rigorously. Check for, and allow, the closing paren of a macro arglist before the putative cast construct. * lisp/progmodes/cc-fonts.el (c-font-lock-declarations): In the lambda function, pass the parameter inside-macro to c-forward-decl-or-cast-1. * lisp/progmodes/cc-langs.el (c-primary-expr-regexp-details): New c-lang-defvar which calculates `c-primary-expr-regexp' and three match numbers for various sub-expressions in the regexp. (c-primary-expr-regexp): Now extracted from `c-primary-expr-regexp-details'. (c-per-++---match, c-per-&*+--match, c-per-\(-match): New c-lang-defconsts/vars extracted from `c-primary-expr-regexp-details'. --- lisp/progmodes/cc-engine.el | 62 ++++++++--- lisp/progmodes/cc-fonts.el | 2 +- lisp/progmodes/cc-langs.el | 207 +++++++++++++++++++++++++++--------- 3 files changed, 201 insertions(+), 70 deletions(-) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index ed06807a87e..d730fddeb08 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -8194,7 +8194,8 @@ multi-line strings (but not C++, for example)." ;; treat possible types (i.e. those that it normally returns 'maybe or ;; 'found for) as actual types (and always return 'found for them). ;; This means that it records them in `c-record-type-identifiers' if -;; that is set, and that it adds them to `c-found-types'. +;; that is set, and that if its value is t (not 'just-one), it adds +;; them to `c-found-types'. (defvar c-promote-possible-types nil) ;; Dynamically bound variable that instructs `c-forward-<>-arglist' to @@ -9191,10 +9192,11 @@ multi-line strings (but not C++, for example)." (goto-char id-end) (if (or res c-promote-possible-types) (progn - (c-add-type id-start (save-excursion - (goto-char id-end) - (c-backward-syntactic-ws) - (point))) + (when (not (eq c-promote-possible-types 'just-one)) + (c-add-type id-start (save-excursion + (goto-char id-end) + (c-backward-syntactic-ws) + (point)))) (when (and c-record-type-identifiers id-range) (c-record-type-id id-range)) (unless res @@ -10029,7 +10031,8 @@ This function might do hidden buffer changes." ;; This identifier is bound only in the inner let. '(setq start id-start)))) -(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end) +(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end + &optional inside-macro) ;; Move forward over a declaration or a cast if at the start of one. ;; The point is assumed to be at the start of some token. Nil is ;; returned if no declaration or cast is recognized, and the point @@ -10118,6 +10121,10 @@ This function might do hidden buffer changes." ;; matched. In that case it's used to discover chains of casts like ;; "(a) (b) c". ;; + ;; INSIDE-MACRO is t when we definitely know we're inside a macro, nil + ;; otherwise. We use it to disambiguate things like "(a) (b);", which is + ;; likely a function call in a macro, but a cast outside of one. + ;; ;; This function records identifier ranges on ;; `c-record-type-identifiers' and `c-record-ref-identifiers' if ;; `c-record-type-identifiers' is non-nil. @@ -11102,11 +11109,17 @@ This function might do hidden buffer changes." ;; Check if the expression begins with a prefix keyword. (match-beginning 2) (if (match-beginning 1) - ;; Expression begins with an ambiguous operator. Treat - ;; it as a cast if it's a type decl or if we've - ;; recognized the type somewhere else. - (or at-decl-or-cast - (memq at-type '(t known found))) + ;; Expression begins with an ambiguous operator. + (cond + ((match-beginning c-per-&*+--match) + (memq at-type '(t known found))) + ((match-beginning c-per-++---match) + t) + ((match-beginning c-per-\(-match) + (or + (memq at-type '(t known found)) + (not inside-macro))) + (t nil)) ;; Unless it's a keyword, it's the beginning of a primary ;; expression. (not (looking-at c-keywords-regexp))))) @@ -11132,18 +11145,33 @@ This function might do hidden buffer changes." ;; surrounding parens). (looking-at c-simple-stmt-key) (and - ;; Check that it isn't a close paren (block close is ok, - ;; though). - (not (memq (char-before) '(?\) ?\]))) + ;; Check that it isn't a close paren (block close , or a + ;; macro arglist is ok, though). + (or + (not (memq (char-before) '(?\) ?\]))) + ;; Have we moved back to a macro arglist? + (and c-opt-cpp-prefix + (eq (char-before) ?\)) + (save-excursion + (and + (c-go-list-backward) + (let (pos) + (c-backward-syntactic-ws) + (and (setq pos (c-on-identifier)) + (goto-char pos))) + (zerop (c-backward-token-2 2)) + (looking-at c-opt-cpp-macro-define-start))))) + ;; Check that it isn't a nonsymbol identifier. (not (c-on-identifier))))))))) ;; Handle the cast. (when (and c-record-type-identifiers at-type - (not (memq at-type '(t maybe)))) ; 'maybe isn't strong enough - ; evidence to promote the type. - (let ((c-promote-possible-types t)) + (not (eq at-type t))) + (let ((c-promote-possible-types (if (eq at-type 'maybe) + 'just-one + t))) (goto-char type-start) (c-forward-type))) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 5bb3e2e0b4c..608919d0c90 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1585,7 +1585,7 @@ casts and declarations are fontified. Used on level 2 and higher." nil) (setq decl-or-cast (c-forward-decl-or-cast-1 - match-pos context last-cast-end)) + match-pos context last-cast-end inside-macro)) ;; Ensure that c-<>-arg-sep c-type properties are in place on the ;; commas separating the arguments inside template/generic <..>s. diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index fc3977967b5..291af038b79 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -3489,76 +3489,179 @@ Note that Java specific rules are currently applied to tell this from (c-lang-defvar c-regular-keywords-regexp (c-lang-const c-regular-keywords-regexp)) -(c-lang-defconst c-primary-expr-regexp - ;; Regexp matching the start of any primary expression, i.e. any - ;; literal, symbol, prefix operator, and '('. It doesn't need to - ;; exclude keywords; they are excluded afterwards unless the second - ;; submatch matches. If the first but not the second submatch - ;; matches then it is an ambiguous primary expression; it could also - ;; be a match of e.g. an infix operator. (The case with ambiguous - ;; keyword operators isn't handled.) - +(c-lang-defconst c-primary-expr-regexp-details + ;; A list of c-primary-expr-regexp and three numbers identifying particular + ;; matches in it. t (let* ((prefix-ops + ;All prefix ops (c-filter-ops (c-lang-const c-operators) '(prefix) (lambda (op) ;; Filter out the special case prefix ;; operators that are close parens. (not (string-match "\\s)" op))))) - - (nonkeyword-prefix-ops - (c-filter-ops prefix-ops - t - "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'")) + (postfix-ops + ;; All postfix ops. + (c-filter-ops (c-lang-const c-operators) + '(postfix) + (lambda (op) (not (string-match "\\s)" op))))) (in-or-postfix-ops + ;; All ops which are postfix, etc. (c-filter-ops (c-lang-const c-operators) '(postfix postfix-if-paren left-assoc right-assoc right-assoc-sequence) - t))) + t)) - (concat - "\\(" - ;; Take out all symbol class operators from `prefix-ops' and make the - ;; first submatch from them together with `c-primary-expr-kwds'. - (c-make-keywords-re t - (append (c-lang-const c-primary-expr-kwds) - (c--set-difference prefix-ops nonkeyword-prefix-ops - :test 'string-equal))) - - "\\|" - ;; Match all ambiguous operators. - (c-make-keywords-re nil - (c--intersection nonkeyword-prefix-ops in-or-postfix-ops - :test 'string-equal)) - "\\)" - - "\\|" - ;; Now match all other symbols. - (c-lang-const c-symbol-start) - - "\\|" - ;; The chars that can start integer and floating point - ;; constants. - "\\.?[0-9]" - - "\\|" - ;; The unambiguous operators from `prefix-ops'. - (c-make-keywords-re nil - (c--set-difference nonkeyword-prefix-ops in-or-postfix-ops - :test 'string-equal)) - - "\\|" - ;; Match string and character literals. - "\\s\"" - (if (memq 'gen-string-delim c-emacs-features) - "\\|\\s|" - "")))) + (nonkeyword-prefix-ops + ;; All prefix ops apart from those which are keywords. + (c-filter-ops prefix-ops + t + "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'")) + (nonkeyword-postfix-ops + ;; All postfix ops apart from those which are keywords. + (c-filter-ops postfix-ops + t + "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'")) + + (cast-ops + ;; All prefix ops which have syntax open-paren. + (c-filter-ops prefix-ops + t + "\\`\\s(\\'")) + + (ambiguous-pre/postfix-ops + ;; All non-keyword ops which are both prefix and postfix, apart + ;; from (. + (c--set-difference (c--intersection nonkeyword-prefix-ops + nonkeyword-postfix-ops + :test 'string-equal) + cast-ops :test 'string-equal)) + (unambiguous-prefix-ops + ;; All non-keyword ops which are prefix ops and not any other type + ;; of op. + (c--set-difference nonkeyword-prefix-ops + in-or-postfix-ops + :test 'string-equal)) + (ambiguous-prefix-ops + ;; All non-keyword ops which are prefix ops and also some other + ;; type of op. + (c--intersection nonkeyword-prefix-ops + in-or-postfix-ops + :test 'string-equal)) ; This has everything we + ; need, plus (, ++, --. + + (ambiguous-prefix-non-postfix-ops + ;; All non-keyword prefix ops which are also other types of ops + ;; apart from postfix ops. + (c--set-difference (c--set-difference ambiguous-prefix-ops + ambiguous-pre/postfix-ops + :test 'string-equal) + cast-ops :test 'string-equal)) + + (primary-expression-keywords-string + ;; Take out all symbol class operators from `prefix-ops' and make + ;; the first submatch from them together with + ;; `c-primary-expr-kwds'. + (c-make-keywords-re t + (append (c-lang-const c-primary-expr-kwds) + (c--set-difference prefix-ops nonkeyword-prefix-ops + :test 'string-equal)))) + (primary-expression-keywords-string-depth + (regexp-opt-depth primary-expression-keywords-string)) + + (ambiguous-pre/postfix-string + (c-make-keywords-re nil ambiguous-pre/postfix-ops)) + (ambiguous-pre/postfix-string-depth + (regexp-opt-depth ambiguous-pre/postfix-string)) + + (ambiguous-prefix-non-postfix-string + (c-make-keywords-re nil ambiguous-prefix-non-postfix-ops)) + (ambiguous-prefix-non-postfix-string-depth + (regexp-opt-depth ambiguous-prefix-non-postfix-string)) + + (per-++---match (+ 2 primary-expression-keywords-string-depth)) + (per-&*+--match (+ 1 per-++---match + ambiguous-pre/postfix-string-depth)) + (per-\(-match (+ 1 per-&*+--match + ambiguous-prefix-non-postfix-string-depth))) + + (list + (concat + "\\(" ; 1 + primary-expression-keywords-string + "\\|" + ;; Match all ambiguous operators. + "\\(" ; 2 + primary-expression-keywords-string-depth + ambiguous-pre/postfix-string + "\\)\\|\\(" ; 3 + primary-expression-keywords-string-depth + ; + ambiguous-pre/postfix-string-depth + ambiguous-prefix-non-postfix-string + "\\)\\|" + "\\((\\)" ; 4 + primary-expression-keywords-string-depth + ; + ambiguous-pre/postfix-string-depth + ; + ambiguous-prefix-non-postfix-string-depth + "\\)" + + "\\|" + ;; Now match all other symbols. + (c-lang-const c-symbol-start) + + "\\|" + ;; The chars that can start integer and floating point + ;; constants. + "\\.?[0-9]" + + "\\|" + ;; The unambiguous operators from `prefix-ops'. + (c-make-keywords-re nil + ;; (c--set-difference nonkeyword-prefix-ops in-or-postfix-ops + ;; :test 'string-equal) + unambiguous-prefix-ops + ) + + "\\|" + ;; Match string and character literals. + "\\s\"" + (if (memq 'gen-string-delim c-emacs-features) + "\\|\\s|" + "")) + per-++---match + per-&*+--match + per-\(-match))) + +(c-lang-defconst c-primary-expr-regexp + ;; Regexp matching the start of any primary expression, i.e. any + ;; literal, symbol, prefix operator, and '('. It doesn't need to + ;; exclude keywords; they are excluded afterwards unless the second + ;; submatch matches. If the first but not the second submatch + ;; matches then it is an ambiguous primary expression; it could also + ;; be a match of e.g. an infix operator. (The case with ambiguous + ;; keyword operators isn't handled.) + t (car (c-lang-const c-primary-expr-regexp-details))) (c-lang-defvar c-primary-expr-regexp (c-lang-const c-primary-expr-regexp)) +(c-lang-defconst c-per-++---match + ;; Match number for group in `c-primary-expr-regexp' which matches (in C) + ;; the ++ and -- operators, and any similar ones in other languages. + t (cadr (c-lang-const c-primary-expr-regexp-details))) +(c-lang-defvar c-per-++---match (c-lang-const c-per-++---match)) + +(c-lang-defconst c-per-&*+--match + ;; Match number for group in `c-primary-expr-regexp' which matches (in C) + ;; the &, *, +, and - operators, and any similar ones in other languages. + t (car (cddr (c-lang-const c-primary-expr-regexp-details)))) +(c-lang-defvar c-per-&*+--match (c-lang-const c-per-&*+--match)) + +(c-lang-defconst c-per-\(-match + ;; Match number for group in `c-primary-expr-regexp' which matches (in C) + ;; the ( operator, and any similar ones in other languages. + t (cadr (cddr (c-lang-const c-primary-expr-regexp-details)))) +(c-lang-defvar c-per-\(-match (c-lang-const c-per-\(-match)) + ;;; Additional constants for parser-level constructs. -- 2.39.5