[clang] [clang-format] Keep compound literals stable in macro bodies (PR #173771)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Dec 28 06:44:51 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 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 <laneljc at qq.com>
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 <laneljc at qq.com>
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);
More information about the cfe-commits
mailing list