[clang] [clang-format] Fix hang when object-like macro is called with arguments (PR #216472)

via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 15 02:06:50 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-format

Author: Aditya Goyal (goyaladitya05)

<details>
<summary>Changes</summary>

### 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.


---
Full diff: https://github.com/llvm/llvm-project/pull/216472.diff


2 Files Affected:

- (modified) clang/lib/Format/UnwrappedLineParser.cpp (+2-1) 
- (modified) clang/unittests/Format/FormatTestMacroExpansion.cpp (+16-2) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/216472


More information about the cfe-commits mailing list