From d4346a7cc784349799552ec31ae66546408ae407 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Fri, 17 Feb 2023 09:14:10 +0000 Subject: [PATCH] Add handling of the C11 _Generic construct to C Mode This fixes bug #61481. * lisp/progmodes/cc-engine.el (c-guess-basic-syntax): Add a new CASE 6 for _Generic. * lisp/progmodes/cc-fonts.el (c-fontify-types-and-refs): Use `let*' rather than `let'. (c-get-fontification-context): Add the new result `generic', and add handling to determine it. (c-font-lock-declarations): Call c-font-lock-c11-generic-clause when needed. (c-font-lock-c11-generic-clause): New function. * lisp/progmodes/cc-langs.el (c-generic-kwds, c-generic-key): New lang constants/variable. --- lisp/progmodes/cc-engine.el | 44 ++++++++++++++++++++++++++- lisp/progmodes/cc-fonts.el | 59 +++++++++++++++++++++++++++++-------- lisp/progmodes/cc-langs.el | 11 +++++++ 3 files changed, 101 insertions(+), 13 deletions(-) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 1899b522ab0..f8ae49c42dd 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -14911,7 +14911,49 @@ comment at the start of cc-engine.el for more info." (c-add-syntax 'topmost-intro-cont (c-point 'boi))) )) - ;; (CASE 6 has been removed.) + ;; ((Old) CASE 6 has been removed.) + ;; CASE 6: line is within a C11 _Generic expression. + ((and c-generic-key + (eq (char-after containing-sexp) ?\() + (progn (setq tmp-pos (c-safe-scan-lists + containing-sexp 1 0 + (min (+ (point) 2000) (point-max)))) + t) + (save-excursion + (and + (progn (goto-char containing-sexp) + (zerop (c-backward-token-2))) + (looking-at c-generic-key) + (progn (goto-char (1+ containing-sexp)) + (c-syntactic-re-search-forward + "," indent-point 'bound t t)) + (setq placeholder (point))))) + (let ((res (c-syntactic-re-search-forward + "[,:)]" + (or tmp-pos (min (+ (point) 2000) (point-max))) + 'bound t t))) + (cond + ((and res + (eq (char-before) ?\)) + (save-excursion + (backward-char) + (c-backward-syntactic-ws indent-point) + (eq (point) indent-point))) + (c-add-stmt-syntax + 'arglist-close (list containing-sexp) t + (c-most-enclosing-brace paren-state indent-point) paren-state)) + ((or (not res) + (eq (char-before) ?\))) + (backward-char) + (c-syntactic-skip-backward "^,:" containing-sexp t) + (c-add-syntax (if (eq (char-before) ?:) + 'statement-case-intro + 'case-label) + (1+ containing-sexp))) + (t (c-add-syntax (if (eq (char-before) ?:) + 'case-label + 'statement-case-intro) + (1+ containing-sexp)))))) ;; CASE 7: line is an expression, not a statement. Most ;; likely we are either in a function prototype or a function diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index c220d8d8789..4ec21af1b25 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -259,14 +259,14 @@ (defmacro c-fontify-types-and-refs (varlist &rest body) (declare (indent 1) (debug let*)) - ;; Like `let', but additionally activates `c-record-type-identifiers' + ;; Like `let*', but additionally activates `c-record-type-identifiers' ;; and `c-record-ref-identifiers', and fontifies the recorded ranges ;; accordingly on exit. ;; ;; This function does hidden buffer changes. - `(let ((c-record-type-identifiers t) - c-record-ref-identifiers - ,@varlist) + `(let* ((c-record-type-identifiers t) + c-record-ref-identifiers + ,@varlist) (prog1 (progn ,@body) (c-fontify-recorded-types-and-refs)))) @@ -1219,6 +1219,7 @@ casts and declarations are fontified. Used on level 2 and higher." ;; inside a function declaration arglist). ;; '<> In an angle bracket arglist. ;; 'arglist Some other type of arglist. + ;; 'generic In a C11 _Generic construct. ;; 'top Some other context and point is at the top-level (either ;; outside any braces or directly inside a class or namespace, ;; etc.) @@ -1345,6 +1346,15 @@ casts and declarations are fontified. Used on level 2 and higher." (c-back-over-member-initializers))) (c-put-char-property (1- match-pos) 'c-type 'c-not-decl) (cons 'not-decl nil)) + ;; In a C11 _Generic construct. + ((and c-generic-key + (eq (char-before match-pos) ?,) + (save-excursion + (and (c-go-up-list-backward match-pos + (max (- (point) 2000) (point-min))) + (zerop (c-backward-token-2)) + (looking-at c-generic-key)))) + (cons 'generic nil)) ;; At start of a declaration inside a declaration paren. ((save-excursion (goto-char match-pos) @@ -1616,13 +1626,16 @@ casts and declarations are fontified. Used on level 2 and higher." (c-forward-syntactic-ws)) ;; Now analyze the construct. - (if (eq context 'not-decl) - (progn - (setq decl-or-cast nil) - (if (c-syntactic-re-search-forward - "," (min limit (point-max)) 'at-limit t) - (c-put-char-property (1- (point)) 'c-type 'c-not-decl)) - nil) + (cond + ((eq context 'not-decl) + (setq decl-or-cast nil) + (if (c-syntactic-re-search-forward + "," (min limit (point-max)) 'at-limit t) + (c-put-char-property (1- (point)) 'c-type 'c-not-decl)) + nil) + ((eq context 'generic) + (c-font-lock-c11-generic-clause)) + (t (setq decl-or-cast (c-forward-decl-or-cast-1 match-pos context last-cast-end inside-macro)) @@ -1683,7 +1696,7 @@ casts and declarations are fontified. Used on level 2 and higher." context (or toplev (nth 4 decl-or-cast)))) - (t t)))) + (t t))))) ;; It was a false alarm. Check if we're in a label (or other ;; construct with `:' except bitfield) instead. @@ -1713,6 +1726,28 @@ casts and declarations are fontified. Used on level 2 and higher." nil)))) +(defun c-font-lock-c11-generic-clause () + ;; Fontify a type inside the C11 _Generic clause. Point will be at the + ;; type and will be left at the next comma of the clause (if any) or the + ;; closing parenthesis, if any, or at the end of the type, otherwise. + ;; The return value is always nil. + (c-fontify-types-and-refs + ((here (point)) + (type-type (c-forward-type t)) + (c-promote-possible-types (if (eq type-type 'maybe) 'just-one t)) + (pos (point)) pos1) + (when (and type-type (eq (char-after) ?:)) + (goto-char here) + (c-forward-type t)) ; Fontify the type. + (cond + ((c-syntactic-re-search-forward "," nil t t t) + (backward-char)) + ((and (setq pos1 (c-up-list-forward)) + (eq (char-before pos1) ?\))) + (goto-char (1- pos1))) + (t (goto-char pos)))) + nil) + (defun c-font-lock-enum-body (limit) ;; Fontify the identifiers of each enum we find by searching forward. ;; diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index daa23bd14fa..61f81f09b3e 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -3085,6 +3085,17 @@ Keywords here should also be in `c-block-stmt-1-kwds'." t (c-make-keywords-re t (c-lang-const c-block-stmt-2-kwds))) (c-lang-defvar c-block-stmt-2-key (c-lang-const c-block-stmt-2-key)) +(c-lang-defconst c-generic-kwds + "The keyword \"_Generic\" which introduces a C11 generic statement." + t nil + c '("_Generic")) + +(c-lang-defconst c-generic-key + ;; Regexp matching the keyword(s) in `c-generic-kwds'. + t (if (c-lang-const c-generic-kwds) + (c-make-keywords-re t (c-lang-const c-generic-kwds)))) +(c-lang-defvar c-generic-key (c-lang-const c-generic-key)) + (c-lang-defconst c-block-stmt-kwds ;; Union of `c-block-stmt-1-kwds' and `c-block-stmt-2-kwds'. t (c--delete-duplicates (append (c-lang-const c-block-stmt-1-kwds) -- 2.39.2