[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