]> git.eshelyaron.com Git - emacs.git/commitdiff
CC Mode: Handle C++20 concepts
authorAlan Mackenzie <acm@muc.de>
Mon, 26 Sep 2022 19:16:33 +0000 (19:16 +0000)
committerAlan Mackenzie <acm@muc.de>
Mon, 26 Sep 2022 19:20:54 +0000 (19:20 +0000)
* lisp/progmodes/cc-align.el (c-lineup-topmost-intro-cont): Amend so as not to
indent lines following a requires line.

* lisp/progmodes/cc-engine.el (c-forward-primary-expression)
(c-forward-c++-requires-clause): New functions.
(c-forward-declarator): Skip forward over any trailing requires clause.
(c-forward-decl-or-cast-1): Skip requires clauses before and after the type.
Amend the second element of the return list to include information on two
consecutive identifiers in <...>.
(c-looking-at-or-maybe-in-bracelist): Don't recognize braces in requires
expressions as brace lists.
(c-guess-basic-syntax): CASE 5D.7: New case to handle the continuation of a
"concept foo = " line.

* lisp/progmodes/cc-fonts.el (c-basic-matchers-before): Add a new clause to
handle the declaration of a concept.
(c-get-fontification-context): Treat the arglist of a requires construct as a
declaration arglist.

* lisp/progmodes/cc-langs.el (c-equals-nontype-decl-kwds/key)
(c-fun-name-substitute-kwds/key, c-pre-concept-<>-kwds/key): New
c-lang-consts/vars.
(c-constant-key): New c-lang-var.
(c-type-decl-suffix-key): Include "requires" in the keywords matched.

* lisp/progmodes/cc-mode.el (c-fl-decl-start): Fix an off by one error.  Use
equal rather than eq to compare two syntax contexts.

lisp/progmodes/cc-align.el
lisp/progmodes/cc-engine.el
lisp/progmodes/cc-fonts.el
lisp/progmodes/cc-langs.el
lisp/progmodes/cc-mode.el

