[clang] [clang-format] Allow breaks before comma-paste operators (PR #214990)
Matthias Görgens via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 8 10:03:00 PDT 2026
https://github.com/matthiasgoergens created https://github.com/llvm/llvm-project/pull/214990
`26ffc71afa7c` disallowed breaking on either side of `##` to keep pasted identifiers together, but the blanket rule also caught the GNU comma-swallowing idiom `call(arg, ##__VA_ARGS__)`, so a macro line containing it could never be wrapped and stayed over the column limit.
Keep forbidding breaks after `##`, and before `##` except when the previous token is a comma: the comma-`##` sequence swallows the comma rather than pasting tokens, and a backslash-newline between them leaves the token stream unchanged. The two tests cover final and non-final `##__VA_ARGS__` and re-format their own output, pinning idempotence.
Fixes #212835.
## Tool use
Per the [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html): AI
tools were involved throughout the preparation of this change. I am the author
and accountable for the contribution.
Assisted-by: OpenAI Codex
Assisted-by: Claude Code
Assisted-by: Kimi
Assisted-by: DeepSeek
>From ff300ec16f5bf53c39a7a2add6369298a4de8979 Mon Sep 17 00:00:00 2001
From: Matthias Goergens <matthias.goergens at gmail.com>
Date: Sun, 2 Aug 2026 23:28:20 +0800
Subject: [PATCH 1/2] [clang-format] Allow breaks before comma-paste operators
Keep ordinary token-paste sequences unbreakable, but allow a line break between a comma and the GNU variadic-macro comma-swallowing operator. This restores useful wrapping before ##__VA_ARGS__ without regressing pasted identifiers.
---
clang/lib/Format/TokenAnnotator.cpp | 6 +++++-
clang/unittests/Format/FormatTest.cpp | 15 +++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index b6c33279b0aca..06b28b37582d1 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -6511,8 +6511,12 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
!(Right.Next &&
Right.Next->isOneOf(TT_FunctionDeclarationName, tok::kw_const)));
}
- if (Left.is(tok::hashhash) || Right.is(tok::hashhash))
+ if (Left.is(tok::hashhash))
return false;
+ // Keep pasted identifiers together, but allow a break before the GNU
+ // variadic-macro comma-swallowing extension: , ##__VA_ARGS__.
+ if (Right.is(tok::hashhash))
+ return Left.is(tok::comma);
if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName,
TT_ClassHeadName, TT_QtProperty, tok::kw_operator)) {
return true;
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index b72a683ac1fff..a06293690a6b2 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -5985,6 +5985,21 @@ TEST_F(FormatTest, HashInMacroDefinition) {
Style);
verifyFormat("#define A void # ## #", Style);
+ auto CommaPasteStyle = getLLVMStyleWithColumns(80);
+ CommaPasteStyle.IndentWidth = 4;
+ CommaPasteStyle.ContinuationIndentWidth = 8;
+ CommaPasteStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
+ verifyFormat(
+ "#define M(f, ...) "
+ " \\\n"
+ " auto f = call("
+ "firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, \\\n"
+ " ##__VA_ARGS__);",
+ "#define M(f, ...) \\\n"
+ " auto f = call("
+ "firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, "
+ "##__VA_ARGS__);",
+ CommaPasteStyle);
Style.ColumnLimit = 60;
Style.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign;
verifyFormat(
>From af989b0375b6e3fabba0e36237dcb01b66ae2968 Mon Sep 17 00:00:00 2001
From: Matthias Goergens <matthias.goergens at gmail.com>
Date: Mon, 3 Aug 2026 02:48:10 +0800
Subject: [PATCH 2/2] [clang-format] Test non-final comma-paste wrapping
---
clang/unittests/Format/FormatTest.cpp | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index a06293690a6b2..bce808a0857c9 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -6000,6 +6000,15 @@ TEST_F(FormatTest, HashInMacroDefinition) {
"firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, "
"##__VA_ARGS__);",
CommaPasteStyle);
+ CommaPasteStyle.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign;
+ verifyFormat(
+ "#define M(...) \\\n"
+ " call(firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, \\\n"
+ " ##__VA_ARGS__, extra)",
+ "#define M(...) \\\n"
+ " call(firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, "
+ "##__VA_ARGS__, extra)",
+ CommaPasteStyle);
Style.ColumnLimit = 60;
Style.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign;
verifyFormat(
More information about the cfe-commits
mailing list