Subject: Re: bug#28050: CC Mode 5.33 (C/*l); indentation of array and enum in GNU style isn't right

From: Alan Mackenzie <[email-masked]>
Date: Tue, 25 Sep 2018 19:32:21 +0000

Hello, Mohammed.

On Sun, Sep 23, 2018 at 05:47:29 +0530, Mohammed Sadiq wrote:
> On 2018-09-23 01:00, Alan Mackenzie wrote:
> Hi,

> > It's been over a year since you reported the bug below.  I'm
> > top-posting this, so that you see exactly what you wrote, it being so
> > long ago.  Sorry about this delay.  Are you still interested in this
> > bug?

> > This has been a difficult bug to solve, not for the cases you gave,
> > e.g.:

> >     typedef enum {
> >                   GOOD,
> > 		  BETTER,
> > 		  BEST
> >     };

> I think I filed a bug for all of them to be miss-indented (Not just the 
> first item)

Sorry, I believe you did indeed.

> ie, this:
>      typedef enum {
>                    GOOD,
>                    BAD,
>                    NICE,
>      };

>      typedef enum {GOOD,
>                    BETTER,
>                    BEST
>      };

> [ ... ]

> > However, the changes to implement these rules have produced a patch a
> > little under 600 lines long.  :-(  These changes are not quite
> > finished: some doc strings need writing, and some new line-up
> > functions need to be documented in the CC Mode manual.  However,
> > other than that it is ready.

> > Are you still interested in this bug?  If so, would you be prepared to
> > apply this ~600 line patch, compile it, and test it?  If so, I would be

> Sure I'm interested. I have been using the following in my .dir-locals
> as a fix for this:

> ((nil . ((fill-column . 80)))
>   (c-mode . ((c-file-style . "GNU")
>              (c-file-offsets
>               (brace-list-intro . +)))))

> > grateful.  :-)  If so 2, would you please tell me in your reply which
> > CC Mode sources you are using (I'm a bit sloppy with version
> > numbers).

> I'm using the builtin cc-mode in GNU Emacs 27.x (from git master branch)

OK.  Here's (part of) the patch.  I haven't included the amendment to the
CC Mode manual for now, but I doubt that will bother you too much.

Would you please follow the following directions carefully: it's all too
easy to end up with a mixture of old and new (I've done that several
times):

1. Please apply the patch to the files in ..../emacs/lisp/progmodes, then
byte compile CC Mode in its entirety.  Something like

    $ emacs -Q -batch -f batch-byte-compile lisp/progmodes/cc-*.el

will do it.  Then please do either:

2a. Move the newly generated .elc files to where your Emacs finds its CC
Mode, e.g. ..../emacs/lisp/progmodes.  Then restart Emacs.

OR

2b. (Assuming your initialisation files don't load a particular other
version of CC Mode) restart your Emacs using -L, like this:

    $ emacs <other arguments> -L path/to/new/CC/Mode

.  This should ensure that the old CC Mode doesn't prevent the new one
loading fully.

Please then remove your workaround from .dir-locals, and test the new CC
Mode with your real source code.  Please let me know how well it works,
telling me about any problems you might still find.

Thanks!

diff -r e38b65ad9145 cc-align.el
--- a/cc-align.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-align.el	Tue Sep 25 19:01:56 2018 +0000
@@ -1086,6 +1086,130 @@
 	      (vector (+ (current-column) c-basic-offset))))
 	(vector 0)))))
 
