From: Alan Mackenzie <acm@muc.de>
Date: Wed, 27 Mar 2019 11:50:53 +0000 (+0000)
Subject: Improve C++ raw string fontification.
X-Git-Tag: emacs-27.0.90~3322
X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=29ec1e48883dbdce8f9f81ac25d9ec38c474cdcb;p=emacs.git

Improve C++ raw string fontification.

Integrate the handling of raw string and ordinary string fontification.

* lisp/progmodes/cc-defs.el (c-font-lock-flush)
(c-search-forward-char-property-without-value-on-char): new macros.
(c-point): In the 'eoll arm, check for eobp.
(c-search-forward-char-property-with-value-on-char): Handle the &optional
limit argument being nil.
(c-clear-char-property-with-value-on-char-function)
(c-clear-char-property-with-value-on-char): Return the position of the first
cleared property.

* lisp/progmodes/cc-engine.el (c-find-decl-prefix-search): Don't spuriously
recognize the change of face at a ) as the start of a string (a
"pseudo match").
(c-old-beg-rs c-old-end-rs): New variables.
(c-raw-string-pos): Analyze raw string delimiters more carefully.
(c-raw-string-in-end-delim): New function.
(c-depropertize-raw-string): Largely rewritten.
(c-before-change-check-raw-strings): New functionality: only remove the
syntax-table text properties from raw strings whose delimiters are about to
change.
(c-propertize-raw-string-id): New function.
(c-after-change-re-mark-raw-strings): Remove, incorporating functionality into
other functions.
(c-propertize-raw-string-opener): Largely rewritten.
(c-after-change-re-mark-raw-strings): Removed.
(c-after-change-unmark-raw-strings, c-after-change-unmark-raw-strings): New
functions.

* lisp/progmodes/cc-fonts.el (c-font-lock-raw-strings): Largely rewritten.

* lisp/progmodes/cc-langs.el (c-before-font-lock-functions): Replace
c-after-change-re-mark-unbalanced-strings by
c-after-change-mark-abnormal-strings in the t, c+objc, c++ and java sections.
Add c-after-change-unmark-raw-strings and remove
c-after-change-re-mark-raw-strings from the c++ section.

* lisp/progmodes/cc-mode.el (c-old-BEG c-old-END): Remove.
(c-old-END-literality): New variable.
(c-depropertize-CPP): Remove syntax-table properties from raw strings within
macros.
(c-before-change-check-unbalanced-strings): Call
c-truncate-semi-nonlit-pos-cache to preserve the integrity of the cache.
(c-before-change-check-unbalanced-strings): Call
c-truncate-semi-nonlit-pos-cache, largely rewritten.
(c-after-change-re-mark-unbalanced-strings): Renamed to
c-after-change-mark-abnormal-strings.  Call c-maybe-re-mark-raw-string.
---

diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el
index 97272ca9d21..87ddf3ac1e2 100644
--- a/lisp/progmodes/cc-defs.el
+++ b/lisp/progmodes/cc-defs.el
@@ -212,6 +212,13 @@ This variant works around bugs in `eval-when-compile' in various
 	`(cl-delete-duplicates ,cl-seq ,@cl-keys)
       `(delete-duplicates ,cl-seq ,@cl-keys))))
 
+(defmacro c-font-lock-flush (beg end)
+  "Declare the region BEG...END's fontification as out-of-date.
+On XEmacs and older Emacsen, this refontifies that region immediately."
+  (if (fboundp 'font-lock-flush)
+      `(font-lock-flush ,beg ,end)
+    `(font-lock-fontify-region ,beg ,end)))
+
 (defmacro c-point (position &optional point)
   "Return the value of certain commonly referenced POSITIONs relative to POINT.
 The current point is used if POINT isn't specified.  POSITION can be
@@ -258,10 +265,12 @@ to it is returned.  This function does not modify the point or the mark."
 	 ((eq position 'eoll)
 	  `(save-excursion
 	     ,@(if point `((goto-char ,point)))
-	     (while (progn
-		      (end-of-line)
-		      (prog1 (eq (logand 1 (skip-chars-backward "\\\\")) 1)))
-	       (beginning-of-line 2))
+	     (while (and
+		     (not (eobp))
+		     (progn
+		       (end-of-line)
+		       (prog1 (eq (logand 1 (skip-chars-backward "\\\\")) 1))))
+	       (forward-line))
 	     (end-of-line)
 	     (point)))
 
@@ -1214,7 +1223,7 @@ Leave point just after the character, and set the match data on
 this character, and return point.  If the search fails, return
 nil; point is then left undefined."
   `(let ((char-skip (concat "^" (char-to-string ,char)))
-	 (-limit- ,limit)
+	 (-limit- (or ,limit (point-max)))
 	 (-value- ,value))
      (while
 	 (and
@@ -1226,15 +1235,39 @@ nil; point is then left undefined."
        (search-forward-regexp ".")	; to set the match-data.
        (point))))
 
+(defmacro c-search-forward-char-property-without-value-on-char
+    (property value char &optional limit)
+  "Search forward for a character CHAR without text property PROPERTY having
+a value CHAR.
+LIMIT bounds the search.  The value comparison is done with `equal'.
+PROPERTY must be a constant.
+
+Leave point just after the character, and set the match data on
+this character, and return point.  If the search fails, return
+nil; point is then left undefined."
+  `(let ((char-skip (concat "^" (char-to-string ,char)))
+	 (-limit- (or ,limit (point-max)))
+	 (-value- ,value))
+     (while
+	 (and
+	  (progn (skip-chars-forward char-skip -limit-)
+		 (< (point) -limit-))
+	  (equal (c-get-char-property (point) ,property) -value-))
+       (forward-char))
+     (when (< (point) -limit-)
+       (search-forward-regexp ".")	; to set the match-data.
+       (point))))
+
 (defun c-clear-char-property-with-value-on-char-function (from to property
 							       value char)
   "Remove all text-properties PROPERTY with value VALUE on
 characters with value CHAR from the region [FROM, TO), as tested
 by `equal'.  These properties are assumed to be over individual
 characters, having been put there by c-put-char-property.  POINT
-remains unchanged."
+remains unchanged.  Return the position of the first removed
+property, or nil."
   (let ((place from)
-	)
+	first)
     (while			  ; loop round occurrences of (PROPERTY VALUE)
 	(progn
 	  (while	   ; loop round changes in PROPERTY till we find VALUE
@@ -1243,28 +1276,34 @@ remains unchanged."
 	       (not (equal (get-text-property place property) value)))
 	    (setq place (c-next-single-property-change place property nil to)))
 	  (< place to))
-      (if (eq (char-after place) char)
-	  (remove-text-properties place (1+ place) (cons property nil)))
+      (when (eq (char-after place) char)
+	(remove-text-properties place (1+ place) (cons property nil))
+	(or first (setq first place)))
       ;; Do we have to do anything with stickiness here?
-      (setq place (1+ place)))))
+      (setq place (1+ place)))
+    first))
 
 (defmacro c-clear-char-property-with-value-on-char (from to property value char)
   "Remove all text-properties PROPERTY with value VALUE on
 characters with value CHAR from the region [FROM, TO), as tested
 by `equal'.  These properties are assumed to be over individual
 characters, having been put there by c-put-char-property.  POINT
-remains unchanged."
+remains unchanged.  Return the position of the first removed
+property, or nil."
   (if c-use-extents
       ;; XEmacs
       `(let ((-property- ,property)
-	     (-char- ,char))
+	     (-char- ,char)
+	     (first (1+ (point-max))))
 	 (map-extents (lambda (ext val)
-			(if (and (equal (extent-property ext -property-) val)
-				 (eq (char-after
-				      (extent-start-position ext))
-				     -char-))
-			    (delete-extent ext)))
-		      nil ,from ,to ,value nil -property-))
+			(when (and (equal (extent-property ext -property-) val)
+				   (eq (char-after
+					(extent-start-position ext))
+				       -char-))
+			  (setq first (min first (extent-start-position ext)))
+			  (delete-extent ext)))
+		      nil ,from ,to ,value nil -property-)
+	 (and (<= first (point-max)) first))
     ;; GNU Emacs
     `(c-clear-char-property-with-value-on-char-function ,from ,to ,property
 							,value ,char)))
@@ -1316,6 +1355,7 @@ with value CHAR in the region [FROM to)."
 ;(eval-after-load "edebug" ; 2006-07-09: def-edebug-spec is now in subr.el.
 ;  '(progn
 (def-edebug-spec cc-eval-when-compile (&rest def-form))
+(def-edebug-spec c-font-lock-flush t)
 (def-edebug-spec c--mapcan t)
 (def-edebug-spec c--set-difference (form form &rest [symbolp form]))
 (def-edebug-spec c--intersection (form form &rest [symbolp form]))
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index cc3753a7ebd..1a8c5164906 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -5646,8 +5646,12 @@ comment at the start of cc-engine.el for more info."
 	       ;; Pseudo match inside a comment or string literal.  Skip out
 	       ;; of comments and string literals.
 	       (while (progn
-			(goto-char (c-next-single-property-change
-				    (point) 'face nil cfd-limit))
+			(unless
+			    (and (match-end 1)
+				 (c-got-face-at (1- (point)) c-literal-faces)
+				 (not (c-got-face-at (point) c-literal-faces)))
+			  (goto-char (c-next-single-property-change
+				      (point) 'face nil cfd-limit)))
 			(and (< (point) cfd-limit)
 			     (c-got-face-at (point) c-literal-faces))))
 	       t)		      ; Continue the loop over pseudo matches.
@@ -6350,9 +6354,8 @@ comment at the start of cc-engine.el for more info."
 ;; Set by c-common-init in cc-mode.el.
 (defvar c-new-BEG)
 (defvar c-new-END)
-;; Set by c-after-change in cc-mode.el.
-(defvar c-old-BEG)
-(defvar c-old-END)
+;; Set by c-before-change-check-raw-strings.
+(defvar c-old-END-literality)
 
 (defun c-before-change-check-<>-operators (beg end)
   ;; Unmark certain pairs of "< .... >" which are currently marked as
@@ -6484,9 +6487,9 @@ comment at the start of cc-engine.el for more info."
 ;; A valid C++ raw string looks like
 ;;     R"<id>(<contents>)<id>"
 ;; , where <id> is an identifier from 0 to 16 characters long, not containing
-;; spaces, control characters, double quote or left/right paren.  <contents>
-;; can include anything which isn't the terminating )<id>", including new
-;; lines, "s, parentheses, etc.
+;; spaces, control characters, or left/right paren.  <contents> can include
+;; anything which isn't the terminating )<id>", including new lines, "s,
+;; parentheses, etc.
 ;;
 ;; CC Mode handles C++ raw strings by the use of `syntax-table' text
 ;; properties as follows:
@@ -6496,16 +6499,18 @@ comment at the start of cc-engine.el for more info."
 ;;   contents is given the property value "punctuation" (`(1)') to prevent it
 ;;   interacting with the "s in the delimiters.
 ;;
-;;   The font locking routine `c-font-lock-c++-raw-strings' (in cc-fonts.el)
+;;   The font locking routine `c-font-lock-raw-strings' (in cc-fonts.el)
 ;;   recognizes valid raw strings, and fontifies the delimiters (apart from
 ;;   the parentheses) with the default face and the parentheses and the
 ;;   <contents> with font-lock-string-face.
 ;;
 ;; (ii) A valid, but unterminated, raw string opening delimiter gets the
 ;;   "punctuation" value (`(1)') of the `syntax-table' text property, and the
-;;   open parenthesis gets the "string fence" value (`(15)').
+;;   open parenthesis gets the "string fence" value (`(15)').  When such a
+;;   delimiter is found, no attempt is made in any way to "correct" any text
+;;   properties after the delimiter.
 ;;
-;;   `c-font-lock-c++-raw-strings' puts c-font-lock-warning-face on the entire
+;;   `c-font-lock-raw-strings' puts c-font-lock-warning-face on the entire
 ;;   unmatched opening delimiter (from the R up to the open paren), and allows
 ;;   the rest of the buffer to get font-lock-string-face, caused by the
 ;;   unmatched "string fence" `syntax-table' text property value.
@@ -6522,10 +6527,14 @@ comment at the start of cc-engine.el for more info."
 ;;   already at the end of the macro, it gets the "punctuation" value, and no
 ;;   "string fence"s are used.
 ;;
-;;   The effect on the fontification of either of these tactics is that rest of
-;;   the macro (if any) after the "(" gets font-lock-string-face, but the rest
-;;   of the file is fontified normally.
+;;   The effect on the fontification of either of these tactics is that the
+;;   rest of the macro (if any) after the "(" gets font-lock-string-face, but
+;;   the rest of the file is fontified normally.
 
+;; The values of the function `c-raw-string-pos' at before-change-functions'
+;; BEG and END.
+(defvar c-old-beg-rs nil)
+(defvar c-old-end-rs nil)
 
 (defun c-raw-string-pos ()
   ;; Get POINT's relationship to any containing raw string.
@@ -6542,7 +6551,7 @@ comment at the start of cc-engine.el for more info."
   ;; characters.)  If the raw string is not terminated, E\) and E\" are set to
   ;; nil.
   ;;
-  ;; Note: this routine is dependant upon the correct syntax-table text
+  ;; Note: this function is dependant upon the correct syntax-table text
   ;; properties being set.
   (let ((state (c-state-semi-pp-to-literal (point)))
 	open-quote-pos open-paren-pos close-paren-pos close-quote-pos id)
@@ -6555,8 +6564,20 @@ comment at the start of cc-engine.el for more info."
 		 (search-backward "\"" (max (- (point) 17) (point-min)) t)))
 	    ((and (eq (cadr state) 'string)
 		  (goto-char (nth 2 state))
-		  (or (eq (char-after) ?\")
-		      (search-backward "\"" (max (- (point) 17) (point-min)) t))
+		  (cond
+		   ((eq (char-after) ?\"))
+		   ((eq (char-after) ?\()
+		    (let ((here (point)))
+		      (goto-char (max (- (point) 18) (point-min)))
+		      (while
+			  (and
+			   (search-forward-regexp
+			    "R\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)("
+			    (1+ here) 'limit)
+			   (< (point) here)))
+		      (and (eq (point) (1+ here))
+			   (match-beginning 1)
+			   (goto-char (1- (match-beginning 1)))))))
 		  (not (bobp)))))
 	   (eq (char-before) ?R)
 	   (looking-at "\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)("))
@@ -6579,6 +6600,21 @@ comment at the start of cc-engine.el for more info."
 	   (t nil))
 	  open-quote-pos open-paren-pos close-paren-pos close-quote-pos))))
 
+(defun c-raw-string-in-end-delim (beg end)
+  ;; If the region (BEG END) intersects a possible raw string terminator,
+  ;; return a cons of the position of the ) and the position of the " in the
+  ;; first one found.
+  (save-excursion
+    (goto-char (max (- beg 17) (point-min)))
+    (while
+	(and
+	 (search-forward-regexp ")\\([^ ()\\\n\r\t]\\{0,16\\}\\)\""
+				(min (+ end 17) (point-max)) t)
+	 (<= (point) beg)))
+    (unless (or (<= (point) beg)
+		(>= (match-beginning 0) end))
+      (cons (match-beginning 0) (match-end 1)))))
+
 (defun c-depropertize-raw-string (id open-quote open-paren bound)
   ;; Point is immediately after a raw string opening delimiter.  Remove any
   ;; `syntax-table' text properties associated with the delimiter (if it's
@@ -6587,29 +6623,55 @@ comment at the start of cc-engine.el for more info."
   ;; ID, a string, is the delimiter's identifier.  OPEN-QUOTE and OPEN-PAREN
   ;; are the buffer positions of the delimiter's components.  BOUND is the
   ;; bound for searching for a matching closing delimiter; it is usually nil,
-  ;; but if we're inside a macro, it's the end of the macro.
+  ;; but if we're inside a macro, it's the end of the macro (i.e. just before
+  ;; the terminating \n).
   ;;
   ;; Point is moved to after the (terminated) raw string, or left after the
   ;; unmatched opening delimiter, as the case may be.  The return value is of
   ;; no significance.
-  (let ((open-paren-prop (c-get-char-property open-paren 'syntax-table)))
+  (let ((open-paren-prop (c-get-char-property open-paren 'syntax-table))
+	first)
+    ;; If the delimiter is "unclosed", or sombody's used " in their id, clear
+    ;; the 'syntax-table property from all of them.
+    (setq first (c-clear-char-property-with-value-on-char
+		 open-quote open-paren 'syntax-table '(1) ?\"))
+    (if first (c-truncate-semi-nonlit-pos-cache first))
     (cond
      ((null open-paren-prop)
-      ;; A terminated raw string
+      ;; Should be a terminated raw string...
       (when (search-forward (concat ")" id "\"") nil t)
+	;; Yes, it is.  :-)
+	;; Clear any '(1)s from "s in the identifier.
+	(setq first (c-clear-char-property-with-value-on-char
+		     (1+ (match-beginning 0)) (1- (match-end 0))
+		     'syntax-table '(1) ?\"))
+	(if first (c-truncate-semi-nonlit-pos-cache first))
+	;; Clear any random `syntax-table' text properties from the contents.
 	(let* ((closing-paren (match-beginning 0))
-	       (first-punctuation
-		(save-match-data
-		  (goto-char (1+ open-paren))
-		  (and (c-search-forward-char-property 'syntax-table '(1)
-						       closing-paren)
-		       (1- (point)))))
-	       )
-	  (when first-punctuation
-	    (c-clear-char-property-with-value
-	     first-punctuation (match-beginning 0) 'syntax-table '(1))
-	    (c-truncate-semi-nonlit-pos-cache first-punctuation)
-	    ))))
+	       (first-st
+		(and
+		 (< (1+ open-paren) closing-paren)
+		 (or
+		  (and (c-get-char-property (1+ open-paren) 'syntax-table)
+		       (1+ open-paren))
+		  (and
+		   (setq first
+			 (c-next-single-property-change
+			  (1+ open-paren) 'syntax-table nil closing-paren))
+		   (< first closing-paren)
+		   first)))))
+	  (when first-st
+	    (c-clear-char-properties first-st (match-beginning 0)
+				     'syntax-table)
+	    (c-truncate-semi-nonlit-pos-cache first-st))
+	  (when (c-get-char-property (1- (match-end 0)) 'syntax-table)
+	    ;; Was previously an unterminated (ordinary) string
+	    (save-excursion
+	      (goto-char (1- (match-end 0)))
+	      (when (c-safe (c-forward-sexp)) ; to '(1) at EOL.
+		(c-clear-char-property (1- (point)) 'syntax-table))
+	      (c-clear-char-property (1- (match-end 0)) 'syntax-table)
+	      (c-truncate-semi-nonlit-pos-cache (1- (match-end 0))))))))
      ((or (and (equal open-paren-prop '(15)) (null bound))
 	  (equal open-paren-prop '(1)))
       ;; An unterminated raw string either not in a macro, or in a macro with
@@ -6623,13 +6685,8 @@ comment at the start of cc-engine.el for more info."
       (c-clear-char-property open-quote 'syntax-table)
       (c-truncate-semi-nonlit-pos-cache open-quote)
       (c-clear-char-property open-paren 'syntax-table)
-      (let ((after-string-fence-pos
-	     (save-excursion
-	       (goto-char (1+ open-paren))
-	       (c-search-forward-char-property 'syntax-table '(15) bound))))
-	(when after-string-fence-pos
-	  (c-clear-char-property (1- after-string-fence-pos) 'syntax-table)))
-      ))))
+      (c-clear-char-property-with-value (1+ open-paren) bound 'syntax-table
+					'(15))))))
 
 (defun c-depropertize-raw-strings-in-region (start finish)
   ;; Remove any `syntax-table' text properties associated with C++ raw strings
@@ -6669,37 +6726,89 @@ comment at the start of cc-engine.el for more info."
 
 (defun c-before-change-check-raw-strings (beg end)
   ;; This function clears `syntax-table' text properties from C++ raw strings
-  ;; in the region (c-new-BEG c-new-END).  BEG and END are the standard
-  ;; arguments supplied to any before-change function.
+  ;; whose delimiters are about to change in the region (c-new-BEG c-new-END).
+  ;; BEG and END are the standard arguments supplied to any before-change
+  ;; function.
   ;;
   ;; Point is undefined on both entry and exit, and the return value has no
   ;; significance.
   ;;
   ;; This function is called as a before-change function solely due to its
   ;; membership of the C++ value of `c-get-state-before-change-functions'.
+  (goto-char end)
+  ;; We use the following to detect a R"<id>( being swallowed into a string by
+  ;; the pending change.
+  (setq c-old-END-literality (c-in-literal))
   (c-save-buffer-state
-      ((beg-rs (progn (goto-char beg) (c-raw-string-pos)))
-       (beg-plus (if (null beg-rs)
-		     beg
-		   (max beg
-			(1+ (or (nth 4 beg-rs) (nth 2 beg-rs))))))
-       (end-rs (progn (goto-char end) (c-raw-string-pos))) ; FIXME!!!
+      (;; (beg-rs (progn (goto-char beg) (c-raw-string-pos)))
+       ;; (end-rs (progn (goto-char end) (c-raw-string-pos)))
+					; FIXME!!!
 					; Optimize this so that we don't call
 					; `c-raw-string-pos' twice when once
 					; will do.  (2016-06-02).
-       (end-minus (if (null end-rs)
-		      end
-		    (min end (cadr end-rs))))
-       )
-    (when beg-rs
-      (setq c-new-BEG (min c-new-BEG (1- (cadr beg-rs)))))
-    (c-depropertize-raw-strings-in-region c-new-BEG beg-plus)
-
-    (when end-rs
-      (setq c-new-END (max c-new-END
-			   (1+ (or (nth 4 end-rs)
-				   (nth 2 end-rs))))))
-    (c-depropertize-raw-strings-in-region end-minus c-new-END)))
+       (term-del (c-raw-string-in-end-delim beg end))
+       Rquote close-quote)
+    (setq c-old-beg-rs (progn (goto-char beg) (c-raw-string-pos))
+	  c-old-end-rs (progn (goto-char end) (c-raw-string-pos)))
+    (cond
+     ;; We're not changing, or we're obliterating raw strings.
+     ((and (null c-old-beg-rs) (null c-old-end-rs)))
+     ;; We're changing the putative terminating delimiter of a raw string
+     ;; containing BEG.
+     ((and c-old-beg-rs term-del
+	   (or (null (nth 3 c-old-beg-rs))
+	       (<= (car term-del) (nth 3 c-old-beg-rs))))
+      (setq Rquote (1- (cadr c-old-beg-rs))
+	    close-quote (1+ (cdr term-del)))
+      (c-depropertize-raw-strings-in-region Rquote close-quote)
+      (setq c-new-BEG (min c-new-BEG Rquote)
+	    c-new-END (max c-new-END close-quote)))
+     ;; We're breaking an escaped NL in a raw string in a macro.
+     ((and c-old-end-rs
+	   (< beg end)
+	   (goto-char end) (eq (char-before) ?\\)
+	   (c-beginning-of-macro))
+      (let ((bom (point))
+	    (eom (progn (c-end-of-macro) (point))))
+	(c-depropertize-raw-strings-in-region bom eom)
+	(setq c-new-BEG (min c-new-BEG bom)
+	      c-new-END (max c-new-END eom))))
+     ;; We're changing only the contents of a raw string.
+     ((and (equal (cdr c-old-beg-rs) (cdr c-old-end-rs))
+	   (null (car c-old-beg-rs)) (null (car c-old-end-rs))))
+     ((or
+       ;; We're removing (at least part of) the R" of the starting delim of a
+       ;; raw string:
+       (null c-old-beg-rs)
+       (and (eq beg (cadr c-old-beg-rs))
+	    (< beg end))
+       ;; Or we're removing the ( of the starting delim of a raw string.
+       (and (eq (car c-old-beg-rs) 'open-delim)
+	    (or (null c-old-end-rs)
+		(not (eq (car c-old-end-rs) 'open-delim))
+		(not (equal (cdr c-old-beg-rs) (cdr c-old-end-rs))))))
+      (let ((close (nth 4 (or c-old-end-rs c-old-beg-rs))))
+	(setq Rquote (1- (cadr (or c-old-end-rs c-old-beg-rs)))
+	      close-quote (if close (1+ close) (point-max))))
+      (c-depropertize-raw-strings-in-region Rquote close-quote)
+      (setq c-new-BEG (min c-new-BEG Rquote)
+	    c-new-END (max c-new-END close-quote)))
+     ;; We're changing only the text of the identifier of the opening
+     ;; delimiter of a raw string.
+     ((and (eq (car c-old-beg-rs) 'open-delim)
+	   (equal c-old-beg-rs c-old-end-rs))))))
+
+(defun c-propertize-raw-string-id (start end)
+  ;; If the raw string identifier between buffer positions START and END
+  ;; contains any double quote characters, put a punctuation syntax-table text
+  ;; property on them.  The return value is of no significance.
+  (save-excursion
+    (goto-char start)
+    (while (and (skip-chars-forward "^\"" end)
+		(< (point) end))
+      (c-put-char-property (point) 'syntax-table '(1))
+      (c-truncate-semi-nonlit-pos-cache (point))
+      (forward-char))))
 
 (defun c-propertize-raw-string-opener (id open-quote open-paren bound)
   ;; Point is immediately after a raw string opening delimiter.  Apply any
@@ -6709,117 +6818,264 @@ comment at the start of cc-engine.el for more info."
   ;; ID, a string, is the delimiter's identifier.  OPEN-QUOTE and OPEN-PAREN
   ;; are the buffer positions of the delimiter's components.  BOUND is the
   ;; bound for searching for a matching closing delimiter; it is usually nil,
-  ;; but if we're inside a macro, it's the end of the macro.
-  ;;
-  ;; Point is moved to after the (terminated) raw string, or left after the
-  ;; unmatched opening delimiter, as the case may be.  The return value is of
-  ;; no significance.
-  (if (search-forward (concat ")" id "\"") bound t)
-      (let ((end-string (match-beginning 0))
-	    (after-quote (match-end 0)))
-	(goto-char open-paren)
-	(while (progn (skip-syntax-forward "^\"" end-string)
-		      (< (point) end-string))
-	  (c-put-char-property (point) 'syntax-table '(1)) ; punctuation
-	  (c-truncate-semi-nonlit-pos-cache (point))
-	  (forward-char))
-	(goto-char after-quote))
-    (c-put-char-property open-quote 'syntax-table '(1))	     ; punctuation
-    (c-truncate-semi-nonlit-pos-cache open-quote)
-    (c-put-char-property open-paren 'syntax-table '(15))     ; generic string
-    (when bound
-      ;; In a CPP construct, we try to apply a generic-string `syntax-table'
-      ;; text property to the last possible character in the string, so that
-      ;; only characters within the macro get "stringed out".
-      (goto-char bound)
-      (if (save-restriction
-	    (narrow-to-region (1+ open-paren) (point-max))
-	    (re-search-backward
-	     (eval-when-compile
-	       ;; This regular expression matches either an escape pair (which
-	       ;; isn't an escaped NL) (submatch 5) or a non-escaped character
-	       ;; (which isn't itself a backslash) (submatch 10).  The long
-	       ;; preambles to these (respectively submatches 2-4 and 6-9)
-	       ;; ensure that we have the correct parity for sequences of
-	       ;; backslashes, etc..
-	       (concat "\\("		; 1
-		       "\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)*" ; 2-4
-		       "\\(\\\\.\\)"	; 5
-		       "\\|"
-		       "\\(\\`\\|[^\\]\\|\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)+\\)" ; 6-9
-		       "\\([^\\]\\)"	; 10
-		       "\\)"
-		       "\\(\\\\\n\\)*\\=")) ; 11
-             (1+ open-paren) t))
-	  (if (match-beginning 10)
-	      (progn
-		(c-put-char-property (match-beginning 10) 'syntax-table '(15))
-		(c-truncate-semi-nonlit-pos-cache (match-beginning 10)))
-	    (c-put-char-property (match-beginning 5) 'syntax-table '(1))
-	    (c-put-char-property (1+ (match-beginning 5)) 'syntax-table '(15))
-	    (c-truncate-semi-nonlit-pos-cache (1+ (match-beginning 5))))
-	(c-put-char-property open-paren 'syntax-table '(1)))
-      (goto-char bound))))
-
-(defun c-after-change-re-mark-raw-strings (_beg _end _old-len)
-  ;; This function applies `syntax-table' text properties to C++ raw strings
-  ;; beginning in the region (c-new-BEG c-new-END).  BEG, END, and OLD-LEN are
-  ;; the standard arguments supplied to any after-change function.
+  ;; but if we're inside a macro, it's the end of the macro (i.e. the position
+  ;; of the closing newline).
+  ;;
+  ;; Point is moved to after the (terminated) raw string and t is returned, or
+  ;; it is left after the unmatched opening delimiter and nil is returned.
+  (c-propertize-raw-string-id (1+ open-quote) open-paren)
+  (prog1
+      (if (search-forward (concat ")" id "\"") bound t)
+	  (let ((end-string (match-beginning 0))
+		(after-quote (match-end 0)))
+	    (c-propertize-raw-string-id
+	     (1+ (match-beginning 0)) (1- (match-end 0)))
+	    (goto-char open-paren)
+	    (while (progn (skip-syntax-forward "^\"" end-string)
+			  (< (point) end-string))
+	      (c-put-char-property (point) 'syntax-table '(1)) ; punctuation
+	      (c-truncate-semi-nonlit-pos-cache (point))
+	      (forward-char))
+	    (goto-char after-quote)
+	    t)
+	(c-put-char-property open-quote 'syntax-table '(1)) ; punctuation
+	(c-truncate-semi-nonlit-pos-cache open-quote)
+	(c-put-char-property open-paren 'syntax-table '(15)) ; generic string
+	(when bound
+	  ;; In a CPP construct, we try to apply a generic-string
+	  ;; `syntax-table' text property to the last possible character in
+	  ;; the string, so that only characters within the macro get
+	  ;; "stringed out".
+	  (goto-char bound)
+	  (if (save-restriction
+		(narrow-to-region (1+ open-paren) (point-max))
+		(re-search-backward
+		 (eval-when-compile
+		   ;; This regular expression matches either an escape pair
+		   ;; (which isn't an escaped NL) (submatch 5) or a
+		   ;; non-escaped character (which isn't itself a backslash)
+		   ;; (submatch 10).  The long preambles to these
+		   ;; (respectively submatches 2-4 and 6-9) ensure that we
+		   ;; have the correct parity for sequences of backslashes,
+		   ;; etc..
+		   (concat "\\("	; 1
+			   "\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)*" ; 2-4
+			   "\\(\\\\.\\)" ; 5
+			   "\\|"
+			   "\\(\\`\\|[^\\]\\|\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)+\\)" ; 6-9
+			   "\\([^\\]\\)" ; 10
+			   "\\)"
+			   "\\(\\\\\n\\)*\\=")) ; 11
+		 (1+ open-paren) t))
+	      (if (match-beginning 10)
+		  (progn
+		    (c-put-char-property (match-beginning 10) 'syntax-table '(15))
+		    (c-truncate-semi-nonlit-pos-cache (match-beginning 10)))
+		(c-put-char-property (match-beginning 5) 'syntax-table '(1))
+		(c-put-char-property (1+ (match-beginning 5)) 'syntax-table '(15))
+		(c-truncate-semi-nonlit-pos-cache (1+ (match-beginning 5))))
+	    ;; (c-put-char-property open-paren 'syntax-table '(1))
+	    )
+	  (goto-char bound))
+	nil)
+  ;; Ensure the opening delimiter will get refontified.
+    (c-font-lock-flush (1- open-quote) (1+ open-paren))))
+
+(defun c-after-change-unmark-raw-strings (beg end _old-len)
+  ;; This function removes `syntax-table' text properties from any raw strings
+  ;; which have been affected by the current change.  These are those which
+  ;; have been "stringed out" and from newly formed raw strings, or any
+  ;; existing raw string which the new text terminates.  BEG, END, and
+  ;; _OLD-LEN are the standard arguments supplied to any
+  ;; after-change-function.
   ;;
   ;; Point is undefined on both entry and exit, and the return value has no
   ;; significance.
   ;;
-  ;; This function is called as an after-change function solely due to its
+  ;; This functions is called as an after-change function by virtue of its
   ;; membership of the C++ value of `c-before-font-lock-functions'.
-  (c-save-buffer-state ()
-    ;; If the region (c-new-BEG c-new-END) has expanded, remove
-    ;; `syntax-table' text-properties from the new piece(s).
-    (when (< c-new-BEG c-old-BEG)
-      (let ((beg-rs (progn (goto-char c-old-BEG) (c-raw-string-pos))))
-	(c-depropertize-raw-strings-in-region
-	 c-new-BEG
-	 (if beg-rs
-	     (1+ (or (nth 4 beg-rs) (nth 2 beg-rs)))
-	   c-old-BEG))))
-    (when (> c-new-END c-old-END)
-      (let ((end-rs (progn (goto-char c-old-END) (c-raw-string-pos))))
-	(c-depropertize-raw-strings-in-region
-	 (if end-rs
-	     (cadr end-rs)
-	   c-old-END)
-	 c-new-END)))
+  ;; (when (< beg end)
+    (c-save-buffer-state (found eoll state id found-beg found-end)
+      ;; Has an inserted " swallowed up a R"(, turning it into "...R"(?
+      (goto-char end)
+      (setq eoll (c-point 'eoll))
+      (when (and (null c-old-END-literality)
+		 (search-forward-regexp "R\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)("
+				 eoll t))
+	(setq state (c-state-semi-pp-to-literal end))
+	(when (eq (cadr state) 'string)
+	  (unwind-protect
+	      ;; Temporarily insert a closing string delimiter....
+	      (progn
+		(goto-char end)
+		(cond
+		 ((c-characterp (nth 3 (car state)))
+		  (insert (nth 3 (car state))))
+		 ((eq (nth 3 (car state)) t)
+		  (insert ?\")
+		  (c-put-char-property end 'syntax-table '(15))))
+		(c-truncate-semi-nonlit-pos-cache end)
+		;; ....ensure c-new-END extends right to the end of the about
+		;; to be un-stringed raw string....
+		(save-excursion
+		  (goto-char (match-beginning 1))
+		  (let ((end-bs (c-raw-string-pos)))
+		    (setq c-new-END
+			  (max c-new-END
+			       (if (nth 4 end-bs)
+				   (1+ (nth 4 end-bs))
+				 eoll)))))
+
+		;; ...and clear `syntax-table' text propertes from the
+		;; following raw strings.
+		(c-depropertize-raw-strings-in-region (point) (1+ eoll)))
+	    ;; Remove the temporary string delimiter.
+	    (goto-char end)
+	    (delete-char 1))))
+
+      ;; Have we just created a new starting id?
+      (goto-char (max (- beg 18) (point-min)))
+      (while
+	  (and
+	   (setq found
+		 (search-forward-regexp "R\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)("
+				       c-new-END 'bound))
+	   (<= (match-end 0) beg)))
+      (when (and found (<= (match-beginning 0) end))
+	(setq c-new-BEG (min c-new-BEG (match-beginning 0)))
+	(c-depropertize-raw-strings-in-region c-new-BEG c-new-END))
+
+      ;; Have we invalidated an opening delimiter by typing into it?
+      (when (and c-old-beg-rs
+		 (eq (car c-old-beg-rs) 'open-delim)
+		 (equal (c-get-char-property (cadr c-old-beg-rs)
+					     'syntax-table)
+			'(1)))
+	(goto-char (1- (cadr c-old-beg-rs)))
+	(unless (looking-at "R\"[^ ()\\\n\r\t]\\{0,16\\}(")
+	  (c-clear-char-property (1+ (point)) 'syntax-table)
+	  (c-truncate-semi-nonlit-pos-cache (1+ (point)))
+	  (if (c-search-forward-char-property 'syntax-table '(15)
+					      (c-point 'eol))
+	      (c-clear-char-property (1- (point)) 'syntax-table))))
+
+      ;; Have we terminated an existing raw string by inserting or removing
+      ;; text?
+      (when (eq c-old-END-literality 'string)
+	(setq state (c-state-semi-pp-to-literal beg))
+	(cond
+	 ;; Possibly terminating a(n un)terminated raw string.
+	 ((eq (nth 3 (car state)) t)
+	  (goto-char (nth 8 (car state)))
+	  (when
+	      (and (eq (char-after) ?\()
+		   (search-backward-regexp
+		    "R\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)\\=" (- (point) 18) t))
+	    (setq id (match-string-no-properties 1)
+		  found-beg (match-beginning 0)
+		  found-end (1+ (match-end 0)))))
+	 ;; Possibly terminating an already terminated raw string.
+	 ((eq (nth 3 (car state)) ?\")
+	  (goto-char (nth 8 (car state)))
+	  (when
+	      (and (eq (char-before) ?R)
+		   (looking-at "\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)("))
+	    (setq id (match-string-no-properties 1)
+		  found-beg (1- (point))
+		  found-end (match-end 0)))))
+	(when id
+	  (goto-char (max (- beg 18) (point-min)))
+	  (when (search-forward (concat ")" id "\"") (+ end 1 (length id)) t)
+	    ;; Has an earlier close delimiter just been inserted into an
+	    ;; already terminated raw string?
+	    (if (and (eq (nth 3 (car state)) ?\")
+		     (search-forward (concat ")" id "\"") nil t))
+		(setq found-end (point)))
+	    (setq c-new-BEG (min c-new-BEG found-beg)
+		  c-new-END (max c-new-END found-end))
+	    (c-clear-char-properties found-beg found-end 'syntax-table)
+	    (c-truncate-semi-nonlit-pos-cache found-beg))))
+
+      ;; Are there any raw strings in a newly created macro?
+      (when (< beg end)
+	(goto-char beg)
+	(setq found-beg (point))
+	(when (search-forward-regexp c-anchored-cpp-prefix end t)
+	  (c-end-of-macro)
+	  (c-depropertize-raw-strings-in-region found-beg (point))))))
 
-    (goto-char c-new-BEG)
-    (while (and (< (point) c-new-END)
-		(re-search-forward
-		 (concat "\\("				       ; 1
-			 c-anchored-cpp-prefix		       ; 2
-			 "\\)\\|\\("			       ; 3
-			 "R\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)("  ; 4
-			 "\\)")
-		 c-new-END t))
-      (when (save-excursion
-	      (goto-char (match-beginning 0)) (not (c-in-literal)))
-	(if (match-beginning 4)		; the id
-	    ;; We've found a raw string.
+(defun c-maybe-re-mark-raw-string ()
+  ;; When this function is called, point is immediately after a ".  If this "
+  ;; is the characteristic " of of a raw string delimiter, apply the pertinent
+  ;; `syntax-table' text properties to the entire raw string (when properly
+  ;; terminated) or just the delimiter (otherwise).
+  ;;
+  ;; If the " is in any way part of a raw string, return non-nil.  Otherwise
+  ;; return nil.
+  (let ((here (point))
+	in-macro macro-end id Rquote found)
+    (cond
+     ((and
+       (eq (char-before (1- (point))) ?R)
+       (looking-at "\\([^ ()\\\n\r\t]\\{0,16\\}\\)("))
+      (save-excursion
+	(setq in-macro (c-beginning-of-macro))
+	(setq macro-end (when in-macro
+			  (c-end-of-macro)
+			  (point) ;; (min (1+ (point)) (point-max))
+			  )))
+      (if (not
+	   (c-propertize-raw-string-opener
+	    (match-string-no-properties 1) ; id
+	    (1- (point))		   ; open quote
+	    (match-end 1)		   ; open paren
+	    macro-end))		     ; bound (end of macro) or nil.
+	  (goto-char (or macro-end (point-max))))
+      t)
+     ((save-excursion
+	(and
+	 (search-backward-regexp ")\\([^ ()\\\n\r\t]\\{0,16\\}\\)\"\\=" nil t)
+	 (setq id (match-string-no-properties 1))
+	 (let* ((quoted-id (regexp-quote id))
+		(quoted-id-depth (regexp-opt-depth quoted-id)))
+	   (while
+	       (and
+		;; Search back for an opening delimiter with identifier `id'.
+		;; A closing delimiter with `id' "blocks" our search.
+		(search-backward-regexp	; This could be slow.
+		 (concat "\\(R\"" quoted-id "(\\)"
+			 "\\|"
+			 "\\()" quoted-id "\"\\)")
+		 nil t)
+		(setq found t)
+		(if (eq (c-in-literal) 'string)
+		    (match-beginning 1)
+		  (match-beginning (+ 2 quoted-id-depth)))))
+	   (and found
+		(null (c-in-literal))
+		(match-beginning 1)))
+	 (setq Rquote (point))))
+      (save-excursion
+	(goto-char Rquote)
+	(setq in-macro (c-beginning-of-macro))
+	(setq macro-end (when in-macro
+			  (c-end-of-macro)
+			  (point))))
+      (if (or (not in-macro)
+	      (<= here macro-end))
+	  (progn
 	    (c-propertize-raw-string-opener
-	     (match-string-no-properties 4) ; id
-	     (1+ (match-beginning 3))	    ; open quote
-	     (match-end 4)		    ; open paren
-	     nil)			    ; bound
-	  ;; We've found a CPP construct.  Search for raw strings within it.
-	  (goto-char (match-beginning 2)) ; the "#"
-	  (c-end-of-macro)
-	  (let ((eom (point)))
-	    (goto-char (match-end 2))	; after the "#".
-	    (while (and (< (point) eom)
-			(c-syntactic-re-search-forward
-			 "R\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)(" eom t))
-	      (c-propertize-raw-string-opener
-	       (match-string-no-properties 1) ; id
-	       (1+ (match-beginning 0))	      ; open quote
-	       (match-end 1)		      ; open paren
-	       eom))))))))		      ; bound
+	     id (1+ (point)) (match-end 1) macro-end)
+	    (goto-char here)
+	    t)
+	(goto-char here)
+	nil))
+
+     (t
+      ;; If the " is in another part of a raw string (whether as part of the
+      ;; identifier, or in the string itself) the `syntax-table' text
+      ;; properties on the raw string will be current.  So, we can use...
+      (c-raw-string-pos)))))
 
 
 ;; Handling of small scale constructs like types and names.
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index 0b41eff1577..e7a3748af43 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1674,25 +1674,36 @@ casts and declarations are fontified.  Used on level 2 and higher."
 			(goto-char string-start)
 			(and (eq (char-before) ?R)
 			     (looking-at "\"\\([^ ()\\\n\r\t]\\{0,16\\}\\)(")
-			     (match-string-no-properties 1))))))
+			     (match-string-no-properties 1)))))
+	 (content-start (and raw-id (point))))
+    ;; We go round the next loop twice per raw string, once for each "end".
     (while (< (point) limit)
       (if raw-id
+	  ;; Search for the raw string end delimiter
 	  (progn
-	    (if (search-forward-regexp (concat ")\\(" (regexp-quote raw-id) "\\)\"")
-				       limit 'limit)
-		(c-put-font-lock-face (match-beginning 1) (point) 'default))
+	    (when (search-forward-regexp (concat ")\\(" (regexp-quote raw-id) "\\)\"")
+					 limit 'limit)
+	      (c-put-font-lock-face content-start (match-beginning 1)
+				    'font-lock-string-face)
+	      (c-remove-font-lock-face (match-beginning 1) (point)))
 	    (setq raw-id nil))
-
+	;; Search for the start of a raw string.
 	(when (search-forward-regexp
 	       "R\\(\"\\)\\([^ ()\\\n\r\t]\\{0,16\\}\\)(" limit 'limit)
 	  (when
-	      (or (and (eobp)
-		       (eq (c-get-char-property (1- (point)) 'face)
-			   'font-lock-warning-face))
-		  (eq (c-get-char-property (point) 'face) 'font-lock-string-face)
-		  (and (equal (c-get-char-property (match-end 2) 'syntax-table) '(1))
-		       (equal (c-get-char-property (match-beginning 1) 'syntax-table)
-			      '(1))))
+	      ;; Make sure we're not in a comment or string.
+	      (and
+	       (not (memq (c-get-char-property (match-beginning 0) 'face)
+			  '(font-lock-comment-face font-lock-comment-delimiter-face
+						   font-lock-string-face)))
+	       (or (and (eobp)
+	  		(eq (c-get-char-property (1- (point)) 'face)
+	  		    'font-lock-warning-face))
+	  	   (not (eq (c-get-char-property (point) 'face) 'font-lock-comment-face))
+		   ;; (eq (c-get-char-property (point) 'face) 'font-lock-string-face)
+	  	   (and (equal (c-get-char-property (match-end 2) 'syntax-table) '(1))
+	  		(equal (c-get-char-property (match-beginning 1) 'syntax-table)
+	  		       '(1)))))
 	    (let ((paren-prop (c-get-char-property (1- (point)) 'syntax-table)))
 	      (if paren-prop
 		  (progn
@@ -1703,8 +1714,9 @@ casts and declarations are fontified.  Used on level 2 and higher."
 			 (equal paren-prop '(15))
 			 (not (c-search-forward-char-property 'syntax-table '(15) limit)))
 		      (goto-char limit)))
-		(c-put-font-lock-face (match-beginning 1) (match-end 2) 'default)
-		(setq raw-id (match-string-no-properties 2)))))))))
+		(c-remove-font-lock-face (match-beginning 0) (match-end 2))
+		(setq raw-id (match-string-no-properties 2))
+		(setq content-start (match-end 0)))))))))
   nil)
 
 (defun c-font-lock-c++-lambda-captures (limit)
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index 7cc8029e0a3..22b7b602f1e 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -497,25 +497,25 @@ parameters \(point-min) and \(point-max).")
   ;; For documentation see the following c-lang-defvar of the same name.
   ;; The value here may be a list of functions or a single function.
   t '(c-depropertize-new-text
-      c-after-change-re-mark-unbalanced-strings
+      c-after-change-mark-abnormal-strings
       c-change-expand-fl-region)
   (c objc) '(c-depropertize-new-text
 	     c-parse-quotes-after-change
-	     c-after-change-re-mark-unbalanced-strings
+	     c-after-change-mark-abnormal-strings
 	     c-extend-font-lock-region-for-macros
 	     c-neutralize-syntax-in-CPP
 	     c-change-expand-fl-region)
   c++ '(c-depropertize-new-text
+	c-after-change-unmark-raw-strings
 	c-parse-quotes-after-change
-	c-after-change-re-mark-unbalanced-strings
+	c-after-change-mark-abnormal-strings
 	c-extend-font-lock-region-for-macros
-	c-after-change-re-mark-raw-strings
 	c-neutralize-syntax-in-CPP
 	c-restore-<>-properties
 	c-change-expand-fl-region)
   java '(c-depropertize-new-text
 	 c-parse-quotes-after-change
-	 c-after-change-re-mark-unbalanced-strings
+	 c-after-change-mark-abnormal-strings
 	 c-restore-<>-properties
 	 c-change-expand-fl-region)
   awk '(c-depropertize-new-text
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 8343978fc38..c1fb6aa0915 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -678,14 +678,12 @@ that requires a literal mode spec at compile time."
 (make-variable-buffer-local 'c-new-BEG)
 (defvar c-new-END 0)
 (make-variable-buffer-local 'c-new-END)
-;; The following two variables record the values of `c-new-BEG' and
-;; `c-new-END' just after `c-new-END' has been adjusted for the length of text
-;; inserted or removed.  They may be read by any after-change function (but
-;; should not be altered by one).
-(defvar c-old-BEG 0)
-(make-variable-buffer-local 'c-old-BEG)
-(defvar c-old-END 0)
-(make-variable-buffer-local 'c-old-END)
+
+;; Buffer local variable which notes the value of calling `c-in-literal' just
+;; before a change.  It is one of 'string, 'c, 'c++ (for the two sorts of
+;; comments), or nil.
+(defvar c-old-END-literality nil)
+(make-variable-buffer-local 'c-old-END-literality)
 
 (defun c-common-init (&optional mode)
   "Common initialization for all CC Mode modes.
@@ -900,7 +898,8 @@ Note that the style variables are always made local to the buffer."
 
 (defun c-depropertize-CPP (beg end)
   ;; Remove the punctuation syntax-table text property from the CPP parts of
-  ;; (c-new-BEG c-new-END).
+  ;; (c-new-BEG c-new-END), and remove all syntax-table properties from any
+  ;; raw strings within these CPP parts.
   ;;
   ;; This function is in the C/C++/ObjC values of
   ;; `c-get-state-before-change-functions' and is called exclusively as a
@@ -912,6 +911,7 @@ Note that the style variables are always made local to the buffer."
       (goto-char (match-beginning 1))
       (setq m-beg (point))
       (c-end-of-macro)
+      (save-excursion (c-depropertize-raw-strings-in-region m-beg (point)))
       (c-clear-char-property-with-value m-beg (point) 'syntax-table '(1)))
 
     (while (and (< (point) end)
@@ -920,14 +920,16 @@ Note that the style variables are always made local to the buffer."
       (goto-char (match-beginning 1))
       (setq m-beg (point))
       (c-end-of-macro))
-    (if (and ss-found (> (point) end))
-	(c-clear-char-property-with-value m-beg (point) 'syntax-table '(1)))
+    (when (and ss-found (> (point) end))
+      (save-excursion (c-depropertize-raw-strings-in-region m-beg (point)))
+      (c-clear-char-property-with-value m-beg (point) 'syntax-table '(1)))
 
     (while (and (< (point) c-new-END)
 		(search-forward-regexp c-anchored-cpp-prefix c-new-END 'bound))
       (goto-char (match-beginning 1))
       (setq m-beg (point))
       (c-end-of-macro)
+      (save-excursion (c-depropertize-raw-strings-in-region m-beg (point)))
       (c-clear-char-property-with-value
        m-beg (point) 'syntax-table '(1)))))
 
@@ -1213,6 +1215,7 @@ Note that the style variables are always made local to the buffer."
 		     "\"\\|\\s|" (point-max) t t)
 		    (progn
 		      (c-clear-char-property (1- (point)) 'syntax-table)
+		      (c-truncate-semi-nonlit-pos-cache (1- (point)))
 		      (not (eq (char-before) ?\")))))
 	       (eq (char-before) ?\"))
 	     (progn
@@ -1247,27 +1250,38 @@ Note that the style variables are always made local to the buffer."
 		(forward-char)
 		(backward-sexp)
 		(c-clear-char-property eoll-1 'syntax-table)
+		(c-truncate-semi-nonlit-pos-cache eoll-1)
 		(c-clear-char-property (point) 'syntax-table))
 	    ;; Opening " at EOB.
 	    (c-clear-char-property (1- (point)) 'syntax-table))
-	(if (c-search-backward-char-property 'syntax-table '(15) c-new-BEG)
-	    ;; Opening " on last line of text (without EOL).
-	    (c-clear-char-property (point) 'syntax-table))))
+	(when (and (c-search-backward-char-property 'syntax-table '(15) c-new-BEG)
+		   (eq (char-after) ?\")) ; Ignore an unterminated raw string's (.
+	  ;; Opening " on last line of text (without EOL).
+	  (c-clear-char-property (point) 'syntax-table)
+	  (c-truncate-semi-nonlit-pos-cache (point)))))
 
      (t (goto-char end)			; point-max
-	(if (c-search-backward-char-property 'syntax-table '(15) c-new-BEG)
-	    (c-clear-char-property (point) 'syntax-table))))
+	(when
+	    (and
+	     (c-search-backward-char-property 'syntax-table '(15) c-new-BEG)
+	     (eq (char-after) ?\"))
+	  (c-clear-char-property (point) 'syntax-table)
+	  (c-truncate-semi-nonlit-pos-cache (point)))))
 
     (unless (and c-multiline-string-start-char
 		 (not (c-characterp c-multiline-string-start-char)))
-      (when (eq end-literal-type 'string)
-	(c-clear-char-property (1- (cdr end-limits)) 'syntax-table))
+      (when (and (eq end-literal-type 'string)
+		 (not (eq (char-before (cdr end-limits)) ?\()))
+	(c-clear-char-property (1- (cdr end-limits)) 'syntax-table)
+	(c-truncate-semi-nonlit-pos-cache (1- (cdr end-limits))))
 
-      (when (eq beg-literal-type 'string)
+      (when (and (eq beg-literal-type 'string)
+		 (eq (char-after (car beg-limits)) ?\"))
 	(setq c-new-BEG (min c-new-BEG (car beg-limits)))
-	(c-clear-char-property (car beg-limits) 'syntax-table)))))
+	(c-clear-char-property (car beg-limits) 'syntax-table)
+	(c-truncate-semi-nonlit-pos-cache (car beg-limits))))))
 
-(defun c-after-change-re-mark-unbalanced-strings (beg end _old-len)
+(defun c-after-change-mark-abnormal-strings (beg end _old-len)
   ;; Mark any unbalanced strings in the region (c-new-BEG c-new-END) with
   ;; string fence syntax-table text properties.
   ;;
@@ -1318,7 +1332,8 @@ Note that the style variables are always made local to the buffer."
 	       (min (1+ (point)) (point-max)))))
 	   ((and (null beg-literal-type)
 		 (goto-char beg)
-		 (eq (char-before) c-multiline-string-start-char)
+		 (and (not (bobp))
+		      (eq (char-before) c-multiline-string-start-char))
 		 (memq (char-after) c-string-delims))
 	    (cons (point)
 		  (progn
@@ -1343,22 +1358,24 @@ Note that the style variables are always made local to the buffer."
 	     (while (progn
 		      (setq s (parse-partial-sexp (point) c-new-END nil
 						  nil s 'syntax-table))
-                      (and (< (point) c-new-END)
-                           (or (not (nth 3 s))
-                               (not (memq (char-before) c-string-delims))))))
+		      (and (< (point) c-new-END)
+			   (or (not (nth 3 s))
+			       (not (memq (char-before) c-string-delims))))))
 	     ;; We're at the start of a string.
 	     (memq (char-before) c-string-delims)))
-	(if (c-unescaped-nls-in-string-p (1- (point)))
-	    (looking-at "\\(\\\\\\(.\\|\n\\|\r\\)\\|[^\"]\\)*")
-	  (looking-at (cdr (assq (char-before) c-string-innards-re-alist))))
-	(cond
-	 ((memq (char-after (match-end 0)) '(?\n ?\r))
-	  (c-put-char-property (1- (point)) 'syntax-table '(15))
-	  (c-put-char-property (match-end 0) 'syntax-table '(15)))
-	 ((or (eq (match-end 0) (point-max))
-	      (eq (char-after (match-end 0)) ?\\)) ; \ at EOB
-	  (c-put-char-property (1- (point)) 'syntax-table '(15))))
-	(goto-char (min (1+ (match-end 0)) (point-max)))
+	(unless (and (c-major-mode-is 'c++-mode)
+		     (c-maybe-re-mark-raw-string))
+	  (if (c-unescaped-nls-in-string-p (1- (point)))
+	      (looking-at "\\(\\\\\\(.\\|\n|\\\r\\)\\|[^\"]\\)*")
+	    (looking-at (cdr (assq (char-before) c-string-innards-re-alist))))
+	  (cond
+	   ((memq (char-after (match-end 0)) '(?\n ?\r))
+	    (c-put-char-property (1- (point)) 'syntax-table '(15))
+	    (c-put-char-property (match-end 0) 'syntax-table '(15)))
+	   ((or (eq (match-end 0) (point-max))
+		(eq (char-after (match-end 0)) ?\\)) ; \ at EOB
+	    (c-put-char-property (1- (point)) 'syntax-table '(15))))
+	  (goto-char (min (1+ (match-end 0)) (point-max))))
 	(setq s nil)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1721,7 +1738,6 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".")
   ;; (c-new-BEG c-new-END) will be the region to fontify.  It may become
   ;; larger than (beg end).
   (setq c-new-END (- (+ c-new-END (- end beg)) old-len))
-  (setq c-old-BEG c-new-BEG  c-old-END c-new-END)
 
   (unless (c-called-from-text-property-change-p)
     (setq c-just-done-before-change nil)