Subject: bug#47191: File as attachment

From: Alan Mackenzie <[email-masked]>
Date: Sat, 20 Mar 2021 20:30:54 +0000

Hello, Eli and Damien.

On Fri, Mar 19, 2021 at 17:28:03 +0200, Eli Zaretskii wrote:
> > From: Damien <[email-masked]>
> > Date: Wed, 17 Mar 2021 07:48:39 +0000

> > Also here is the file as attachment to make sure it was not a copy/paste error

> Thanks.

> Emacs infloops in redisplay when visiting this file, because
> fontification of this file infloops, and that happens on the master
> branch as well, so it has nothing to do with native-compilation
> branch.  The infloop seems to be in c-forward-name, I attach the
> backtrace below.

> Alan, could you please look into this?

[ .... ]

>   Lisp Backtrace:
>   "c-forward-name" (0x8234a0)
>   "c-forward-type" (0x823e30)
>   "c-forward-decl-or-cast-1" (0x825240)
>   0x6d7e6e0 PVEC_COMPILED
>   "c-find-decl-spots" (0x826d30)
>   "c-font-lock-declarations" (0x8273c8)
>   "font-lock-fontify-keywords-region" (0x827b40)
>   "font-lock-default-fontify-region" (0x828190)
>   "c-font-lock-fontify-region" (0x828808)
>   "font-lock-fontify-region" (0x828dc8)
>   0x6fdbd30 PVEC_COMPILED
>   "run-hook-wrapped" (0x829690)
>   "jit-lock--run-functions" (0x829cc0)
>   "jit-lock-fontify-now" (0x82a3e8)
>   "jit-lock-function" (0x82aa68)
>   "redisplay_internal (C function)" (0x0)

The trigger for the bug was a CC Mode "name" (the return type) which was
longer than 500 characters.  This 500 was a search limit introduced for
performance reasons around the turn of the year.  Part of the bug was
that when a forward scanning macro (c-forward-syntactic-ws) was given a
limit less than point, point was set back to that limit, causing the
loop.

The fix involves not ever moving point backwards in
c-forward-syntactic-ws, and recalculating the 500 character limit after
scanning a template construct; template expressions can easily be very
long.

Damien, would you please try out the following patch (the files are in
..../emacs/lisp/progmodes) on your real C++ source code, and either
confirm to us that the bug is fixed, or tell us what still needs fixing.
After applying the patch, please recompile _all_ of CC Mode (the files
..../emacs/progmodes/cc-*.el), since two macros have been amended.  If
you want any help with the patching or byte compiling, feel free to send
me private mail.  Thanks!

Here's the patch:

diff -r 11350de71bc6 cc-defs.el
--- a/cc-defs.el	Sat Mar 06 15:11:24 2021 +0000
+++ b/cc-defs.el	Sat Mar 20 20:08:58 2021 +0000
@@ -656,14 +656,16 @@
 
 LIMIT sets an upper limit of the forward movement, if specified.  If
 LIMIT or the end of the buffer is reached inside a comment or
-preprocessor directive, the point will be left there.
+preprocessor directive, the point will be left there.  If point starts
+on the wrong side of LIMIT, it stays unchanged.
 
 Note that this function might do hidden buffer changes.  See the
 comment at the start of cc-engine.el for more info."
   (if limit
-      `(save-restriction
-	 (narrow-to-region (point-min) (or ,limit (point-max)))
-	 (c-forward-sws))
+      `(when (< (point) (or ,limit (point-max)))
+	 (save-restriction
+	   (narrow-to-region (point-min) (or ,limit (point-max)))
+	   (c-forward-sws)))
     '(c-forward-sws)))
 
 (defmacro c-backward-syntactic-ws (&optional limit)
@@ -675,14 +677,16 @@
 
 LIMIT sets a lower limit of the backward movement, if specified.  If
 LIMIT is reached inside a line comment or preprocessor directive then
-the point is moved into it past the whitespace at the end.
+the point is moved into it past the whitespace at the end.  If point
+starts on the wrong side of LIMIT, it stays unchanged.
 
 Note that this function might do hidden buffer changes.  See the
 comment at the start of cc-engine.el for more info."
   (if limit
-      `(save-restriction
-	 (narrow-to-region (or ,limit (point-min)) (point-max))
-	 (c-backward-sws))
+      `(when (> (point) (or ,limit (point-min)))
+	 (save-restriction
+	   (narrow-to-region (or ,limit (point-min)) (point-max))
+	   (c-backward-sws)))
     '(c-backward-sws)))
 
 (defmacro c-forward-sexp (&optional count)
diff -r 11350de71bc6 cc-engine.el
--- a/cc-engine.el	Sat Mar 06 15:11:24 2021 +0000
+++ b/cc-engine.el	Sat Mar 20 20:08:58 2021 +0000
@@ -8318,7 +8318,7 @@
   ;; o - nil if no name is found;
   ;; o - 'template if it's an identifier ending with an angle bracket
   ;;   arglist;
-  ;; o - 'operator of it's an operator identifier;
+  ;; o - 'operator if it's an operator identifier;
   ;; o - t if it's some other kind of name.
   ;;
   ;; This function records identifier ranges on
@@ -8338,6 +8338,7 @@
 	(lim+ (c-determine-+ve-limit 500)))
     (while
 	(and
+	 (< (point) lim+)
 	 (looking-at c-identifier-key)
 
 	 (progn
@@ -8387,23 +8388,28 @@
 			  ;; '*', '&' or a name followed by ":: *",
 			  ;; where each can be followed by a sequence
 			  ;; of `c-opt-type-modifier-key'.
-			  (while (cond ((looking-at "[*&]")
-					(goto-char (match-end 0))
-					t)
-				       ((looking-at c-identifier-start)
-					(and (c-forward-name)
-					     (looking-at "::")
-					     (progn
-					       (goto-char (match-end 0))
-					       (c-forward-syntactic-ws lim+)
-					       (eq (char-after) ?*))
-					     (progn
-					       (forward-char)
-					       t))))
+			  (while 
+			      (and
+			       (< (point) lim+)
+			       (cond ((looking-at "[*&]")
+				      (goto-char (match-end 0))
+				      t)
+				     ((looking-at c-identifier-start)
+				      (and (c-forward-name)
+					   (looking-at "::")
+					   (progn
+					     (goto-char (match-end 0))
+					     (c-forward-syntactic-ws lim+)
+					     (eq (char-after) ?*))
+					   (progn
+					     (forward-char)
+					     t)))))
 			    (while (progn
 				     (c-forward-syntactic-ws lim+)
 				     (setq pos (point))
-				     (looking-at c-opt-type-modifier-key))
+				     (and
+				      (<= (point) lim+)
+				      (looking-at c-opt-type-modifier-key)))
 			      (goto-char (match-end 1))))))
 
 		       ((looking-at c-overloadable-operators-regexp)
@@ -8449,6 +8455,9 @@
 	       ;; Maybe an angle bracket arglist.
 	       (when (let (c-last-identifier-range)
 		       (c-forward-<>-arglist nil))
+		 ;; <> arglists can legitimately be very long, so recalculate
+		 ;; `lim+'.
+		 (setq lim+ (c-determine-+ve-limit 500))
 
 		 (c-forward-syntactic-ws lim+)
 		 (unless (eq (char-after) ?\()

-- 
Alan Mackenzie (Nuremberg, Germany).