+(defun c-lineup-2nd-brace-entry-in-arglist (langelem)
+  "Lineup the second entry of a brace block under the first, when the first
+line is also contained in an arglist or an enclosing brace ON THAT LINE.
+
+I.e. handle something like the following:
+
+    set_line (line_t {point_t{0.4, 0.2},
+                      point_t{0.2, 0.5},       <---- brace-list-intro
+                      .....});
+             ^ enclosing parenthesis.
+
+The middle line of that example will have a syntactic context
+with three syntactic symbols, arglist-cont-nonempty, brace-list-intro, and
+brace-list-entry.
+
+This function is intended for use in a list.  If the construct
+being analyzed isn't like the preceding, the function returns nil.
+Otherwise it returns the function `c-lineup-arglist-intro-after-paren', which
+the caller then uses to perform indentation.
+
+Works with brace-list-intro."
+  ;; brace-list-intro and brace-list-entry are both present for the second
+  ;; entry of the list when the first entry is on the same line as the opening
+  ;; brace.
+  (and (assq 'brace-list-intro c-syntactic-context)
+       (assq 'brace-list-entry c-syntactic-context)
+       (or (assq 'arglist-cont-nonempty c-syntactic-context) ; "(" earlier on
+							     ; the line.
+	   (save-excursion		; "{" earlier on the line
+	     (goto-char (c-langelem-pos
+			 (assq 'brace-list-intro c-syntactic-context)))
+	     (and
+	      (eq (c-backward-token-2
+		   1 nil
+		   (c-point 'bol (c-langelem-pos
+				  (assq 'brace-list-entry
+					c-syntactic-context))))
+		  0)
+	      (eq (char-after) ?{))))
+       'c-lineup-arglist-intro-after-paren))
+
+(defun c-lineup-class-decl-init-+ (langelem)
+  "Line up the second entry of a class (etc.) initializer c-basic-offset
+characters in from the identifier when:
+\(i) The type is a class, struct, union, etc. (but not an enum);
+\(ii) There is a brace block in the type declaration, specifying it; and
+\(iii) The first element of the initializer is on the same line as its opening
+brace.
+
+I.e. we have a construct like this:
+
+    struct STR {
+        int i; float f;
+    } str_1 = {1, 1.7},
+        str_2 = {2,
+             3.1                   <---- brace-list-intro
+        };
+        <-->                       <---- c-basic-offset
+
+Note that the syntactic context of the brace-list-intro line also has a
+syntactic element with the symbol brace-list-entry.
+
+This function is intended for use in a list.  If the above structure isn't
+present, this function returns nil, allowing a different offset specification
+to indent the line.
+
+Works with: brace-list-intro."
+  (and (assq 'brace-list-intro c-syntactic-context)
+       (assq 'brace-list-entry c-syntactic-context)
+       (let ((init-pos (c-point 'boi (c-langelem-pos
+				      (assq 'brace-list-entry
+					    c-syntactic-context))))
+	     )
+	 (save-excursion
+	   (goto-char (c-langelem-pos (assq 'brace-list-intro
+					    c-syntactic-context)))
+	   (and
+	    (c-forward-class-decl)
+	    (not (c-do-declarators init-pos t nil nil nil))
+	    (eq (point) init-pos)
+	    (vector (+ (current-column) c-basic-offset)))))))
+
+(defun c-lineup-class-decl-init-after-brace (langelem)
+  "Line up the second entry of a class (etc.) initializer after its opening
+brace when:
+\(i) The type is a class, struct, union, etc. (but not an enum);
+\(ii) There is a brace block in the type declaration, specifying it; and
+\(iii) The first element of the initializer is on the same line as its opening
+brace.
+
+I.e. we have a construct like this:
+
+    struct STR {
+        int i; float f;
+    } str_1 = {1, 1.7},
+        str_2 = {2,
+                 3.1                   <---- brace-list-intro
+        };
+
+Note that the syntactic context of the brace-list-intro line also has a
+syntactic element with the symbol brace-list-entry.  Also note that this
+function works by returning the symbol `c-lineup-arglist-intro-after-paren',
+which the caller then uses to perform the indentation.
+
+This function is intended for use in a list.  If the above structure isn't
+present, this function returns nil, allowing a different offset specification
+to indent the line.
+
+Works with: brace-list-intro."
+  (and (assq 'brace-list-intro c-syntactic-context)
+       (assq 'brace-list-entry c-syntactic-context)
+       (let ((init-pos (c-point 'boi (c-langelem-pos
+				      (assq 'brace-list-entry
+					    c-syntactic-context))))
+	     )
+	 (save-excursion
+	   (goto-char (c-langelem-pos (assq 'brace-list-intro
+					    c-syntactic-context)))
+	   (and
+	    (c-forward-class-decl)
+	    (not (c-do-declarators init-pos t nil nil nil))
+	    (eq (point) init-pos)
+	    'c-lineup-arglist-intro-after-paren)))))
+
 (defun c-lineup-cpp-define (_langelem)
   "Line up macro continuation lines according to the indentation of
 the construct preceding the macro.  E.g.:
diff -r e38b65ad9145 cc-engine.el
--- a/cc-engine.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-engine.el	Tue Sep 25 19:01:56 2018 +0000
@@ -7936,49 +7936,28 @@
     (or res (goto-char here))
     res))
 
+(defun c-forward-class-decl ()
+  "From the beginning of a struct/union, etc. move forward to
+after the brace block which defines it, leaving point at the
+start of the next token and returning pointt.  On failure leave
+point unchanged and return nil."
+  (let ((here (point)))
+    (if
+	(and
+	 (looking-at c-class-key)
+	 (eq (c-forward-token-2) 0)
+	 (c-on-identifier)
+	 (eq (c-forward-token-2) 0)
+	 (eq (char-after) ?{)
+	 (c-go-list-forward))
+	(progn
+	  (c-forward-syntactic-ws)
+	  (point))
+      (goto-char here)
+      nil)))
 
 ;; Handling of large scale constructs like statements and declarations.
 
-;; Macro used inside `c-forward-decl-or-cast-1'.  It ought to be a
-;; defsubst or perhaps even a defun, but it contains lots of free
-;; variables that refer to things inside `c-forward-decl-or-cast-1'.
-(defmacro c-fdoc-shift-type-backward (&optional short)
-  ;; `c-forward-decl-or-cast-1' can consume an arbitrary length list
-  ;; of types when parsing a declaration, which means that it
-  ;; sometimes consumes the identifier in the declaration as a type.
-  ;; This is used to "backtrack" and make the last type be treated as
-  ;; an identifier instead.
-  `(progn
-     ,(unless short
-	;; These identifiers are bound only in the inner let.
-	'(setq identifier-type at-type
-	       identifier-start type-start
-	       got-parens nil
-	       got-identifier t
-	       got-suffix t
-	       got-suffix-after-parens id-start
-	       paren-depth 0))
-
-     (if (setq at-type (if (eq backup-at-type 'prefix)
-			   t
-			 backup-at-type))
-	 (setq type-start backup-type-start
-	       id-start backup-id-start)
-       (setq type-start start-pos
-	     id-start start-pos))
-
-     ;; When these flags already are set we've found specifiers that
-     ;; unconditionally signal these attributes - backtracking doesn't
-     ;; change that.  So keep them set in that case.
-     (or at-type-decl
-	 (setq at-type-decl backup-at-type-decl))
-     (or maybe-typeless
-	 (setq maybe-typeless backup-maybe-typeless))
-
-     ,(unless short
-	;; This identifier is bound only in the inner let.
-	'(setq start id-start))))
-
 (defun c-forward-declarator (&optional limit accept-anon)
   ;; Assuming point is at the start of a declarator, move forward over it,
   ;; leaving point at the next token after it (e.g. a ) or a ; or a ,).
@@ -8131,6 +8110,171 @@
       (goto-char here)
       nil)))
 
+(defun c-do-declarators (limit list not-top comma-prop cdd-function)
+  "Assuming point is at the start of a comma separated list of declarators,
+apply CDD-FUNCTION to each declarator (when LIST is non-nil) or just the
+first declarator (when LIST is nil).  When CDD-FUNCTION is nil, no
+function is applied.
+
+CDD-FUNCTION is supplied with 6 arguments:
+0. The start position of the declarator's identifier;
+1. The end position of this identifier;
+\[Note: if there is no identifier, as in int (*);, both of these are nil.]
+2. The position of the next token after the declarator (CLARIFY!!!).
+3. NOT-TOP;
+4. Non-nil if the identifier is of a function.
+5. When there is an initialization following the declarator (such as \"=
+....\" or \"( ....\".), the character which introduces this initialization,
+otherwise nil.
+
+Additionally, if COMMA-PROP is non-nil, mark the separating commas with
+this value of the c-type property, when LIST is non-nil.
+
+Stop at or before LIMIT (which may NOT be nil).
+
+If NOT-TOP is non-nil, we are not at the top-level (\"top-level\" includes
+being directly inside a class or namespace, etc.).
+
+Nil is always returned.  The function leaves point at the delimiter after
+the last declarator it processes.  FIXME!!! Check this (2018-09-21)
+
+This function might do hidden buffer changes."
+  (let
+      ((pos (point)) next-pos id-start id-end
+       decl-res got-func got-type got-init
+       c-last-identifier-range exhausted result)
+
+    ;; The following `while' fontifies a single declarator id each time round.
+    ;; It loops only when LIST is non-nil.
+    (while
+	(and (not exhausted)
+	     (setq decl-res (c-forward-declarator limit)))
+      (setq next-pos (point)
+	    id-start (car decl-res)
+	    id-end (cadr decl-res)
+	    got-func (and (eq (char-after) ?\()
+			  (or (not (c-major-mode-is 'c++-mode))
+			      (not not-top)
+			      (car (cddr (cddr decl-res))) ; Id is in
+					; parens, etc.
+			      (save-excursion
+				(forward-char)
+				(c-forward-syntactic-ws)
+				(looking-at "[*&]")))
+			  (not (car (cddr decl-res)))
+			  (or (not (c-major-mode-is 'c++-mode))
+			      (save-excursion
+				(let (c-last-identifier-range)
+				  (forward-char)
+				  (c-forward-syntactic-ws)
+				  (catch 'is-function
+				    (while
+					(progn
+					  (if (eq (char-after) ?\))
+					      (throw 'is-function t))
+					  (setq got-type (c-forward-type))
+					  (cond
+					   ((null got-type)
+					    (throw 'is-function nil))
+					   ((not (eq got-type 'maybe))
+					    (throw 'is-function t)))
+					  (c-forward-declarator nil t)
+					  (eq (char-after) ?,))
+				      (forward-char)
+				      (c-forward-syntactic-ws))
+				    t)))))
+	    got-init (and (cadr (cddr decl-res))
+			  (char-after)))
+
+      ;; Jump past any initializer or function prototype to see if
+      ;; there's a ',' to continue at.
+      (cond (got-func
+	     ;; Skip a parenthesized initializer (C++) or a function
+	     ;; prototype.
+	     (if (c-go-list-forward (point) limit) ; over the parameter list.
+		 (c-forward-syntactic-ws limit)
+	       (setq exhausted t)))	; unbalanced parens
+
+	    (got-init	; "=" sign OR opening "(", "[", or "{"
+	     ;; Skip an initializer expression.  If we're at a '='
+	     ;; then accept a brace list directly after it to cope
+	     ;; with array initializers.  Otherwise stop at braces
+	     ;; to avoid going past full function and class blocks.
+	     (if (and (if (and (eq got-init ?=)
+			       (= (c-forward-token-2 1 nil limit) 0)
+			       (looking-at "{"))
+			  (c-go-list-forward (point) limit)
+			t)
+		      ;; FIXME: Should look for c-decl-end markers here;
+		      ;; we might go far into the following declarations
+		      ;; in e.g. ObjC mode (see e.g. methods-4.m).
+		      (c-syntactic-re-search-forward "[;,{]" limit 'move t))
+		 (backward-char)
+	       (setq exhausted t)
+	       ))
+
+	    (t (c-forward-syntactic-ws limit)))
+
+      (if cdd-function
+	  (funcall cdd-function id-start id-end next-pos not-top got-func
+		   got-init))
+
+      ;; If a ',' is found we set pos to the next declarator and iterate.
+      (if (and list (< (point) limit) (looking-at ","))
+	  (progn
+	    (when comma-prop
+	      (c-put-char-property (point) 'c-type comma-prop))
+	    (when list
+	      (forward-char)
+	      (c-forward-syntactic-ws limit)
+	      (setq pos (point))))
+	(setq exhausted t)))
+
+    (if (> (point) pos)
+	t
+      (goto-char pos)
+      nil)))
+
+;; Macro used inside `c-forward-decl-or-cast-1'.  It ought to be a
+;; defsubst or perhaps even a defun, but it contains lots of free
+;; variables that refer to things inside `c-forward-decl-or-cast-1'.
+(defmacro c-fdoc-shift-type-backward (&optional short)
+  ;; `c-forward-decl-or-cast-1' can consume an arbitrary length list
+  ;; of types when parsing a declaration, which means that it
+  ;; sometimes consumes the identifier in the declaration as a type.
+  ;; This is used to "backtrack" and make the last type be treated as
+  ;; an identifier instead.
+  `(progn
+     ,(unless short
+	;; These identifiers are bound only in the inner let.
+	'(setq identifier-type at-type
+	       identifier-start type-start
+	       got-parens nil
+	       got-identifier t
+	       got-suffix t
+	       got-suffix-after-parens id-start
+	       paren-depth 0))
+
+     (if (setq at-type (if (eq backup-at-type 'prefix)
+			   t
+			 backup-at-type))
+	 (setq type-start backup-type-start
+	       id-start backup-id-start)
+       (setq type-start start-pos
+	     id-start start-pos))
+
+     ;; When these flags already are set we've found specifiers that
+     ;; unconditionally signal these attributes - backtracking doesn't
+     ;; change that.  So keep them set in that case.
+     (or at-type-decl
+	 (setq at-type-decl backup-at-type-decl))
+     (or maybe-typeless
+	 (setq maybe-typeless backup-maybe-typeless))
+
+     ,(unless short
+	;; This identifier is bound only in the inner let.
+	'(setq start id-start))))
+
 (defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end)
   ;; Move forward over a declaration or a cast if at the start of one.
   ;; The point is assumed to be at the start of some token.  Nil is
@@ -10608,12 +10752,17 @@
       )))
 
 (defun c-inside-bracelist-p (containing-sexp paren-state accept-in-paren)
-  ;; return the buffer position of the beginning of the brace list
-  ;; statement if we're inside a brace list, otherwise return nil.
-  ;; CONTAINING-SEXP is the buffer pos of the innermost containing
-  ;; paren.  PAREN-STATE is the remainder of the state of enclosing
-  ;; braces.  ACCEPT-IN-PAREN is non-nil iff we will accept as a brace
-  ;; list a brace directly enclosed in a parenthesis.
+  ;; return the buffer position of the beginning of the brace list statement
+  ;; if CONTAINING-SEXP is inside a brace list, otherwise return nil.
+  ;;
+  ;; CONTAINING-SEXP is the buffer pos of the innermost containing paren.  NO
+  ;; IT ISN'T!!!  [This function is badly designed, and probably needs
+  ;; reformulating without its first argument, and the critical position being
+  ;; at point.]
+  ;;
+  ;; PAREN-STATE is the remainder of the state of enclosing braces.
+  ;; ACCEPT-IN-PAREN is non-nil iff we will accept as a brace list a brace
+  ;; directly enclosed in a parenthesis.
   ;;
   ;; The "brace list" here is recognized solely by its context, not by
   ;; its contents.
@@ -10733,7 +10882,8 @@
 (defun c-looking-at-statement-block ()
   ;; Point is at an opening brace.  If this is a statement block (i.e. the
   ;; elements in the block are terminated by semicolons, or the block is
-  ;; empty, or the block contains a keyword) return t.  Otherwise, return nil.
+  ;; empty, or the block contains a keyword) return non-nil.  Otherwise,
+  ;; return nil.
   (let ((here (point)))
     (prog1
 	(if (c-go-list-forward)
@@ -11237,7 +11387,7 @@
 
 	    (if (and (eq step-type 'same)
 		     (/= paren-pos (point)))
-		(let (inexpr)
+		(let (inexpr bspec)
 		  (cond
 		   ((save-excursion
 		      (goto-char paren-pos)
@@ -11259,6 +11409,13 @@
 			  (c-looking-at-statement-block))
 			(c-add-syntax 'defun-block-intro nil)
 		      (c-add-syntax 'brace-list-intro nil)))
+		   ((save-excursion
+		      (goto-char paren-pos)
+		      (setq bspec (c-looking-at-or-maybe-in-bracelist
+				   containing-sexp containing-sexp))
+		      (and (consp bspec)
+			   (eq (cdr bspec) 'in-paren)))
+		    (c-add-syntax 'brace-list-intro (car bspec)))
 		   (t (c-add-syntax 'defun-block-intro nil))))
 
 	      (c-add-syntax 'statement-block-intro nil)))
@@ -13082,7 +13239,7 @@
 Cannot combine relative offset %S with absolute %S in `%s' method"
 				       (car offset) symbol res val method)
 		     (setq res (vector (funcall method (aref res 0)
-						(aref val 0)))))))
+			   			(aref val 0)))))))
 		 (setq offset (cdr offset)))
 	       res))
 
