Subject: bug#5209: 23.1.90; CC Mode version 5.31.8 does not indent properly when writing new C files

From: Alan Mackenzie <[email-masked]>
Date: Sun, 13 Dec 2009 20:33:11 +0000

Hi, Steve,

On Sun, Dec 13, 2009 at 11:22:10AM -0500, Steve Revilak wrote:

> STEPS TO REPRODUCE
> ------------------

> (1) Start emacs with the command line "emacs -nw -Q foo.c".

>     foo.c should be a new file, which does not exist prior to starting
>     emacs.

> (2) Type "#include <stdio.h>" and RETURN

> (3) Type RETURN to leave a blank line

> (4) type "static void add_one(int * x) {" and RETURN

> (5) type "*x += 1;" and RETURN

> (6) type "}" and RETURN (to close the function definition)

> NOTE: the statement written in step (5) is left-aligned to column
> zero.  The statement should be indented.

> (7) Place point in column zero of the line "*x += 1;".  Press TAB.

>     Before pressing TAB, "*x += 1;" is aligned to column zero.  After
>     pressing TAB, "*x += 1;" is still aligned to column zero.  TAB did
>     not indent the statement.

> (8) Type "C-x h TAB" (i.e., to select and re-indent the entire
>     buffer).

>     The minibuffer displays "Indenting region... done".  However, the
>     statement "*x += 1;" is still aligned to column zero.

> After step 8, buffer "foo.c" looks like this:
> ---------------------------------
> #include <stdio.h>

> static void add_one(int * x) {
> *x += 1;
> }
> ---------------------------------
> NOTE: the fourth line is not indented (but it should be).

First thing, thanks for such a simple and clear bug report.

There is a bug in `c-parse-state', one of the low-level functions in CC
mode, which generates and changes a cache of brace and paren positions.

Would you apply the following patch, and test it, please.  If anything is
still wrong, please let me know again.

*** cc-engine.orig.el	2009-12-13 19:43:27.268817800 +0000
--- cc-engine.el	2009-12-13 20:06:47.819901672 +0000
***************
*** 2231,2236 ****
--- 2231,2249 ----
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Defuns which analyse the buffer, yet don't change `c-state-cache'.
+ (defun c-p1-not-in-p0-macro (p0 p1)
+   ;; Is P0 in a CPP construct and p1 not in it?
+   (save-restriction
+     (widen)
+     (save-excursion
+       (let ((p0-macro-begin
+ 	     (progn (goto-char p0) (and (c-beginning-of-macro) (point)))))
+ 	(and p0-macro-begin
+ 	     (not
+ 	      (eq p0-macro-begin
+ 		  (progn (goto-char p1) (and (c-beginning-of-macro) (point)))))
+ 	     )))))
+ 
  (defun c-get-fallback-scan-pos (here)
    ;; Return a start position for building `c-state-cache' from
    ;; scratch.  This will be at the top level, 2 defuns back.
***************
*** 2587,2593 ****
    ;; o - SCAN-BACK-POS, if not nil, indicates there may be a brace pair
    ;;   preceding POS which needs to be recorded in `c-state-cache'.  It is a
    ;;   position to scan backwards from.
!   ;; o - PPS-STATE is the parse-partial-sexp state at PPS-POINT.
    (save-restriction
      (narrow-to-region 1 (point-max))
      (save-excursion
--- 2600,2607 ----
    ;; o - SCAN-BACK-POS, if not nil, indicates there may be a brace pair
    ;;   preceding POS which needs to be recorded in `c-state-cache'.  It is a
    ;;   position to scan backwards from.
!   ;; o - PPS-STATE is the parse-partial-sexp state at PPS-POINT or nil if we
!   ;;   don't scan past PPS-POINT.
    (save-restriction
      (narrow-to-region 1 (point-max))
      (save-excursion
***************
*** 2624,2630 ****
  			   (< (point-max) c-state-old-cpp-end)))
  		  (point-max)
  		(min (point-max) c-state-old-cpp-beg)))
! 	(while (and c-state-cache (> (c-state-cache-top-lparen) upper-lim))
  	  (setq c-state-cache (cdr c-state-cache)))
  	;; If `upper-lim' is inside the last recorded brace pair, remove its
  	;; RBrace and indicate we'll need to search backwards for a previous
--- 2638,2645 ----
  			   (< (point-max) c-state-old-cpp-end)))
  		  (point-max)
  		(min (point-max) c-state-old-cpp-beg)))
