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