Package: cc-mode;
Reported by: Arsen Arsenović <arsen <at> aarsen.me>
Date: Sun, 18 Aug 2024 09:52:02 UTC
Severity: normal
Done: Alan Mackenzie <acm <at> muc.de>
Bug is archived. No further changes may be made.
To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 72695 in the body.
You can then email your comments to 72695 AT debbugs.gnu.org in the normal way.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Sun, 18 Aug 2024 09:52:02 GMT) Full text and rfc822 format available.Arsen Arsenović <arsen <at> aarsen.me>
:bug-cc-mode <at> gnu.org
.
(Sun, 18 Aug 2024 09:52:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: bug-gnu-emacs <at> gnu.org Subject: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sun, 18 Aug 2024 11:50:32 +0200
Package: cc-mode <#secure method=pgpmime mode=sign> Package: cc-mode Hi! CC-mode appears to misindent the following C++ snippet: void f() { auto data_printer = [] <typename T> (const T& data) { if constexpr (true) { if constexpr (std::same_as<T, std::string>) std::println (" data: \"{}\"", data); else std::println (" data: {}", data); } else std::println (""); }; if constexpr (false) { foo (); } } This example is supposed to be formatted in accordance to the default GNU style, but the 'if constexpr' in the lambda expression are misindented. This might be the case because the inline-open of the lambda gets mis-interpreted as statement-cont, so the statements in the body are indented as brace-list-intro and brace-entry-open rather than statement-block-intro and substatement-open. WRT lambda indentation generally, CC-mode appears to format lambdas in GNU style so that their '{' aligns with their '[' (i.e. aligns their compound-statement with their lambda-introducer per ISO parlance). Maybe we should consider indenting them less. For instance, that indentation leads to: std::visit ([] <typename T> (const T& data) { if constexpr (!std::same_as<T, std::monostate>) { if constexpr (std::same_as<T, std::string>) std::println (" data: \"{}\"", data); else std::println (" data: {}", data); } else std::println (""); }, tok.data); ... which is /really/ aggressive. Maybe it'd be better to do something like: std::visit ([] (args) { body }, visit-args-cont); I realize this is a bit inconsistent with the rest of the style, but given that placing the introducer (and generally function arguments) on a new line does not work in the GNU C/++ style as it does in Lisp: function_name ( arg1, arg2); ... versus: (function-name arg1 arg2) Anyway, thanks in advance! Have a most lovely day. Emacs : GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.42, cairo version 1.18.0) of 2024-08-13 Package: CC Mode 5.35.2 (C++//l) Buffer Style: gnu c-emacs-features: (pps-extended-state col-0-paren posix-char-classes gen-string-delim gen-comment-delim syntax-properties category-properties 1-bit) current state: ============== (setq c-basic-offset 2 c-comment-only-line-offset '(0 . 0) c-indent-comment-alist '((anchored-comment column . 0) (end-block space . 1) (cpp-end-block space . 2)) c-indent-comments-syntactically-p nil c-block-comment-prefix "" c-comment-prefix-regexp '((pike-mode . "//+!?\\|\\**") (awk-mode . "#+") (other . "//+\\|\\**")) c-doc-comment-style '((java-mode . javadoc) (pike-mode . autodoc) (c-mode . gtkdoc) (c++-mode . gtkdoc)) c-cleanup-list '(scope-operator) c-hanging-braces-alist '((substatement-open before after) (arglist-cont-nonempty)) c-hanging-colons-alist nil c-hanging-semi&comma-criteria '(c-semi&comma-inside-parenlist) c-backslash-column 48 c-backslash-max-column 72 c-special-indent-hook '(t c-gnu-impose-minimum) c-label-minimum-indentation 1 c-offsets-alist '((inexpr-class . +) (inexpr-statement . +) (lambda-intro-cont . +) (inlambda . 0) (template-args-cont c-lineup-template-args c-lineup-template-args-indented-from-margin) (incomposition . +) (inmodule . +) (innamespace . +) (inextern-lang . +) (composition-close . 0) (module-close . 0) (namespace-close . 0) (extern-lang-close . 0) (composition-open . 0) (module-open . 0) (namespace-open . 0) (extern-lang-open . 0) (objc-method-call-cont c-lineup-ObjC-method-call-colons c-lineup-ObjC-method-call +) (objc-method-args-cont . c-lineup-ObjC-method-args) (objc-method-intro . [0]) (friend . 0) (cpp-define-intro c-lineup-cpp-define +) (cpp-macro-cont . +) (cpp-macro . [0]) (inclass . +) (stream-op . c-lineup-streamop) (arglist-cont-nonempty c-lineup-gcc-asm-reg c-lineup-arglist) (arglist-cont c-lineup-gcc-asm-reg 0) (comment-intro c-lineup-knr-region-comment c-lineup-comment) (catch-clause . 0) (else-clause . 0) (do-while-closure . 0) (access-label . -) (case-label . 0) (substatement . +) (statement-case-intro . +) (statement . 0) (brace-entry-open . 0) (brace-list-entry . 0) (brace-list-close . 0) (block-close . 0) (block-open . 0) (inher-cont . c-lineup-multi-inher) (inher-intro . +) (member-init-cont . c-lineup-multi-inher) (member-init-intro . +) (annotation-var-cont . +) (annotation-top-cont . 0) (constraint-cont . +) (topmost-intro . 0) (knr-argdecl . 0) (func-decl-cont . +) (inline-close . 0) (class-close . 0) (class-open . 0) (defun-block-intro . +) (defun-close . 0) (defun-open . 0) (c . c-lineup-C-comments) (string . c-lineup-dont-change) (topmost-intro-cont first c-lineup-topmost-intro-cont c-lineup-gnu-DEFUN-intro-cont) (brace-list-intro first c-lineup-2nd-brace-entry-in-arglist c-lineup-class-decl-init-+ +) (brace-list-open . +) (inline-open . 0) (arglist-close . c-lineup-arglist) (arglist-intro . c-lineup-arglist-intro-after-paren) (statement-cont . +) (statement-case-open . +) (label . 0) (substatement-label . 0) (substatement-open . +) (knr-argdecl-intro . 5) (statement-block-intro . +) ) c-buffer-is-cc-mode 'c++-mode c-tab-always-indent t c-syntactic-indentation t c-syntactic-indentation-in-macros t c-ignore-auto-fill '(string cpp code) c-auto-align-backslashes t c-backspace-function 'backward-delete-char-untabify c-delete-function 'delete-char c-electric-pound-behavior nil c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "gnu")) c-enable-xemacs-performance-kludge-p nil c-old-style-variable-behavior nil defun-prompt-regexp nil tab-width 8 comment-column 32 parse-sexp-ignore-comments t parse-sexp-lookup-properties t auto-fill-function 'c-do-auto-fill comment-multi-line t comment-start-skip "\\(?://+\\|/\\*+\\)\\s *" fill-prefix nil fill-column 79 paragraph-start "[ ]*\\(//+\\|\\**\\)[ ]*$\\|^\f" adaptive-fill-mode t adaptive-fill-regexp "[ ]*\\(//+\\|\\**\\)[ ]*\\([-–!|#%;>*·•‣⁃◦ ]*\\)" ) -- Arsen Arsenović
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Sun, 18 Aug 2024 15:05:02 GMT) Full text and rfc822 format available.Message #8 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sun, 18 Aug 2024 15:03:31 +0000
Hello, Arsen. Thanks for the bug report! On Sun, Aug 18, 2024 at 11:50:32 +0200, Arsen Arsenović via CC-Mode-help wrote: > Package: cc-mode > <#secure method=pgpmime mode=sign> > Package: cc-mode > Hi! > CC-mode appears to misindent the following C++ snippet: > void f() > { > auto data_printer = [] <typename T> > (const T& data) > { > if constexpr (true) > { > if constexpr (std::same_as<T, std::string>) > std::println (" data: \"{}\"", data); > else > std::println (" data: {}", data); > } > else > std::println (""); > }; > if constexpr (false) > { > foo (); > } > } > This example is supposed to be formatted in accordance to the default > GNU style, but the 'if constexpr' in the lambda expression are > misindented. Yes. > This might be the case because the inline-open of the lambda gets > mis-interpreted as statement-cont, so the statements in the body are > indented as brace-list-intro and brace-entry-open rather than > statement-block-intro and substatement-open. It's definitely something to do with the lambda function. If I comment out Lines 3 and 4, the remainder of the function then indents correctly. As you remark, at the moment some lines are being wrongly analysed as brace-list constructs. > WRT lambda indentation generally, CC-mode appears to format lambdas in > GNU style so that their '{' aligns with their '[' (i.e. aligns their > compound-statement with their lambda-introducer per ISO parlance). > Maybe we should consider indenting them less. .... OK. I'll fix the "brace-list" indentation bug first, then come back to this lambda indentation problem. [ .... ] > Anyway, thanks in advance! > Have a most lovely day. And yourself, too! [ .... ] -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Wed, 21 Aug 2024 20:42:01 GMT) Full text and rfc822 format available.Message #11 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Wed, 21 Aug 2024 22:40:02 +0200
[Message part 1 (text/plain, inline)]
Hello Alan, Alan Mackenzie <acm <at> muc.de> writes: > Hello, Arsen. > > Thanks for the bug report! My pleasure. Thanks for being as fast as always. > On Sun, Aug 18, 2024 at 11:50:32 +0200, Arsen Arsenović via CC-Mode-help wrote: >> Package: cc-mode > >> <#secure method=pgpmime mode=sign> >> Package: cc-mode >> Hi! > >> CC-mode appears to misindent the following C++ snippet: > >> void f() >> { >> auto data_printer = [] <typename T> >> (const T& data) >> { >> if constexpr (true) >> { >> if constexpr (std::same_as<T, std::string>) >> std::println (" data: \"{}\"", data); >> else >> std::println (" data: {}", data); >> } >> else >> std::println (""); >> }; > >> if constexpr (false) >> { >> foo (); >> } >> } > >> This example is supposed to be formatted in accordance to the default >> GNU style, but the 'if constexpr' in the lambda expression are >> misindented. > > Yes. > >> This might be the case because the inline-open of the lambda gets >> mis-interpreted as statement-cont, so the statements in the body are >> indented as brace-list-intro and brace-entry-open rather than >> statement-block-intro and substatement-open. > > It's definitely something to do with the lambda function. If I comment > out Lines 3 and 4, the remainder of the function then indents correctly. > As you remark, at the moment some lines are being wrongly analysed as > brace-list constructs. > >> WRT lambda indentation generally, CC-mode appears to format lambdas in >> GNU style so that their '{' aligns with their '[' (i.e. aligns their >> compound-statement with their lambda-introducer per ISO parlance). >> Maybe we should consider indenting them less. .... > > OK. I'll fix the "brace-list" indentation bug first, then come back to > this lambda indentation problem. Thank you very much! In the in-between time since I've sent this bug report with a feature request snuck in, I've noticed the following in the GCC code: stattype = (cp_build_type_attribute_variant (stattype, TYPE_ATTRIBUTES (optype))); ... which clued me into a different way to get the effect Lisp had, so I figured out I can use the same trick for nested multi-line constructs: (std::visit ([] <typename T> (const T& data) { if constexpr (!std::same_as<T, std::monostate>) { if constexpr (std::same_as<T, std::string>) /* TODO(arsen): use {:?} here */ std::println (" data: \"{}\"", data); else std::println (" data: {}", data); } else std::println (""); }, tok.data)); } ... which is an okay immitation of what Lisp does, and seems to provide okay indentation here. So maybe it's best not to avoid messing with peoples styles ;) - please do not worry about the latter part of my original message. Thank you again! Have a lovely evening. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Fri, 30 Aug 2024 17:52:01 GMT) Full text and rfc822 format available.Message #14 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Fri, 30 Aug 2024 17:50:35 +0000
Hello, Arsen. A short progress report. On Wed, Aug 21, 2024 at 22:40:02 +0200, Arsen Arsenović wrote: > Hello Alan, > Alan Mackenzie <acm <at> muc.de> writes: > > Thanks for the bug report! > My pleasure. Thanks for being as fast as always. I apologise for not having been as fast over the last few days. Partly, I'm bringing up a new computer, which never goes as fast as one would like. But mainly I'm struggling with the unfathomable complexity of C++ lambda functions, as documented on https://en.cppreference.com/w/cpp/language/lambda. In my not so humble opinion, the C++ standards people have completely lost the plot. The number of special cases in just the syntax, apparently just to save a few characters from a program's text, has got beyond the point where anybody can grasp it. Nobody in this group of people appears to be able to say "STOP!", though somebody sorely needs to. I dread to think what will be coming out in C++26. :-( Anyhow, I've spent all afternoon on the problem so far, and I think I can see my way to a solution. It will still take a few more days yet, I'm afraid. [ .... ] > > OK. I'll fix the "brace-list" indentation bug first, then come back to > > this lambda indentation problem. > Thank you very much! [ .... ] > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Sat, 31 Aug 2024 00:56:02 GMT) Full text and rfc822 format available.Message #17 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sat, 31 Aug 2024 02:54:01 +0200
[Message part 1 (text/plain, inline)]
Hello, Alan, Alan Mackenzie <acm <at> muc.de> writes: > Hello, Arsen. > > A short progress report. > > On Wed, Aug 21, 2024 at 22:40:02 +0200, Arsen Arsenović wrote: >> Hello Alan, > >> Alan Mackenzie <acm <at> muc.de> writes: >> > Thanks for the bug report! > >> My pleasure. Thanks for being as fast as always. > > I apologise for not having been as fast over the last few days. Partly, > I'm bringing up a new computer, which never goes as fast as one would > like. No worries, of course! Hope the setup went well. > But mainly I'm struggling with the unfathomable complexity of C++ lambda > functions, as documented on > https://en.cppreference.com/w/cpp/language/lambda. It should be quite simple - just a few optional productions preceded by an introducer production (see https://eel.is/c++draft/expr.prim.lambda for a formal grammar - this is the text from the standard draft). > In my not so humble opinion, the C++ standards people have completely > lost the plot. Should help be necessary, I do work on G++ so I'm quite familiar with the language. Feel free to ping me. > The number of special cases in just the syntax, apparently just to > save a few characters from a program's text, has got beyond the point > where anybody can grasp it. Nobody in this group of people appears to > be able to say "STOP!", though somebody sorely needs to. I dread to > think what will be coming out in C++26. :-( It really shouldn't be that bad - I'll help demystify if need be. > Anyhow, I've spent all afternoon on the problem so far, and I think I can > see my way to a solution. It will still take a few more days yet, I'm > afraid. Thank you very much! My apologies for the trouble. Have a lovely day. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Sat, 31 Aug 2024 17:03:02 GMT) Full text and rfc822 format available.Message #20 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sat, 31 Aug 2024 17:01:07 +0000
Hello again, Arsen. On Sat, Aug 31, 2024 at 02:54:01 +0200, Arsen Arsenović wrote: > Hello, Alan, > Alan Mackenzie <acm <at> muc.de> writes: > > Hello, Arsen. > > A short progress report. > > On Wed, Aug 21, 2024 at 22:40:02 +0200, Arsen Arsenović wrote: > >> Hello Alan, > >> Alan Mackenzie <acm <at> muc.de> writes: > >> > Thanks for the bug report! > >> My pleasure. Thanks for being as fast as always. > > I apologise for not having been as fast over the last few days. Partly, > > I'm bringing up a new computer, which never goes as fast as one would > > like. > No worries, of course! Hope the setup went well. It's going well, thanks! At the current rate of progress, I'll have my mail setup set up by this time next week, and that is the point at which, at least for me, my new machine becomes my current machine. > > But mainly I'm struggling with the unfathomable complexity of C++ lambda > > functions, as documented on > > https://en.cppreference.com/w/cpp/language/lambda. > It should be quite simple - just a few optional productions preceded by > an introducer production (see https://eel.is/c++draft/expr.prim.lambda > for a formal grammar - this is the text from the standard draft). > > In my not so humble opinion, the C++ standards people have completely > > lost the plot. > Should help be necessary, I do work on G++ so I'm quite familiar with > the language. Feel free to ping me. Thanks! It's just that the explanatory text on en.cppreference.com about just lambda functions takes 20 scrolls of ~45 lines per scroll, coming out at many hundreds of lines, just for lambda functions. I feel that the entire language part of Emacs Lisp could fit into that quite comfortably, at the same density. > > The number of special cases in just the syntax, apparently just to > > save a few characters from a program's text, has got beyond the point > > where anybody can grasp it. Nobody in this group of people appears to > > be able to say "STOP!", though somebody sorely needs to. I dread to > > think what will be coming out in C++26. :-( > It really shouldn't be that bad - I'll help demystify if need be. Thanks! > > Anyhow, I've spent all afternoon on the problem so far, and I think I can > > see my way to a solution. It will still take a few more days yet, I'm > > afraid. > Thank you very much! My apologies for the trouble. No apologies called for. You raised a bug report, for which thanks! Anyhow, I've got a first version, incompletely tested, of a new handling for a lambda expression. The patch is not nearly as big as it looks, two thirds of it is just re-indenting a large function I put an extra `if' form aroun. Would you please apply this patch, which should fix the test case, and then test it out on your real C++ code, then let me know how it's gone. I'll see if I can get some new test cases written. Thanks! diff -r 48ad661a2d7d cc-engine.el --- a/cc-engine.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-engine.el Sat Aug 31 16:44:39 2024 +0000 @@ -13178,229 +13178,223 @@ ;; ;; Here, "brace list" does not include the body of an enum. (save-excursion - (let ((start (point)) - (braceassignp 'dontknow) - inexpr-brace-list bufpos macro-start res pos after-type-id-pos - pos2 in-paren parens-before-brace - paren-state paren-pos) - - (setq res - (or (progn (c-backward-syntactic-ws) - (c-back-over-compound-identifier)) - (c-backward-token-2 1 t lim))) - ;; Checks to do only on the first sexp before the brace. - ;; Have we a C++ initialization, without an "="? - (if (and (c-major-mode-is 'c++-mode) - (cond - ((and (or (not (memq res '(t 0))) - (eq (char-after) ?,)) - (setq paren-state (c-parse-state)) - (setq paren-pos (c-pull-open-brace paren-state)) - (eq (char-after paren-pos) ?\()) - (goto-char paren-pos) - (setq braceassignp 'c++-noassign - in-paren 'in-paren)) - ((looking-at c-pre-brace-non-bracelist-key) - (setq braceassignp nil)) - ((and - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_))) - (setq braceassignp nil)) - ((looking-at c-return-key)) - ((and (looking-at c-symbol-start) - (not (looking-at c-keywords-regexp))) - (if (save-excursion - (and (zerop (c-backward-token-2 1 t lim)) - (looking-at c-pre-id-bracelist-key))) - (setq braceassignp 'c++-noassign) - (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) - (not (eq (char-after (match-end 0)) ?_)))) + (unless (and (c-major-mode-is 'c++-mode) + (c-backward-over-lambda-expression lim)) + (let ((start (point)) + (braceassignp 'dontknow) + inexpr-brace-list bufpos macro-start res pos after-type-id-pos + pos2 in-paren ;; parens-before-brace STOUGH, 2024-08-31 + paren-state paren-pos) + + (setq res + (or (progn (c-backward-syntactic-ws) + (c-back-over-compound-identifier)) + (c-backward-token-2 1 t lim))) + ;; Checks to do only on the first sexp before the brace. + ;; Have we a C++ initialization, without an "="? + (if (and (c-major-mode-is 'c++-mode) + (cond + ((and (or (not (memq res '(t 0))) + (eq (char-after) ?,)) + (setq paren-state (c-parse-state)) + (setq paren-pos (c-pull-open-brace paren-state)) + (eq (char-after paren-pos) ?\()) + (goto-char paren-pos) + (setq braceassignp 'c++-noassign + in-paren 'in-paren)) + ((looking-at c-pre-brace-non-bracelist-key) (setq braceassignp nil)) - nil) - (t nil)) - (save-excursion - (cond - ((or (not (memq res '(t 0))) - (eq (char-after) ?,)) - (and (setq paren-state (c-parse-state)) - (setq paren-pos (c-pull-open-brace paren-state)) - (eq (char-after paren-pos) ?\() - (setq in-paren 'in-paren) - (goto-char paren-pos))) - ((looking-at c-pre-brace-non-bracelist-key)) + ((and + (looking-at c-fun-name-substitute-key) + (not (eq (char-after (match-end 0)) ?_))) + (setq braceassignp nil)) ((looking-at c-return-key)) ((and (looking-at c-symbol-start) - (not (looking-at c-keywords-regexp)) - (save-excursion - (and (zerop (c-backward-token-2 1 t lim)) - (looking-at c-pre-id-bracelist-key))))) - (t (setq after-type-id-pos (point)) - nil)))) - (setq braceassignp 'c++-noassign)) - - (when (and c-opt-inexpr-brace-list-key - (eq (char-after) ?\[)) - ;; In Java, an initialization brace list may follow - ;; directly after "new Foo[]", so check for a "new" - ;; earlier. - (while (eq braceassignp 'dontknow) - (setq braceassignp - (cond ((/= (c-backward-token-2 1 t lim) 0) nil) - ((looking-at c-opt-inexpr-brace-list-key) - (setq inexpr-brace-list t) - t) - ((looking-at "\\sw\\|\\s_\\|[.[]") - ;; Carry on looking if this is an - ;; identifier (may contain "." in Java) - ;; or another "[]" sexp. - 'dontknow) - (t nil))))) - - (setq pos (point)) - (cond - ((not braceassignp) - nil) - ((and after-type-id-pos - (goto-char after-type-id-pos) - (setq res (c-back-over-member-initializers)) - (goto-char res) - (eq (car (c-beginning-of-decl-1 lim)) 'same)) - (cons (point) nil)) ; Return value. - - ((and after-type-id-pos - (progn - (c-backward-syntactic-ws) - (eq (char-before) ?\())) - ;; Single identifier between '(' and '{'. We have a bracelist. - (cons after-type-id-pos 'in-paren)) - - ;; Are we at the parens of a C++ lambda expression? - ((and parens-before-brace - (save-excursion - (and - (zerop (c-backward-token-2 1 t lim)) - (c-looking-at-c++-lambda-capture-list)))) - nil) ; a lambda expression isn't a brace list. - - (t - (goto-char pos) - (when (eq braceassignp 'dontknow) - (let* ((cache-entry (and containing-sexp - (c-laomib-get-cache containing-sexp pos))) - (lim2 (or (cadr cache-entry) lim)) - sub-bassign-p) - (if cache-entry - (cond - ((<= (point) (cadr cache-entry)) - ;; We're inside the region we've already scanned over, so - ;; just go to that scan's end position. - (goto-char (nth 2 cache-entry)) - (setq braceassignp (nth 3 cache-entry))) - ((> (point) (cadr cache-entry)) - ;; We're beyond the previous scan region, so just scan as - ;; far as the end of that region. - (setq sub-bassign-p (c-laomib-loop lim2)) - (if (<= (point) (cadr cache-entry)) - (progn - (c-laomib-put-cache containing-sexp - start (nth 2 cache-entry) - (nth 3 cache-entry) ;; sub-bassign-p - ) - (setq braceassignp (nth 3 cache-entry)) - (goto-char (nth 2 cache-entry))) - (c-laomib-put-cache containing-sexp - start (point) sub-bassign-p) - (setq braceassignp sub-bassign-p))) - (t)) - - (setq braceassignp (c-laomib-loop lim)) - (when lim - (c-laomib-put-cache lim start (point) braceassignp))))) - + (not (looking-at c-keywords-regexp))) + (if (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))) + (setq braceassignp 'c++-noassign) + (setq after-type-id-pos (point)))) + ((eq (char-after) ?\() + ;; (setq parens-before-brace t) STOUGH, 2024-08-31 + ;; 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) + (not (eq (char-after (match-end 0)) ?_)))) + (setq braceassignp nil)) + nil) + (t nil)) + (save-excursion + (cond + ((or (not (memq res '(t 0))) + (eq (char-after) ?,)) + (and (setq paren-state (c-parse-state)) + (setq paren-pos (c-pull-open-brace paren-state)) + (eq (char-after paren-pos) ?\() + (setq in-paren 'in-paren) + (goto-char paren-pos))) + ((looking-at c-pre-brace-non-bracelist-key)) + ((looking-at c-return-key)) + ((and (looking-at c-symbol-start) + (not (looking-at c-keywords-regexp)) + (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))))) + (t (setq after-type-id-pos (point)) + nil)))) + (setq braceassignp 'c++-noassign)) + + (when (and c-opt-inexpr-brace-list-key + (eq (char-after) ?\[)) + ;; In Java, an initialization brace list may follow + ;; directly after "new Foo[]", so check for a "new" + ;; earlier. + (while (eq braceassignp 'dontknow) + (setq braceassignp + (cond ((/= (c-backward-token-2 1 t lim) 0) nil) + ((looking-at c-opt-inexpr-brace-list-key) + (setq inexpr-brace-list t) + t) + ((looking-at "\\sw\\|\\s_\\|[.[]") + ;; Carry on looking if this is an + ;; identifier (may contain "." in Java) + ;; or another "[]" sexp. + 'dontknow) + (t nil))))) + + (setq pos (point)) (cond - (braceassignp - ;; We've hit the beginning of the aggregate list. - (setq pos2 (point)) - (cons - (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) - (point) - pos2) - (or in-paren inexpr-brace-list))) + ((not braceassignp) + nil) + ((and after-type-id-pos + (goto-char after-type-id-pos) + (setq res (c-back-over-member-initializers)) + (goto-char res) + (eq (car (c-beginning-of-decl-1 lim)) 'same)) + (cons (point) nil)) ; Return value. + ((and after-type-id-pos - (save-excursion - (when (eq (char-after) ?\;) - (c-forward-over-token-and-ws t)) - (setq bufpos (point)) - (when (looking-at c-opt-<>-sexp-key) - (c-forward-over-token-and-ws) - (when (and (eq (char-after) ?<) - (c-get-char-property (point) 'syntax-table)) - (c-go-list-forward nil after-type-id-pos) - (c-forward-syntactic-ws))) - (if (and (not (eq (point) after-type-id-pos)) - (or (not (looking-at c-class-key)) - (save-excursion - (goto-char (match-end 1)) - (c-forward-syntactic-ws) - (not (eq (point) after-type-id-pos))))) - (progn - (setq res - (c-forward-decl-or-cast-1 (c-point 'bosws) - nil nil)) - (and (consp res) - (cond - ((eq (car res) after-type-id-pos)) - ((> (car res) after-type-id-pos) nil) - (t - (catch 'find-decl + (progn + (c-backward-syntactic-ws) + (eq (char-before) ?\())) + ;; Single identifier between '(' and '{'. We have a bracelist. + (cons after-type-id-pos 'in-paren)) + + (t + (goto-char pos) + (when (eq braceassignp 'dontknow) + (let* ((cache-entry (and containing-sexp + (c-laomib-get-cache containing-sexp pos))) + (lim2 (or (cadr cache-entry) lim)) + sub-bassign-p) + (if cache-entry + (cond + ((<= (point) (cadr cache-entry)) + ;; We're inside the region we've already scanned over, so + ;; just go to that scan's end position. + (goto-char (nth 2 cache-entry)) + (setq braceassignp (nth 3 cache-entry))) + ((> (point) (cadr cache-entry)) + ;; We're beyond the previous scan region, so just scan as + ;; far as the end of that region. + (setq sub-bassign-p (c-laomib-loop lim2)) + (if (<= (point) (cadr cache-entry)) + (progn + (c-laomib-put-cache containing-sexp + start (nth 2 cache-entry) + (nth 3 cache-entry) ;; sub-bassign-p + ) + (setq braceassignp (nth 3 cache-entry)) + (goto-char (nth 2 cache-entry))) + (c-laomib-put-cache containing-sexp + start (point) sub-bassign-p) + (setq braceassignp sub-bassign-p))) + (t)) + + (setq braceassignp (c-laomib-loop lim)) + (when lim + (c-laomib-put-cache lim start (point) braceassignp))))) + + (cond + (braceassignp + ;; We've hit the beginning of the aggregate list. + (setq pos2 (point)) + (cons + (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) + (point) + pos2) + (or in-paren inexpr-brace-list))) + ((and after-type-id-pos + (save-excursion + (when (eq (char-after) ?\;) + (c-forward-over-token-and-ws t)) + (setq bufpos (point)) + (when (looking-at c-opt-<>-sexp-key) + (c-forward-over-token-and-ws) + (when (and (eq (char-after) ?<) + (c-get-char-property (point) 'syntax-table)) + (c-go-list-forward nil after-type-id-pos) + (c-forward-syntactic-ws))) + (if (and (not (eq (point) after-type-id-pos)) + (or (not (looking-at c-class-key)) (save-excursion - (goto-char (car res)) - (c-do-declarators - (point-max) t nil nil - (lambda (id-start _id-end _tok _not-top _func _init) - (cond - ((> id-start after-type-id-pos) - (throw 'find-decl nil)) - ((eq id-start after-type-id-pos) - (throw 'find-decl t))))) - nil)))))) - (save-excursion - (goto-char start) - (not (c-looking-at-statement-block)))))) - (cons bufpos (or in-paren inexpr-brace-list))) - ((or (eq (char-after) ?\;) - ;; Brace lists can't contain a semicolon, so we're done. - (save-excursion - (c-backward-syntactic-ws) - (eq (char-before) ?})) - ;; They also can't contain a bare }, which is probably the end - ;; of a function. - ) - nil) - ((and (setq macro-start (point)) - (c-forward-to-cpp-define-body) - (eq (point) start)) - ;; We've a macro whose expansion starts with the '{'. - ;; Heuristically, if we have a ';' in it we've not got a - ;; brace list, otherwise we have. - (let ((macro-end (progn (c-end-of-macro) (point)))) - (goto-char start) - (forward-char) - (if (and (c-syntactic-re-search-forward "[;,]" macro-end t t) - (eq (char-before) ?\;)) - nil - (cons macro-start nil)))) ; (2016-08-30): Lazy! We have no + (goto-char (match-end 1)) + (c-forward-syntactic-ws) + (not (eq (point) after-type-id-pos))))) + (progn + (setq res + (c-forward-decl-or-cast-1 (c-point 'bosws) + nil nil)) + (and (consp res) + (cond + ((eq (car res) after-type-id-pos)) + ((> (car res) after-type-id-pos) nil) + (t + (catch 'find-decl + (save-excursion + (goto-char (car res)) + (c-do-declarators + (point-max) t nil nil + (lambda (id-start _id-end _tok _not-top _func _init) + (cond + ((> id-start after-type-id-pos) + (throw 'find-decl nil)) + ((eq id-start after-type-id-pos) + (throw 'find-decl t))))) + nil)))))) + (save-excursion + (goto-char start) + (not (c-looking-at-statement-block)))))) + (cons bufpos (or in-paren inexpr-brace-list))) + ((or (eq (char-after) ?\;) + ;; Brace lists can't contain a semicolon, so we're done. + (save-excursion + (c-backward-syntactic-ws) + (eq (char-before) ?})) + ;; They also can't contain a bare }, which is probably the end + ;; of a function. + ) + nil) + ((and (setq macro-start (point)) + (c-forward-to-cpp-define-body) + (eq (point) start)) + ;; We've a macro whose expansion starts with the '{'. + ;; Heuristically, if we have a ';' in it we've not got a + ;; brace list, otherwise we have. + (let ((macro-end (progn (c-end-of-macro) (point)))) + (goto-char start) + (forward-char) + (if (and (c-syntactic-re-search-forward "[;,]" macro-end t t) + (eq (char-before) ?\;)) + nil + (cons macro-start nil)))) ; (2016-08-30): Lazy! We have no ; languages where ; `c-opt-inexpr-brace-list-key' is ; non-nil and we have macros. - (t t)))) ;; The caller can go up one level. - ))) + (t t)))) ;; The caller can go up one level. + )))) (defun c-inside-bracelist-p (containing-sexp paren-state accept-in-paren) ;; return the buffer position of the beginning of the brace list statement @@ -13863,6 +13857,71 @@ (looking-at c-pre-lambda-tokens-re))) (not (c-in-literal)))) +(defun c-looking-at-c++-lambda-expression (&optional lim) + ;; If point is at the [ opening a C++ lambda expressions's capture list, + ;; and the lambda expression is complete, return the position of the { which + ;; opens the body form, otherwise return nil. LIM is the limit for forward + ;; searching for the {. + (let ((here (point)) + got-params) + (when (and (c-looking-at-c++-lambda-capture-list) + (c-go-list-forward nil lim)) + (c-forward-syntactic-ws lim) + (when (c-forward-<>-arglist t) + (c-forward-syntactic-ws lim) + (when (looking-at "requires\\([^a-zA-Z0-9_]\\|$\\)") + (c-forward-c++-requires-clause lim nil))) + (when (and (eq (char-after) ?\() + (c-go-list-forward nil lim)) + (setq got-params t) + (c-forward-syntactic-ws lim)) + (while (and c-lambda-spec-key (looking-at c-lambda-spec-key)) + (goto-char (match-end 1)) + (c-forward-syntactic-ws lim)) + (let (after-except-pos) + (cond + ((save-excursion + (and (looking-at "throw\\([^a-zA-Z0-9_]\\|$\\)") + (progn (goto-char (match-beginning 1)) + (c-forward-syntactic-ws lim) + (eq (char-after) ?\()) + (c-go-list-forward nil lim) + (progn (c-forward-syntactic-ws lim) + (setq after-except-pos (point))))) + (goto-char after-except-pos) + (c-forward-syntactic-ws lim)) + ((looking-at "noexcept\\([^a-zA-Z0-9_]\\|$\\)") + (c-forward-syntactic-ws lim) + (setq after-except-pos (point)) + (when (and (eq (char-after) ?\() + (c-go-list-forward nil lim)) + (c-forward-syntactic-ws lim))))) + (and (looking-at c-haskell-op-re) + (goto-char (match-end 0)) + (c-forward-syntactic-ws lim) + (c-forward-type t)) ; t is BRACE-BLOCK-TOO. + (and got-params + (looking-at "requires\\([^a-zA-Z0-9_]\\|$\\)") + (c-forward-c++-requires-clause lim nil)) + (prog1 (and (eq (char-after) ?{) + (point)) + (goto-char here))))) + +(defun c-backward-over-lambda-expression (&optional lim) + ;; Point is at a {. Move back over the lambda expression this is a part of, + ;; stopping at the [ of the capture list, if this is the case, returning + ;; the position of that opening bracket. If we're not at such a list, leave + ;; point unchanged and return nil. + (let ((here (point))) + (c-syntactic-skip-backward "^;}]" lim t) + (if (and (eq (char-before) ?\]) + (c-go-list-backward nil lim) + (eq (c-looking-at-c++-lambda-expression (1+ here)) + here)) + (point) + (goto-char here) + nil))) + (defun c-c++-vsemi-p (&optional pos) ;; C++ Only - Is there a "virtual semicolon" at POS or point? ;; (See cc-defs.el for full details of "virtual semicolons".) diff -r 48ad661a2d7d cc-langs.el --- a/cc-langs.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-langs.el Sat Aug 31 16:44:39 2024 +0000 @@ -2639,6 +2639,19 @@ 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-lambda-spec-kwds + "Keywords which are specifiers of certain elements of a C++ lambda function. +This is only used in C++ Mode." + t nil + c++ '("mutable" "constexpr" "consteval" "static")) + +(c-lang-defconst c-lambda-spec-key + ;; A regular expression which matches a member of `c-lambda-spec-kwds', + ;; or nil. + t (if (c-lang-const c-lambda-spec-kwds) + (c-make-keywords-re t (c-lang-const c-lambda-spec-kwds)))) +(c-lang-defvar c-lambda-spec-key (c-lang-const c-lambda-spec-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 > Have a lovely day. > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Sun, 01 Sep 2024 01:05:02 GMT) Full text and rfc822 format available.Message #23 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sun, 01 Sep 2024 03:03:30 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Alan Mackenzie <acm <at> muc.de> writes: > It's going well, thanks! At the current rate of progress, I'll have my > mail setup set up by this time next week, and that is the point at > which, at least for me, my new machine becomes my current machine. Great to hear! >> > But mainly I'm struggling with the unfathomable complexity of C++ lambda >> > functions, as documented on >> > https://en.cppreference.com/w/cpp/language/lambda. > >> It should be quite simple - just a few optional productions preceded by >> an introducer production (see https://eel.is/c++draft/expr.prim.lambda >> for a formal grammar - this is the text from the standard draft). > >> > In my not so humble opinion, the C++ standards people have completely >> > lost the plot. > >> Should help be necessary, I do work on G++ so I'm quite familiar with >> the language. Feel free to ping me. > > Thanks! It's just that the explanatory text on en.cppreference.com > about just lambda functions takes 20 scrolls of ~45 lines per scroll, > coming out at many hundreds of lines, just for lambda functions. I feel > that the entire language part of Emacs Lisp could fit into that quite > comfortably, at the same density. Heh, given there's only a dozen or so syntax elements in Elisp, I agree ;-). C++ exposes lots of flexibility, so the syntax grew to match. Lambda expressions declare both a data type and a function, and their syntax exposes the syntax elements of both, hence the proverbial complexity being the sum of the two. >> > The number of special cases in just the syntax, apparently just to >> > save a few characters from a program's text, has got beyond the point >> > where anybody can grasp it. Nobody in this group of people appears to >> > be able to say "STOP!", though somebody sorely needs to. I dread to >> > think what will be coming out in C++26. :-( > >> It really shouldn't be that bad - I'll help demystify if need be. > > Thanks! > >> > Anyhow, I've spent all afternoon on the problem so far, and I think I can >> > see my way to a solution. It will still take a few more days yet, I'm >> > afraid. > >> Thank you very much! My apologies for the trouble. > > No apologies called for. You raised a bug report, for which thanks! > > Anyhow, I've got a first version, incompletely tested, of a new handling > for a lambda expression. The patch is not nearly as big as it looks, > two thirds of it is just re-indenting a large function I put an extra > `if' form aroun. I've tried it on a few examples I came up with, it seems to work well in 'real-life' code. I've also constructed the following "stress test" which includes (almost - see below) all the productions that can exist in a lambda function, in the same context as the original bad example: void def () { auto foo = [&, a, b = 123, c, ...d, ...e = 123 ] <typename T, typename S> //alignas (128) [[gnu::noreturn]] requires some_concept<T> (int a, int b, int c) consteval constexpr mutable static //noexcept(true) [[gnu::packed]] //alignas (128) //-> //int //requires (std::same_as <decltype (a), int>) { if constexpr (true) ; } } c-backward-over-lambda-expression does not work for this example. It seems that it gets confused by the trailing requires clauses, the alignas, the noexcept and the trailing return type (all commented - uncomment any or all to reproduce). However, this does not appear to negatively impact the syntax recognition instead, for the most part (I removed an element from the stress test above in order to demonstrate that the issue with indentation appears to be separate). In the following example: void def () { auto foo = [&, a, b = 123, c, ...d, ...e = 123 ] <typename T, typename S> alignas (128) [[gnu::noreturn]] //requires some_concept<T> (int a, int b, int c) consteval constexpr mutable static noexcept(true) [[gnu::packed]] alignas (128) -> int requires (std::same_as <decltype (a), int>) { if constexpr (true) ; } } ... uncommenting the requires line confuses the indentation engine. Hopefully these examples help. Please also note that in a few places (such as alignas, the noexcept specifier, RHS of assignments in the capture list) near-arbitrary expressions can appear, and that requires clause have special boolean expression syntax (see https://eel.is/c++draft/temp.pre#nt:requires-clause). Both of these pieces of syntax are reused in other contexts (other declarations), so if there's a bug in them in the lambda case, it likely carries over to the general declaration case. Thank you again for maintaining CC-mode! It is immeasurably useful. And apologies for the trouble :-) Have a lovely day. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Mon, 09 Sep 2024 15:16:01 GMT) Full text and rfc822 format available.Message #26 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Mon, 9 Sep 2024 15:15:34 +0000
Hello, Arsen. On Sun, Sep 01, 2024 at 03:03:30 +0200, Arsen Arsenović wrote: > Hi Alan, > Alan Mackenzie <acm <at> muc.de> writes: > > It's going well, thanks! At the current rate of progress, I'll have my > > mail setup set up by this time next week, and that is the point at > > which, at least for me, my new machine becomes my current machine. > Great to hear! Thanks! I'm afraid I've spent all week on that dashed email setup, and it's now finally working. So I've made only some progress on the bug, sorry. But here is what I have: [ .... ] > Heh, given there's only a dozen or so syntax elements in Elisp, I agree > ;-). C++ exposes lots of flexibility, so the syntax grew to match. Anyhow, I'm feeling less negative about C++'s syntax, now. > Lambda expressions declare both a data type and a function, and their > syntax exposes the syntax elements of both, hence the proverbial > complexity being the sum of the two. [ .... ] > > Anyhow, I've got a first version, incompletely tested, of a new > > handling for a lambda expression. The patch is not nearly as big as > > it looks, two thirds of it is just re-indenting a large function I > > put an extra `if' form aroun. > I've tried it on a few examples I came up with, it seems to work well in > 'real-life' code. > I've also constructed the following "stress test" which includes (almost > - see below) all the productions that can exist in a lambda function, in > the same context as the original bad example: Thanks, this was very useful. As an important question, can and alignas specifier exist inside a lambda expression? If so, what does it mean? My new patch below doesn't (yet) handle it. > void > def () > { > auto foo = [&, > a, > b = 123, > c, > ...d, > ...e = 123 > ] > <typename T, > typename S> > //alignas (128) > [[gnu::noreturn]] > requires some_concept<T> > (int a, > int b, > int c) > consteval > constexpr > mutable > static > //noexcept(true) > [[gnu::packed]] > //alignas (128) > //-> > //int > //requires (std::same_as <decltype (a), int>) > { > if constexpr (true) > ; > } > } Here's a modified version which is now fully handled (as far as it goes): void def () { auto foo = [&, a, b = 123, c, ...d, ...e = 123 ] <typename T, typename S> //alignas (128) [[gnu::noreturn]] requires some_concept<T> (int a, int b, int c) consteval constexpr mutable static noexcept(true) [[gnu::packed]] //alignas (128) -> int requires (std::same_as <decltype (a), int>) { if constexpr (true) ; } } [ .... ] > It seems that it gets confused by the trailing requires clauses, the > alignas, the noexcept and the trailing return type (all commented - > uncomment any or all to reproduce). These were coding errors in CC Mode. I hope the patch below will have fixed these, apart from the alignas (see above). > However, this does not appear to negatively impact the syntax > recognition instead, for the most part (I removed an element from the > stress test above in order to demonstrate that the issue with > indentation appears to be separate). > In the following example: > void > def () > { > auto foo = [&, > a, > b = 123, > c, > ...d, > ...e = 123 > ] > <typename T, > typename S> > alignas (128) > [[gnu::noreturn]] > //requires some_concept<T> > (int a, > int b, > int c) > consteval > constexpr > mutable > static > noexcept(true) > [[gnu::packed]] > alignas (128) > -> > int > requires (std::same_as <decltype (a), int>) > { > if constexpr (true) > ; > } > } > ... uncommenting the requires line confuses the indentation engine. I think this is fixed, now. > Hopefully these examples help. Very much so. > Please also note that in a few places (such as alignas, the noexcept > specifier, RHS of assignments in the capture list) near-arbitrary > expressions can appear, and that requires clause have special boolean > expression syntax (see > https://eel.is/c++draft/temp.pre#nt:requires-clause). I'm afraid I haven't looked at this, yet. > Both of these pieces of syntax are reused in other contexts (other > declarations), so if there's a bug in them in the lambda case, it likely > carries over to the general declaration case. > Thank you again for maintaining CC-mode! It is immeasurably useful. > And apologies for the trouble :-) No trouble at all! Here's an up to date version of the patch, which should work better than the last version. Please do the usual with it, and let me know how it goes. Thanks! diff -r 48ad661a2d7d cc-engine.el --- a/cc-engine.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-engine.el Mon Sep 09 14:49:55 2024 +0000 @@ -9929,12 +9929,20 @@ ((eq (char-after) ?\() (and (c-go-list-forward (point) limit) (eq (char-before) ?\)) - (let ((after-paren (point))) - (c-forward-syntactic-ws limit) - (prog1 - (not (eq (char-after) ?{)) - (when stop-at-end - (goto-char after-paren)))))) +;;;; OLD STOUGH, 2024-09-01 + ;; (let ((after-paren (point))) + ;; (c-forward-syntactic-ws limit) + ;; (prog1 + ;; (not (eq (char-after) ?{)) + ;; (when stop-at-end + ;; (goto-char after-paren)))) +;;;; NEW STOUGH, 2024-09-01 + (progn + (unless stop-at-end + (c-forward-syntactic-ws limit)) + t) +;;;; END OF NEW STOUGH + )) ((c-forward-over-compound-identifier) (let ((after-id (point))) (c-forward-syntactic-ws limit) @@ -13178,229 +13186,223 @@ ;; ;; Here, "brace list" does not include the body of an enum. (save-excursion - (let ((start (point)) - (braceassignp 'dontknow) - inexpr-brace-list bufpos macro-start res pos after-type-id-pos - pos2 in-paren parens-before-brace - paren-state paren-pos) - - (setq res - (or (progn (c-backward-syntactic-ws) - (c-back-over-compound-identifier)) - (c-backward-token-2 1 t lim))) - ;; Checks to do only on the first sexp before the brace. - ;; Have we a C++ initialization, without an "="? - (if (and (c-major-mode-is 'c++-mode) - (cond - ((and (or (not (memq res '(t 0))) - (eq (char-after) ?,)) - (setq paren-state (c-parse-state)) - (setq paren-pos (c-pull-open-brace paren-state)) - (eq (char-after paren-pos) ?\()) - (goto-char paren-pos) - (setq braceassignp 'c++-noassign - in-paren 'in-paren)) - ((looking-at c-pre-brace-non-bracelist-key) - (setq braceassignp nil)) - ((and - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_))) - (setq braceassignp nil)) - ((looking-at c-return-key)) - ((and (looking-at c-symbol-start) - (not (looking-at c-keywords-regexp))) - (if (save-excursion - (and (zerop (c-backward-token-2 1 t lim)) - (looking-at c-pre-id-bracelist-key))) - (setq braceassignp 'c++-noassign) - (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) - (not (eq (char-after (match-end 0)) ?_)))) + (unless (and (c-major-mode-is 'c++-mode) + (c-backward-over-lambda-expression lim)) + (let ((start (point)) + (braceassignp 'dontknow) + inexpr-brace-list bufpos macro-start res pos after-type-id-pos + pos2 in-paren ;; parens-before-brace STOUGH, 2024-08-31 + paren-state paren-pos) + + (setq res + (or (progn (c-backward-syntactic-ws) + (c-back-over-compound-identifier)) + (c-backward-token-2 1 t lim))) + ;; Checks to do only on the first sexp before the brace. + ;; Have we a C++ initialization, without an "="? + (if (and (c-major-mode-is 'c++-mode) + (cond + ((and (or (not (memq res '(t 0))) + (eq (char-after) ?,)) + (setq paren-state (c-parse-state)) + (setq paren-pos (c-pull-open-brace paren-state)) + (eq (char-after paren-pos) ?\()) + (goto-char paren-pos) + (setq braceassignp 'c++-noassign + in-paren 'in-paren)) + ((looking-at c-pre-brace-non-bracelist-key) (setq braceassignp nil)) - nil) - (t nil)) - (save-excursion - (cond - ((or (not (memq res '(t 0))) - (eq (char-after) ?,)) - (and (setq paren-state (c-parse-state)) - (setq paren-pos (c-pull-open-brace paren-state)) - (eq (char-after paren-pos) ?\() - (setq in-paren 'in-paren) - (goto-char paren-pos))) - ((looking-at c-pre-brace-non-bracelist-key)) + ((and + (looking-at c-fun-name-substitute-key) + (not (eq (char-after (match-end 0)) ?_))) + (setq braceassignp nil)) ((looking-at c-return-key)) ((and (looking-at c-symbol-start) - (not (looking-at c-keywords-regexp)) - (save-excursion - (and (zerop (c-backward-token-2 1 t lim)) - (looking-at c-pre-id-bracelist-key))))) - (t (setq after-type-id-pos (point)) - nil)))) - (setq braceassignp 'c++-noassign)) - - (when (and c-opt-inexpr-brace-list-key - (eq (char-after) ?\[)) - ;; In Java, an initialization brace list may follow - ;; directly after "new Foo[]", so check for a "new" - ;; earlier. - (while (eq braceassignp 'dontknow) - (setq braceassignp - (cond ((/= (c-backward-token-2 1 t lim) 0) nil) - ((looking-at c-opt-inexpr-brace-list-key) - (setq inexpr-brace-list t) - t) - ((looking-at "\\sw\\|\\s_\\|[.[]") - ;; Carry on looking if this is an - ;; identifier (may contain "." in Java) - ;; or another "[]" sexp. - 'dontknow) - (t nil))))) - - (setq pos (point)) - (cond - ((not braceassignp) - nil) - ((and after-type-id-pos - (goto-char after-type-id-pos) - (setq res (c-back-over-member-initializers)) - (goto-char res) - (eq (car (c-beginning-of-decl-1 lim)) 'same)) - (cons (point) nil)) ; Return value. - - ((and after-type-id-pos - (progn - (c-backward-syntactic-ws) - (eq (char-before) ?\())) - ;; Single identifier between '(' and '{'. We have a bracelist. - (cons after-type-id-pos 'in-paren)) - - ;; Are we at the parens of a C++ lambda expression? - ((and parens-before-brace - (save-excursion - (and - (zerop (c-backward-token-2 1 t lim)) - (c-looking-at-c++-lambda-capture-list)))) - nil) ; a lambda expression isn't a brace list. - - (t - (goto-char pos) - (when (eq braceassignp 'dontknow) - (let* ((cache-entry (and containing-sexp - (c-laomib-get-cache containing-sexp pos))) - (lim2 (or (cadr cache-entry) lim)) - sub-bassign-p) - (if cache-entry - (cond - ((<= (point) (cadr cache-entry)) - ;; We're inside the region we've already scanned over, so - ;; just go to that scan's end position. - (goto-char (nth 2 cache-entry)) - (setq braceassignp (nth 3 cache-entry))) - ((> (point) (cadr cache-entry)) - ;; We're beyond the previous scan region, so just scan as - ;; far as the end of that region. - (setq sub-bassign-p (c-laomib-loop lim2)) - (if (<= (point) (cadr cache-entry)) - (progn - (c-laomib-put-cache containing-sexp - start (nth 2 cache-entry) - (nth 3 cache-entry) ;; sub-bassign-p - ) - (setq braceassignp (nth 3 cache-entry)) - (goto-char (nth 2 cache-entry))) - (c-laomib-put-cache containing-sexp - start (point) sub-bassign-p) - (setq braceassignp sub-bassign-p))) - (t)) - - (setq braceassignp (c-laomib-loop lim)) - (when lim - (c-laomib-put-cache lim start (point) braceassignp))))) - + (not (looking-at c-keywords-regexp))) + (if (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))) + (setq braceassignp 'c++-noassign) + (setq after-type-id-pos (point)))) + ((eq (char-after) ?\() + ;; (setq parens-before-brace t) STOUGH, 2024-08-31 + ;; 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) + (not (eq (char-after (match-end 0)) ?_)))) + (setq braceassignp nil)) + nil) + (t nil)) + (save-excursion + (cond + ((or (not (memq res '(t 0))) + (eq (char-after) ?,)) + (and (setq paren-state (c-parse-state)) + (setq paren-pos (c-pull-open-brace paren-state)) + (eq (char-after paren-pos) ?\() + (setq in-paren 'in-paren) + (goto-char paren-pos))) + ((looking-at c-pre-brace-non-bracelist-key)) + ((looking-at c-return-key)) + ((and (looking-at c-symbol-start) + (not (looking-at c-keywords-regexp)) + (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))))) + (t (setq after-type-id-pos (point)) + nil)))) + (setq braceassignp 'c++-noassign)) + + (when (and c-opt-inexpr-brace-list-key + (eq (char-after) ?\[)) + ;; In Java, an initialization brace list may follow + ;; directly after "new Foo[]", so check for a "new" + ;; earlier. + (while (eq braceassignp 'dontknow) + (setq braceassignp + (cond ((/= (c-backward-token-2 1 t lim) 0) nil) + ((looking-at c-opt-inexpr-brace-list-key) + (setq inexpr-brace-list t) + t) + ((looking-at "\\sw\\|\\s_\\|[.[]") + ;; Carry on looking if this is an + ;; identifier (may contain "." in Java) + ;; or another "[]" sexp. + 'dontknow) + (t nil))))) + + (setq pos (point)) (cond - (braceassignp - ;; We've hit the beginning of the aggregate list. - (setq pos2 (point)) - (cons - (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) - (point) - pos2) - (or in-paren inexpr-brace-list))) + ((not braceassignp) + nil) + ((and after-type-id-pos + (goto-char after-type-id-pos) + (setq res (c-back-over-member-initializers)) + (goto-char res) + (eq (car (c-beginning-of-decl-1 lim)) 'same)) + (cons (point) nil)) ; Return value. + ((and after-type-id-pos - (save-excursion - (when (eq (char-after) ?\;) - (c-forward-over-token-and-ws t)) - (setq bufpos (point)) - (when (looking-at c-opt-<>-sexp-key) - (c-forward-over-token-and-ws) - (when (and (eq (char-after) ?<) - (c-get-char-property (point) 'syntax-table)) - (c-go-list-forward nil after-type-id-pos) - (c-forward-syntactic-ws))) - (if (and (not (eq (point) after-type-id-pos)) - (or (not (looking-at c-class-key)) - (save-excursion - (goto-char (match-end 1)) - (c-forward-syntactic-ws) - (not (eq (point) after-type-id-pos))))) - (progn - (setq res - (c-forward-decl-or-cast-1 (c-point 'bosws) - nil nil)) - (and (consp res) - (cond - ((eq (car res) after-type-id-pos)) - ((> (car res) after-type-id-pos) nil) - (t - (catch 'find-decl + (progn + (c-backward-syntactic-ws) + (eq (char-before) ?\())) + ;; Single identifier between '(' and '{'. We have a bracelist. + (cons after-type-id-pos 'in-paren)) + + (t + (goto-char pos) + (when (eq braceassignp 'dontknow) + (let* ((cache-entry (and containing-sexp + (c-laomib-get-cache containing-sexp pos))) + (lim2 (or (cadr cache-entry) lim)) + sub-bassign-p) + (if cache-entry + (cond + ((<= (point) (cadr cache-entry)) + ;; We're inside the region we've already scanned over, so + ;; just go to that scan's end position. + (goto-char (nth 2 cache-entry)) + (setq braceassignp (nth 3 cache-entry))) + ((> (point) (cadr cache-entry)) + ;; We're beyond the previous scan region, so just scan as + ;; far as the end of that region. + (setq sub-bassign-p (c-laomib-loop lim2)) + (if (<= (point) (cadr cache-entry)) + (progn + (c-laomib-put-cache containing-sexp + start (nth 2 cache-entry) + (nth 3 cache-entry) ;; sub-bassign-p + ) + (setq braceassignp (nth 3 cache-entry)) + (goto-char (nth 2 cache-entry))) + (c-laomib-put-cache containing-sexp + start (point) sub-bassign-p) + (setq braceassignp sub-bassign-p))) + (t)) + + (setq braceassignp (c-laomib-loop lim)) + (when lim + (c-laomib-put-cache lim start (point) braceassignp))))) + + (cond + (braceassignp + ;; We've hit the beginning of the aggregate list. + (setq pos2 (point)) + (cons + (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) + (point) + pos2) + (or in-paren inexpr-brace-list))) + ((and after-type-id-pos + (save-excursion + (when (eq (char-after) ?\;) + (c-forward-over-token-and-ws t)) + (setq bufpos (point)) + (when (looking-at c-opt-<>-sexp-key) + (c-forward-over-token-and-ws) + (when (and (eq (char-after) ?<) + (c-get-char-property (point) 'syntax-table)) + (c-go-list-forward nil after-type-id-pos) + (c-forward-syntactic-ws))) + (if (and (not (eq (point) after-type-id-pos)) + (or (not (looking-at c-class-key)) (save-excursion - (goto-char (car res)) - (c-do-declarators - (point-max) t nil nil - (lambda (id-start _id-end _tok _not-top _func _init) - (cond - ((> id-start after-type-id-pos) - (throw 'find-decl nil)) - ((eq id-start after-type-id-pos) - (throw 'find-decl t))))) - nil)))))) - (save-excursion - (goto-char start) - (not (c-looking-at-statement-block)))))) - (cons bufpos (or in-paren inexpr-brace-list))) - ((or (eq (char-after) ?\;) - ;; Brace lists can't contain a semicolon, so we're done. - (save-excursion - (c-backward-syntactic-ws) - (eq (char-before) ?})) - ;; They also can't contain a bare }, which is probably the end - ;; of a function. - ) - nil) - ((and (setq macro-start (point)) - (c-forward-to-cpp-define-body) - (eq (point) start)) - ;; We've a macro whose expansion starts with the '{'. - ;; Heuristically, if we have a ';' in it we've not got a - ;; brace list, otherwise we have. - (let ((macro-end (progn (c-end-of-macro) (point)))) - (goto-char start) - (forward-char) - (if (and (c-syntactic-re-search-forward "[;,]" macro-end t t) - (eq (char-before) ?\;)) - nil - (cons macro-start nil)))) ; (2016-08-30): Lazy! We have no + (goto-char (match-end 1)) + (c-forward-syntactic-ws) + (not (eq (point) after-type-id-pos))))) + (progn + (setq res + (c-forward-decl-or-cast-1 (c-point 'bosws) + nil nil)) + (and (consp res) + (cond + ((eq (car res) after-type-id-pos)) + ((> (car res) after-type-id-pos) nil) + (t + (catch 'find-decl + (save-excursion + (goto-char (car res)) + (c-do-declarators + (point-max) t nil nil + (lambda (id-start _id-end _tok _not-top _func _init) + (cond + ((> id-start after-type-id-pos) + (throw 'find-decl nil)) + ((eq id-start after-type-id-pos) + (throw 'find-decl t))))) + nil)))))) + (save-excursion + (goto-char start) + (not (c-looking-at-statement-block)))))) + (cons bufpos (or in-paren inexpr-brace-list))) + ((or (eq (char-after) ?\;) + ;; Brace lists can't contain a semicolon, so we're done. + (save-excursion + (c-backward-syntactic-ws) + (eq (char-before) ?})) + ;; They also can't contain a bare }, which is probably the end + ;; of a function. + ) + nil) + ((and (setq macro-start (point)) + (c-forward-to-cpp-define-body) + (eq (point) start)) + ;; We've a macro whose expansion starts with the '{'. + ;; Heuristically, if we have a ';' in it we've not got a + ;; brace list, otherwise we have. + (let ((macro-end (progn (c-end-of-macro) (point)))) + (goto-char start) + (forward-char) + (if (and (c-syntactic-re-search-forward "[;,]" macro-end t t) + (eq (char-before) ?\;)) + nil + (cons macro-start nil)))) ; (2016-08-30): Lazy! We have no ; languages where ; `c-opt-inexpr-brace-list-key' is ; non-nil and we have macros. - (t t)))) ;; The caller can go up one level. - ))) + (t t)))) ;; The caller can go up one level. + )))) (defun c-inside-bracelist-p (containing-sexp paren-state accept-in-paren) ;; return the buffer position of the beginning of the brace list statement @@ -13863,6 +13865,72 @@ (looking-at c-pre-lambda-tokens-re))) (not (c-in-literal)))) +(defun c-looking-at-c++-lambda-expression (&optional lim) + ;; If point is at the [ opening a C++ lambda expressions's capture list, + ;; and the lambda expression is complete, return the position of the { which + ;; opens the body form, otherwise return nil. LIM is the limit for forward + ;; searching for the {. + (let ((here (point)) + got-params) + (when (and (c-looking-at-c++-lambda-capture-list) + (c-go-list-forward nil lim)) + (c-forward-syntactic-ws lim) + (when (c-forward-<>-arglist t) + (c-forward-syntactic-ws lim) + (when (looking-at "requires\\([^a-zA-Z0-9_]\\|$\\)") + (c-forward-c++-requires-clause lim nil))) + (when (and (eq (char-after) ?\() + (c-go-list-forward nil lim)) + (setq got-params t) + (c-forward-syntactic-ws lim)) + (while (and c-lambda-spec-key (looking-at c-lambda-spec-key)) + (goto-char (match-end 1)) + (c-forward-syntactic-ws lim)) + (let (after-except-pos) + (cond + ((save-excursion + (and (looking-at "throw\\([^a-zA-Z0-9_]\\|$\\)") + (progn (goto-char (match-beginning 1)) + (c-forward-syntactic-ws lim) + (eq (char-after) ?\()) + (c-go-list-forward nil lim) + (progn (c-forward-syntactic-ws lim) + (setq after-except-pos (point))))) + (goto-char after-except-pos) + (c-forward-syntactic-ws lim)) + ((looking-at "noexcept\\([^a-zA-Z0-9_]\\|$\\)") + (goto-char (match-beginning 1)) + (c-forward-syntactic-ws lim) + (setq after-except-pos (point)) + (when (and (eq (char-after) ?\() + (c-go-list-forward nil lim)) + (c-forward-syntactic-ws lim))))) + (and (looking-at c-haskell-op-re) + (goto-char (match-end 0)) + (progn (c-forward-syntactic-ws lim) + (c-forward-type t))) ; t is BRACE-BLOCK-TOO. + (and got-params + (looking-at "requires\\([^a-zA-Z0-9_]\\|$\\)") + (c-forward-c++-requires-clause lim nil)) + (prog1 (and (eq (char-after) ?{) + (point)) + (goto-char here))))) + +(defun c-backward-over-lambda-expression (&optional lim) + ;; Point is at a {. Move back over the lambda expression this is a part of, + ;; stopping at the [ of the capture list, if this is the case, returning + ;; the position of that opening bracket. If we're not at such a list, leave + ;; point unchanged and return nil. + (let ((here (point))) + (c-syntactic-skip-backward "^;}]" lim t) + (if (and (eq (char-before) ?\]) + (c-go-list-backward nil lim) + (eq (c-looking-at-c++-lambda-expression (1+ here)) + here)) + (point) + (goto-char here) + nil))) + (defun c-c++-vsemi-p (&optional pos) ;; C++ Only - Is there a "virtual semicolon" at POS or point? ;; (See cc-defs.el for full details of "virtual semicolons".) @@ -13870,11 +13938,17 @@ ;; This is true when point is at the last non syntactic WS position on the ;; line, and either there is a "macro with semicolon" just before it (see ;; `c-at-macro-vsemi-p') or there is a "requires" clause which ends there. - (let (res) - (cond - ((setq res (c-in-requires-or-at-end-of-clause pos)) - (and res (eq (cdr res) t))) - ((c-at-macro-vsemi-p))))) +;;;; TEMP COMMENTED OUT STOUGH, 2024-09-01 + ;; (let (res) + ;; (cond + ;; ((setq res (c-in-requires-or-at-end-of-clause pos)) + ;; (and res (eq (cdr res) t))) + ;; ( +;;;; END OF TCO STOUGH + (c-at-macro-vsemi-p pos ; POS is NEW STOUGH, 2024-09-01 + ) + ;; ))) TEMP COMMENTED OUT STOUGH, 2024-09-01 +) (defun c-at-macro-vsemi-p (&optional pos) ;; Is there a "virtual semicolon" at POS or point? diff -r 48ad661a2d7d cc-langs.el --- a/cc-langs.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-langs.el Mon Sep 09 14:49:55 2024 +0000 @@ -2639,6 +2639,19 @@ 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-lambda-spec-kwds + "Keywords which are specifiers of certain elements of a C++ lambda function. +This is only used in C++ Mode." + t nil + c++ '("mutable" "constexpr" "consteval" "static")) + +(c-lang-defconst c-lambda-spec-key + ;; A regular expression which matches a member of `c-lambda-spec-kwds', + ;; or nil. + t (if (c-lang-const c-lambda-spec-kwds) + (c-make-keywords-re t (c-lang-const c-lambda-spec-kwds)))) +(c-lang-defvar c-lambda-spec-key (c-lang-const c-lambda-spec-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 > Have a lovely day. > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Mon, 09 Sep 2024 23:04:02 GMT) Full text and rfc822 format available.Message #29 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Tue, 10 Sep 2024 01:02:50 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Alan Mackenzie <acm <at> muc.de> writes: > Thanks! I'm afraid I've spent all week on that dashed email setup, > and it's now finally working. So I've made only some progress on the > bug, sorry. No worries! > But here is what I have: > > [ .... ] > >> Heh, given there's only a dozen or so syntax elements in Elisp, I agree >> ;-). C++ exposes lots of flexibility, so the syntax grew to match. > > Anyhow, I'm feeling less negative about C++'s syntax, now. :-) >> Lambda expressions declare both a data type and a function, and their >> syntax exposes the syntax elements of both, hence the proverbial >> complexity being the sum of the two. > > [ .... ] > >> > Anyhow, I've got a first version, incompletely tested, of a new >> > handling for a lambda expression. The patch is not nearly as big as >> > it looks, two thirds of it is just re-indenting a large function I >> > put an extra `if' form aroun. > >> I've tried it on a few examples I came up with, it seems to work well in >> 'real-life' code. > >> I've also constructed the following "stress test" which includes (almost >> - see below) all the productions that can exist in a lambda function, in >> the same context as the original bad example: > > Thanks, this was very useful. As an important question, can and alignas > specifier exist inside a lambda expression? If so, what does it mean? > My new patch below doesn't (yet) handle it. It certainly can in the body. I presume you mean in the leader/declarator/whathaveyou however; in that case, yes, as part of the attribute-specifier-seq in lambda-expression and lambda-declarator, and its meaning depends on the position (see https://eel.is/c++draft/expr.prim.lambda#closure-6). In short: in the following: [] alignas (128) () alignas (64) {...} ... the operator() - i.e. the function containing the lambda body - of the closure type will have alignment of 128 (whatever that means for functions), and the function type itself will be have alignment of 64 (whatever that means for the function type). >> void >> def () >> { >> auto foo = [&, >> a, >> b = 123, >> c, >> ...d, >> ...e = 123 >> ] >> <typename T, >> typename S> >> //alignas (128) >> [[gnu::noreturn]] >> requires some_concept<T> >> (int a, >> int b, >> int c) >> consteval >> constexpr >> mutable >> static >> //noexcept(true) >> [[gnu::packed]] >> //alignas (128) >> //-> >> //int >> //requires (std::same_as <decltype (a), int>) >> { >> if constexpr (true) >> ; >> } >> } > > Here's a modified version which is now fully handled (as far as it goes): > > > void > def () > { > auto foo = [&, > a, > b = 123, > c, > ...d, > ...e = 123 > ] > <typename T, > typename S> > //alignas (128) > [[gnu::noreturn]] > requires some_concept<T> > (int a, > int b, > int c) > consteval > constexpr > mutable > static > noexcept(true) > [[gnu::packed]] > //alignas (128) > -> int > requires (std::same_as <decltype (a), int>) > { > if constexpr (true) > ; > } > } > > > [ .... ] > >> It seems that it gets confused by the trailing requires clauses, the >> alignas, the noexcept and the trailing return type (all commented - >> uncomment any or all to reproduce). > > These were coding errors in CC Mode. I hope the patch below will have > fixed these, apart from the alignas (see above). Indeed that appears to be the case! >> However, this does not appear to negatively impact the syntax >> recognition instead, for the most part (I removed an element from the >> stress test above in order to demonstrate that the issue with >> indentation appears to be separate). > >> In the following example: > >> void >> def () >> { >> auto foo = [&, >> a, >> b = 123, >> c, >> ...d, >> ...e = 123 >> ] >> <typename T, >> typename S> >> alignas (128) >> [[gnu::noreturn]] >> //requires some_concept<T> >> (int a, >> int b, >> int c) >> consteval >> constexpr >> mutable >> static >> noexcept(true) >> [[gnu::packed]] >> alignas (128) >> -> >> int >> requires (std::same_as <decltype (a), int>) >> { >> if constexpr (true) >> ; >> } >> } > >> ... uncommenting the requires line confuses the indentation engine. > > I think this is fixed, now. > >> Hopefully these examples help. > > Very much so. > >> Please also note that in a few places (such as alignas, the noexcept >> specifier, RHS of assignments in the capture list) near-arbitrary >> expressions can appear, and that requires clause have special boolean >> expression syntax (see >> https://eel.is/c++draft/temp.pre#nt:requires-clause). > > I'm afraid I haven't looked at this, yet. No worries. This is somewhat akin to a few other constructs in C and C++ where expressions can appear in an otherwise "type" context, so it might already be handled (and the handling for concepts /could/ be generic handling probably, without worrying about the special case in the grammar - just something to consider). >> Both of these pieces of syntax are reused in other contexts (other >> declarations), so if there's a bug in them in the lambda case, it likely >> carries over to the general declaration case. > >> Thank you again for maintaining CC-mode! It is immeasurably useful. >> And apologies for the trouble :-) > > No trouble at all! > > Here's an up to date version of the patch, which should work better than > the last version. Please do the usual with it, and let me know how it > goes. Thanks! Seems to be working quite well in some contrived tests and a few fragments of realistic looking code. Thank you! Have a lovely evening. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Fri, 20 Sep 2024 15:00:02 GMT) Full text and rfc822 format available.Message #32 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Fri, 20 Sep 2024 14:59:01 +0000
Hello, Arsen. On Tue, Sep 10, 2024 at 01:02:50 +0200, Arsen Arsenović wrote: > Hi Alan, > Alan Mackenzie <acm <at> muc.de> writes: [ .... ] I'm hoping I've now fixed this bug (see patch below). [ .... ] > >> I've tried it on a few examples I came up with, it seems to work well in > >> 'real-life' code. > >> I've also constructed the following "stress test" which includes (almost > >> - see below) all the productions that can exist in a lambda function, in > >> the same context as the original bad example: > > Thanks, this was very useful. As an important question, can and alignas > > specifier exist inside a lambda expression? If so, what does it mean? > > My new patch below doesn't (yet) handle it. > It certainly can in the body. I presume you mean in the > leader/declarator/whathaveyou however; in that case, yes, as part of the > attribute-specifier-seq in lambda-expression and lambda-declarator, and > its meaning depends on the position (see > https://eel.is/c++draft/expr.prim.lambda#closure-6). Thanks, I've now found the place in the C++ syntax where alignas can go in a lambda construct. It is the only specifier which may go in these places, although any number of attributes are allowed. (As a matter of interest, attributes in CC Mode are treated as syntactic whitespace.) [ .... ] > >> void > >> def () > >> { > >> auto foo = [&, > >> a, > >> b = 123, > >> c, > >> ...d, > >> ...e = 123 > >> ] > >> <typename T, > >> typename S> > >> //alignas (128) > >> [[gnu::noreturn]] > >> requires some_concept<T> > >> (int a, > >> int b, > >> int c) > >> consteval > >> constexpr > >> mutable > >> static > >> //noexcept(true) > >> [[gnu::packed]] > >> //alignas (128) > >> //-> > >> //int > >> //requires (std::same_as <decltype (a), int>) > >> { > >> if constexpr (true) > >> ; > >> } > >> } Question: Is that first (commented out) alignas correct where it is? My reading of the version of the standard suggests that the alignas (128) should come _after_ the requires some_concept<T>, not before it. > > Here's a modified version which is now fully handled (as far as it goes): And here's a further modified version, which my patch (below) now handles: void def () { auto foo = [&, a, b = 123, c, ...d, ...e = 123 ] <typename T, typename S> //alignas (128) [[gnu::noreturn]] requires some_concept<T> alignas (128) (int a, int b, int c) consteval constexpr mutable static noexcept(true) [[gnu::packed]] alignas (128) -> int requires (std::same_as <decltype (a), int>) { if constexpr (true) ; } } [ .... ] > >> Hopefully these examples help. > > Very much so. > >> Please also note that in a few places (such as alignas, the noexcept > >> specifier, RHS of assignments in the capture list) near-arbitrary > >> expressions can appear, and that requires clause have special boolean > >> expression syntax (see > >> https://eel.is/c++draft/temp.pre#nt:requires-clause). > > I'm afraid I haven't looked at this, yet. I have now. :-) [ .... ] > > Here's an up to date version of the patch, which should work better than > > the last version. Please do the usual with it, and let me know how it > > goes. Thanks! And here's what I'm hoping will be the final version of the patch to fix this bug. Again, could you try it out (on real code as well as test code), and let me know how well it works. It should apply cleanly to the Emacs master branch. Thanks! Even it the patch is good, I'm not quite ready to commit it yet, since I need to amend the test suite somewhat. diff -r 48ad661a2d7d cc-align.el --- a/cc-align.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-align.el Fri Sep 20 14:15:17 2024 +0000 @@ -95,7 +95,9 @@ (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-after-special-operator-id))) + (progn (goto-char (c-langelem-pos langelem)) + (not (looking-at c-requires-clause-key)))) c-basic-offset)))) (defun c-lineup-gnu-DEFUN-intro-cont (langelem) diff -r 48ad661a2d7d cc-engine.el --- a/cc-engine.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-engine.el Fri Sep 20 14:15:17 2024 +0000 @@ -8694,10 +8694,9 @@ (c-forward-syntactic-ws) (c-forward-id-comma-list ref t t)) - ((and (c-keyword-member kwd-sym 'c-paren-any-kwds) + ((and (c-keyword-member kwd-sym 'c-paren-type-kwds) (eq (char-after) ?\()) ;; There's an open paren after a keyword in `c-paren-any-kwds'. - (forward-char) (when (and (setq pos (c-up-list-forward)) (eq (char-before pos) ?\))) @@ -8715,6 +8714,12 @@ (setq safe-pos (point))) (c-forward-syntactic-ws)) + ((c-keyword-member kwd-sym 'c-paren-nontype-kwds) + (when (and (eq (char-after) ?\() + (c-go-list-forward)) + (setq safe-pos (point)) + (c-forward-syntactic-ws))) + ((and (c-keyword-member kwd-sym 'c-<>-sexp-kwds) (eq (char-after) ?<) (c-forward-<>-arglist (c-keyword-member kwd-sym 'c-<>-type-kwds))) @@ -9914,9 +9919,6 @@ ;; ;; Note that this function is incomplete, handling only those cases expected ;; to be common in a C++20 requires clause. - ;; - ;; Note also that (...) is not recognized as a primary expression if the - ;; next token is an open brace. (let ((here (point)) (c-restricted-<>-arglists t) (c-parse-and-markup-<>-arglists nil) @@ -9929,12 +9931,10 @@ ((eq (char-after) ?\() (and (c-go-list-forward (point) limit) (eq (char-before) ?\)) - (let ((after-paren (point))) - (c-forward-syntactic-ws limit) - (prog1 - (not (eq (char-after) ?{)) - (when stop-at-end - (goto-char after-paren)))))) + (progn + (unless stop-at-end + (c-forward-syntactic-ws limit)) + t))) ((c-forward-over-compound-identifier) (let ((after-id (point))) (c-forward-syntactic-ws limit) @@ -9953,9 +9953,7 @@ (c-forward-over-compound-identifier) (c-forward-syntactic-ws limit)))))) (goto-char after-id))) - ((and - (looking-at c-fun-name-substitute-key) ; "requires" - (not (eq (char-after (match-end 0)) ?_))) + ((looking-at c-fun-name-substitute-key) ; "requires" (goto-char (match-end 1)) (c-forward-syntactic-ws limit) (and @@ -10210,9 +10208,7 @@ ((looking-at c-type-decl-suffix-key) (cond ((save-match-data - (and - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_)))) + (looking-at c-requires-clause-key)) (c-forward-c++-requires-clause)) ((eq (char-after) ?\() (if (c-forward-decl-arglist not-top decorated limit) @@ -10667,10 +10663,8 @@ (c-forward-keyword-clause 1) (when (and (c-major-mode-is 'c++-mode) (c-keyword-member kwd-sym 'c-<>-sexp-kwds) - (save-match-data - (and - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_))))) + (save-match-data ; Probably unnecessary (2024-09-20) + (looking-at c-requires-clause-key))) (c-forward-c++-requires-clause)) (setq kwd-clause-end (point)))) ((and c-opt-cpp-prefix @@ -11028,9 +11022,7 @@ ((save-match-data (looking-at "\\s(")) (c-safe (c-forward-sexp 1) t)) ((save-match-data - (and - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_)))) ; C++ requires + (looking-at c-requires-clause-key)) (c-forward-c++-requires-clause)) (t (goto-char (match-end 1)) t)) @@ -13178,229 +13170,218 @@ ;; ;; Here, "brace list" does not include the body of an enum. (save-excursion - (let ((start (point)) - (braceassignp 'dontknow) - inexpr-brace-list bufpos macro-start res pos after-type-id-pos - pos2 in-paren parens-before-brace - paren-state paren-pos) - - (setq res - (or (progn (c-backward-syntactic-ws) - (c-back-over-compound-identifier)) - (c-backward-token-2 1 t lim))) - ;; Checks to do only on the first sexp before the brace. - ;; Have we a C++ initialization, without an "="? - (if (and (c-major-mode-is 'c++-mode) - (cond - ((and (or (not (memq res '(t 0))) - (eq (char-after) ?,)) - (setq paren-state (c-parse-state)) - (setq paren-pos (c-pull-open-brace paren-state)) - (eq (char-after paren-pos) ?\()) - (goto-char paren-pos) - (setq braceassignp 'c++-noassign - in-paren 'in-paren)) - ((looking-at c-pre-brace-non-bracelist-key) - (setq braceassignp nil)) - ((and - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_))) - (setq braceassignp nil)) - ((looking-at c-return-key)) - ((and (looking-at c-symbol-start) - (not (looking-at c-keywords-regexp))) - (if (save-excursion - (and (zerop (c-backward-token-2 1 t lim)) - (looking-at c-pre-id-bracelist-key))) - (setq braceassignp 'c++-noassign) - (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) - (not (eq (char-after (match-end 0)) ?_)))) + (unless (and (c-major-mode-is 'c++-mode) + (c-backward-over-lambda-expression lim)) + (let ((start (point)) + (braceassignp 'dontknow) + inexpr-brace-list bufpos macro-start res pos after-type-id-pos + pos2 in-paren paren-state paren-pos) + + (setq res + (or (progn (c-backward-syntactic-ws) + (c-back-over-compound-identifier)) + (c-backward-token-2 1 t lim))) + ;; Checks to do only on the first sexp before the brace. + ;; Have we a C++ initialization, without an "="? + (if (and (c-major-mode-is 'c++-mode) + (cond + ((and (or (not (memq res '(t 0))) + (eq (char-after) ?,)) + (setq paren-state (c-parse-state)) + (setq paren-pos (c-pull-open-brace paren-state)) + (eq (char-after paren-pos) ?\()) + (goto-char paren-pos) + (setq braceassignp 'c++-noassign + in-paren 'in-paren)) + ((looking-at c-pre-brace-non-bracelist-key) (setq braceassignp nil)) - nil) - (t nil)) - (save-excursion - (cond - ((or (not (memq res '(t 0))) - (eq (char-after) ?,)) - (and (setq paren-state (c-parse-state)) - (setq paren-pos (c-pull-open-brace paren-state)) - (eq (char-after paren-pos) ?\() - (setq in-paren 'in-paren) - (goto-char paren-pos))) - ((looking-at c-pre-brace-non-bracelist-key)) + ((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)) - (save-excursion - (and (zerop (c-backward-token-2 1 t lim)) - (looking-at c-pre-id-bracelist-key))))) - (t (setq after-type-id-pos (point)) - nil)))) - (setq braceassignp 'c++-noassign)) - - (when (and c-opt-inexpr-brace-list-key - (eq (char-after) ?\[)) - ;; In Java, an initialization brace list may follow - ;; directly after "new Foo[]", so check for a "new" - ;; earlier. - (while (eq braceassignp 'dontknow) - (setq braceassignp - (cond ((/= (c-backward-token-2 1 t lim) 0) nil) - ((looking-at c-opt-inexpr-brace-list-key) - (setq inexpr-brace-list t) - t) - ((looking-at "\\sw\\|\\s_\\|[.[]") - ;; Carry on looking if this is an - ;; identifier (may contain "." in Java) - ;; or another "[]" sexp. - 'dontknow) - (t nil))))) - - (setq pos (point)) - (cond - ((not braceassignp) - nil) - ((and after-type-id-pos - (goto-char after-type-id-pos) - (setq res (c-back-over-member-initializers)) - (goto-char res) - (eq (car (c-beginning-of-decl-1 lim)) 'same)) - (cons (point) nil)) ; Return value. - - ((and after-type-id-pos - (progn - (c-backward-syntactic-ws) - (eq (char-before) ?\())) - ;; Single identifier between '(' and '{'. We have a bracelist. - (cons after-type-id-pos 'in-paren)) - - ;; Are we at the parens of a C++ lambda expression? - ((and parens-before-brace - (save-excursion - (and - (zerop (c-backward-token-2 1 t lim)) - (c-looking-at-c++-lambda-capture-list)))) - nil) ; a lambda expression isn't a brace list. - - (t - (goto-char pos) - (when (eq braceassignp 'dontknow) - (let* ((cache-entry (and containing-sexp - (c-laomib-get-cache containing-sexp pos))) - (lim2 (or (cadr cache-entry) lim)) - sub-bassign-p) - (if cache-entry - (cond - ((<= (point) (cadr cache-entry)) - ;; We're inside the region we've already scanned over, so - ;; just go to that scan's end position. - (goto-char (nth 2 cache-entry)) - (setq braceassignp (nth 3 cache-entry))) - ((> (point) (cadr cache-entry)) - ;; We're beyond the previous scan region, so just scan as - ;; far as the end of that region. - (setq sub-bassign-p (c-laomib-loop lim2)) - (if (<= (point) (cadr cache-entry)) - (progn - (c-laomib-put-cache containing-sexp - start (nth 2 cache-entry) - (nth 3 cache-entry) ;; sub-bassign-p - ) - (setq braceassignp (nth 3 cache-entry)) - (goto-char (nth 2 cache-entry))) - (c-laomib-put-cache containing-sexp - start (point) sub-bassign-p) - (setq braceassignp sub-bassign-p))) - (t)) - - (setq braceassignp (c-laomib-loop lim)) - (when lim - (c-laomib-put-cache lim start (point) braceassignp))))) - + (not (looking-at c-keywords-regexp))) + (if (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))) + (setq braceassignp 'c++-noassign) + (setq after-type-id-pos (point)))) + ((eq (char-after) ?\() + ;; 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 + (cond + ((or (not (memq res '(t 0))) + (eq (char-after) ?,)) + (and (setq paren-state (c-parse-state)) + (setq paren-pos (c-pull-open-brace paren-state)) + (eq (char-after paren-pos) ?\() + (setq in-paren 'in-paren) + (goto-char paren-pos))) + ((looking-at c-pre-brace-non-bracelist-key)) + ((looking-at c-return-key)) + ((and (looking-at c-symbol-start) + (not (looking-at c-keywords-regexp)) + (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))))) + (t (setq after-type-id-pos (point)) + nil)))) + (setq braceassignp 'c++-noassign)) + + (when (and c-opt-inexpr-brace-list-key + (eq (char-after) ?\[)) + ;; In Java, an initialization brace list may follow + ;; directly after "new Foo[]", so check for a "new" + ;; earlier. + (while (eq braceassignp 'dontknow) + (setq braceassignp + (cond ((/= (c-backward-token-2 1 t lim) 0) nil) + ((looking-at c-opt-inexpr-brace-list-key) + (setq inexpr-brace-list t) + t) + ((looking-at "\\sw\\|\\s_\\|[.[]") + ;; Carry on looking if this is an + ;; identifier (may contain "." in Java) + ;; or another "[]" sexp. + 'dontknow) + (t nil))))) + + (setq pos (point)) (cond - (braceassignp - ;; We've hit the beginning of the aggregate list. - (setq pos2 (point)) - (cons - (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) - (point) - pos2) - (or in-paren inexpr-brace-list))) + ((not braceassignp) + nil) + ((and after-type-id-pos + (goto-char after-type-id-pos) + (setq res (c-back-over-member-initializers)) + (goto-char res) + (eq (car (c-beginning-of-decl-1 lim)) 'same)) + (cons (point) nil)) ; Return value. + ((and after-type-id-pos - (save-excursion - (when (eq (char-after) ?\;) - (c-forward-over-token-and-ws t)) - (setq bufpos (point)) - (when (looking-at c-opt-<>-sexp-key) - (c-forward-over-token-and-ws) - (when (and (eq (char-after) ?<) - (c-get-char-property (point) 'syntax-table)) - (c-go-list-forward nil after-type-id-pos) - (c-forward-syntactic-ws))) - (if (and (not (eq (point) after-type-id-pos)) - (or (not (looking-at c-class-key)) - (save-excursion - (goto-char (match-end 1)) - (c-forward-syntactic-ws) - (not (eq (point) after-type-id-pos))))) - (progn - (setq res - (c-forward-decl-or-cast-1 (c-point 'bosws) - nil nil)) - (and (consp res) - (cond - ((eq (car res) after-type-id-pos)) - ((> (car res) after-type-id-pos) nil) - (t - (catch 'find-decl + (progn + (c-backward-syntactic-ws) + (eq (char-before) ?\())) + ;; Single identifier between '(' and '{'. We have a bracelist. + (cons after-type-id-pos 'in-paren)) + + (t + (goto-char pos) + (when (eq braceassignp 'dontknow) + (let* ((cache-entry (and containing-sexp + (c-laomib-get-cache containing-sexp pos))) + (lim2 (or (cadr cache-entry) lim)) + sub-bassign-p) + (if cache-entry + (cond + ((<= (point) (cadr cache-entry)) + ;; We're inside the region we've already scanned over, so + ;; just go to that scan's end position. + (goto-char (nth 2 cache-entry)) + (setq braceassignp (nth 3 cache-entry))) + ((> (point) (cadr cache-entry)) + ;; We're beyond the previous scan region, so just scan as + ;; far as the end of that region. + (setq sub-bassign-p (c-laomib-loop lim2)) + (if (<= (point) (cadr cache-entry)) + (progn + (c-laomib-put-cache containing-sexp + start (nth 2 cache-entry) + (nth 3 cache-entry) ;; sub-bassign-p + ) + (setq braceassignp (nth 3 cache-entry)) + (goto-char (nth 2 cache-entry))) + (c-laomib-put-cache containing-sexp + start (point) sub-bassign-p) + (setq braceassignp sub-bassign-p))) + (t)) + + (setq braceassignp (c-laomib-loop lim)) + (when lim + (c-laomib-put-cache lim start (point) braceassignp))))) + + (cond + (braceassignp + ;; We've hit the beginning of the aggregate list. + (setq pos2 (point)) + (cons + (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) + (point) + pos2) + (or in-paren inexpr-brace-list))) + ((and after-type-id-pos + (save-excursion + (when (eq (char-after) ?\;) + (c-forward-over-token-and-ws t)) + (setq bufpos (point)) + (when (looking-at c-opt-<>-sexp-key) + (c-forward-over-token-and-ws) + (when (and (eq (char-after) ?<) + (c-get-char-property (point) 'syntax-table)) + (c-go-list-forward nil after-type-id-pos) + (c-forward-syntactic-ws))) + (if (and (not (eq (point) after-type-id-pos)) + (or (not (looking-at c-class-key)) (save-excursion - (goto-char (car res)) - (c-do-declarators - (point-max) t nil nil - (lambda (id-start _id-end _tok _not-top _func _init) - (cond - ((> id-start after-type-id-pos) - (throw 'find-decl nil)) - ((eq id-start after-type-id-pos) - (throw 'find-decl t))))) - nil)))))) - (save-excursion - (goto-char start) - (not (c-looking-at-statement-block)))))) - (cons bufpos (or in-paren inexpr-brace-list))) - ((or (eq (char-after) ?\;) - ;; Brace lists can't contain a semicolon, so we're done. - (save-excursion - (c-backward-syntactic-ws) - (eq (char-before) ?})) - ;; They also can't contain a bare }, which is probably the end - ;; of a function. - ) - nil) - ((and (setq macro-start (point)) - (c-forward-to-cpp-define-body) - (eq (point) start)) - ;; We've a macro whose expansion starts with the '{'. - ;; Heuristically, if we have a ';' in it we've not got a - ;; brace list, otherwise we have. - (let ((macro-end (progn (c-end-of-macro) (point)))) - (goto-char start) - (forward-char) - (if (and (c-syntactic-re-search-forward "[;,]" macro-end t t) - (eq (char-before) ?\;)) - nil - (cons macro-start nil)))) ; (2016-08-30): Lazy! We have no + (goto-char (match-end 1)) + (c-forward-syntactic-ws) + (not (eq (point) after-type-id-pos))))) + (progn + (setq res + (c-forward-decl-or-cast-1 (c-point 'bosws) + nil nil)) + (and (consp res) + (cond + ((eq (car res) after-type-id-pos)) + ((> (car res) after-type-id-pos) nil) + (t + (catch 'find-decl + (save-excursion + (goto-char (car res)) + (c-do-declarators + (point-max) t nil nil + (lambda (id-start _id-end _tok _not-top _func _init) + (cond + ((> id-start after-type-id-pos) + (throw 'find-decl nil)) + ((eq id-start after-type-id-pos) + (throw 'find-decl t))))) + nil)))))) + (save-excursion + (goto-char start) + (not (c-looking-at-statement-block)))))) + (cons bufpos (or in-paren inexpr-brace-list))) + ((or (eq (char-after) ?\;) + ;; Brace lists can't contain a semicolon, so we're done. + (save-excursion + (c-backward-syntactic-ws) + (eq (char-before) ?})) + ;; They also can't contain a bare }, which is probably the end + ;; of a function. + ) + nil) + ((and (setq macro-start (point)) + (c-forward-to-cpp-define-body) + (eq (point) start)) + ;; We've a macro whose expansion starts with the '{'. + ;; Heuristically, if we have a ';' in it we've not got a + ;; brace list, otherwise we have. + (let ((macro-end (progn (c-end-of-macro) (point)))) + (goto-char start) + (forward-char) + (if (and (c-syntactic-re-search-forward "[;,]" macro-end t t) + (eq (char-before) ?\;)) + nil + (cons macro-start nil)))) ; (2016-08-30): Lazy! We have no ; languages where ; `c-opt-inexpr-brace-list-key' is ; non-nil and we have macros. - (t t)))) ;; The caller can go up one level. - ))) + (t t)))) ;; The caller can go up one level. + )))) (defun c-inside-bracelist-p (containing-sexp paren-state accept-in-paren) ;; return the buffer position of the beginning of the brace list statement @@ -13652,7 +13633,7 @@ nil)) ((progn (goto-char req-pos) - (if (looking-at c-fun-name-substitute-key) + (if (looking-at c-requires-clause-key) (setq found-clause (c-forward-c++-requires-clause nil t)) (and (c-forward-concept-fragment) (setq found-clause (point)))) @@ -13863,18 +13844,84 @@ (looking-at c-pre-lambda-tokens-re))) (not (c-in-literal)))) +(defun c-looking-at-c++-lambda-expression (&optional lim) + ;; If point is at the [ opening a C++ lambda expressions's capture list, + ;; and the lambda expression is complete, return the position of the { which + ;; opens the body form, otherwise return nil. LIM is the limit for forward + ;; searching for the {. + (let ((here (point)) + (lim-or-max (or lim (point-max))) + got-params) + (when (and (c-looking-at-c++-lambda-capture-list) + (c-go-list-forward nil lim)) + (c-forward-syntactic-ws lim) + (when (c-forward-<>-arglist t) + (c-forward-syntactic-ws lim) + (when (looking-at c-requires-clause-key) + (c-forward-c++-requires-clause lim nil))) + (when (looking-at "\\(alignas\\)\\([^a-zA-Z0-9_$]\\|$\\)") + (c-forward-keyword-clause 1)) + (when (and (eq (char-after) ?\() + (c-go-list-forward nil lim)) + (setq got-params t) + (c-forward-syntactic-ws lim)) + (while (and c-lambda-spec-key (looking-at c-lambda-spec-key)) + (goto-char (match-end 1)) + (c-forward-syntactic-ws lim)) + (let (after-except-pos) + (while + (and (<= (point) lim-or-max) + (cond + ((save-excursion + (and (looking-at "throw\\([^a-zA-Z0-9_]\\|$\\)") + (progn (goto-char (match-beginning 1)) + (c-forward-syntactic-ws lim) + (eq (char-after) ?\()) + (c-go-list-forward nil lim) + (progn (c-forward-syntactic-ws lim) + (setq after-except-pos (point))))) + (goto-char after-except-pos) + (c-forward-syntactic-ws lim) + t) + ((looking-at c-paren-nontype-key) ; "noexcept" or "alignas" + (c-forward-keyword-clause 1)))))) + (and (<= (point) lim-or-max) + (looking-at c-haskell-op-re) + (goto-char (match-end 0)) + (progn (c-forward-syntactic-ws lim) + (c-forward-type t))) ; t is BRACE-BLOCK-TOO. + (and got-params + (<= (point) lim-or-max) + (looking-at c-requires-clause-key) + (c-forward-c++-requires-clause lim nil)) + (prog1 (and (<= (point) lim-or-max) + (eq (char-after) ?{) + (point)) + (goto-char here))))) + +(defun c-backward-over-lambda-expression (&optional lim) + ;; Point is at a {. Move back over the lambda expression this is a part of, + ;; stopping at the [ of the capture list, if this is the case, returning + ;; the position of that opening bracket. If we're not at such a list, leave + ;; point unchanged and return nil. + (let ((here (point))) + (c-syntactic-skip-backward "^;}]" lim t) + (if (and (eq (char-before) ?\]) + (c-go-list-backward nil lim) + (eq (c-looking-at-c++-lambda-expression (1+ here)) + here)) + (point) + (goto-char here) + nil))) + (defun c-c++-vsemi-p (&optional pos) ;; C++ Only - Is there a "virtual semicolon" at POS or point? ;; (See cc-defs.el for full details of "virtual semicolons".) ;; ;; This is true when point is at the last non syntactic WS position on the - ;; line, and either there is a "macro with semicolon" just before it (see - ;; `c-at-macro-vsemi-p') or there is a "requires" clause which ends there. - (let (res) - (cond - ((setq res (c-in-requires-or-at-end-of-clause pos)) - (and res (eq (cdr res) t))) - ((c-at-macro-vsemi-p))))) + ;; line, and there is a "macro with semicolon" just before it (see + ;; `c-at-macro-vsemi-p'). + (c-at-macro-vsemi-p pos)) (defun c-at-macro-vsemi-p (&optional pos) ;; Is there a "virtual semicolon" at POS or point? @@ -14857,7 +14904,6 @@ (progn (c-backward-syntactic-ws lim) (zerop (c-backward-token-2 nil nil lim))) (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_)) (setq placeholder (point)))) (goto-char placeholder) (back-to-indentation) @@ -15283,6 +15329,11 @@ ;; NOTE: The point is at the end of the previous token here. + ;; CASE 5U: We are just after a requires clause. + ((and (setq placeholder (c-in-requires-or-at-end-of-clause)) + (eq (cdr-safe placeholder) t)) + (c-add-syntax 'topmost-intro-cont (car placeholder))) + ;; CASE 5J: we are at the topmost level, make ;; sure we skip back past any access specifiers ((and @@ -15818,8 +15869,7 @@ (c-go-list-backward nil lim)) (progn (c-backward-syntactic-ws lim) (zerop (c-backward-token-2 nil nil lim))) - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_)))) + (looking-at c-fun-name-substitute-key))) (goto-char containing-sexp) (back-to-indentation) (c-add-stmt-syntax 'defun-close nil t lim paren-state)) @@ -15983,8 +16033,7 @@ (c-go-list-backward nil lim)) (progn (c-backward-syntactic-ws lim) (zerop (c-backward-token-2 nil nil lim))) - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_)))) + (looking-at c-fun-name-substitute-key))) (goto-char containing-sexp) (back-to-indentation) (c-add-syntax 'defun-block-intro (point))) diff -r 48ad661a2d7d cc-fonts.el --- a/cc-fonts.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-fonts.el Fri Sep 20 14:15:17 2024 +0000 @@ -1389,8 +1389,7 @@ (memq type '(c-decl-arg-start c-decl-type-start)))))))) ((and (zerop (c-backward-token-2)) - (looking-at c-fun-name-substitute-key) - (not (eq (char-after (match-end 0)) ?_)))))))))) + (looking-at c-fun-name-substitute-key))))))))) ;; Cache the result of this test for next time around. (c-put-char-property (1- match-pos) 'c-type 'c-decl-arg-start) (cons 'decl nil)) diff -r 48ad661a2d7d cc-langs.el --- a/cc-langs.el Mon Jul 22 12:09:34 2024 +0000 +++ b/cc-langs.el Fri Sep 20 14:15:17 2024 +0000 @@ -2639,6 +2639,19 @@ 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-lambda-spec-kwds + "Keywords which are specifiers of certain elements of a C++ lambda function. +This is only used in C++ Mode." + t nil + c++ '("mutable" "constexpr" "consteval" "static")) + +(c-lang-defconst c-lambda-spec-key + ;; A regular expression which matches a member of `c-lambda-spec-kwds', + ;; or nil. + t (if (c-lang-const c-lambda-spec-kwds) + (c-make-keywords-re t (c-lang-const c-lambda-spec-kwds)))) +(c-lang-defvar c-lambda-spec-key (c-lang-const c-lambda-spec-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 @@ -2657,20 +2670,33 @@ (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." +expression. An arglist may or may not follow such a keyword. +Not to be confused with `c-requires-clause-kwds'." t nil c++ '("requires")) (c-lang-defconst c-fun-name-substitute-key ;; An unadorned regular expression which matches any member of ;; `c-fun-name-substitute-kwds'. - t (c-make-keywords-re 'appendable (c-lang-const c-fun-name-substitute-kwds))) + t (c-make-keywords-re t (c-lang-const c-fun-name-substitute-kwds))) ;; We use 'appendable, so that we get "\\>" on the regexp, but without a further ;; character, which would mess up backward regexp search from just after the ;; keyword. If only XEmacs had \\_>. ;-( (c-lang-defvar c-fun-name-substitute-key (c-lang-const c-fun-name-substitute-key)) +(c-lang-defconst c-requires-clause-kwds + "Keywords which introduce a C++ requires clause, or something analogous. +This should not be confused with `c-fun-name-substitute-kwds'." + t nil + c++ '("requires")) + +(c-lang-defconst c-requires-clause-key + ;; A regexp matching any member of `c-requires-clause-kwds'. + t (c-make-keywords-re t (c-lang-const c-requires-clause-kwds))) +;; See `c-fun-name-substitute-key' for the justification of appendable. +(c-lang-defvar c-requires-clause-key (c-lang-const c-requires-clause-key)) + (c-lang-defconst c-modifier-kwds "Keywords that can prefix normal declarations of identifiers \(and typically act as flags). Things like argument declarations > Seems to be working quite well in some contrived tests and a few > fragments of realistic looking code. > Thank you! > Have a lovely evening. > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Sat, 21 Sep 2024 19:34:02 GMT) Full text and rfc822 format available.Message #35 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sat, 21 Sep 2024 21:32:36 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Alan Mackenzie <acm <at> muc.de> writes: > Hello, Arsen. > > On Tue, Sep 10, 2024 at 01:02:50 +0200, Arsen Arsenović wrote: >> Hi Alan, > >> Alan Mackenzie <acm <at> muc.de> writes: > > [ .... ] > > I'm hoping I've now fixed this bug (see patch below). Thank you! > [ .... ] > >> >> I've tried it on a few examples I came up with, it seems to work well in >> >> 'real-life' code. > >> >> I've also constructed the following "stress test" which includes (almost >> >> - see below) all the productions that can exist in a lambda function, in >> >> the same context as the original bad example: > >> > Thanks, this was very useful. As an important question, can and alignas >> > specifier exist inside a lambda expression? If so, what does it mean? >> > My new patch below doesn't (yet) handle it. > >> It certainly can in the body. I presume you mean in the >> leader/declarator/whathaveyou however; in that case, yes, as part of the >> attribute-specifier-seq in lambda-expression and lambda-declarator, and >> its meaning depends on the position (see >> https://eel.is/c++draft/expr.prim.lambda#closure-6). > > Thanks, I've now found the place in the C++ syntax where alignas can go > in a lambda construct. It is the only specifier which may go in these > places, although any number of attributes are allowed. (As a matter of > interest, attributes in CC Mode are treated as syntactic whitespace.) > > [ .... ] > >> >> void >> >> def () >> >> { >> >> auto foo = [&, >> >> a, >> >> b = 123, >> >> c, >> >> ...d, >> >> ...e = 123 >> >> ] >> >> <typename T, >> >> typename S> >> >> //alignas (128) >> >> [[gnu::noreturn]] >> >> requires some_concept<T> >> >> (int a, >> >> int b, >> >> int c) >> >> consteval >> >> constexpr >> >> mutable >> >> static >> >> //noexcept(true) >> >> [[gnu::packed]] >> >> //alignas (128) >> >> //-> >> >> //int >> >> //requires (std::same_as <decltype (a), int>) >> >> { >> >> if constexpr (true) >> >> ; >> >> } >> >> } > > Question: Is that first (commented out) alignas correct where it is? My > reading of the version of the standard suggests that the alignas (128) > should come _after_ the requires some_concept<T>, not before it. I think so, through the 2nd lambda-expression derivation: lambda-introducer < template-parameter-list > requires-clause_opt attribute-specifier-seq_opt lambda-declarator compound-statement ... where attribute-specifier-seq is: attribute-specifier-seq_opt attribute-specifier ... and attribute-specifier is also possibly alignment-specifier. Maybe I am missing something, though? >> > Here's a modified version which is now fully handled (as far as it goes): > > And here's a further modified version, which my patch (below) now > handles: > > void > def () > { > auto foo = [&, > a, > b = 123, > c, > ...d, > ...e = 123 > ] > <typename T, > typename S> > //alignas (128) > [[gnu::noreturn]] > requires some_concept<T> > alignas (128) > (int a, > int b, > int c) > consteval > constexpr > mutable > static > noexcept(true) > [[gnu::packed]] > alignas (128) > -> int > requires (std::same_as <decltype (a), int>) > { > if constexpr (true) > ; > } > } > > [ .... ] > >> >> Hopefully these examples help. > >> > Very much so. > >> >> Please also note that in a few places (such as alignas, the noexcept >> >> specifier, RHS of assignments in the capture list) near-arbitrary >> >> expressions can appear, and that requires clause have special boolean >> >> expression syntax (see >> >> https://eel.is/c++draft/temp.pre#nt:requires-clause). > >> > I'm afraid I haven't looked at this, yet. > > I have now. :-) > > [ .... ] > >> > Here's an up to date version of the patch, which should work better than >> > the last version. Please do the usual with it, and let me know how it >> > goes. Thanks! > > And here's what I'm hoping will be the final version of the patch to fix > this bug. Again, could you try it out (on real code as well as test > code), and let me know how well it works. It should apply cleanly to the > Emacs master branch. Thanks! > > Even it the patch is good, I'm not quite ready to commit it yet, since I > need to amend the test suite somewhat. I will test it at a later time tonight or tomorrow. Sorry for the delay. Thank you once again for your efforts! Have a lovely evening! -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Wed, 25 Sep 2024 20:11:01 GMT) Full text and rfc822 format available.Message #38 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Wed, 25 Sep 2024 22:01:56 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Apologies for the delay. Alan Mackenzie <acm <at> muc.de> writes: > And here's what I'm hoping will be the final version of the patch to fix > this bug. Again, could you try it out (on real code as well as test > code), and let me know how well it works. It should apply cleanly to the > Emacs master branch. Thanks! I've tested on my real-life code now and it worked as intended. Thank you again! Have a lovely evening. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Thu, 26 Sep 2024 11:36:02 GMT) Full text and rfc822 format available.Message #41 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Thu, 26 Sep 2024 11:34:24 +0000
Hello, Arsen. On Sat, Sep 21, 2024 at 21:32:36 +0200, Arsen Arsenović wrote: > Hi Alan, > Alan Mackenzie <acm <at> muc.de> writes: > > On Tue, Sep 10, 2024 at 01:02:50 +0200, Arsen Arsenović wrote: > >> Alan Mackenzie <acm <at> muc.de> writes: [ .... ] > > Thanks, I've now found the place in the C++ syntax where alignas can go > > in a lambda construct. It is the only specifier which may go in these > > places, although any number of attributes are allowed. (As a matter of > > interest, attributes in CC Mode are treated as syntactic whitespace.) > > [ .... ] > >> >> void > >> >> def () > >> >> { > >> >> auto foo = [&, > >> >> a, > >> >> b = 123, > >> >> c, > >> >> ...d, > >> >> ...e = 123 > >> >> ] > >> >> <typename T, > >> >> typename S> > >> >> //alignas (128) > >> >> [[gnu::noreturn]] > >> >> requires some_concept<T> > >> >> (int a, > >> >> int b, > >> >> int c) > >> >> consteval > >> >> constexpr > >> >> mutable > >> >> static > >> >> //noexcept(true) > >> >> [[gnu::packed]] > >> >> //alignas (128) > >> >> //-> > >> >> //int > >> >> //requires (std::same_as <decltype (a), int>) > >> >> { > >> >> if constexpr (true) > >> >> ; > >> >> } > >> >> } > > Question: Is that first (commented out) alignas correct where it is? My > > reading of the version of the standard suggests that the alignas (128) > > should come _after_ the requires some_concept<T>, not before it. Sorry to keep going on about this, but .... > I think so, through the 2nd lambda-expression derivation: I'm interpreting this as being the answer to "Is that first .... alignas correct?", not a comment on my second sentence "My reading of ...., not before it.". If I'm mistaken there, then my next paragraph is without effect, and I apologise. > lambda-introducer < template-parameter-list > requires-clause_opt > attribute-specifier-seq_opt lambda-declarator compound-statement > ... where attribute-specifier-seq is: > attribute-specifier-seq_opt attribute-specifier > ... and attribute-specifier is also possibly alignment-specifier. > Maybe I am missing something, though? ..... My reading of that lambda-expression derivation is that the "alignas (128)" is the atribute-specifier-seq_opt, and the "requires some_concept<T>" is the requires-clause_opt. Surely the first of these, the "alignas" needs to come after the second, the "requires"? Phew! [ .... ] > > Even it the patch is good, I'm not quite ready to commit it yet, since I > > need to amend the test suite somewhat. > I will test it at a later time tonight or tomorrow. Sorry for the > delay. I got your other post where you reported on that the tests you did were satisfactory. Thanks for that! I'm hoping to get the patch committed before the weekend. > Thank you once again for your efforts! > Have a lovely evening! > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Thu, 26 Sep 2024 20:16:02 GMT) Full text and rfc822 format available.Message #44 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Thu, 26 Sep 2024 22:14:28 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Alan Mackenzie <acm <at> muc.de> writes: > Sorry to keep going on about this, but .... > >> I think so, through the 2nd lambda-expression derivation: > > I'm interpreting this as being the answer to "Is that first .... alignas > correct?", not a comment on my second sentence "My reading of ...., not > before it.". If I'm mistaken there, then my next paragraph is without > effect, and I apologise. Ah! Please forgive me, I misread the standard. >> lambda-introducer < template-parameter-list > requires-clause_opt >> attribute-specifier-seq_opt lambda-declarator compound-statement > >> ... where attribute-specifier-seq is: > >> attribute-specifier-seq_opt attribute-specifier > >> ... and attribute-specifier is also possibly alignment-specifier. > >> Maybe I am missing something, though? > > ..... My reading of that lambda-expression derivation is that the "alignas > (128)" is the atribute-specifier-seq_opt, and the "requires > some_concept<T>" is the requires-clause_opt. Surely the first of these, > the "alignas" needs to come after the second, the "requires"? > > Phew! Yes! You're completely right, I accidentally "combined" the two cases of lambda-expression while writing that example. Forgive me. (serves me right for bug-reporting late.. ;-) ) > [ .... ] > >> > Even it the patch is good, I'm not quite ready to commit it yet, since I >> > need to amend the test suite somewhat. > >> I will test it at a later time tonight or tomorrow. Sorry for the >> delay. > > I got your other post where you reported on that the tests you did were > satisfactory. Thanks for that! > > I'm hoping to get the patch committed before the weekend. Yay! :-) Thank you very much! Have a lovely night. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Fri, 27 Sep 2024 14:21:02 GMT) Full text and rfc822 format available.Message #47 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Fri, 27 Sep 2024 14:13:14 +0000
Hello, Arsen. On Thu, Sep 26, 2024 at 22:14:28 +0200, Arsen Arsenović wrote: > Hi Alan, [ .... ] > > ..... My reading of that lambda-expression derivation is that the > > "alignas (128)" is the atribute-specifier-seq_opt, and the "requires > > some_concept<T>" is the requires-clause_opt. Surely the first of > > these, the "alignas" needs to come after the second, the "requires"? > > Phew! > Yes! You're completely right, I accidentally "combined" the two cases > of lambda-expression while writing that example. Forgive me. > (serves me right for bug-reporting late.. ;-) ) I'm glad we agree on this point. :-) [ .... ] > > I'm hoping to get the patch committed before the weekend. > Yay! :-) I've now committed a slightly different patch, after noticing and fixing another bug. The bug was that in code like 1 template <typename T> 2 requires requires (T x) 3 { x + x; } 4 T add(T a, T b) 5 { 6 return a + b; 7 } , L4, a topmost-intro-cont used to be anchored on the previous topmost-intro-cont line, L2, whereas it should be anchored on the topmost-intro, L1. I've now corrected this. I didn't send you yet another patch with request for testing, because I've done that rather a lot of late, and I'm confident about the patch. I haven't yet closed the bug, just in case you wanted to do some final, final testing on the committed version. Please let me know what you feel about that, so that I can finally close this bug. Just as a matter of interest, one or two other bugs reared their heads, but I'm fixing these separately in the next few days. > Thank you very much! Thank you again. > Have a lovely night. > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Fri, 27 Sep 2024 21:57:02 GMT) Full text and rfc822 format available.Message #50 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Fri, 27 Sep 2024 23:54:57 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Alan Mackenzie <acm <at> muc.de> writes: > I've now committed a slightly different patch, after noticing and fixing > another bug. > > The bug was that in code like > > 1 template <typename T> > 2 requires requires (T x) > 3 { x + x; } > 4 T add(T a, T b) > 5 { > 6 return a + b; > 7 } > > , L4, a topmost-intro-cont used to be anchored on the previous > topmost-intro-cont line, L2, whereas it should be anchored on the > topmost-intro, L1. > > I've now corrected this. Ah! Good catch. > I didn't send you yet another patch with request for testing, because > I've done that rather a lot of late, and I'm confident about the patch. > > I haven't yet closed the bug, just in case you wanted to do some final, > final testing on the committed version. Please let me know what you feel > about that, so that I can finally close this bug. I'm not sure this is a bug but it is something I noticed by accident: (std::visit ([] <typename T> (const T& data) { })); ... the { is not an inline-open, but an arglist-cont-nonempty (though, to be clear, the body is indented okay; I've elided it from the example above for that reason). Same goes for the (const T& data), which doesn't get indented the same as a 'standalone' lambda, but it does indent the same as one saved into a variable: [] () { }; auto x = [] () { }; auto x = ([] () { }); I do not know whether this is correct. But thank you again for working on this! Have a lovely day. -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Fri, 27 Sep 2024 22:39:02 GMT) Full text and rfc822 format available.Message #53 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Fri, 27 Sep 2024 22:38:07 +0000
Hello, Arsen. On Fri, Sep 27, 2024 at 23:54:57 +0200, Arsen Arsenović wrote: > Hi Alan, > Alan Mackenzie <acm <at> muc.de> writes: > > I've now committed a slightly different patch, after noticing and fixing > > another bug. > > The bug was that in code like > > 1 template <typename T> > > 2 requires requires (T x) > > 3 { x + x; } > > 4 T add(T a, T b) > > 5 { > > 6 return a + b; > > 7 } > > , L4, a topmost-intro-cont used to be anchored on the previous > > topmost-intro-cont line, L2, whereas it should be anchored on the > > topmost-intro, L1. > > I've now corrected this. > Ah! Good catch. > > I didn't send you yet another patch with request for testing, because > > I've done that rather a lot of late, and I'm confident about the patch. > > I haven't yet closed the bug, just in case you wanted to do some final, > > final testing on the committed version. Please let me know what you feel > > about that, so that I can finally close this bug. So, can I close bug#72695 now? > I'm not sure this is a bug but it is something I noticed by accident: > (std::visit > ([] <typename T> > (const T& data) > { > })); > ... the { is not an inline-open, but an arglist-cont-nonempty (though, > to be clear, the body is indented okay; I've elided it from the example > above for that reason). That is a bug, yes, but a different bug. Could I ask you to raise a new Emacs bug report for it, please? I think it will require some effort to solve, since it involves "function type" constructs inside parentheses, which I think was something new to CC Mode with C++20 (but I'm not sure). Rather than just relying on the context of the outer (, c-guess-basic-syntax will have to examine the contents of (...) to get better analysis. > Same goes for the (const T& data), which doesn't get indented the same > as a 'standalone' lambda, but it does indent the same as one saved into > a variable: > [] > () > { > }; > auto x = > [] > () > { > }; > auto x = > ([] > () > { > }); > I do not know whether this is correct. Neither do I, this late at night. ;-) I'll have a look at all these examples tomorrow. > But thank you again for working on this! A pleasure! > Have a lovely day. > -- > Arsen Arsenović -- Alan Mackenzie (Nuremberg, Germany).
bug-cc-mode <at> gnu.org
:bug#72695
; Package cc-mode
.
(Fri, 27 Sep 2024 23:27:02 GMT) Full text and rfc822 format available.Message #56 received at 72695 <at> debbugs.gnu.org (full text, mbox):
From: Arsen Arsenović <arsen <at> aarsen.me> To: Alan Mackenzie <acm <at> muc.de> Cc: 72695 <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sat, 28 Sep 2024 01:24:52 +0200
[Message part 1 (text/plain, inline)]
Hi Alan, Alan Mackenzie <acm <at> muc.de> writes: > So, can I close bug#72695 now? Ah, excuse me. Yes, I do believe so. >> I'm not sure this is a bug but it is something I noticed by accident: > >> (std::visit >> ([] <typename T> >> (const T& data) >> { >> })); > >> ... the { is not an inline-open, but an arglist-cont-nonempty (though, >> to be clear, the body is indented okay; I've elided it from the example >> above for that reason). > > That is a bug, yes, but a different bug. Could I ask you to raise a new > Emacs bug report for it, please? I can do so tomorrow - getting drowsy at this point. > I think it will require some effort to solve, since it involves "function > type" constructs inside parentheses, which I think was something new to > CC Mode with C++20 (but I'm not sure). Rather than just relying on the > context of the outer (, c-guess-basic-syntax will have to examine the > contents of (...) to get better analysis. Hmm, I think this was here in 11 also (and perhaps before, due to the GNU statement expression extension). >> Same goes for the (const T& data), which doesn't get indented the same >> as a 'standalone' lambda, but it does indent the same as one saved into >> a variable: > >> [] >> () >> { >> }; >> auto x = >> [] >> () >> { >> }; >> auto x = >> ([] >> () >> { >> }); > >> I do not know whether this is correct. > > Neither do I, this late at night. ;-) I'll have a look at all these > examples tomorrow. :-) Thanks. I will file a bug with these tomorrow. Have a lovely evening! -- Arsen Arsenović
[signature.asc (application/pgp-signature, inline)]
Alan Mackenzie <acm <at> muc.de>
:Arsen Arsenović <arsen <at> aarsen.me>
:Message #61 received at 72695-done <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Arsen Arsenović <arsen <at> aarsen.me> Cc: acm <at> muc.de, 72695-done <at> debbugs.gnu.org Subject: Re: bug#72695: CC Mode 5.35.2 (C++//l); c++-mode misindents 'if constexpr' inside lambdas Date: Sat, 28 Sep 2024 10:43:55 +0000
Bug#72695 has been fixed. Closing. -- Alan Mackenzie (Nuremberg, Germany).
Debbugs Internal Request <help-debbugs <at> gnu.org>
to internal_control <at> debbugs.gnu.org
.
(Sat, 26 Oct 2024 11:24:15 GMT) Full text and rfc822 format available.
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.