https://github.com/Lane0218 updated 
https://github.com/llvm/llvm-project/pull/173771

>From 8dce6c2109e79f002e4becd9276e34f1de51d47e Mon Sep 17 00:00:00 2001
From: Lane0218 <[email protected]>
Date: Sun, 28 Dec 2025 21:01:23 +0800
Subject: [PATCH 1/5] [clang-format] Keep compound literals stable in macro
 bodies

Fixes https://github.com/llvm/llvm-project/issues/173583.

Test: FormatTests
---
 clang/lib/Format/UnwrappedLineParser.cpp | 41 ++++++++++++++++++++++--
 clang/unittests/Format/FormatTest.cpp    | 20 ++++++++++++
 2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/UnwrappedLineParser.cpp 
b/clang/lib/Format/UnwrappedLineParser.cpp
index c1a9161b10720..2d1ddde2b438b 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -491,6 +491,31 @@ void UnwrappedLineParser::calculateBraceTypes(bool 
ExpectClassBody) {
   SmallVector<StackEntry, 8> LBraceStack;
   assert(Tok->is(tok::l_brace));
 
+  constexpr int MaxLookBack = 64;
+  const auto IsAddressOfParenExpression = [](const FormatToken *RightParen) {
+    if (!RightParen || RightParen->isNot(tok::r_paren))
+      return false;
+
+    int ParenDepth = 0;
+    const FormatToken *Current = RightParen;
+    const FormatToken *LeftParen = nullptr;
+    for (int I = 0; I < MaxLookBack && Current; ++I) {
+      if (Current->is(tok::r_paren)) {
+        ++ParenDepth;
+      } else if (Current->is(tok::l_paren)) {
+        --ParenDepth;
+        if (ParenDepth == 0) {
+          LeftParen = Current;
+          break;
+        }
+      }
+      Current = Current->Previous;
+    }
+
+    return LeftParen && LeftParen->Previous &&
+           LeftParen->Previous->is(tok::amp);
+  };
+
   do {
     auto *NextTok = Tokens->getNextNonComment();
 
@@ -528,7 +553,16 @@ void UnwrappedLineParser::calculateBraceTypes(bool 
ExpectClassBody) {
           Tok->setBlockKind(BK_Block);
         }
       } else {
-        Tok->setBlockKind(BK_Unknown);
+        // In macro bodies we try to keep compound literal expressions like
+        // `&(type){v}` on a single line. Without this, the '{' can be mistaken
+        // for a block/function body and clang-format will reflow the macro 
with
+        // backslashes and spaces (e.g. `&(type) { v }`).
+        if (IsCpp && Line->InMacroBody && PrevTok &&
+            IsAddressOfParenExpression(PrevTok)) {
+          Tok->setBlockKind(BK_BracedInit);
+        } else {
+          Tok->setBlockKind(BK_Unknown);
+        }
       }
       LBraceStack.push_back({Tok, PrevTok});
       break;
@@ -2563,7 +2597,10 @@ bool UnwrappedLineParser::parseBracedList(bool 
IsAngleBracket, bool IsEnum) {
       // lists (in so-called TypeMemberLists). Thus, the semicolon cannot be
       // used for error recovery if we have otherwise determined that this is
       // a braced list.
-      if (Style.isJavaScript()) {
+      // In macro bodies we can also see non-syntactic braced lists (e.g.
+      // compound literal expressions) where clang-format should still remain
+      // stable.
+      if (Style.isJavaScript() || (IsCpp && Line->InMacroBody)) {
         nextToken();
         break;
       }
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 3ee7ce38578aa..8e4dc74378691 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -5858,6 +5858,26 @@ TEST_F(FormatTest, RespectWhitespaceInMacroDefinitions) {
   verifyFormat("#define false((foo)0)", Style);
 }
 
+TEST_F(FormatTest, CompoundLiteralInMacroDefinition) {
+  // https://github.com/llvm/llvm-project/issues/173583
+  //
+  // A C compound literal `(type){...}` is not a function/block. When used in a
+  // macro definition, clang-format should not treat `&` as a function name and
+  // reformat it as if it were `&(type) { ... }`.
+  FormatStyle Style = getLLVMStyle();
+  Style.Language = FormatStyle::LK_Cpp;
+  Style.IndentWidth = 4;
+  Style.TabWidth = 4;
+  Style.UseTab = FormatStyle::UT_Never;
+  Style.AlignEscapedNewlines = FormatStyle::ENAS_LeftWithLastLine;
+  Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
+  Style.BreakBeforeBraces = FormatStyle::BS_Attach;
+
+  verifyNoChange("#define getAddr(v, type) &(type){v}", Style);
+  verifyNoChange("#define getAddr2(v, type) int &(type){v;}", Style);
+  verifyNoChange("#define ctos(c) (char[2]){c, '\\0'}", Style);
+}
+
 TEST_F(FormatTest, EmptyLinesInMacroDefinitions) {
   verifyFormat("#define A b;",
                "#define A \\\n"

>From 47988e008f88aff87df7155e5edcd90b4ce18b4d Mon Sep 17 00:00:00 2001
From: Lane0218 <[email protected]>
Date: Sun, 28 Dec 2025 22:16:02 +0800
Subject: [PATCH 2/5] [clang-format] Minimize style overrides in compound
 literal macro test

Test: FormatTests
---
 clang/unittests/Format/FormatTest.cpp | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 8e4dc74378691..fb5523dc6ce85 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -5865,13 +5865,6 @@ TEST_F(FormatTest, CompoundLiteralInMacroDefinition) {
   // macro definition, clang-format should not treat `&` as a function name and
   // reformat it as if it were `&(type) { ... }`.
   FormatStyle Style = getLLVMStyle();
-  Style.Language = FormatStyle::LK_Cpp;
-  Style.IndentWidth = 4;
-  Style.TabWidth = 4;
-  Style.UseTab = FormatStyle::UT_Never;
-  Style.AlignEscapedNewlines = FormatStyle::ENAS_LeftWithLastLine;
-  Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
-  Style.BreakBeforeBraces = FormatStyle::BS_Attach;
 
   verifyNoChange("#define getAddr(v, type) &(type){v}", Style);
   verifyNoChange("#define getAddr2(v, type) int &(type){v;}", Style);

>From 113856e112cd934ce412f5bbab0686b7507505f8 Mon Sep 17 00:00:00 2001
From: Lane0218 <[email protected]>
Date: Sun, 28 Dec 2025 22:22:47 +0800
Subject: [PATCH 3/5] [clang-format] Simplify preconditions for macro compound
 literals

NFC, addresses review feedback.

Test: FormatTests
---
 clang/lib/Format/UnwrappedLineParser.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Format/UnwrappedLineParser.cpp 
b/clang/lib/Format/UnwrappedLineParser.cpp
index 2d1ddde2b438b..a239511eabd89 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -493,8 +493,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool 
ExpectClassBody) {
 
   constexpr int MaxLookBack = 64;
   const auto IsAddressOfParenExpression = [](const FormatToken *RightParen) {
-    if (!RightParen || RightParen->isNot(tok::r_paren))
-      return false;
+    assert(RightParen && RightParen->is(tok::r_paren));
 
     int ParenDepth = 0;
     const FormatToken *Current = RightParen;
@@ -558,7 +557,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool 
ExpectClassBody) {
         // for a block/function body and clang-format will reflow the macro 
with
         // backslashes and spaces (e.g. `&(type) { v }`).
         if (IsCpp && Line->InMacroBody && PrevTok &&
-            IsAddressOfParenExpression(PrevTok)) {
+            PrevTok->is(tok::r_paren) && IsAddressOfParenExpression(PrevTok)) {
           Tok->setBlockKind(BK_BracedInit);
         } else {
           Tok->setBlockKind(BK_Unknown);

>From 41d1b7ab9ed3a6e1fe1548482ee79bf1caa0daa1 Mon Sep 17 00:00:00 2001
From: Lane0218 <[email protected]>
Date: Mon, 29 Dec 2025 09:56:37 +0800
Subject: [PATCH 4/5] [clang-format] Use verifyFormat for compound literal
 macro test

Drop the invalid getAddr2 macro case.

Test: FormatTests
---
 clang/unittests/Format/FormatTest.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index fb5523dc6ce85..92add80fb82ba 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -5864,11 +5864,8 @@ TEST_F(FormatTest, CompoundLiteralInMacroDefinition) {
   // A C compound literal `(type){...}` is not a function/block. When used in a
   // macro definition, clang-format should not treat `&` as a function name and
   // reformat it as if it were `&(type) { ... }`.
-  FormatStyle Style = getLLVMStyle();
-
-  verifyNoChange("#define getAddr(v, type) &(type){v}", Style);
-  verifyNoChange("#define getAddr2(v, type) int &(type){v;}", Style);
-  verifyNoChange("#define ctos(c) (char[2]){c, '\\0'}", Style);
+  verifyFormat("#define getAddr(v, type) &(type){v}");
+  verifyFormat("#define ctos(c) (char[2]){c, '\\0'}");
 }
 
 TEST_F(FormatTest, EmptyLinesInMacroDefinitions) {

>From 171035ff4946e36bcc4070d3aa12f31d9105e9cb Mon Sep 17 00:00:00 2001
From: Lane0218 <[email protected]>
Date: Mon, 29 Dec 2025 10:31:05 +0800
Subject: [PATCH 5/5] [clang-format] Classify compound literal braces in macros

Handle the parser path for &(type){...} by setting the following { to 
BK_BracedInit.

Test: FormatTests
---
 clang/lib/Format/UnwrappedLineParser.cpp      | 43 +++----------------
 clang/unittests/Format/TokenAnnotatorTest.cpp |  4 ++
 2 files changed, 9 insertions(+), 38 deletions(-)

diff --git a/clang/lib/Format/UnwrappedLineParser.cpp 
b/clang/lib/Format/UnwrappedLineParser.cpp
index a239511eabd89..775fb9b7b3ed5 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -491,30 +491,6 @@ void UnwrappedLineParser::calculateBraceTypes(bool 
ExpectClassBody) {
   SmallVector<StackEntry, 8> LBraceStack;
   assert(Tok->is(tok::l_brace));
 
-  constexpr int MaxLookBack = 64;
-  const auto IsAddressOfParenExpression = [](const FormatToken *RightParen) {
-    assert(RightParen && RightParen->is(tok::r_paren));
-
-    int ParenDepth = 0;
-    const FormatToken *Current = RightParen;
-    const FormatToken *LeftParen = nullptr;
-    for (int I = 0; I < MaxLookBack && Current; ++I) {
-      if (Current->is(tok::r_paren)) {
-        ++ParenDepth;
-      } else if (Current->is(tok::l_paren)) {
-        --ParenDepth;
-        if (ParenDepth == 0) {
-          LeftParen = Current;
-          break;
-        }
-      }
-      Current = Current->Previous;
-    }
-
-    return LeftParen && LeftParen->Previous &&
-           LeftParen->Previous->is(tok::amp);
-  };
-
   do {
     auto *NextTok = Tokens->getNextNonComment();
 
@@ -552,16 +528,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool 
ExpectClassBody) {
           Tok->setBlockKind(BK_Block);
         }
       } else {
-        // In macro bodies we try to keep compound literal expressions like
-        // `&(type){v}` on a single line. Without this, the '{' can be mistaken
-        // for a block/function body and clang-format will reflow the macro 
with
-        // backslashes and spaces (e.g. `&(type) { v }`).
-        if (IsCpp && Line->InMacroBody && PrevTok &&
-            PrevTok->is(tok::r_paren) && IsAddressOfParenExpression(PrevTok)) {
-          Tok->setBlockKind(BK_BracedInit);
-        } else {
-          Tok->setBlockKind(BK_Unknown);
-        }
+        Tok->setBlockKind(BK_Unknown);
       }
       LBraceStack.push_back({Tok, PrevTok});
       break;
@@ -2596,10 +2563,7 @@ bool UnwrappedLineParser::parseBracedList(bool 
IsAngleBracket, bool IsEnum) {
       // lists (in so-called TypeMemberLists). Thus, the semicolon cannot be
       // used for error recovery if we have otherwise determined that this is
       // a braced list.
-      // In macro bodies we can also see non-syntactic braced lists (e.g.
-      // compound literal expressions) where clang-format should still remain
-      // stable.
-      if (Style.isJavaScript() || (IsCpp && Line->InMacroBody)) {
+      if (Style.isJavaScript()) {
         nextToken();
         break;
       }
@@ -2700,6 +2664,9 @@ bool UnwrappedLineParser::parseParens(TokenType 
AmpAmpTokenType,
           RParen->setFinalizedType(TT_TypeDeclarationParen);
         } else if (Prev->is(tok::greater) && RParen->Previous == LParen) {
           Prev->setFinalizedType(TT_TemplateCloser);
+        } else if (FormatTok->is(tok::l_brace) && Prev->is(tok::amp) &&
+                   !Prev->Previous) {
+          FormatTok->setBlockKind(BK_BracedInit);
         } else if (OptionalParens()) {
           LParen->Optional = true;
           RParen->Optional = true;
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp 
b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 69c2c2f17b376..a04c6bea4d050 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -3943,6 +3943,10 @@ TEST_F(TokenAnnotatorTest, BraceKind) {
   // Not TT_FunctionDeclarationName.
   EXPECT_TOKEN(Tokens[6], tok::kw_operator, TT_Unknown);
   EXPECT_BRACE_KIND(Tokens[9], BK_BracedInit);
+
+  Tokens = annotate("&(type){v}");
+  ASSERT_EQ(Tokens.size(), 8u) << Tokens;
+  EXPECT_BRACE_KIND(Tokens[4], BK_BracedInit);
 }
 
 TEST_F(TokenAnnotatorTest, UnderstandsElaboratedTypeSpecifier) {

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

Reply via email to