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

Reply via email to