Subject: bug#59267: CC Mode 5.35.2 (C/*l); implicit int declaration recognized as type

From: Alan Mackenzie <[email-masked]>
Date: Wed, 16 Nov 2022 21:50:31 +0000

Hello, Po.

Thanks, once again, for a high quality bug report!

On Mon, Nov 14, 2022 at 20:07:09 +0800, Po Lu via CC-Mode-help wrote:
> Package: cc-mode

> Place the following code in a C-mode buffer:

> {
>   register rtcnt,ipU;

>   ipU += rtcnt;
> }

> Notice that `rtcnt' is recognized as a type, but is actually a variable
> of type int put in a register.

Yes.  I think this would be very rare in modern code, depending on an
implicit type, int, following a specifier such as "register".  At least,
I hope so!

This was actually quite tricky to fix.  As well as "register", the code
should now handle the implicit int following (all of the?) other
specifiers, too.

Would you please try out the attached patch on your real code, and let
me know how well it works.  Thanks!

> Emacs  : GNU Emacs 29.0.50 (build 1, x86_64-pc-linux-gnu)
>  of 2022-11-12
> Package: CC Mode 5.35.2 (C/*l)
> Buffer Style: gnu
> c-emacs-features: (pps-extended-state col-0-paren posix-char-classes gen-string-delim gen-comment-delim syntax-properties category-properties 1-bit)

-- 
Alan Mackenzie (Nuremberg, Germany).

diff -r 7facafbca2e2 cc-engine.el
--- a/cc-engine.el	Mon Nov 14 09:31:02 2022 +0000
+++ b/cc-engine.el	Wed Nov 16 21:31:29 2022 +0000
@@ -9058,7 +9058,8 @@
   ;;   o - 'found if it's a type that matches one in `c-found-types';
   ;;   o - 'maybe if it's an identifier that might be a type;
   ;;   o - 'decltype if it's a decltype(variable) declaration; - or
-  ;;   o - 'no-id if "auto" precluded parsing a type identifier.
+  ;;   o - 'no-id if "auto" precluded parsing a type identifier (C++)
+  ;;      or the type int was implicit (C).
   ;;   o -  nil if it can't be a type (the point isn't moved then).
   ;;
   ;; The point is assumed to be at the beginning of a token.
@@ -9082,10 +9083,11 @@
 
     ;; Skip leading type modifiers.  If any are found we know it's a
     ;; prefix of a type.
-    (when c-opt-type-modifier-prefix-key ; e.g. "const" "volatile", but NOT "typedef"
-      (while (looking-at c-opt-type-modifier-prefix-key)
-	(when (looking-at c-no-type-key)
-	  (setq res 'no-id))
+    (when c-maybe-typeless-specifier-re
+      (while (looking-at c-maybe-typeless-specifier-re)
+	(save-match-data
+	  (when (looking-at c-no-type-key)
+	    (setq res 'no-id)))
 	(goto-char (match-end 1))
 	(c-forward-syntactic-ws)
 	(or (eq res 'no-id)
@@ -9150,6 +9152,9 @@
        (not (eq res 'no-id))
        (progn
 	 (setq pos nil)
+	 (while (and c-opt-cpp-prefix
+		     (looking-at c-noise-macro-with-parens-name-re))
+	   (c-forward-noise-clause))
 	 (if (looking-at c-identifier-start)
 	     (save-excursion
 	       (setq id-start (point)
@@ -9209,6 +9214,18 @@
 	    (goto-char (match-end 1))
 	    (c-forward-syntactic-ws)))))
 
+     ((and (eq name-res t)
+	   (eq res 'prefix)
+	   (c-major-mode-is 'c-mode)
+	   (save-excursion
+	     (goto-char id-end)
+	     (and (not (looking-at c-symbol-start))
+		  (not (looking-at c-type-decl-prefix-key)))))
+      ;; A C specifier followed by an implicit int, e.g.
+      ;; "register count;"
+      (goto-char id-start)
+      (setq res 'no-id))
+
      (name-res
       (cond ((eq name-res t)
 	     ;; A normal identifier.
@@ -9246,7 +9263,11 @@
 	    (t
 	     ;; Otherwise it's an operator identifier, which is not a type.
 	     (goto-char start)
-	     (setq res nil)))))
+	     (setq res nil))))
+
+     ((eq res 'prefix)
+      ;; Deal with "extern "C" foo_t my_foo;"
+      (setq res nil)))
 
     (when (not (memq res '(nil no-id)))
       ;; Skip trailing type modifiers.  If any are found we know it's
@@ -10034,9 +10055,11 @@
 	       got-suffix-after-parens id-start
 	       paren-depth 0))
 
-     (if (setq at-type (if (eq backup-at-type 'prefix)
-			   t
-			 backup-at-type))
+     (if (not (memq
+	       (setq at-type (if (eq backup-at-type 'prefix)
+				 t
+			       backup-at-type))
+	       '(nil no-id)))
 	 (setq type-start backup-type-start
 	       id-start backup-id-start)
        (setq type-start start-pos
@@ -11239,7 +11262,8 @@
 
       ;; Record the type's coordinates in `c-record-type-identifiers' for
       ;; later fontification.
-      (when (and c-record-type-identifiers at-type ;; (not (eq at-type t))
+      (when (and c-record-type-identifiers
+		 (not (memq at-type '(nil no-id)))
 		 ;; There seems no reason to exclude a token from
 		 ;; fontification just because it's "a known type that can't
 		 ;; be a name or other expression".  2013-09-18.
diff -r 7facafbca2e2 cc-langs.el
--- a/cc-langs.el	Mon Nov 14 09:31:02 2022 +0000
+++ b/cc-langs.el	Wed Nov 16 21:31:29 2022 +0000
@@ -3873,6 +3873,14 @@
 		     t)
 	 "\\>")))
 
+(c-lang-defconst c-maybe-typeless-specifier-re
+  "Regexp matching keywords which might, but needn't, declare variables with
+no explicit type given, or nil in languages without such specifiers."
+  t (c-lang-const c-opt-type-modifier-prefix-key)
+  c (c-lang-const c-type-decl-prefix-keywords-key))
+(c-lang-defvar c-maybe-typeless-specifier-re
+  (c-lang-const c-maybe-typeless-specifier-re))
+
 (c-lang-defconst c-type-decl-prefix-key
   "Regexp matching any declarator operator that might precede the
 identifier in a declaration, e.g. the \"*\" in \"char *argv\".  This