Subject: Re: bug#56841: Emacs-28 C Mode: Fontification errors when arglist closing ) is on next line

From: Alan Mackenzie <[email-masked]>
Date: Tue, 2 Aug 2022 19:30:43 +0000

Hello again, Bill.

On Sat, Jul 30, 2022 at 17:14:43 +0300, Eli Zaretskii wrote:
> > Cc: Bill Sacks <[email-masked]>
> > Date: Sat, 30 Jul 2022 13:05:24 +0000
> > From: Alan Mackenzie <[email-masked]>

> > 1. Start emacs -Q.
> > 2. Insert the following file in C Mode:

> > void myfunc(

> >   ) {

> > }

> > 3. In line 2 (the first blank line) type "<TAB>int somevar".
> > 4. Note that somevar is not fontified.  This is a bug, given that the
> >   arglist to myfunc is terminated with a ) on line 3.
> > 5. Do something (e.g. typing M-x) to cause a redisplay.  somevar gets
> >   its correct face.
> > 6. Note that any insertion or deletion in L2 causes somevar to lose its
> >   fontification.  This is a bug.
> > 7. (After 6).  Move point onto somevar and do C-u C-x =.  This shows
> >   that the face text property is set on the character despite the face
> >   not appearing on the screen.

This bug, although the symptoms were similar to the other bug you
reported, was an entirely different bug, more difficult to fix.

I now have a patch for it, and would ask you to apply the patch to your
Emacs 28.1 and byte compile the files in it, as before.  (As before,
help is available from me by private email.)  Then please test it on
your actual C code and let us know how it went.  Thanks!

diff -r e4e62074b8a6 cc-engine.el
--- a/cc-engine.el	Sat Jul 30 09:15:53 2022 +0000
+++ b/cc-engine.el	Tue Aug 02 19:14:15 2022 +0000
@@ -9576,7 +9576,7 @@
 	 (or (= paren-depth 0)
 	     (c-safe (goto-char (scan-lists (point) 1 paren-depth))))
 
-	 (<= (point) limit)
+	 (< (point) limit)
 
 	 ;; Skip over any trailing bit, such as "__attribute__".
 	 (progn
diff -r e4e62074b8a6 cc-mode.el
--- a/cc-mode.el	Sat Jul 30 09:15:53 2022 +0000
+++ b/cc-mode.el	Tue Aug 02 19:14:15 2022 +0000
@@ -2412,49 +2412,59 @@
       (and (/= new-pos pos) new-pos))))
 
 (defun c-fl-decl-end (pos)
-  ;; If POS is inside a declarator, return the end of the token that follows
-  ;; the declarator, otherwise return nil.  POS being in a literal does not
-  ;; count as being in a declarator (on pragmatic grounds).  POINT is not
-  ;; preserved.
+  ;; If POS is inside a declarator, return the position of the end of the
+  ;; paren pair that terminates it, or of the end of the token that follows
+  ;; the declarator, otherwise return nil.  If there is no such token, the end
+  ;; of the last token in the buffer is used.  POS being in a literal is now
+  ;; (2022-07) handled correctly.  POINT is not preserved.
   (goto-char pos)
   (let ((lit-start (c-literal-start))
 	(lim (c-determine-limit 1000))
 	enclosing-attribute pos1)
-    (unless lit-start
-      (c-backward-syntactic-ws
-       lim)
-      (when (setq enclosing-attribute (c-enclosing-c++-attribute))
-	(goto-char (car enclosing-attribute))) ; Only happens in C++ Mode.
-      (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 (eq (char-after) ?\()
-		   (and
-		    (c-go-list-forward nil lim)
-		    (progn (c-forward-syntactic-ws lim)
-			   (not (eobp)))
-		    (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))))
-		 (and (progn (c-forward-syntactic-ws lim)
-			     (not (eobp)))
+    (if lit-start
+	(goto-char lit-start))
+    (c-backward-syntactic-ws lim)
+    (when (setq enclosing-attribute (c-enclosing-c++-attribute))
+      (goto-char (car enclosing-attribute)) ; Only happens in C++ Mode.
+      (c-backward-syntactic-ws lim))
+    (while (and (> (point) lim)
+		(memq (char-before) '(?\[ ?\()))
+      (backward-char)
+      (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))
+		 (and
+		  (progn (c-forward-syntactic-ws lim)
+			 (not (eobp)))
+		  (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)))))))))
+		      (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))))))))
 
 (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

-- 
Alan Mackenzie (Nuremberg, Germany).