Subject: bug#62841: CC Mode 5.35.2 (Group); Template indentation problems

From: Alan Mackenzie <[email-masked]>
Date: Wed, 17 May 2023 16:14:39 +0000

Hello, Michael.

On Sun, Apr 23, 2023 at 13:44:29 +0000, Alan Mackenzie wrote:
> Thanks for the bug report.

> On Fri, Apr 14, 2023 at 14:18:16 -0400, Michael Welsh Duggan wrote:
> > Package: cc-mode

> > Package: cc-mode

> > Load the included file, bar.cpp:

> > template <typename T>
> > using value_variant=
> >   std::pair<
> >   std::variant<
> >     ipfix::octetArray_t,
> >     ipfix::unsigned8_t,
> >     ipfix::unsigned16_t,
> >     ipfix::unsigned32_t,
> >     ipfix::unsigned64_t,
> >     ipfix::signed8_t,
> >     ipfix::signed16_t,
> >     ipfix::signed32_t,
> >     ipfix::signed64_t,
> >     ipfix::float32_t,
> >     ipfix::float64_t,
> >     ipfix::boolean_t,
> >     ipfix::macAddress_t,
> >     ipfix::string_t,
> >     ipfix::dateTimeSeconds_t,
> >     ipfix::dateTimeMilliseconds_t,
> >     ipfix::dateTimeMicroseconds_t,
> >     ipfix::dateTimeNanoseconds_t,
> >     ipfix::ipv4Address_t,
> >     ipfix::ipv6Address_t,
> >     std::pair<ipfix::InfoElementID,std::vector<T>>,
> >     std::pair<ipfix::TemplateID,std::vector<std::vector<T>>>,
> >     std::pair<std::vector<
> >                 std::pair<
> >                   ipfix::TemplateID,
> >                   std::vector<std::vector<T>>>>>>,
> >   ipfix::length_t>;

> > For example:

> > emacs -Q /tmp/bar.cpp

> > Type:
> > C-x h C-M-\

> > First, note that std::variant is not indented with respect to std::pair.

I have amended the indentation logic, by introducing a new line-up
function, so this should be fixed, now.

> There are actually two distinct bugs in play, here.  Up till now I've
> been concentrating on the second one ....

> > Next, delete and re-insert the final angle bracket on the line that
> > reads:

> >     std::pair<ipfix::InfoElementID,std::vector<T>>,

> > Note the re-indention.

This, together with the other indentation problems, I hope are fixed,
too.

[ .... ]

Would you please test the enclosed patch, which should apply cleanly to
the Emacs master branch.  And then let me know whether there are any
problems with it.  Thanks!

Just, there might be unfound interactions between the template markers
and raw strings, but I hope not.

> > Emacs  : GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, X toolkit, cairo =
> > version 1.16.0)
> >  of 2023-04-14
> > Package: CC Mode 5.35.2 (C++//l)
> > Buffer Style: gnu
> > c-emacs-features: (pps-extended-state col-0-paren posix-char-classes gen-st=
> > ring-delim gen-comment-delim syntax-properties category-properties 1-bit)

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

-- 
Alan Mackenzie (Nuremberg, Germany).

diff --git a/doc/misc/cc-mode.texi b/doc/misc/cc-mode.texi
index 71bf3fcee4a..7bf794e03e4 100644
--- a/doc/misc/cc-mode.texi
+++ b/doc/misc/cc-mode.texi
@@ -6252,6 +6252,16 @@ List Line-Up
 
 @comment ------------------------------------------------------------
 
+@defun c-lineup-template-args-indented-from-margin
+@findex lineup-template-args-indented-from-margin (c-)
+Indent a template argument line `c-basic-offset' from the left-hand
+margin of the line with the containing <.
+
+@workswith @code{template-args-cont}.
+@end defun
+
+@comment ------------------------------------------------------------
+
 @defun c-lineup-ObjC-method-call
 @findex lineup-ObjC-method-call @r{(c-)}
 For Objective-C code, line up selector args as Emacs Lisp mode does
diff --git a/lisp/progmodes/cc-align.el b/lisp/progmodes/cc-align.el
index 34ef0b9c1af..91a7665edbb 100644
--- a/lisp/progmodes/cc-align.el
+++ b/lisp/progmodes/cc-align.el
@@ -940,6 +940,16 @@ c-lineup-template-args
 	     (zerop (c-forward-token-2 1 nil (c-point 'eol))))
 	(vector (current-column)))))
 
+(defun c-lineup-template-args-indented-from-margin (_langelem)
+  "Indent a template argument line `c-basic-offset' from the margin
+of the line with the containing <.
+
+Works with: template-args-cont."
+  (save-excursion
+    (goto-char (c-langelem-2nd-pos c-syntactic-element))
+    (back-to-indentation)
+    (vector (+ (current-column) c-basic-offset))))
+
 (defun c-lineup-ObjC-method-call (langelem)
   "Line up selector args as Emacs Lisp mode does with function args:
 Go to the position right after the message receiver, and if you are at
diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el
index 1d98b215525..f9b63cbeed6 100644
--- a/lisp/progmodes/cc-defs.el
+++ b/lisp/progmodes/cc-defs.el
@@ -1284,6 +1284,29 @@ c-min-property-position
        pos)
       (most-positive-fixnum))))
 