index e14f5b9058f4e0604437773f3cceb9cb9df57c49..7b45be3c5c162f10d13287cbaef06391101ec598 100644 (file)
@@ -85,11 +85,14 @@ statement-cont.)
 Works with: topmost-intro-cont."
   (save-excursion
     (beginning-of-line)
-    (c-backward-syntactic-ws (c-langelem-pos langelem))
-    (if (and (memq (char-before) '(?} ?,))
-            (not (and c-overloadable-operators-regexp
-                      (c-after-special-operator-id))))
-       c-basic-offset)))
+    (unless (re-search-forward c-fun-name-substitute-key
+                              (c-point 'eol) t)
+      (beginning-of-line)
+      (c-backward-syntactic-ws (c-langelem-pos langelem))
+      (if (and (memq (char-before) '(?} ?,))
+              (not (and c-overloadable-operators-regexp
+                        (c-after-special-operator-id))))
+         c-basic-offset))))
 
 (defun c-lineup-gnu-DEFUN-intro-cont (langelem)
   "Line up the continuation lines of a DEFUN macro in the Emacs C source.
index 1127ffe2498badf0085e99c9281d57cbbda4070b..e0aef2b2ee6c4c41a406089dff6553e4166002c9 100644 (file)
@@ -9512,6 +9512,84 @@ point unchanged and return nil."
 \f
 ;; Handling of large scale constructs like statements and declarations.
 
+(defun c-forward-primary-expression (&optional limit)
+  ;; Go over the primary expression (if any) at point, moving to the next
+  ;; token and return non-nil.  If we're not at a primary expression leave
+  ;; point unchanged and return nil.
+  ;;
+  ;; Note that this function is incomplete, handling only those cases expected
+  ;; to be common in a C++20 requires clause.
+  (let ((here (point))
+       (c-restricted-<>-arglists t)
+       (c-parse-and-markup-<>-arglists nil)
+       )
+    (if        (cond
+        ((looking-at c-constant-key)
+         (goto-char (match-end 1))
+         (c-forward-syntactic-ws limit)
+         t)
+        ((eq (char-after) ?\()
+         (and (c-go-list-forward (point) limit)
+              (eq (char-before) ?\))
+              (progn (c-forward-syntactic-ws limit)
+                     t)))
+        ((c-forward-over-compound-identifier)
+         (c-forward-syntactic-ws limit)
+         (while (cond
+                 ((looking-at "<")
+                  (prog1
+                      (c-forward-<>-arglist nil)
+                    (c-forward-syntactic-ws limit)))
+                 ((looking-at c-opt-identifier-concat-key)
+                  (and
+                   (zerop (c-forward-token-2 1 nil limit))
+                   (prog1
+                       (c-forward-over-compound-identifier)
+                     (c-forward-syntactic-ws limit))))))
+         t)
+        ((looking-at c-fun-name-substitute-key) ; "requires"
+         (goto-char (match-end 1))
+         (c-forward-syntactic-ws limit)
+         (and
+          (or (not (eq (char-after) ?\())
+              (prog1
+                  (and (c-go-list-forward (point) limit)
+                       (eq (char-before) ?\)))
+                (c-forward-syntactic-ws)))
+          (eq (char-after) ?{)
+          (and (c-go-list-forward (point) limit)
+               (eq (char-before) ?}))
+          (progn
+            (c-forward-syntactic-ws limit)
+            t))))
+       t
+      (goto-char here)
+      nil)))
+
+(defun c-forward-c++-requires-clause (&optional limit)
+  ;; Point is at the keyword "requires".  Move forward over the requires
+  ;; clause to the next token after it and return non-nil.  If there is no
+  ;; valid requires clause at point, leave point unmoved and return nil.
+  (let ((here (point))
+       final-point)
+    (or limit (setq limit (point-max)))
+    (if (and
+        (zerop (c-forward-token-2 1 nil limit)) ; over "requires".
+        (prog1
+            (c-forward-primary-expression limit)
+          (setq final-point (point))
+          (while
+              (and (looking-at "\\(?:&&\\|||\\)")
+                   (progn (goto-char (match-end 0))
+                          (c-forward-syntactic-ws limit)
+                          (and (< (point) limit)
+                               (c-forward-primary-expression limit))))
+            (setq final-point (point)))))
+       (progn (goto-char final-point)
+              t)
+      (goto-char here)
+      nil)))
+
 (defun c-forward-declarator (&optional limit accept-anon)
   ;; Assuming point is at the start of a declarator, move forward over it,
   ;; leaving point at the next token after it (e.g. a ) or a ; or a ,), or at
@@ -9565,7 +9643,7 @@ point unchanged and return nil."
                ((and (looking-at c-type-decl-prefix-key)
                      (if (and (c-major-mode-is 'c++-mode)
                               (match-beginning 4)) ; Was 3 - 2021-01-01
-                         ;; If the third submatch matches in C++ then
+                         ;; If the fourth submatch matches in C++ then
                          ;; we're looking at an identifier that's a
                          ;; prefix only if it specifies a member pointer.
                          (progn
@@ -9621,6 +9699,11 @@ point unchanged and return nil."
              (while (cond
                      ((looking-at c-decl-hangon-key)
                       (c-forward-keyword-clause 1))
+                     ((looking-at c-type-decl-suffix-key)
+                      (if (save-match-data
+                            (looking-at c-fun-name-substitute-key))
+                          (c-forward-c++-requires-clause)
+                        (c-forward-keyword-clause 1)))
                      ((and c-opt-cpp-prefix
                            (looking-at c-noise-macro-with-parens-name-re))
                       (c-forward-noise-clause))))
@@ -9890,13 +9973,13 @@ This function might do hidden buffer changes."
   ;;
   ;;
   ;;
-  ;;   The second element of the return value is non-nil when a
-  ;;   `c-typedef-decl-kwds' specifier is found in the declaration.
-  ;;   Specifically it is a dotted pair (A . B) where B is t when a
-  ;;   `c-typedef-kwds' ("typedef") is present, and A is t when some
-  ;;   other `c-typedef-decl-kwds' (e.g. class, struct, enum)
-  ;;   specifier is present.  I.e., (some of) the declared
-  ;;   identifier(s) are types.
+  ;;   The second element of the return value is non-nil when something
+  ;;   indicating the identifier is a type occurs in the declaration.
+  ;;   Specifically it is nil, or a three element list (A B C) where C is t
+  ;;   when context is '<> and the "identifier" is a found type, B is t when a
+  ;;   `c-typedef-kwds' ("typedef") is present, and A is t when some other
+  ;;   `c-typedef-declkwds' (e.g. class, struct, enum) specifier is present.
+  ;;   I.e., (some of) the declared identifier(s) are types.
   ;;
   ;;   The third element of the return value is non-nil when the declaration
   ;;   parsed might be an expression.  The fourth element is the position of
@@ -9972,6 +10055,9 @@ This function might do hidden buffer changes."
        at-type-decl
        ;; Set if we've a "typedef" keyword.
        at-typedef
+       ;; Set if `context' is '<> and the identifier is definitely a type, or
+       ;; has already been recorded as a found type.
+       at-<>-type
        ;; Set if we've found a specifier that can start a declaration
        ;; where there's no type.
        maybe-typeless
@@ -10050,6 +10136,11 @@ This function might do hidden buffer changes."
            (setq kwd-sym (c-keyword-sym (match-string 1)))
            (save-excursion
              (c-forward-keyword-clause 1)
+             (when (and (c-major-mode-is 'c++-mode)
+                        (c-keyword-member kwd-sym 'c-<>-sexp-kwds)
+                        (save-match-data
+                          (looking-at c-fun-name-substitute-key)))
+               (c-forward-c++-requires-clause))
              (setq kwd-clause-end (point))))
           ((and c-opt-cpp-prefix
                 (looking-at c-noise-macro-with-parens-name-re))
@@ -10089,6 +10180,11 @@ This function might do hidden buffer changes."
                               (point))))
                      found-type-list))
 
+             ;; Might we have a C++20 concept?  i.e. template<foo bar>?
+             (setq at-<>-type
+                   (and (eq context '<>)
+                        (memq found-type '(t known prefix found))))
+
              ;; Signal a type declaration for "struct foo {".
              (when (and backup-at-type-decl
                         (eq (char-after) ?{))
@@ -10377,8 +10473,11 @@ This function might do hidden buffer changes."
                  t)
              (when (if (save-match-data (looking-at "\\s("))
                        (c-safe (c-forward-sexp 1) t)
-                     (goto-char (match-end 1))
-                     t)
+                     (if (save-match-data
+                           (looking-at c-fun-name-substitute-key)) ; requires
+                         (c-forward-c++-requires-clause)
+                       (goto-char (match-end 1))
+                       t))
                (when (and (not got-suffix-after-parens)
                           (= paren-depth 0))
                  (setq got-suffix-after-parens (match-beginning 0)))
@@ -10971,8 +11070,8 @@ This function might do hidden buffer changes."
            (c-forward-type))))
 
       (list id-start
-           (and (or at-type-decl at-typedef)
-                (cons at-type-decl at-typedef))
+           (and (or at-type-decl at-typedef at-<>-type)
+                (list at-type-decl at-typedef at-<>-type))
            maybe-expression
            type-start
            (or (eq context 'top) make-top)))
@@ -12429,6 +12528,8 @@ comment at the start of cc-engine.el for more info."
                       in-paren 'in-paren))
                ((looking-at c-pre-brace-non-bracelist-key)
                 (setq braceassignp nil))
+               ((looking-at c-fun-name-substitute-key)
+                (setq braceassignp nil))
                ((looking-at c-return-key))
                ((and (looking-at c-symbol-start)
                      (not (looking-at c-keywords-regexp)))
@@ -12439,6 +12540,11 @@ comment at the start of cc-engine.el for more info."
                   (setq after-type-id-pos (point))))
                ((eq (char-after) ?\()
                 (setq parens-before-brace t)
+                ;; Have we a requires with a parenthesis list?
+                (when (save-excursion
+                        (and (zerop (c-backward-token-2 1 nil lim))
+                             (looking-at c-fun-name-substitute-key)))
+                  (setq braceassignp nil))
                 nil)
                (t nil))
               (save-excursion
@@ -14201,6 +14307,25 @@ comment at the start of cc-engine.el for more info."
            (goto-char placeholder)
            (c-add-syntax 'inher-cont (c-point 'boi)))
 
+          ;; CASE 5D.7: Continuation of a "concept foo =" line in C++20 (or
+          ;; similar).
+          ((and c-equals-nontype-decl-key
+                (save-excursion
+                  (prog1
+                      (and (zerop (c-backward-token-2 1 nil lim))
+                           (looking-at c-operator-re)
+                           (equal (match-string 0) "=")
+                           (zerop (c-backward-token-2 1 nil lim))
+                           (looking-at c-symbol-start)
+                           (not (looking-at c-keywords-regexp))
+                           (zerop (c-backward-token-2 1 nil lim))
+                           (looking-at c-equals-nontype-decl-key)
+                           (eq (c-beginning-of-statement-1 lim) 'same))
+                    (setq placeholder (point)))))
+           (goto-char placeholder)
+           (c-add-stmt-syntax 'topmost-intro-cont nil nil containing-sexp
+                              paren-state))
+
           ;; CASE 5D.5: Continuation of the "expression part" of a
           ;; top level construct.  Or, perhaps, an unrecognized construct.
           (t
index 5d80eb58e38e0aaca6bab353cb6301b174cf3a5b..753ae480878b0f728dcac4084ccfd569e9268a32 100644 (file)
@@ -887,6 +887,23 @@ casts and declarations are fontified.  Used on level 2 and higher."
       ,@(when (c-major-mode-is 'c++-mode)
          '(c-font-lock-c++-modules))
 
+      ;; The next regexp is highlighted with narrowing.  This is so that the
+      ;; final "context" bit of the regexp, "\\(?:[^=]\\|$\\)", which cannot
+      ;; match anything non-empty at LIMIT, will match "$" instead.
+      ,@(when (c-lang-const c-equals-nontype-decl-kwds)
+         `((,(byte-compile
+              `(lambda (limit)
+                 (save-restriction
+                   (narrow-to-region (point-min) limit)
+                   ,(c-make-font-lock-search-form
+                     (concat (c-lang-const c-equals-nontype-decl-key) ;no \\(
+                             (c-lang-const c-simple-ws) "+\\("
+                             (c-lang-const c-symbol-key) "\\)"
+                             (c-lang-const c-simple-ws) "*"
+                             "=\\(?:[^=]\\|$\\)")
+                     `((,(+ 1 (c-lang-const c-simple-ws-depth))
+                        font-lock-type-face t)))))))))
+
       ;; Fontify the special declarations in Objective-C.
       ,@(when (c-major-mode-is 'objc-mode)
          `(;; Fontify class names in the beginning of message expressions.
@@ -1278,15 +1295,19 @@ casts and declarations are fontified.  Used on level 2 and higher."
                    (or (memq type '(c-decl-arg-start c-decl-type-start))
                        (and
                         (progn (c-backward-syntactic-ws) t)
-                        (c-back-over-compound-identifier)
-                        (progn
-                          (c-backward-syntactic-ws)
-                          (or (bobp)
-                              (progn
-                                (setq type (c-get-char-property (1- (point))
-                                                                'c-type))
-                                (memq type '(c-decl-arg-start
-                                             c-decl-type-start))))))))))
+                        (or
+                         (and
+                          (c-back-over-compound-identifier)
+                          (progn
+                            (c-backward-syntactic-ws)
+                            (or (bobp)
+                                (progn
+                                  (setq type (c-get-char-property (1- (point))
+                                                                  'c-type))
+                                  (memq type '(c-decl-arg-start
+                                               c-decl-type-start))))))
+                         (and (zerop (c-backward-token-2))
+                              (looking-at c-fun-name-substitute-key))))))))
           (cons 'decl nil))
          (t (cons 'arglist t)))))
 
index d33ed4bcda5062f7290a7003fa035737017fd8e4..85f43d6a26b797ff99e4cbfead09ac516ac3b666 100644 (file)
@@ -2593,6 +2593,35 @@ will be handled."
   t (c-make-keywords-re t (c-lang-const c-equals-type-clause-kwds)))
 (c-lang-defvar c-equals-type-clause-key (c-lang-const c-equals-type-clause-key))
 
+(c-lang-defconst c-equals-nontype-decl-kwds
+  "Keywords which are followed by an identifier then an \"=\"
+sign, which declares the identifier to be something other than a
+type."
+  t nil
+  c++ '("concept"))
+
+(c-lang-defconst c-equals-nontype-decl-key
+  ;; An unadorned regular expression which matches any member of
+  ;; `c-equals-decl-kwds', or nil if such don't exist in the current language.
+  t (when (c-lang-const c-equals-nontype-decl-kwds)
+      (c-make-keywords-re nil (c-lang-const c-equals-nontype-decl-kwds))))
+(c-lang-defvar c-equals-nontype-decl-key
+              (c-lang-const c-equals-nontype-decl-key))
+
+(c-lang-defconst c-fun-name-substitute-kwds
+  "Keywords which take the place of type+declarator at the beginning
+of a function-like structure, such as a C++20 \"requires\"
+clause.  An arglist may or may not follow such a keyword."
+  t nil
+  c++ '("requires"))
+
+(c-lang-defconst c-fun-name-substitute-key
+  ;; An adorned regular expression which matches any member of
+  ;; `c-fun-name-substitute-kwds'.
+  t (c-make-keywords-re t (c-lang-const c-fun-name-substitute-kwds)))
+(c-lang-defvar c-fun-name-substitute-key
+              (c-lang-const c-fun-name-substitute-key))
+
 (c-lang-defconst c-modifier-kwds
   "Keywords that can prefix normal declarations of identifiers
 \(and typically act as flags).  Things like argument declarations
@@ -2938,6 +2967,17 @@ if this isn't nil."
         ;; In CORBA PSDL:
         "ref"))
 
+(c-lang-defconst c-pre-concept-<>-kwds
+  "Keywords that may be followed by an angle bracket expression containing
+uses of \"concepts\".  This is currently (2022-09) used only by C++."
+  t nil
+  c++ '("template"))
+
+(c-lang-defconst c-pre-concept-<>-key
+  ;; Regexp matching any element of `c-pre-concept-<>-kwds'.
+  t (c-make-keywords-re t (c-lang-const c-pre-concept-<>-kwds)))
+(c-lang-defvar c-pre-concept-<>-key (c-lang-const c-pre-concept-<>-key))
+
 (c-lang-defconst c-<>-arglist-kwds
   "Keywords that can be followed by a C++ style template arglist; see
 `c-recognize-<>-arglists' for details.  That language constant is
@@ -3146,6 +3186,10 @@ not really template operators."
   java    '("true" "false" "null") ; technically "literals", not keywords
   pike    '("UNDEFINED")) ;; Not a keyword, but practically works as one.
 
+(c-lang-defconst c-constant-key
+  t (c-make-keywords-re t (c-lang-const c-constant-kwds)))
+(c-lang-defvar c-constant-key (c-lang-const c-constant-key))
+
 (c-lang-defconst c-primary-expr-kwds
   "Keywords besides constants and operators that start primary expressions."
   t    nil
@@ -3781,7 +3825,10 @@ is in effect when this is matched (see `c-identifier-syntax-table')."
                     ;; "throw" in `c-type-modifier-kwds' is followed
                     ;; by a parenthesis list, but no extra measures
                     ;; are necessary to handle that.
-                    (regexp-opt (c-lang-const c-type-modifier-kwds) t)
+                    (regexp-opt 
+                     (append (c-lang-const c-fun-name-substitute-kwds)
+                             (c-lang-const c-type-modifier-kwds))
+                     t)
                     "\\>")
                  "")
                "\\)")
index 15dfdc4b0e5681e43d0192588bf8ed0c8b242dd7..9dd5ddb465621edec864c26eeaab08abb0b8ede6 100644 (file)
@@ -2403,7 +2403,7 @@ with // and /*, not more generic line and block comments."
                           (setq pseudo (c-cheap-inside-bracelist-p (c-parse-state)))))))
               (goto-char pseudo))
             t)
-          (> (point) bod-lim)
+          (>= (point) bod-lim)
           (progn (c-forward-syntactic-ws)
                  ;; Have we got stuck in a comment at EOB?
                  (not (and (eobp)
@@ -2427,7 +2427,8 @@ with // and /*, not more generic line and block comments."
             (and (> (point) bod-lim)
                  (or (memq (char-before) '(?\( ?\[))
                      (and (eq (char-before) ?\<)
-                          (eq (c-get-char-property
+                          (equal
+                           (c-get-char-property
                                (1- (point)) 'syntax-table)
                               c-<-as-paren-syntax))
                      (and (eq (char-before) ?{)