[clang] [clang-format] Fix hang when object-like macro is called with arguments (PR #216472)
Aditya Goyal via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 15 02:06:17 PDT 2026
https://github.com/goyaladitya05 created https://github.com/llvm/llvm-project/pull/216472
### Cause
When a macro configured as object-like (e.g. `Macros: [CASE=case]`) is called with arguments (`CASE(1, "1")`), the parser rewinds the token stream to reparse the arguments as regular tokens. It then called `nextToken()`, which first pushes the current `FormatTok` onto the line being built, but at that point `FormatTok` is the token *past* the macro call's closing paren, left over from `parseMacroCall()`. The rewound stream serves that token again later, so it ends up in the unwrapped lines twice.
A duplicated token makes `AnnotatedLine`'s Next/Previous re-linking cyclic, and the first backward walk over the line (`TokenAnnotator::calculateUnbreakableTailLengths`) loops forever. With a single macro call the two copies land in different lines, which terminates but silently corrupts the layout; with two calls (or one top-level call) they land in the same line and clang-format hangs.
### Changes
This PR restores `FormatTok` with `Tokens->getNextToken()` instead, which yields the identical stream state without the bogus push, matching the direct restores in the neighboring fallback paths.
The expected output of `KeepParensWhenExpandingObjectLikeMacros` changed because it exercised the silent variant of the bug: the phantom `;` pushed onto the `void f() {` line is what kept the macro call from being merged with the function header.
Fixes #163338.
>From 7de53f28732e17ca4542633cbe5cb3baf5e2aaab Mon Sep 17 00:00:00 2001
From: Aditya Goyal <goyaladitya2403 at gmail.com>
Date: Sat, 15 Aug 2026 13:53:45 +0530
Subject: [PATCH] [clang-format] Fix hang when object-like macro is called with
arguments
---
clang/lib/Format/UnwrappedLineParser.cpp | 3 ++-
.../Format/FormatTestMacroExpansion.cpp | 18 ++++++++++++++++--
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 8e6f7e2f2ce07..4520e4cb52407 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -5110,7 +5110,8 @@ void UnwrappedLineParser::readToken(int LevelDifference) {
Args.reset();
UnexpandedLine->Tokens.resize(1);
Tokens->setPosition(Position);
- nextToken();
+ // Not nextToken(), which would push the stale FormatTok onto the line.
+ FormatTok = Tokens->getNextToken();
assert(!Args && Macros.objectLike(ID->TokenText));
}
if ((!Args && Macros.objectLike(ID->TokenText)) ||
diff --git a/clang/unittests/Format/FormatTestMacroExpansion.cpp b/clang/unittests/Format/FormatTestMacroExpansion.cpp
index d391fe3d715c3..8ff3282d31aee 100644
--- a/clang/unittests/Format/FormatTestMacroExpansion.cpp
+++ b/clang/unittests/Format/FormatTestMacroExpansion.cpp
@@ -231,8 +231,7 @@ a))",
TEST_F(FormatTestMacroExpansion, KeepParensWhenExpandingObjectLikeMacros) {
FormatStyle Style = getLLVMStyle();
Style.Macros.push_back("FN=class C { int f");
- verifyFormat("void f() {\n"
- " FN(a *b);\n"
+ verifyFormat("void f() { FN(a *b);\n"
" };\n"
"}",
Style);
@@ -301,6 +300,21 @@ TEST_F(FormatTestMacroExpansion, IndentChildrenWithinMacroCall) {
Style);
}
+TEST_F(FormatTestMacroExpansion, ObjectLikeMacroCalledWithArgsDoesNotHang) {
+ FormatStyle Style = getLLVMStyle();
+ Style.Macros.push_back("CASE=case");
+ verifyNoCrash("const char *fct(int wki) {\n"
+ " switch (wki) {\n"
+ " CASE(1, \"1\");\n"
+ " CASE(2, \"2\");\n"
+ " default:\n"
+ " return \"123\";\n"
+ " }\n"
+ "}",
+ Style);
+ verifyNoCrash("CASE(1, \"1\");", Style);
+}
+
} // namespace
} // namespace test
} // namespace format
More information about the cfe-commits
mailing list