+(defmacro c-put-char-properties (from to property value)
+  ;; FIXME!!!  Doc comment here!
+  (declare (debug t))
+  (setq property (eval property))
+  `(let ((-to- ,to) (-from- ,from))
+     ,(if c-use-extents
+	  ;; XEmacs
+	  `(progn
+	     (map-extents (lambda (ext ignored)
+			    (delete-extent ext))
+			  nil -from- -to- nil nil ',property)
+	     (set-extent-properties (make-extent -from- -to-)
+				    (cons property
+					  (cons ,value
+						'(start-open t
+							     end-open t)))))
+	;; Emacs
+	`(progn
+	   ,@(when (and (fboundp 'syntax-ppss)
+			(eq `,property 'syntax-table))
+	       `((setq c-syntax-table-hwm (min c-syntax-table-hwm -from-))))
+	   (put-text-property -from- -to- ',property ,value)))))
+
 (defmacro c-clear-char-properties (from to property)
   ;; Remove all the occurrences of the given property in the given
   ;; region that has been put with `c-put-char-property'.  PROPERTY is
@@ -1379,7 +1402,8 @@ c-search-forward-non-nil-char-property
        value)
       (t (let ((place (c-next-single-property-change
 		       (point) ,property nil -limit-)))
-	   (when place
+	   (when (and place
+		      (< place -limit-))
 	     (goto-char (1+ place))
 	     (c-get-char-property place ,property)))))))
 
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index d21e082d0b6..99746d02c67 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -2672,6 +2672,7 @@ c-partial-ws-p
 	  (progn (goto-char beg)
 		 (c-skip-ws-forward end+1)
 		 (eq (point) end+1))))))
+
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; We maintain a sopisticated cache of positions which are in a literal,
@@ -7039,8 +7040,8 @@ c-clear-<-pair-props-if-match-after
   ;; POS (default point) is at a < character.  If it is both marked
   ;; with open/close paren syntax-table property, and has a matching >
   ;; (also marked) which is after LIM, remove the property both from
-  ;; the current > and its partner.  Return t when this happens, nil
-  ;; when it doesn't.
+  ;; the current > and its partner.  Return the position after the >
+  ;; when this happens, nil when it doesn't.
   (save-excursion
     (if pos
 	(goto-char pos)
@@ -7054,15 +7055,15 @@ c-clear-<-pair-props-if-match-after
 			c->-as-paren-syntax)) ; should always be true.
 	(c-unmark-<->-as-paren (1- (point)))
 	(c-unmark-<->-as-paren pos)
-	(c-truncate-lit-pos-cache pos))
-      t)))
+	(c-truncate-lit-pos-cache pos)
+      (point)))))
 
 (defun c-clear->-pair-props-if-match-before (lim &optional pos)
   ;; POS (default point) is at a > character.  If it is both marked
   ;; with open/close paren syntax-table property, and has a matching <
   ;; (also marked) which is before LIM, remove the property both from
-  ;; the current < and its partner.  Return t when this happens, nil
-  ;; when it doesn't.
+  ;; the current < and its partner.  Return the position of the < when
+  ;; this happens, nil when it doesn't.
   (save-excursion
     (if pos
 	(goto-char pos)
@@ -7076,8 +7077,8 @@ c-clear->-pair-props-if-match-before
 			c-<-as-paren-syntax)) ; should always be true.
 	(c-unmark-<->-as-paren (point))
 	(c-truncate-lit-pos-cache (point))
-	(c-unmark-<->-as-paren pos))
-      t)))
+	(c-unmark-<->-as-paren pos)
+	(point)))))
 
 ;; Set by c-common-init in cc-mode.el.
 (defvar c-new-BEG)
