From ce1ed9c8116e280c69f04a455176f6097ca0855c Mon Sep 17 00:00:00 2001
From: Alan Mackenzie <acm@muc.de>
Date: Mon, 15 Aug 2016 11:52:32 +0000
Subject: [PATCH] Handle C++11 lambda functions.

* lisp/progmodes/cc-engine.el (c-looking-at-inexpr-block): Enhance also to
handle C++ lambda functions.
(c-looking-at-c++-lambda-capture-list): New function.

* lisp/progmodes/cc-fonts.el (c-font-lock-declarations): Recognize the
parameter list of a lambda function and set `context' and
`c-restricted-<>-arglists' suitably for it.
(c-font-lock-c++-lambda-captures): New function.
(c-complex-decl-matchers): Insert c-font-lock-c++-lambda-captures into it.

* lisp/progmodes/cc-langs.el (c-pre-lambda-tokens, c-pre-lambda-tokens-re):
New language constants/variables.
(c-paren-nontype-kwds): Include "noexcept" in the C++ value.

* lisp/progmodes/cc-mode.el (c-fl-decl-start): Handle being in a C++ lambda
function capture list.
---
 lisp/progmodes/cc-engine.el |  85 +++++++++++++++++++++++++-----
 lisp/progmodes/cc-fonts.el  | 101 ++++++++++++++++++++++++++++++++++++
 lisp/progmodes/cc-langs.el  |  24 ++++++++-
 lisp/progmodes/cc-mode.el   |  46 ++++++++++++++++
 4 files changed, 243 insertions(+), 13 deletions(-)

diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index e22b98dbc4d..4a29896b4a5 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -10005,12 +10005,27 @@ comment at the start of cc-engine.el for more info."
   ;; This function might do hidden buffer changes.
 
   (save-excursion
-    (let ((res 'maybe) passed-paren
+    (let ((res 'maybe) (passed-bracket-pairs 0) bracket-pos passed-paren
+	  haskell-op-pos
 	  (closest-lim (or containing-sexp lim (point-min)))
 	  ;; Look at the character after point only as a last resort
 	  ;; when we can't disambiguate.
 	  (block-follows (and (eq (char-after) ?{) (point))))
 
+      ;; Search for a C++11 "->" which suggests a lambda declaration.
+      (when (and (c-major-mode-is 'c++-mode)
+		 (setq haskell-op-pos
+		       (save-excursion
+			 (while
+			     (progn
+			       (c-syntactic-skip-backward "^;=}>" closest-lim t)
+			       (and (eq (char-before) ?>)
+				    (c-backward-token-2)
+				    (not (looking-at c-haskell-op-re)))))
+			 (and (looking-at c-haskell-op-re)
+			      (point)))))
+	(goto-char haskell-op-pos))
+
       (while (and (eq res 'maybe)
 		  (progn (c-backward-syntactic-ws)
 			 (> (point) closest-lim))
@@ -10048,6 +10063,11 @@ comment at the start of cc-engine.el for more info."
 					     (zerop (c-forward-token-2 1 t)))
 				      (eq (char-after) ?\())))
 			   (cons 'inexpr-class (point))))
+		     ((c-keyword-member kw-sym 'c-paren-any-kwds) ; e.g. C++11 "throw" or "noexcept"
+		      (setq passed-paren nil)
+		      (setq passed-bracket-pairs 0)
+		      (setq bracket-pos nil)
+		      'maybe)
 		     ((c-keyword-member kw-sym 'c-inexpr-block-kwds)
 		      (when (not passed-paren)
 			(cons 'inexpr-statement (point))))
@@ -10062,20 +10082,49 @@ comment at the start of cc-engine.el for more info."
 
 		(if (looking-at "\\s(")
 		    (if passed-paren
-			(if (and (eq passed-paren ?\[)
-				 (eq (char-after) ?\[))
-			    ;; Accept several square bracket sexps for
-			    ;; Java array initializations.
-			    'maybe)
-		      (setq passed-paren (char-after))
+			(cond
+			 ((and (eq passed-paren ?\[)
+			       (eq (char-after) ?\[)
+			       (not (eq (char-after (1+ (point))) ?\[))) ; C++ attribute.
+			  ;; Accept several square bracket sexps for
+			  ;; Java array initializations.
+			  (setq passed-bracket-pairs (1+ passed-bracket-pairs))
+			  'maybe)
+			 ((and (eq passed-paren ?\()
+			       (eq (char-after) ?\[)
+			       (not (eq (char-after (1+ (point))) ?\[))
+			       (eq passed-bracket-pairs 0))
+			  ;; C++11 lambda function declaration
+			  (setq passed-bracket-pairs 1)
+			  (setq bracket-pos (point))
+			  'maybe)
+			 (t nil))
+		      (when (not (looking-at "\\[\\["))
+			(setq passed-paren (char-after))
+			(when (eq passed-paren ?\[)
+			  (setq passed-bracket-pairs 1)
+			  (setq bracket-pos (point))))
 		      'maybe)
 		  'maybe))))
 
       (if (eq res 'maybe)
-	  (when (and c-recognize-paren-inexpr-blocks
-		     block-follows
-		     containing-sexp
-		     (eq (char-after containing-sexp) ?\())
+	  (cond
+	   ((and (c-major-mode-is 'c++-mode)
+		 block-follows
+		 (eq passed-bracket-pairs 1)
+		 (save-excursion
+		   (goto-char bracket-pos)
+		   (or (<= (point) (or lim (point-min)))
+		       (progn
+			 (c-backward-token-2 1 nil lim)
+			 (and
+			  (not (c-on-identifier))
+			  (not (looking-at c-opt-op-identifier-prefix)))))))
+	    (cons 'inlambda bracket-pos))
+	   ((and c-recognize-paren-inexpr-blocks
+		 block-follows
+		 containing-sexp
+		 (eq (char-after containing-sexp) ?\())
 	    (goto-char containing-sexp)
 	    (if (or (save-excursion
 		      (c-backward-syntactic-ws lim)
@@ -10089,7 +10138,7 @@ comment at the start of cc-engine.el for more info."
 		    (and c-special-brace-lists
 			 (c-looking-at-special-brace-list)))
 		nil
-	      (cons 'inexpr-statement (point))))
+	      (cons 'inexpr-statement (point)))))
 
 	res))))
 
@@ -10115,6 +10164,18 @@ comment at the start of cc-engine.el for more info."
 						    paren-state)
 				   containing-sexp)))))
 
+(defun c-looking-at-c++-lambda-capture-list ()
+  ;; Return non-nil if we're at the opening "[" of the capture list of a C++
+  ;; lambda function, nil otherwise.
+  (and
+   (eq (char-after) ?\[)
+   (not (eq (char-before) ?\[))
+   (not (eq (char-after (1+ (point))) ?\[))
+   (save-excursion
+     (or (eq (c-backward-token-2 1) 1)
+	 (looking-at c-pre-lambda-tokens-re)))
+   (not (c-in-literal))))
+
 (defun c-at-macro-vsemi-p (&optional pos)
   ;; Is there a "virtual semicolon" at POS or point?
   ;; (See cc-defs.el for full details of "virtual semicolons".)
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index b45686c81b0..ae18d0a9436 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1242,6 +1242,20 @@ casts and declarations are fontified.  Used on level 2 and higher."
 		    ((eq type 'c-decl-arg-start)
 		     (setq context 'decl
 			   c-restricted-<>-arglists nil))
+		    ;; Inside a C++11 lambda function arglist.
+		    ((and (c-major-mode-is 'c++-mode)
+			  (eq (char-before match-pos) ?\()
+			  (save-excursion
+			    (goto-char match-pos)
+			    (c-backward-token-2)
+			    (and
+			     (c-safe (goto-char (scan-sexps (point) -1)))
+			     (c-looking-at-c++-lambda-capture-list))))
+		     (setq context 'decl
+			   c-restricted-<>-arglists nil)
+		     (c-put-char-property (1- match-pos) 'c-type
+					  'c-decl-arg-start))
+
 		    ;; Inside an angle bracket arglist.
 		    ((or (eq type 'c-<>-arg-sep)
 			 (eq (char-before match-pos) ?<))
@@ -1583,6 +1597,90 @@ casts and declarations are fontified.  Used on level 2 and higher."
 		(setq raw-id (match-string-no-properties 2)))))))))
   nil)
 
+(defun c-font-lock-c++-lambda-captures (limit)
+  ;; Fontify the lambda capture component of C++ lambda declarations.
+  ;;
+  ;; This function will be called from font-lock for a region bounded by POINT
+  ;; and LIMIT, as though it were to identify a keyword for
+  ;; font-lock-keyword-face.  It always returns NIL to inhibit this and
+  ;; prevent a repeat invocation.  See elisp/lispref page "Search-based
+  ;; Fontification".
+  (let (mode capture-default id-start id-end declaration sub-begin sub-end)
+    (while (and (< (point) limit)
+		(search-forward "[" limit t))
+      (when (progn (backward-char)
+		   (prog1
+		       (c-looking-at-c++-lambda-capture-list)
+		     (forward-char)))
+	(c-forward-syntactic-ws)
+	(setq mode (and (memq (char-after) '(?= ?&))
+			(char-after)))
+	;; Is the first element of the list a bare "=" or "&"?
+	(when mode
+	  (forward-char)
+	  (c-forward-syntactic-ws)
+	  (if (memq (char-after) '(?, ?\]))
+	      (progn
+		(setq capture-default mode)
+		(when (eq (char-after) ?,)
+		  (forward-char)
+		  (c-forward-syntactic-ws)))
+	    (c-backward-token-2)))
+
+	;; Go round the following loop once per captured item.
+	(while (and (not (eq (char-after) ?\]))
+		    (< (point) limit))
+	  (if (eq (char-after) ?&)
+	      (progn (setq mode ?&)
+		     (forward-char)
+		     (c-forward-syntactic-ws))
+	    (setq mode ?=))
+	  (if (c-on-identifier)
+	      (progn
+		(setq id-start (point))
+		(forward-char)
+		(c-end-of-current-token)
+		(setq id-end (point))
+		(c-forward-syntactic-ws)
+
+		(setq declaration (eq (char-after) ?=))
+		(when declaration
+		  (forward-char)	; over "="
+		  (c-forward-syntactic-ws)
+		  (setq sub-begin (point)))
+		(if (or (and (< (point) limit)
+			     (c-syntactic-re-search-forward "," limit t t))
+			(and (c-go-up-list-forward nil limit)
+			     (eq (char-before) ?\])))
+		    (backward-char)
+		  (goto-char limit))
+		(when declaration
+		  (save-excursion
+		    (setq sub-end (point))
+		    (goto-char sub-begin)
+		    (c-font-lock-c++-lambda-captures sub-end)))
+
+		(c-put-font-lock-face id-start id-end
+				      (cond
+				       (declaration
+					'font-lock-variable-name-face)
+				       ((and capture-default
+					     (eq mode capture-default))
+					'font-lock-warning-face)
+				       ((eq mode ?=) font-lock-constant-face)
+				       (t 'font-lock-variable-name-face))))
+	    (c-syntactic-re-search-forward "," limit 'bound t))
+
+	  (c-forward-syntactic-ws)
+	  (when (eq (char-after) ?,)
+	    (forward-char)
+	    (c-forward-syntactic-ws)))
+
+	(setq capture-default nil)
+	(forward-char))))			; over the terminating "]".
+  nil)
+
+
 (c-lang-defconst c-simple-decl-matchers
   "Simple font lock matchers for types and declarations.  These are used
 on level 2 only and so aren't combined with `c-complex-decl-matchers'."
@@ -1700,6 +1798,9 @@ on level 2 only and so aren't combined with `c-complex-decl-matchers'."
       ,@(when (c-lang-const c-recognize-<>-arglists)
 	  `(c-font-lock-<>-arglists))
 
+      ,@(when (c-major-mode-is 'c++-mode)
+	  `(c-font-lock-c++-lambda-captures))
+
       ;; The first two rules here mostly find occurrences that
       ;; `c-font-lock-declarations' has found already, but not
       ;; declarations containing blocks in the type (see note below).
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index ec894f619a8..934186da7bd 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -474,6 +474,7 @@ so that all identifiers are recognized as words.")
   ;; The value here may be a list of functions or a single function.
   t nil
   c++ '(c-extend-region-for-CPP
+;	c-before-after-change-extend-region-for-lambda-capture ; doesn't seem needed.
 	c-before-change-check-raw-strings
 	c-before-change-check-<>-operators
 	c-depropertize-CPP
@@ -517,6 +518,7 @@ parameters \(point-min) and \(point-max).")
 	     c-change-expand-fl-region)
   c++ '(c-depropertize-new-text
 	c-extend-font-lock-region-for-macros
+;	c-before-after-change-extend-region-for-lambda-capture ; doens't seem needed.
 	c-before-after-change-digit-quote
 	c-after-change-re-mark-raw-strings
 	c-neutralize-syntax-in-and-mark-CPP
@@ -1360,6 +1362,25 @@ operators."
   t '(";" "{" "}"))
 (c-lang-defvar c-pre-start-tokens (c-lang-const c-pre-start-tokens))
 
+(c-lang-defconst c-pre-lambda-tokens
+  "List of tokens which may precede a lambda declaration.
+In C++ this is something like \"[a,b] (foo, bar) -> int { ... };\".
+Currently (2016-08) only used in C++ mode."
+  t (c--set-difference
+     (c--delete-duplicates
+      (append (c-lang-const c-operator-list)
+	      (c-lang-const c-other-op-syntax-tokens)))
+     (append
+      '("#" "%:" "??=" "##" "%:%:" "??=??=" "::" "." "->"
+	"]" "<:" ":>" "??(" "??)" "??-" "new" "delete"
+	")" ".*" "->*" "??'" "??!" "??!??!" "??!=" "??'=")
+      '("<%" "%>" "<:" ":>" "%:" "%:%:" "#" "##" "::" "..."))
+     :test #'string-equal))
+
+(c-lang-defconst c-pre-lambda-tokens-re
+  ;; Regexp matching any token in the list `c-pre-lambda-tokens'.
+  t (regexp-opt (c-lang-const c-pre-lambda-tokens)))
+(c-lang-defvar c-pre-lambda-tokens-re (c-lang-const c-pre-lambda-tokens-re))
 
 ;;; Syntactic whitespace.
 
@@ -2284,7 +2305,8 @@ contain type identifiers."
   (c c++) '(;; GCC extension.
 	    "__attribute__"
 	    ;; MSVC extension.
-	    "__declspec"))
+	    "__declspec")
+  c++ (append (c-lang-const c-paren-nontype-kwds) '("noexcept")))
 
 (c-lang-defconst c-paren-nontype-key
   t (c-make-keywords-re t (c-lang-const c-paren-nontype-kwds)))
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 07476013354..f630b053edc 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -1142,6 +1142,38 @@ Note that the style variables are always made local to the buffer."
       (goto-char try-end)
       (setq num-begin (point)))))
 
+;; The following doesn't seem needed at the moment (2016-08-15).
+;; (defun c-before-after-change-extend-region-for-lambda-capture
+;;     (_beg _end &optional _old-len)
+;;   ;; In C++ Mode, extend the region (c-new-BEG c-new-END) to cover any lambda
+;;   ;; function capture lists we happen to be inside.  This function is expected
+;;   ;; to be called both as a before-change and after change function.
+;;   ;;
+;;   ;; Note that these things _might_ be nested, with a capture list looking
+;;   ;; like:
+;;   ;;
+;;   ;;     [ ...., &foo = [..](){...}(..), ... ]
+;;   ;;
+;;   ;; .  What a wonderful language is C++.  ;-)
+;;   (c-save-buffer-state (paren-state pos)
+;;     (goto-char c-new-BEG)
+;;     (setq paren-state (c-parse-state))
+;;     (while (setq pos (c-pull-open-brace paren-state))
+;;       (goto-char pos)
+;;       (when (c-looking-at-c++-lambda-capture-list)
+;; 	(setq c-new-BEG (min c-new-BEG pos))
+;; 	(if (c-go-list-forward)
+;; 	    (setq c-new-END (max c-new-END (point))))))
+
+;;     (goto-char c-new-END)
+;;     (setq paren-state (c-parse-state))
+;;     (while (setq pos (c-pull-open-brace paren-state))
+;;       (goto-char pos)
+;;       (when (c-looking-at-c++-lambda-capture-list)
+;; 	(setq c-new-BEG (min c-new-BEG pos))
+;; 	(if (c-go-list-forward)
+;; 	    (setq c-new-END (max c-new-END (point))))))))
+
 (defun c-before-change (beg end)
   ;; Function to be put on `before-change-functions'.  Primarily, this calls
   ;; the language dependent `c-get-state-before-change-functions'.  It is
@@ -1329,12 +1361,24 @@ Note that the style variables are always made local to the buffer."
   ;; lock context (etc.) fontification.
   (let ((lit-start (c-literal-start))
 	(new-pos pos)
+	capture-opener
 	bod-lim bo-decl)
     (goto-char (c-point 'bol new-pos))
     (when lit-start			; Comment or string.
       (goto-char lit-start))
     (setq bod-lim (c-determine-limit 500))
 
+    ;; In C++ Mode, first check if we are within a (possibly nested) lambda
+    ;; form capture list.
+    (when (c-major-mode-is 'c++-mode)
+      (let ((paren-state (c-parse-state))
+	    opener)
+	(save-excursion
+	  (while (setq opener (c-pull-open-brace paren-state))
+	    (goto-char opener)
+	    (if (c-looking-at-c++-lambda-capture-list)
+		(setq capture-opener (point)))))))
+
     (while
 	;; Go to a less nested declaration each time round this loop.
 	(and
@@ -1361,6 +1405,8 @@ Note that the style variables are always made local to the buffer."
 			     c-<-as-paren-syntax)))))
 	 (not (bobp)))
       (backward-char))			; back over (, [, <.
+    (when (and capture-opener (< capture-opener new-pos))
+      (setq new-pos capture-opener))
     (and (/= new-pos pos) new-pos)))
 
 (defun c-change-expand-fl-region (_beg _end _old-len)
-- 
2.39.5