[clang] [clang-format] Keep compound literals stable in macro bodies (PR #173771)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Dec 28 05:51:03 PST 2025
https://github.com/Lane0218 updated https://github.com/llvm/llvm-project/pull/173771
>From 8dce6c2109e79f002e4becd9276e34f1de51d47e Mon Sep 17 00:00:00 2001
From: Lane0218 <laneljc at qq.com>
Date: Sun, 28 Dec 2025 21:01:23 +0800
Subject: [PATCH] [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"
More information about the cfe-commits
mailing list