@@ -7085,7 +7086,48 @@ c-new-END
 ;; Set by c-before-change-check-raw-strings.
 (defvar c-old-END-literality)
 
-(defun c-before-change-check-<>-operators (beg end)
+(defun c-end-of-literal (pt-s pt-search)
+  ;; If a literal is open in the `c-semi-pp-to-literal' state PT-S, return the
+  ;; end point of this literal (or point-max) assuming PT-S is valid at
+  ;; PT-SEARCH.  Otherwise, return nil.
+  (when (car (cddr pt-s))		; Literal start
+    (let ((lit-type (cadr pt-s))
+	  (lit-beg (car (cddr pt-s)))
+	  ml-end-re
+	  )
+      (save-excursion
+	(cond
+	 ((eq lit-type 'string)
+	  (if (and c-ml-string-opener-re
+		   (c-ml-string-opener-at-or-around-point lit-beg))
+	      (progn
+		(setq ml-end-re
+		      (funcall c-make-ml-string-closer-re-function
+			       (match-string 1)))
+		(goto-char (max (- pt-search (1- (length ml-end-re)))
+				(point-min)))
+		(re-search-forward ml-end-re nil 'stay))
+	    ;; For an ordinary string, we can't use `parse-partial-sexp' since
+	    ;; not all syntax-table properties have yet been set.
+	    (goto-char pt-search)
+	    (re-search-forward
+	       "\\(?:\\\\\\(?:.\\|\n\\)\\|[^\"\n\\]\\)*[\"\n]" nil 'stay)))
+	 ((memq lit-type '(c c++))
+	  ;; To work around a bug in parse-partial-sexp, where effect is given
+	  ;; to the syntax of a backslash, even the the scan starts with point
+	  ;; just after it.
+	  (if (and (eq (char-before pt-search) ?\\)
+		   (eq (char-after pt-search) ?\n))
+	      (progn
+		(c-put-char-property (1- pt-search) 'syntax-table '(1))
+		(parse-partial-sexp pt-search (point-max) nil nil (car pt-s)
+				    'syntax-table)
+		(c-clear-char-property (1- pt-search) 'syntax-table))
+	  (parse-partial-sexp pt-search (point-max) nil nil (car pt-s)
+			      'syntax-table))))
+	(point)))))
+
+(defun c-unmark-<>-around-region (beg end &optional old-len)
   ;; Unmark certain pairs of "< .... >" which are currently marked as
   ;; template/generic delimiters.  (This marking is via syntax-table text
   ;; properties), and expand the (c-new-BEG c-new-END) region to include all
@@ -7099,66 +7141,201 @@ c-before-change-check-<>-operators
   ;; enclose a brace or semicolon, so we use these as bounds on the
   ;; region we must work on.
   ;;
+  ;; The buffer is widened, and point is undefined, both at entry and exit.
+  ;;
+  ;; FIXME!!!  This routine ignores the possibility of macros entirely.
+  ;; 2010-01-29.
+
+  (when (> end beg)
+    ;; Extend the region (BEG END) to deal with any complicating literals.
+    (let* ((lit-search-beg (if (memq (char-before beg) '(?/ ?*))
+			       (1- beg) beg))
+	   (lit-search-end (if (memq (char-after end) '(?/ ?*))
+			       (1+ end) end))
+	   ;; Note we can't use c-full-pp-to-literal here, since we haven't
+	   ;; yet applied syntax-table properties to ends of lines, etc.
+	   (lit-search-beg-s (c-semi-pp-to-literal lit-search-beg))
+	   (beg-literal-beg (car (cddr lit-search-beg-s)))
+	   (lit-search-end-s (c-semi-pp-to-literal lit-search-end))
+	   (end-literal-beg (car (cddr lit-search-end-s)))
+	   (beg-literal-end (c-end-of-literal lit-search-beg-s beg))
+	   (end-literal-end (c-end-of-literal lit-search-end-s end))
+	   new-beg new-end search-region)
+
+      ;; Determine any new end of literal resulting from the insertion/deletion.
+      (setq search-region
+	    (if (and (eq beg-literal-beg end-literal-beg)
+		     (eq beg-literal-end end-literal-end))
+		(if beg-literal-beg
+		    nil
+		  (cons beg
+			(max end
+			     (or beg-literal-end (point-min))
+			     (or end-literal-end (point-min)))))
+	      (cons (or beg-literal-beg beg)
+		    (max end
+			 (or beg-literal-end (point-min))
+			 (or end-literal-end (point-min))))))
+
+      (when search-region
+	;; If we've just inserted text, mask its syntaxes temporarily so that
+	;; they won't interfere with the undoing of the properties on the <s
+	;; and >s.
+	(c-save-buffer-state (syn-tab-settings syn-tab-value
+					       swap-open-string-ends)
+	  (unwind-protect
+	      (progn
+		(when old-len
+		  ;; Special case: If a \ has just been inserted into a
+		  ;; string, escaping or unescaping a LF, temporarily swap
+		  ;; the LF's syntax-table text property with that of the
+		  ;; former end of the open string.
+		  (goto-char end)
+		  (when (and (eq (cadr lit-search-beg-s) 'string)
+			     (not (eq beg-literal-end end-literal-end))
+			     (skip-chars-forward "\\\\")
+			     (eq (char-after) ?\n)
+			     (not (zerop (skip-chars-backward "\\\\"))))
+		    (setq swap-open-string-ends t)
+		    (if (c-get-char-property (1- beg-literal-end)
+					     'syntax-table)
+			(progn
+			  (c-clear-char-property (1- beg-literal-end)
+						 'syntax-table)
+			  (c-put-char-property (1- end-literal-end)
+					       'syntax-table '(15)))
+		      (c-put-char-property (1- beg-literal-end)
+					   'syntax-table '(15))
+		      (c-clear-char-property (1- end-literal-end)
+					     'syntax-table)))
+
+		  ;; Save current settings of the 'syntax-table property in
+		  ;; (BEG END), then splat these with the punctuation value.
+		  (goto-char beg)
+		  (while (progn (skip-syntax-forward "" end)
+				(< (point) end))
+		    (setq syn-tab-value
+			  (c-get-char-property (point) 'syntax-table))
+		    (when (not (c-get-char-property (point) 'category))
+		      (push (cons (point) syn-tab-value) syn-tab-settings))
+		    (forward-char))
+
+		  (c-put-char-properties beg end 'syntax-table '(1))
+		  ;; If an open string's opener has just been neutralized,
+		  ;; do the same to the terminating LF.
+		  (when (and end-literal-end
+			     (eq (char-before end-literal-end) ?\n)
+			     (equal (c-get-char-property
+				     (1- end-literal-end) 'syntax-table)
+				    '(15)))
+		    (push (cons (1- end-literal-end) '(15)) syn-tab-settings)
+		    (c-put-char-property (1- end-literal-end) 'syntax-table
+					 '(1))))
+
+		(let
+		    ((beg-lit-start (progn (goto-char beg) (c-literal-start)))
+		     beg-limit end-limit <>-pos)
+		  ;; Locate the earliest < after the barrier before the
+		  ;; changed region, which isn't already marked as a paren.
+		  (goto-char (or beg-lit-start beg))
+		  (setq beg-limit (c-determine-limit 5000))
+
+		  ;; Remove the syntax-table/category properties from each pertinent <...>
+		  ;; pair.  Firstly, the ones with the < before beg and > after beg....
+		  (goto-char (cdr search-region))
+		  (while (progn (c-syntactic-skip-backward "^;{}<" beg-limit)
+				(eq (char-before) ?<))
+		    (c-backward-token-2)
+		    (when (eq (char-after) ?<)
+		      (when (setq <>-pos (c-clear-<-pair-props-if-match-after
+					  (car search-region)))
+			(setq new-end <>-pos))
+		      (setq new-beg (point))))
+
+		  ;; ...Then the ones with < before end and > after end.
+		  (goto-char (car search-region))
+		  (setq end-limit (c-determine-+ve-limit 5000))
+		  (while (and (c-syntactic-re-search-forward "[;{}>]" end-limit 'end)
+			      (eq (char-before) ?>))
+		    (when (eq (char-before) ?>)
+		      (if (and (looking-at c->-op-cont-regexp)
+			       (not (eq (char-after) ?>)))
+			  (goto-char (match-end 0))
+			(when
+			    (and (setq <>-pos
+				       (c-clear->-pair-props-if-match-before
+					(cdr search-region)
+					(1- (point))))
+				 (or (not new-beg)
+				     (< <>-pos new-beg)))
+			  (setq new-beg <>-pos))
+			(when (or (not new-end) (> (point) new-end))
+			  (setq new-end (point))))))))
+
+	    (when old-len
+	      (c-clear-char-properties beg end 'syntax-table)
+	      (dolist (elt syn-tab-settings)
+		(if (cdr elt)
+		    (c-put-char-property (car elt) 'syntax-table (cdr elt)))))
+	    ;; Swap the '(15) syntax-table property on open string LFs back
+	    ;; again.
+	    (when swap-open-string-ends
+	      (if (c-get-char-property (1- beg-literal-end)
+				       'syntax-table)
+		  (progn
+		    (c-clear-char-property (1- beg-literal-end)
+					   'syntax-table)
+		    (c-put-char-property (1- end-literal-end)
+					 'syntax-table '(15)))
+		(c-put-char-property (1- beg-literal-end)
+				     'syntax-table '(15))
+		(c-clear-char-property (1- end-literal-end)
+				       'syntax-table)))))
+	  ;; Extend the fontification region, if needed.
+	  (and new-beg
+	       (< new-beg c-new-BEG)
+	       (setq c-new-BEG new-beg))
+	  (and new-end
+	       (> new-end c-new-END)
+	       (setq c-new-END new-end))))))
+
+(defun c-before-change-check-<>-operators (beg end)
+  ;; When we're deleting text, unmark certain pairs of "< .... >" which are
+  ;; currently marked as template/generic delimiters.  (This marking is via
+  ;; syntax-table text properties), and expand the (c-new-BEG c-new-END)
+  ;; region to include all unmarked < and > operators within the certain
+  ;; bounds (see below).
+  ;;
+  ;; These pairs are those which are in the current "statement" (i.e.,
+  ;; the region between the {, }, or ; before BEG and the one after
+  ;; END), and which enclose any part of the interval (BEG END).
+  ;; Also unmark a < or > which is about to become part of a multi-character
+  ;; operator, e.g. <=.
+  ;;
+  ;; Note that in C++ (?and Java), template/generic parens cannot
+  ;; enclose a brace or semicolon, so we use these as bounds on the
+  ;; region we must work on.
+  ;;
   ;; This function is called from before-change-functions (via
   ;; c-get-state-before-change-functions).  Thus the buffer is widened,
   ;; and point is undefined, both at entry and exit.
   ;;
   ;; FIXME!!!  This routine ignores the possibility of macros entirely.
   ;; 2010-01-29.
-  (when (and (or (> end beg)
-		 (and (> c-<-pseudo-digraph-cont-len 0)
-		      (goto-char beg)
-		      (progn
-			(skip-chars-backward
-			 "^<" (max (- (point) c-<-pseudo-digraph-cont-len)
-				   (point-min)))
-			(eq (char-before) ?<))
-		      (looking-at c-<-pseudo-digraph-cont-regexp)))
-	     (or
-	      (progn
-		(goto-char beg)
-		(search-backward "<" (max (- (point) 1024) (point-min)) t))
-	      (progn
-		(goto-char end)
-		(search-forward ">" (min (+ (point) 1024) (point-max)) t))))
-    (save-excursion
-      (c-save-buffer-state
-	  ((beg-lit-start (progn (goto-char beg) (c-literal-start)))
-	   (end-lit-limits (progn (goto-char end) (c-literal-limits)))
-	   new-beg new-end beg-limit end-limit)
-	;; Locate the earliest < after the barrier before the changed region,
-	;; which isn't already marked as a paren.
-	(goto-char (or beg-lit-start beg))
-	(setq beg-limit (c-determine-limit 512))
-
-	;; Remove the syntax-table/category properties from each pertinent <...>
-	;; pair.  Firstly, the ones with the < before beg and > after beg....
-	(while (progn (c-syntactic-skip-backward "^;{}<" beg-limit)
-		      (eq (char-before) ?<))
-	  (c-backward-token-2)
-	  (when (eq (char-after) ?<)
-	    (c-clear-<-pair-props-if-match-after beg)
-	    (setq new-beg (point))))
-	(c-forward-syntactic-ws)
-
-	;; ...Then the ones with < before end and > after end.
-	(goto-char (if end-lit-limits (cdr end-lit-limits) end))
-	(setq end-limit (c-determine-+ve-limit 512))
-	(while (and (c-syntactic-re-search-forward "[;{}>]" end-limit 'end)
-		    (eq (char-before) ?>))
-	  (c-end-of-current-token)
-	  (when (eq (char-before) ?>)
-	    (c-clear->-pair-props-if-match-before end (1- (point)))
-	    (setq new-end (point))))
-	(c-backward-syntactic-ws)
-
-	;; Extend the fontification region, if needed.
-	(and new-beg
-	     (< new-beg c-new-BEG)
-	     (setq c-new-BEG new-beg))
-	(and new-end
-	     (> new-end c-new-END)
-	     (setq c-new-END new-end))))))
+  (when (> end beg)
+  ;; Cope with removing (beg end) coalescing a < or > with, say, an = sign.
+    (goto-char beg)
+    (let ((ch (char-before)))
+      (if (and (memq ch '(?< ?>))
+	       (c-get-char-property (1- (point)) 'syntax-table)
+	       (progn
+		 (goto-char end)
+		 (looking-at (if (eq ch ?<)
+				 c-<-op-cont-regexp
+			       c->-op-cont-regexp)))
+	       (or (eq ch ?<)
+		   (not (eq (char-after) ?>))))
+	  (c-unmark-<>-around-region (1- beg) beg)))))
 
 (defun c-after-change-check-<>-operators (beg end)
   ;; This is called from `after-change-functions' when
@@ -7198,29 +7375,38 @@ c-after-change-check-<>-operators
 	    (c-clear-<>-pair-props)
 	    (forward-char)))))))
 
+(defun c-<>-get-restricted ()
+  ;; With point at the < at the start of the purported <>-arglist, determine
+  ;; the value of `c-restricted-<>-arglists' to use for the call of
+  ;; `c-forward-<>-arglist' starting there.
+  (save-excursion
+    (c-backward-token-2)
+    (and (not (looking-at c-opt-<>-sexp-key))
+	 (progn (c-backward-syntactic-ws)		  ; to ( or ,
+		(and (memq (char-before) '(?\( ?,))	  ; what about <?
+		     (not (eq (c-get-char-property (point) 'c-type)
+			      'c-decl-arg-start)))))))
+
 (defun c-restore-<>-properties (_beg _end _old-len)
   ;; This function is called as an after-change function.  It restores the
   ;; category/syntax-table properties on template/generic <..> pairs between
   ;; c-new-BEG and c-new-END.  It may do hidden buffer changes.
-  (c-save-buffer-state ((c-parse-and-markup-<>-arglists t)
-			c-restricted-<>-arglists lit-limits)
+  (c-save-buffer-state ((c-parse-and-markup-<>-arglists t) lit-limits)
     (goto-char c-new-BEG)
     (if (setq lit-limits (c-literal-limits))
 	(goto-char (cdr lit-limits)))
     (while (and (< (point) c-new-END)
-		(c-syntactic-re-search-forward "<" c-new-END 'bound))
-      (backward-char)
-      (save-excursion
-	(c-backward-token-2)
-	(setq c-restricted-<>-arglists
-	     (and (not (looking-at c-opt-<>-sexp-key))
-		  (progn (c-backward-syntactic-ws) ; to ( or ,
-			 (and (memq (char-before) '(?\( ?,)) ; what about <?
-			      (not (eq (c-get-char-property (point) 'c-type)
-				       'c-decl-arg-start)))))))
-      (or (c-forward-<>-arglist nil)
-	  (c-forward-over-token-and-ws)
-	  (goto-char c-new-END)))))
+		(c-syntactic-re-search-forward "[<>]" c-new-END 'bound))
+      (if (eq (char-before) ?<)
+	  (progn
+	    (backward-char)
+	    (let ((c-restricted-<>-arglists (c-<>-get-restricted)))
+	      (or (c-forward-<>-arglist nil)
+		  (c-forward-over-token-and-ws)
+		  (goto-char c-new-END))))
+	(save-excursion
+	  (when (c-backward-<>-arglist nil nil #'c-<>-get-restricted)
+	    (setq c-new-BEG (min c-new-BEG (point)))))))))
 
 
 ;; Handling of CC Mode multi-line strings.
@@ -7372,13 +7558,13 @@ c-ml-string-opener-around-point
 
 (defun c-ml-string-opener-intersects-region (&optional start finish)
   ;; If any part of the region [START FINISH] is inside an ml-string opener,
-  ;; return a dotted list of the start, end and double-quote position of that
-  ;; opener.  That list will not include any "context characters" before or
-  ;; after the opener.  If an opener is found, the match-data will indicate
-  ;; it, with (match-string 1) being the entire delimiter, and (match-string
-  ;; 2) the "main" double-quote.  Otherwise, the match-data is undefined.
-  ;; Both START and FINISH default to point.  FINISH may not be at an earlier
-  ;; buffer position than START.
+  ;; return a dotted list of the start, end and double-quote position of the
+  ;; first such opener.  That list wlll not include any "context characters"
+  ;; before or after the opener.  If an opener is found, the match-data will
+  ;; indicate it, with (match-string 1) being the entire delimiter, and
+  ;; (match-string 2) the "main" double-quote.  Otherwise, the match-data is
+  ;; undefined.  Both START and FINISH default to point.  FINISH may not be at
+  ;; an earlier buffer position than START.
   (let ((here (point)) found)
     (or finish (setq finish (point)))
     (or start (setq start (point)))
@@ -7402,7 +7588,10 @@ c-ml-string-opener-at-or-around-point
   ;; If POSITION (default point) is at or inside an ml string opener, return a
   ;; dotted list of the start and end of that opener, and the position of the
   ;; double-quote in it.  That list will not include any "context characters"
-  ;; before or after the opener.
+  ;; before or after the opener.  If an opener is found, the match-data will
+  ;; indicate it, with (match-string 1) being the entire delimiter, and
+  ;; (match-string 2) the "main" double-quote.  Otherwise, the match-data is
+  ;; undefined.
   (let ((here (point))
 	found)
     (or position (setq position (point)))
@@ -7414,7 +7603,7 @@ c-ml-string-opener-at-or-around-point
 		c-ml-string-opener-re
 		(min (+ position c-ml-string-max-opener-len) (point-max))
 		'bound))
-	 (<= (match-end 1) position)))
+	 (< (match-end 1) position)))
     (prog1
 	(and found
 	     (<= (match-beginning 1) position)
@@ -8821,7 +9010,7 @@ c-forward-<>-arglist-recur
       (if res
 	  (or c-record-found-types t)))))
 
-(defun c-backward-<>-arglist (all-types &optional limit)
+(defun c-backward-<>-arglist (all-types &optional limit restricted-function)
   ;; The point is assumed to be directly after a ">".  Try to treat it
   ;; as the close paren of an angle bracket arglist and move back to
   ;; the corresponding "<".  If successful, the point is left at
@@ -8830,7 +9019,12 @@ c-backward-<>-arglist
   ;; `c-forward-<>-arglist'.
   ;;
   ;; If the optional LIMIT is given, it bounds the backward search.
-  ;; It's then assumed to be at a syntactically relevant position.
+  ;; It's then assumed to be at a syntactically relevant position.  If
+  ;; RESTRICTED-FUNCTION is non-nil, it should be a function taking no
+  ;; arguments, called with point at a < at the start of a purported
+  ;; <>-arglist, which will return the value of
+  ;; `c-restricted-<>-arglists' to be used in the `c-forward-<>-arglist'
+  ;; call starting at that <.
   ;;
   ;; This is a wrapper around `c-forward-<>-arglist'.  See that
   ;; function for more details.
@@ -8866,7 +9060,11 @@ c-backward-<>-arglist
 		   t
 
 		 (backward-char)
-		 (let ((beg-pos (point)))
+		 (let ((beg-pos (point))
+		       (c-restricted-<>-arglists
+			(if restricted-function
+			    (funcall restricted-function)
+			  c-restricted-<>-arglists)))
 		   (if (c-forward-<>-arglist all-types)
 		       (cond ((= (point) start)
 			      ;; Matched the arglist.  Break the while.
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index 9118e3253c2..d220af2ab0e 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -2659,7 +2659,9 @@ c-font-lock-c++-using
   ;; prevent a repeat invocation.  See elisp/lispref page "Search-based
   ;; fontification".
   (let (pos)
-    (while (c-syntactic-re-search-forward c-using-key limit 'end)
+    (while 
+	(and (< (point) limit)
+	     (c-syntactic-re-search-forward c-using-key limit 'end))
       (while  ; Do one declarator of a comma separated list, each time around.
 	  (progn
 	    (c-forward-syntactic-ws)
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index 3b4fdc6e141..6483f87030a 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -455,6 +455,7 @@ c-populate-syntax-table
   c++ '(c-extend-region-for-CPP
 	c-depropertize-CPP
 	c-before-change-check-ml-strings
+	c-unmark-<>-around-region
 	c-before-change-check-<>-operators
 	c-before-after-change-check-c++-modules
 	c-truncate-bs-cache
@@ -468,6 +469,7 @@ c-populate-syntax-table
 	     c-parse-quotes-before-change
 	     c-before-change-fix-comment-escapes)
   java '(c-parse-quotes-before-change
+	 c-unmark-<>-around-region
 	 c-before-change-check-unbalanced-strings
 	 c-before-change-check-<>-operators)
   pike '(c-before-change-check-ml-strings
@@ -516,6 +518,7 @@ c-populate-syntax-table
 	c-after-change-unmark-ml-strings
 	c-parse-quotes-after-change
 	c-after-change-mark-abnormal-strings
+	c-unmark-<>-around-region
 	c-extend-font-lock-region-for-macros
 	c-before-after-change-check-c++-modules
 	c-neutralize-syntax-in-CPP
@@ -524,6 +527,7 @@ c-populate-syntax-table
   java '(c-depropertize-new-text
 	 c-after-change-escape-NL-in-string
 	 c-parse-quotes-after-change
+	 c-unmark-<>-around-region
 	 c-after-change-mark-abnormal-strings
 	 c-restore-<>-properties
 	 c-change-expand-fl-region)
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 11a1d3fe6c2..a99e58ef862 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -1367,7 +1367,9 @@ c-clear-string-fences
 		      (and		;(< (point) end)
 		       (not (nth 3 s))
 		       (c-get-char-property (1- (point)) 'c-fl-syn-tab))
-		    (c-put-char-property pos 'syntax-table '(1)))
+		    (c-put-char-property pos 'syntax-table '(1))
+		    (c-put-char-properties (1+ pos) (c-point 'eol pos)
+					   'syntax-table '(1)))
 		  (setq pos (point)))
 	      (setq pos (1+ pos)))))))))
 
@@ -1384,6 +1386,9 @@ c-restore-string-fences
 	   (setq pos
 		 (c-min-property-position pos c-max-syn-tab-mkr 'c-fl-syn-tab))
 	   (< pos c-max-syn-tab-mkr))
+	(when (and (equal (c-get-char-property pos 'syntax-table) '(1))
+		   (equal (c-get-char-property pos 'c-fl-syn-tab) '(15)))
+	  (c-clear-char-properties (1+ pos) (c-point 'eol pos) 'syntax-table))
 	(c-put-char-property pos 'syntax-table
 			     (c-get-char-property pos 'c-fl-syn-tab))
 	(setq pos (1+ pos))))))
diff --git a/lisp/progmodes/cc-vars.el b/lisp/progmodes/cc-vars.el
index 72d4b93ee59..286d569aaca 100644
--- a/lisp/progmodes/cc-vars.el
+++ b/lisp/progmodes/cc-vars.el
@@ -1219,7 +1219,8 @@ c-default-style
        (incomposition         . +)
        ;; Anchor pos: At the extern/namespace/etc block open brace if
        ;; it's at boi, otherwise boi at the keyword.
-       (template-args-cont    . (c-lineup-template-args +))
+       (template-args-cont    . (c-lineup-template-args
+				 c-lineup-template-args-indented-from-margin))
        ;; Anchor pos: Boi at the decl start.  This might be changed;
        ;; the logical position is clearly the opening '<'.
        (inlambda              . 0)