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