https://github.com/abdulm5 updated 
https://github.com/llvm/llvm-project/pull/220792

>From 4eb06a7acbcd37bea72f0d798303e9be5854063e Mon Sep 17 00:00:00 2001
From: abdulm6 <[email protected]>
Date: Wed, 2 Sep 2026 00:06:51 -0700
Subject: [PATCH 1/2] [clang-format] Fix formatting with consecutive requires
 clauses

---
 clang/docs/ReleaseNotes.md                    |  2 ++
 clang/lib/Format/TokenAnnotator.cpp           | 30 ++++++++++++++-----
 clang/unittests/Format/FormatTest.cpp         |  7 +++++
 clang/unittests/Format/TokenAnnotatorTest.cpp | 14 +++++++++
 4 files changed, 46 insertions(+), 7 deletions(-)

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 <int N, int C>\n"
+               "  requires(N > 2) && (C > 0)\n"
+               "template <typename U>\n"
+               "  requires(sizeof(U) > 0)\n"
+               "void S<N, C>::f(U) {}",
+               Style);
+
   verifyFormat("template <typename T>\n"
                "  requires(Foo<T> && std::trait<T>)\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<typename T>\n"
+             "template<typename U>\n"
+             "  requires Bar<U>\n"
+             "void S<T>::f(U) {}";
+  ConstrainedCode = "template<typename T>\n"
+                    "  requires Foo<T> && Baz<T>\n"
+                    "template<typename U>\n"
+                    "  requires Bar<U>\n"
+                    "void S<T>::f(U) {}";
+  BaseTokenCount = 28;
+  RequiresTokenCount = 10;
+  PrefixTokenCount = 5;
+  TestRequires(__LINE__);
 }
 
 TEST_F(TokenAnnotatorTest, UnderstandsAsm) {

>From 0cd1d34c8a2a3978f5773c8cd1c86407fea233a2 Mon Sep 17 00:00:00 2001
From: abdulm6 <[email protected]>
Date: Thu, 3 Sep 2026 21:54:39 -0700
Subject: [PATCH 2/2] [clang-format] Address requires-clause review feedback

---
 clang/lib/Format/TokenAnnotator.cpp   | 45 +++++++++++++++++----------
 clang/unittests/Format/FormatTest.cpp | 10 ++++++
 2 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/clang/lib/Format/TokenAnnotator.cpp 
b/clang/lib/Format/TokenAnnotator.cpp
index 6d41074c76839..7ee5fb8d9fd6e 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -3259,17 +3259,14 @@ 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 != RequiresClauseLimit &&
-           (Current->is(tok::kw_return) ||
-            (Current->is(tok::colon) &&
-             Current->isOneOf(TT_ObjCMethodExpr, TT_DictLiteral)))) {
+    while (Current && (Current->is(tok::kw_return) ||
+                       (Current->is(tok::colon) &&
+                        Current->isOneOf(TT_ObjCMethodExpr, TT_DictLiteral)))) 
{
       next();
     }
 
-    if (!Current || Current == RequiresClauseLimit ||
-        Precedence > PrecedenceArrowAndPeriod) {
+    if (!Current || Precedence > PrecedenceArrowAndPeriod)
       return;
-    }
 
     // Conditional expressions need to be parsed separately for proper nesting.
     if (Precedence == prec::Conditional) {
@@ -3290,7 +3287,7 @@ class ExpressionParser {
     // The first name of the current type in a port list.
     FormatToken *VerilogFirstOfType = nullptr;
 
-    while (Current && Current != RequiresClauseLimit) {
+    while (Current && Current != RequiresClauseEnd) {
       // 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,
@@ -3304,7 +3301,8 @@ class ExpressionParser {
 
       // Consume operators with higher precedence.
       parse(Precedence + 1);
-      if (!Current || Current == RequiresClauseLimit)
+      // The recursive call may have reached the end of a requires clause.
+      if (Current == RequiresClauseEnd)
         break;
 
       int CurrentPrecedence = getCurrentPrecedence();
@@ -3358,15 +3356,12 @@ class ExpressionParser {
       // constraints in that are correctly indented.
       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;
+        const auto *PreviousEnd = RequiresClauseEnd;
+        if (const auto *End = findRequiresClauseEnd(Current))
+          RequiresClauseEnd = End;
         next();
         parse();
-        RequiresClauseLimit = PreviousLimit;
+        RequiresClauseEnd = PreviousEnd;
       } 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.
@@ -3418,6 +3413,22 @@ class ExpressionParser {
   }
 
 private:
+  const FormatToken *findRequiresClauseEnd(const FormatToken *Requires) const {
+    unsigned NestedRequires = 0;
+    for (const auto *Token = Requires->Next; Token; Token = Token->Next) {
+      if (Token->is(TT_RequiresClause))
+        ++NestedRequires;
+      if (!Token->ClosesRequiresClause)
+        continue;
+      if (NestedRequires > 0) {
+        --NestedRequires;
+        continue;
+      }
+      return Token->getNextNonComment();
+    }
+    return nullptr;
+  }
+
   /// Gets the precedence (+1) of the given token for binary operators
   /// and other tokens that we treat like binary operators.
   int getCurrentPrecedence() {
@@ -3672,7 +3683,7 @@ class ExpressionParser {
   const AnnotatedLine &Line;
   FormatToken *Current;
   // The first non-comment token after the requires clause being parsed.
-  const FormatToken *RequiresClauseLimit = nullptr;
+  const FormatToken *RequiresClauseEnd = nullptr;
 };
 
 } // end anonymous namespace
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 2145bcc0edeee..6675c55ecd603 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24186,6 +24186,16 @@ TEST_F(FormatTest, RequiresClausesPositions) {
                "void S<N, C>::f(U) {}",
                Style);
 
+  verifyFormat("template <typename T>\n"
+               "  requires([]<typename V>\n"
+               "             requires Foo<V>\n"
+               "           {}()) &&\n"
+               "          Bar<T>\n"
+               "template <typename U>\n"
+               "  requires Baz<U>\n"
+               "void S<T>::f(U) {}",
+               Style);
+
   verifyFormat("template <typename T>\n"
                "  requires(Foo<T> && std::trait<T>)\n"
                "struct Bar;",

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to