https://github.com/tadeuszjt created 
https://github.com/llvm/llvm-project/pull/221209

**Problem**
`ParseOptionalCXXScopeSpecifier` is called with a `MayBePseudoDestructor` 
argument present, meaning it has to check for `~` symbols, but the token stream 
contains an `annot_cxxscope` token instead of regular tokens which was created 
by a different tentative branch. The code logic had simply not accounted for 
this possibility.

**Solution**
Added the missing logic to `ParseOptionalCXXScopeSpecifier` which is consistent 
with the other `~` checks in this function and created tests to verify the 
output. 

**Assumptions**
Unfortunately the code in this area is complex and has lots of different 
functionality built into it so it's hard to be sure about the solution but it 
based on the following assumptions. Luckily, there is only one case where 
`ParseOptionalCXXScopeSpecifier` is called with a `MayBePsuedoDestructor` 
argument, in `ParsePostfixExpressionSuffix`, so the added logic shouldn't 
affect anything else:

1.) That it is normal for a `annot_cxxscope` token to be left in the stream 
from a different path because it can be re-used.

2.) That the sequence of `annot_cxxscope, token(~), ...` is a valid 
early-exit-if-possible-destructor case.

Call stack which creates the `annot_cxxscope`:
```
ParseFunctionDefinition
...
              → ParseAssignmentExpression
                → ParseCastExpression
                  → ParseCastExpression
                    → ParseParenExpression
                      → isCXXTypeId
                        → TryParseDeclarator
                          → TryParseFunctionDeclarator
                            → isCXXTypeId
                              → isCXXDeclarationSpecifier
                                → TryAnnotateTypeOrScopeToken
                                  → TryAnnotateTypeOrScopeTokenAfterScopeSpec
                                    → setTypeAnnotation
                                      → Token::setAnnotationValue
                                        → AnnotateScopeToken
```

Call stack which fails on the `annot_cxxscope`:
```
ParseFunctionDefinition
...
              → ParseAssignmentExpression
                → ParseCastExpression
                  → ParseCastExpression
                    → ParseParenExpression
                      → ParseExpression
                        → ParseAssignmentExpression
                          → ParseCastExpression
                            → ParseCastExpression
                              → ParsePostfixExpressionSuffix
                                → ParseOptionalCXXScopeSpecifier
```

>From 27009f71b5876d5f9c8054102d64442cee69d1e0 Mon Sep 17 00:00:00 2001
From: Tadeusz Tomoszek <[email protected]>
Date: Thu, 3 Sep 2026 15:28:24 +0200
Subject: [PATCH] [Clang] Fixed Assertion Checking For Destructor On
 'annot_cxxscope' Token

---
 clang/lib/Parse/ParseExprCXX.cpp | 20 ++++++++++++--------
 clang/test/Parser/gh220186.cpp   | 14 ++++++++++++++
 2 files changed, 26 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/Parser/gh220186.cpp

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index b1216a07f7624..dbd1e89070939 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -112,9 +112,20 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
   assert(getLangOpts().CPlusPlus &&
          "Call sites of this function should be guarded by checking for C++");
 
+  // Has to happen before any "return false"s in this function.
+  bool CheckForDestructor = false;
+  if (MayBePseudoDestructor && *MayBePseudoDestructor) {
+    CheckForDestructor = true;
+    *MayBePseudoDestructor = false;
+  }
+
   if (Tok.is(tok::annot_cxxscope)) {
     assert(!LastII && "want last identifier but have already annotated scope");
-    assert(!MayBePseudoDestructor && "unexpected annot_cxxscope");
+    if (CheckForDestructor && Tok.is(tok::tilde)) {
+      *MayBePseudoDestructor = true;
+      return false;
+    }
+
     Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
                                                  Tok.getAnnotationRange(),
                                                  SS);
@@ -122,13 +133,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
     return false;
   }
 
-  // Has to happen before any "return false"s in this function.
-  bool CheckForDestructor = false;
-  if (MayBePseudoDestructor && *MayBePseudoDestructor) {
-    CheckForDestructor = true;
-    *MayBePseudoDestructor = false;
-  }
-
   if (LastII)
     *LastII = nullptr;
 
diff --git a/clang/test/Parser/gh220186.cpp b/clang/test/Parser/gh220186.cpp
new file mode 100644
index 0000000000000..d1d8827053337
--- /dev/null
+++ b/clang/test/Parser/gh220186.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+
+// GH220186: nested-name-specifier annotated during tentative parsing of an
+// ambiguous "(auto() -> ...)" construct was left as a stale annot_cxxscope
+// token for the real parse to re-encounter, crashing when re-checked for a
+// possible pseudo-destructor.
+void foo1() { (auto()->bar::); }
+// expected-error@-1 {{use of undeclared identifier 'bar'}}
+// expected-error@-2 {{initializer for functional-style cast to 'auto' is 
empty}}
+// expected-error@-3 {{expected unqualified-id}}
+
+void foo2() { (auto()->bar::~bar()); }
+// expected-error@-1 {{use of undeclared identifier 'bar'}}
+// expected-error@-2 {{initializer for functional-style cast to 'auto' is 
empty}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to