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/3] [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/3] [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/3] [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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
