https://github.com/tylergibbs1 created https://github.com/llvm/llvm-project/pull/213174
`determineStarAmpUsage()` finalizes the second `*` of a chain like `a * b * c` as `TT_BinaryOperator`, so that the whole chain keeps being treated as multiplication: https://github.com/llvm/llvm-project/blob/17088c9b104e/clang/lib/Format/TokenAnnotator.cpp#L3154-L3158 `modifyContext()` then walks back over the `*`/`&` tokens preceding `)`, `>` or `,` and unconditionally calls `setType(TT_PointerOrReference)` on them, which asserts when the type has already been finalized: ``` Assertion failed: ((!TypeIsFinalized || T == Type) && "Please use overwriteFixedType to change a fixed type."), function setType, file FormatToken.h ``` So formatting `(A*B*)` crashes. The Objective-C case reported in the issue, ```objc - (void)f:(NSError *__autoreleasing *)e; ``` is the same bug: `determineStarAmpUsage()` guards on `isObjCLifetimeQualifier()`, but `guessLanguage()` annotates a `.h` file as C++ first, where `__autoreleasing` is not recognised as a lifetime qualifier, so the guard does not apply. This patch skips finalized tokens in that loop, matching the guard already used for the equivalent loop a few lines above (added in 2d0b45bc0a7fcb29aa77242b9262eb7c9eba5333). Fixes #212870 🤖 Generated with [Claude Code](https://claude.com/claude-code) >From 3cafb89dc7728540dbea6fbb7281f87a51a74584 Mon Sep 17 00:00:00 2001 From: Tyler Gibbs <[email protected]> Date: Thu, 30 Jul 2026 17:49:06 -0500 Subject: [PATCH] [clang-format] Don't re-type finalized tokens before a closing paren determineStarAmpUsage() finalizes the second `*` of a chain like `a * b * c` as TT_BinaryOperator so the whole chain keeps being treated as multiplication. modifyContext() then walked back over the `*`/`&` tokens preceding `)`, `>` or `,` and unconditionally called setType(TT_PointerOrReference) on them, which asserts when the type has already been finalized. Formatting `(A*B*)`, or an Objective-C declaration such as `- (void)f:(NSError *__autoreleasing *)e;`, therefore hit "Please use overwriteFixedType to change a fixed type.". Skip finalized tokens in that loop, matching the guard already used for the equivalent loop a few lines above. Fixes #212870 Assisted-by: Claude Opus 5 (Claude Code) --- clang/lib/Format/TokenAnnotator.cpp | 3 ++- clang/unittests/Format/FormatTest.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 32ae8990f52c5..9cf894cb97039 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2336,7 +2336,8 @@ class AnnotatingParser { for (FormatToken *Previous = Current.Previous; Previous && Previous->isOneOf(tok::star, tok::amp); Previous = Previous->Previous) { - Previous->setType(TT_PointerOrReference); + if (!Previous->isTypeFinalized()) + Previous->setType(TT_PointerOrReference); } if (Line.MustBeDeclaration && Contexts.front().ContextType != Context::CtorInitializer) { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index b72a683ac1fff..22815d608bbcb 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22563,6 +22563,8 @@ TEST_F(FormatTest, DoNotCrashOnInvalidInput) { verifyNoCrash( "#xxxx??x<xxxxxxx||??x<xxxxxxx and xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); verifyNoCrash("a &alias & ="); + verifyNoCrash("(A*B*)"); + verifyNoCrash("- (void)f:(NSError *__autoreleasing *)e;"); } TEST_F(FormatTest, FormatsTableGenCode) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