! 	(while (and c-state-cache
! 		    (>= (c-state-cache-top-lparen) upper-lim))
  	  (setq c-state-cache (cdr c-state-cache)))
  	;; If `upper-lim' is inside the last recorded brace pair, remove its
  	;; RBrace and indicate we'll need to search backwards for a previous
***************
*** 2641,2647 ****
  	;; (car c-state-cache).  There can be no open parens/braces/brackets
  	;; between `good-pos'/`good-pos-actual-macro-start' and (point-max),
  	;; due to the interface spec to this function.
! 	(setq pos (if good-pos-actual-macro-end
  		      (1+ good-pos-actual-macro-end) ; get outside the macro as
  					; marked by a `category' text property.
  		    good-pos))
--- 2656,2663 ----
  	;; (car c-state-cache).  There can be no open parens/braces/brackets
  	;; between `good-pos'/`good-pos-actual-macro-start' and (point-max),
  	;; due to the interface spec to this function.
! 	(setq pos (if (and good-pos-actual-macro-start
! 			   (not (eq good-pos-actual-macro-start in-macro-start)))
  		      (1+ good-pos-actual-macro-end) ; get outside the macro as
  					; marked by a `category' text property.
  		    good-pos))
***************
*** 2701,2707 ****
  	  (setq c-state-cache (cons (cons pair-beg pos)
  				    c-state-cache)))
  
! 	(list pos scan-back-pos pps-state)))))
  
  (defun c-remove-stale-state-cache-backwards (here cache-pos)
    ;; Strip stale elements of `c-state-cache' by moving backwards through the
--- 2717,2723 ----
  	  (setq c-state-cache (cons (cons pair-beg pos)
  				    c-state-cache)))
  
! 	(list pos scan-back-pos pps-point-state)))))
  
  (defun c-remove-stale-state-cache-backwards (here cache-pos)
    ;; Strip stale elements of `c-state-cache' by moving backwards through the
***************
*** 2769,2777 ****
  	  (list (1+ pos) pos t)) ; return value.  We've just converted a brace
  			         ; pair entry into a { entry, so the caller
  			         ; needs to search for a brace pair before the
! 			         ; {.
  
!       ;; ;; `here' might be inside a literal.  Check for this.
        (setq lit (c-state-literal-at here)
  	    here-lit-start (or (car lit) here)
  	    here-lit-end (or (cdr lit) here))
--- 2785,2793 ----
  	  (list (1+ pos) pos t)) ; return value.  We've just converted a brace
  			         ; pair entry into a { entry, so the caller
  			         ; needs to search for a brace pair before the
! 			         ; {, hence the `pos' in second last place.
  
!       ;; `here' might be inside a literal.  Check for this.
        (setq lit (c-state-literal-at here)
  	    here-lit-start (or (car lit) here)
  	    here-lit-end (or (cdr lit) here))
***************
*** 2801,2812 ****
  		    (setq pos pa))))	; might signal
  	(if (setq ren (c-safe-scan-lists pos -1 -1 too-far-back))
  	    ;; CASE 3: After a }/)/] before `here''s BOL.
! 	    (list (1+ ren) (and dropped-cons pos) nil) ; Return value
  
  	  ;; CASE 4; Best of a bad job: BOL before `here-bol', or beginning of
  	  ;; literal containing it.
  	  (setq good-pos (c-state-lit-beg (c-point 'bopl here-bol)))
! 	  (list good-pos (and dropped-cons good-pos) nil))))))

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
--- 2817,2836 ----
  		    (setq pos pa))))	; might signal
  	(if (setq ren (c-safe-scan-lists pos -1 -1 too-far-back))
  	    ;; CASE 3: After a }/)/] before `here''s BOL.
! 	    (list (1+ ren)
! 		  (or (and (c-p1-not-in-p0-macro here c-state-cache-good-pos)
! 			   here)
! 		      (and dropped-cons pos))
! 		  nil) ; Return value
  
  	  ;; CASE 4; Best of a bad job: BOL before `here-bol', or beginning of
  	  ;; literal containing it.
  	  (setq good-pos (c-state-lit-beg (c-point 'bopl here-bol)))
! 	  (list good-pos
! 		(or (and (c-p1-not-in-p0-macro here c-state-cache-good-pos)
! 			 here)
! 		    (and dropped-cons good-pos))
! 		nil))))))

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

-- 
Alan Mackenzie (Nuremberg, Germany).