Subject: Re: bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.)

From: Alan Mackenzie <[email-masked]>
Date: Sun, 28 Oct 2012 11:36:10 +0000

Hello again, Michael.

On Thu, Oct 25, 2012 at 09:41:11AM -0400, Michael Welsh Duggan wrote:
> A new recipe.  This is a strange one.  I hope it works for you as well.

> emacs -Q
> M-x tool-bar-mode
> resize the frame to be height 56 (according to the window manager)
> Load the included file
> M-x c-toggle-parse-state-debug
> C-s FIXME C-s
> Move cursor to the "o" on the next line (1175).
> Type: 
> PyTYPE( 
> C-f 
> )

> I get errors at the first open paren, and the close paren.  Resizing the
> fame is important!  I think it's due to caching that happens during font
> locking. 

Thanks for this one.

The cause was the cache invalidation function being called from
after-change-functions.  The newly inserted parens were fouling up the
invalidation algorithm.  :-(  The solution is to call that function from
before-change-functions instead.

The changes are currently as follows.  Would you try out the patch as
usual, please, and let me know how it goes.

diff -r ac6584d14c06 cc-engine.el
--- a/cc-engine.el	Sun Sep 09 11:15:13 2012 +0000
+++ b/cc-engine.el	Sun Oct 28 11:31:48 2012 +0000
@@ -2560,8 +2560,11 @@
 	      start-point cache-pos)))
 
     ;; Might we be better off starting from the top level, two defuns back,
-    ;; instead?
-    (when (> how-far c-state-cache-too-far)
+    ;; instead?  This heuristic no longer works well in C++, where
+    ;; declarations inside namespace brace blocks are frequently placed at
+    ;; column zero.
+    (when (and (not (c-major-mode-is 'c++-mode))
+	       (> how-far c-state-cache-too-far))
       (setq BOD-pos (c-get-fallback-scan-pos here)) ; somewhat EXPENSIVE!!!
       (if (< (- here BOD-pos) how-far)
 	  (setq strategy 'BOD
@@ -2648,17 +2651,19 @@
 	;; If we're essentially repeating a fruitless search, just give up.
 	(unless (and c-state-brace-pair-desert
 		     (eq cache-pos (car c-state-brace-pair-desert))
+		     (> from (car c-state-brace-pair-desert))
 		     (<= from (cdr c-state-brace-pair-desert)))
-	  ;; DESERT-LIM.  Only search what we absolutely need to,
+	  ;; DESERT-LIM.  Avoid repeated searching through the cached desert.
 	  (let ((desert-lim
 		 (and c-state-brace-pair-desert
 		      (eq cache-pos (car c-state-brace-pair-desert))
+		      (>= from (cdr c-state-brace-pair-desert))
 		      (cdr c-state-brace-pair-desert)))
 		;; CACHE-LIM.  This limit will be necessary when an opening
 		;; paren at `cache-pos' has just had its matching close paren
-		;; inserted.  `cache-pos' continues to be a search bound, even
-		;; though the algorithm below would skip over the new paren
-		;; pair.
+		;; inserted into the buffer.  `cache-pos' continues to be a
+		;; search bound, even though the algorithm below would skip
+		;; over the new paren pair.
 		(cache-lim (and cache-pos (< cache-pos from) cache-pos)))
 	    (narrow-to-region
 		(cond
@@ -3354,13 +3359,19 @@
   (fset 'c-real-parse-state (symbol-function 'c-parse-state)))
 (cc-bytecomp-defun c-real-parse-state)
 
+(defvar c-parse-state-point nil)
 (defvar c-parse-state-state nil)
 (make-variable-buffer-local 'c-parse-state-state)
 (defun c-record-parse-state-state ()
+  (setq c-parse-state-point (point))
   (setq c-parse-state-state
 	(mapcar
 	 (lambda (arg)
-	   (cons arg (symbol-value arg)))
+	   (let ((val (symbol-value arg)))
+	     (cons arg
+		   (if (consp val)
+		       (copy-tree val)
+		     val))))
 	 '(c-state-cache
 	   c-state-cache-good-pos
 	   c-state-nonlit-pos-cache
@@ -3373,7 +3384,8 @@
 	   c-state-point-min-lit-start
 	   c-state-min-scan-pos
 	   c-state-old-cpp-beg
-	   c-state-old-cpp-end))))
+	   c-state-old-cpp-end
+	   c-parse-state-point))))
 (defun c-replay-parse-state-state ()
   (message
    (concat "(setq "
@@ -3416,7 +3428,8 @@
       (message "Old state:")
       (c-replay-parse-state-state))
     (c-record-parse-state-state)
-    res1))
+    res2 ; res1 correct a cascading series of errors ASAP
+    ))
 
 (defun c-toggle-parse-state-debug (&optional arg)
   (interactive "P")
diff -r ac6584d14c06 cc-mode.el
--- a/cc-mode.el	Sun Sep 09 11:15:13 2012 +0000
+++ b/cc-mode.el	Sun Oct 28 11:31:48 2012 +0000
@@ -1058,7 +1058,10 @@
 	      (mapc (lambda (fn)
 		      (funcall fn beg end))
 		    c-get-state-before-change-functions))
-	  )))))
+	  )))
+    ;; The following must be done here rather than in `c-after-change' because
+    ;; newly inserted parens would foul up the invalidation algorithm.
+    (c-invalidate-state-cache beg)))
 
 (defvar c-in-after-change-fontification nil)
 (make-variable-buffer-local 'c-in-after-change-fontification)
@@ -1108,7 +1111,7 @@
 
 	(c-trim-found-types beg end old-len) ; maybe we don't need all of these.
 	(c-invalidate-sws-region-after beg end)
-	(c-invalidate-state-cache beg)
+	;; (c-invalidate-state-cache beg) ; moved to `c-before-change'.
 	(c-invalidate-find-decl-cache beg)
 
 	(when c-recognize-<>-arglists

> -- 
> Michael Welsh Duggan
> ([email-masked])

-- 
Alan Mackenzie (Nuremberg, Germany).