@@ -13126,7 +13283,7 @@
 	   nil))))
 
     (if (or (null res) (integerp res)
-	    (and (vectorp res) (= (length res) 1) (integerp (aref res 0))))
+	    (and (vectorp res) (>= (length res) 1) (integerp (aref res 0))))
 	res
       (c-benign-error "Error evaluating offset %S for %s: Got invalid value %S"
 		      offset symbol res)
@@ -13149,12 +13306,11 @@
       (if c-strict-syntax-p
 	  (c-benign-error "No offset found for syntactic symbol %s" symbol))
       (setq offset 0))
-    (if (vectorp offset)
-	offset
-      (or (and (numberp offset) offset)
-	  (and (symbolp offset) (symbol-value offset))
-	  0))
-    ))
+    (cond
+     ((or (vectorp offset) (numberp offset))
+      offset)
+     ((and (symbolp offset) (symbol-value offset)))
+     (t 0))))
 
 (defun c-get-offset (langelem)
   ;; This is a compatibility wrapper for `c-calc-offset' in case
diff -r e38b65ad9145 cc-fonts.el
--- a/cc-fonts.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-fonts.el	Tue Sep 25 19:01:56 2018 +0000
@@ -1029,114 +1029,41 @@
 
   ;;(message "c-font-lock-declarators from %s to %s" (point) limit)
   (c-fontify-types-and-refs
-      ((pos (point)) next-pos id-start
-       decl-res
-       id-face got-type got-init
-       c-last-identifier-range
-       (separator-prop (if types 'c-decl-type-start 'c-decl-id-start)))
-
-    ;; The following `while' fontifies a single declarator id each time round.
-    ;; It loops only when LIST is non-nil.
-    (while
-	(and pos (setq decl-res (c-forward-declarator)))
-      (setq next-pos (point)
-	    id-start (car decl-res)
-	    id-face (if (and (eq (char-after) ?\()
-			     (or (not (c-major-mode-is 'c++-mode))
-				 (not not-top)
-				 (car (cddr (cddr decl-res))) ; Id is in
-							      ; parens, etc.
-				 (save-excursion
-				   (forward-char)
-				   (c-forward-syntactic-ws)
-				   (looking-at "[*&]")))
-			     (not (car (cddr decl-res)))
-			     (or (not (c-major-mode-is 'c++-mode))
-				 (save-excursion
-				   (let (c-last-identifier-range)
-				     (forward-char)
-				     (c-forward-syntactic-ws)
-				     (catch 'is-function
-				       (while
-					   (progn
-					     (if (eq (char-after) ?\))
-						 (throw 'is-function t))
-					     (setq got-type (c-forward-type))
-					     (cond
-					      ((null got-type)
-					       (throw 'is-function nil))
-					      ((not (eq got-type 'maybe))
-					       (throw 'is-function t)))
-					     (c-forward-declarator nil t)
-					     (eq (char-after) ?,))
-					 (forward-char)
-					 (c-forward-syntactic-ws))
-				       t)))))
-			'font-lock-function-name-face
-		      'font-lock-variable-name-face)
-	    got-init (and (cadr (cddr decl-res)) ; got-init
-			  (char-after)))
-
-      (if types
-	  ;; Register and fontify the identifier as a type.
-	  (let ((c-promote-possible-types t))
-	    (goto-char id-start)
-	    (c-forward-type))
-	;; Fontify the last symbol in the identifier if it isn't fontified
-	;; already.  The check is necessary only in certain cases where this
-	;; function is used "sloppily", e.g. in `c-simple-decl-matchers'.
-	(when (and c-last-identifier-range
-		   (not (get-text-property (car c-last-identifier-range)
-					   'face)))
-	  (c-put-font-lock-face (car c-last-identifier-range)
-				(cdr c-last-identifier-range)
-				id-face)))
-
-      (goto-char next-pos)
-      (setq pos nil)	      ; So as to terminate the enclosing `while' form.
-      (if (and template-class
-	       (eq got-init ?=) ; C++ "<class X = Y>"?
-	       (c-forward-token-2 1 nil limit) ; Over "="
-	       (let ((c-promote-possible-types t))
-		 (c-forward-type t)))	       ; Over "Y"
-	  (setq list nil)) ; Shouldn't be needed.  We can't have a list, here.
-
-      (when list
-	;; Jump past any initializer or function prototype to see if
-	;; there's a ',' to continue at.
-	(cond ((eq id-face 'font-lock-function-name-face)
-	       ;; Skip a parenthesized initializer (C++) or a function
-	       ;; prototype.
-	       (if (c-safe (c-forward-sexp 1) t) ; over the parameter list.
-		   (c-forward-syntactic-ws limit)
-		 (goto-char limit)))	; unbalanced parens
-
-	      (got-init	; "=" sign OR opening "(", "[", or "{"
-	       ;; Skip an initializer expression.  If we're at a '='
-	       ;; then accept a brace list directly after it to cope
-	       ;; with array initializers.  Otherwise stop at braces
-	       ;; to avoid going past full function and class blocks.
-	       (and (if (and (eq got-init ?=)
-			     (= (c-forward-token-2 1 nil limit) 0)
-			     (looking-at "{"))
-			(c-safe (c-forward-sexp) t) ; over { .... }
-		      t)
-		    (< (point) limit)
-		    ;; FIXME: Should look for c-decl-end markers here;
-		    ;; we might go far into the following declarations
-		    ;; in e.g. ObjC mode (see e.g. methods-4.m).
-		    (c-syntactic-re-search-forward "[;,{]" limit 'move t)
-		    (backward-char)))
-
-	      (t (c-forward-syntactic-ws limit)))
-
-	;; If a ',' is found we set pos to the next declarator and iterate.
-	(when (and (< (point) limit) (looking-at ","))
-	  (c-put-char-property (point) 'c-type separator-prop)
-	  (forward-char)
-	  (c-forward-syntactic-ws limit)
-	  (setq pos (point))))))     ; acts to make the `while' form continue.
-  nil)
+      ()
+    (c-do-declarators
+     limit list not-top
+     (if types 'c-decl-type-start 'c-decl-id-start)
+     (lambda (id-start id-end end-pos not-top is-function init-char)
+       (if types
+	   ;; Register and fontify the identifier as a type.
+	   (let ((c-promote-possible-types t))
+	     (goto-char id-start)
+	     (c-forward-type))
+	 ;; The following doesn't work properly (yet, 2018-09-22).
+	 ;; (c-put-font-lock-face id-start id-end
+	 ;; 		       (if is-function
+	 ;; 			   'font-lock-function-name-face
+	 ;; 			 'font-lock-variable-name-face))
+	 (when (and c-last-identifier-range
+	 	    (not (get-text-property (car c-last-identifier-range)
+	 				    'face)))
+	   ;; We use `c-last-identifier-range' rather than `id-start' and
+	   ;; `id-end', since the latter two can be erroneous.  E.g. in
+	   ;; "~Foo", `id-start' is at the tilde.  This is a bug in
+	   ;; `c-forward-declarator'.
+	   (c-put-font-lock-face (car c-last-identifier-range)
+	 			 (cdr c-last-identifier-range)
+	 			 (if is-function
+	 			     'font-lock-function-name-face
+	 			   'font-lock-variable-name-face))))
+       (and template-class
+	    (eq init-char ?=)		; C++ "<class X = Y>"?
+	    (progn
+	      (goto-char end-pos)
+	      (c-forward-token-2 1 nil limit) ; Over "="
+	      (let ((c-promote-possible-types t))
+		(c-forward-type t))))))
+    nil))
 
 (defun c-get-fontification-context (match-pos not-front-decl &optional toplev)
   ;; Return a cons (CONTEXT . RESTRICTED-<>-ARGLISTS) for MATCH-POS.
diff -r e38b65ad9145 cc-langs.el
--- a/cc-langs.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-langs.el	Tue Sep 25 19:01:56 2018 +0000
@@ -3221,7 +3221,8 @@
 	       "\\|"
 	       "\\.\\.\\."
 	       "\\|"
-	       "[*(&]"
+;	       "[*(&]"
+	       "[*(&~]"
 	       "\\|"
 	       (c-lang-const c-type-decl-prefix-key)
 	       "\\|"
diff -r e38b65ad9145 cc-mode.el
--- a/cc-mode.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-mode.el	Tue Sep 25 19:01:56 2018 +0000
@@ -1781,7 +1781,20 @@
     (while
 	;; Go to a less nested declaration each time round this loop.
 	(and
-	 (c-syntactic-skip-backward "^;{}" bod-lim t)
+;;;; OLD STOUGH, 2018-09-22
+	 ;(c-syntactic-skip-backward "^;{}" bod-lim t)
+;;;; NEW STOUGH, 2018-09-22
+	 (let (pseudo)
+	   (while
+	       (progn
+		 (c-syntactic-skip-backward "^;{}" bod-lim t)
+		 (and (eq (char-before) ?})
+		      (save-excursion
+			(backward-char)
+			(setq pseudo (c-cheap-inside-bracelist-p (c-parse-state))))))
+	     (goto-char pseudo))
+	   t)
+;;;; END OF NEW STOUGH
 	 (> (point) bod-lim)
 	 (progn (c-forward-syntactic-ws)
 		(setq bo-decl (point))
diff -r e38b65ad9145 cc-styles.el
--- a/cc-styles.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-styles.el	Tue Sep 25 19:01:56 2018 +0000
@@ -73,7 +73,9 @@
 			 (arglist-close . c-lineup-arglist)
 			 (inline-open . 0)
 			 (brace-list-open . +)
-			 (brace-list-intro . c-lineup-arglist-intro-after-paren)
+			 (brace-list-intro . (first
+					      c-lineup-2nd-brace-entry-in-arglist
+					      c-lineup-class-decl-init-+ +))
 			 (topmost-intro-cont
 			  . (first c-lineup-topmost-intro-cont
 				   c-lineup-gnu-DEFUN-intro-cont))))
@@ -100,6 +102,9 @@
 			 (label . 0)
 			 (statement-cont . +)
 			 (inline-open . 0)
+			 (brace-list-intro . (first
+					      c-lineup-2nd-brace-entry-in-arglist
+					      c-lineup-class-decl-init-+ +))
 			 (inexpr-class . 0))))
 
     ("stroustrup"
@@ -109,6 +114,9 @@
 			 (substatement-open . 0)
 			 (substatement-label . 0)
 			 (label . 0)
+			 (brace-list-intro . (first
+					      c-lineup-2nd-brace-entry-in-arglist
+					      c-lineup-class-decl-init-+ +))
 			 (statement-cont . +))))
 
     ("whitesmith"
@@ -199,6 +207,9 @@
      (c-offsets-alist  . ((substatement-open . 0)
 			  (inextern-lang . 0)
 			  (arglist-intro . +)
+			  (brace-list-intro . (first
+					       c-lineup-2nd-brace-entry-in-arglist
+					       c-lineup-class-decl-init-+ +))
 			  (knr-argdecl-intro . +)))
      (c-hanging-braces-alist . ((brace-list-open)
 				(brace-list-intro)
@@ -224,6 +235,9 @@
 			 (statement-cont        . +)
 			 (arglist-intro  . c-lineup-arglist-intro-after-paren)
 			 (arglist-close  . c-lineup-arglist)
+			 (brace-list-intro . (first
+					      c-lineup-2nd-brace-entry-in-arglist
+					      c-lineup-class-decl-init-+ +))
 			 (access-label   . 0)
 			 (inher-cont     . c-lineup-java-inher)
 			 (func-decl-cont . c-lineup-java-throws))))
diff -r e38b65ad9145 cc-vars.el
--- a/cc-vars.el	Mon Aug 27 20:23:23 2018 +0000
+++ b/cc-vars.el	Tue Sep 25 19:01:56 2018 +0000
@@ -1117,7 +1117,7 @@
        ;; Anchor pos: At the brace list decl start(*).
        (brace-list-intro      . +)
        ;; Anchor pos: At the brace list decl start(*).
-       (brace-list-entry      . c-lineup-under-anchor)
+       (brace-list-entry      . 0)
        ;; Anchor pos: At the first non-ws char after the open paren if
        ;; the first token is on the same line, otherwise boi at that
        ;; token.

> [ ... ]

> Thanks

-- 
Alan Mackenzie (Nuremberg, Germany).