[clang] [clang-format] Don't re-type finalized tokens before a closing paren (PR #213174)
Tyler Gibbs via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 30 16:57:29 PDT 2026
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 <tylergibbs049 at gmail.com>
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) {
More information about the cfe-commits
mailing list