diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 4c2bf55f6ebdd..782eb5602f886 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -741,6 +741,8 @@ features cannot lower the translation-unit ABI level; - Add `AfterRequiresExpression` sub-option of `BraceWrapping` to wrap the body of requires expressions. It is enabled by the `Allman`, `Whitesmiths`, and `GNU` styles of `BreakBeforeBraces`. +- Fixed incorrect indentation of consecutive template declarations with + requires clauses. (#GH219801) - `QualifierOrder` now supports `typedef`, `consteval`, `constinit`, `thread_local`, `extern`, `mutable`, `signed`, `unsigned`, `long`, `short`, diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 105c98e704c3b..6d41074c76839 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3259,14 +3259,17 @@ class ExpressionParser { void parse(int Precedence = 0) { // Skip 'return' and ObjC selector colons as they are not part of a binary // expression. - while (Current && (Current->is(tok::kw_return) || - (Current->is(tok::colon) && - Current->isOneOf(TT_ObjCMethodExpr, TT_DictLiteral)))) { + while (Current && Current != RequiresClauseLimit && + (Current->is(tok::kw_return) || + (Current->is(tok::colon) && + Current->isOneOf(TT_ObjCMethodExpr, TT_DictLiteral)))) { next(); } - if (!Current || Precedence > PrecedenceArrowAndPeriod) + if (!Current || Current == RequiresClauseLimit || + Precedence > PrecedenceArrowAndPeriod) { return; + } // Conditional expressions need to be parsed separately for proper nesting. if (Precedence == prec::Conditional) { @@ -3287,7 +3290,7 @@ class ExpressionParser { // The first name of the current type in a port list. FormatToken *VerilogFirstOfType = nullptr; - while (Current) { + while (Current && Current != RequiresClauseLimit) { // In Verilog ports in a module header that don't have a type take the // type of the previous one. For example, // module a(output b, @@ -3301,6 +3304,8 @@ class ExpressionParser { // Consume operators with higher precedence. parse(Precedence + 1); + if (!Current || Current == RequiresClauseLimit) + break; int CurrentPrecedence = getCurrentPrecedence(); if (CurrentPrecedence > prec::Conditional && @@ -3351,9 +3356,18 @@ class ExpressionParser { // Consume scopes: (), [], <> and {} // In addition to that we handle require clauses as scope, so that the // constraints in that are correctly indented. - if (Current->opensScope() || - Current->isOneOf(TT_RequiresClause, + if (Current->isOneOf(TT_RequiresClause, TT_RequiresClauseInARequiresExpression)) { + const auto *End = Current; + while (End && !End->ClosesRequiresClause) + End = End->Next; + + const auto *PreviousLimit = RequiresClauseLimit; + RequiresClauseLimit = End ? End->getNextNonComment() : PreviousLimit; + next(); + parse(); + RequiresClauseLimit = PreviousLimit; + } else if (Current->opensScope()) { // In fragment of a JavaScript template string can look like '}..${' and // thus close a scope and open a new one at the same time. while (Current && (!Current->closesScope() || Current->opensScope())) { @@ -3657,6 +3671,8 @@ class ExpressionParser { const AdditionalKeywords &Keywords; const AnnotatedLine &Line; FormatToken *Current; + // The first non-comment token after the requires clause being parsed. + const FormatToken *RequiresClauseLimit = nullptr; }; } // end anonymous namespace diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 5aed37aa56d52..2145bcc0edeee 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -24179,6 +24179,13 @@ TEST_F(FormatTest, RequiresClausesPositions) { // when the default was REI_Keyword. Style.RequiresExpressionIndentation = FormatStyle::REI_Keyword; + verifyFormat("template \n" + " requires(N > 2) && (C > 0)\n" + "template \n" + " requires(sizeof(U) > 0)\n" + "void S::f(U) {}", + Style); + verifyFormat("template \n" " requires(Foo && std::trait)\n" "struct Bar;", diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index b71147aaf1bc2..5dca71507a6e5 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1949,6 +1949,20 @@ TEST_F(TokenAnnotatorTest, RequiresDoesNotChangeParsingOfTheRest) { RequiresTokenCount = 4; PrefixTokenCount = 5; TestRequires(__LINE__); + + BaseCode = "template\n" + "template\n" + " requires Bar\n" + "void S::f(U) {}"; + ConstrainedCode = "template\n" + " requires Foo && Baz\n" + "template\n" + " requires Bar\n" + "void S::f(U) {}"; + BaseTokenCount = 28; + RequiresTokenCount = 10; + PrefixTokenCount = 5; + TestRequires(__LINE__); } TEST_F(TokenAnnotatorTest, UnderstandsAsm) {