Skip to content

Conversation

@a-tarasyuk
Copy link
Member

Fixes #167905


This patch addresses an issue where invalid nested name specifier sequences containing a single colon (a:c::) could be treated during recovery as valid scope specifiers, which in turn led to a crash

// If we get foo:bar, this is almost certainly a typo for foo::bar. Recover
// and emit a fixit hint for it.
if (Next.is(tok::colon) && !ColonIsSacred) {
if (Actions.IsInvalidUnlessNestedName(getCurScope(), SS, IdInfo,
EnteringContext) &&
// If the token after the colon isn't an identifier, it's still an
// error, but they probably meant something else strange so don't
// recover like this.
PP.LookAhead(1).is(tok::identifier)) {
Diag(Next, diag::err_unexpected_colon_in_nested_name_spec)
<< FixItHint::CreateReplacement(Next.getLocation(), "::");
// Recover as if the user wrote '::'.
Next.setKind(tok::coloncolon);
}
}

For malformed inputs like a:c::, the single colon recovery incorrectly triggers and produces an annot_cxxscope. When tentative parsing later runs

TPResult TPR = isCXXDeclarationSpecifier(
AllowImplicitTypename, TPResult::False, InvalidAsDeclaration);

the classifier returns Ambiguous, which doesn't stop parsing. The parser then enters the

SeenType |= isCXXDeclarationSpecifierAType();
if (TryConsumeDeclarationSpecifier() == TPResult::Error)
return TPResult::Error;

and consumes the invalid scope annotation, eventually reaching EOF and crashing.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 23, 2025

@llvm/pr-subscribers-clang

Author: Oleksandr T. (a-tarasyuk)

Changes

Fixes #167905


This patch addresses an issue where invalid nested name specifier sequences containing a single colon (a:c::) could be treated during recovery as valid scope specifiers, which in turn led to a crash

// If we get foo:bar, this is almost certainly a typo for foo::bar. Recover
// and emit a fixit hint for it.
if (Next.is(tok::colon) && !ColonIsSacred) {
if (Actions.IsInvalidUnlessNestedName(getCurScope(), SS, IdInfo,
EnteringContext) &&
// If the token after the colon isn't an identifier, it's still an
// error, but they probably meant something else strange so don't
// recover like this.
PP.LookAhead(1).is(tok::identifier)) {
Diag(Next, diag::err_unexpected_colon_in_nested_name_spec)
<< FixItHint::CreateReplacement(Next.getLocation(), "::");
// Recover as if the user wrote '::'.
Next.setKind(tok::coloncolon);
}
}

For malformed inputs like a:c::, the single colon recovery incorrectly triggers and produces an annot_cxxscope. When tentative parsing later runs

TPResult TPR = isCXXDeclarationSpecifier(
AllowImplicitTypename, TPResult::False, InvalidAsDeclaration);

the classifier returns Ambiguous, which doesn't stop parsing. The parser then enters the

SeenType |= isCXXDeclarationSpecifierAType();
if (TryConsumeDeclarationSpecifier() == TPResult::Error)
return TPResult::Error;

and consumes the invalid scope annotation, eventually reaching EOF and crashing.


Full diff: https://github.com/llvm/llvm-project/pull/169246.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/lib/Parse/ParseTentative.cpp (+2-1)
  • (added) clang/test/Parser/cxx-nested-name-spec.cpp (+10)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 63930f43c25e3..5b481dc9ae249 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -719,6 +719,8 @@ Crash and bug fixes
   ``[[assume(expr)]]`` attribute was enclosed in parentheses.  (#GH151529)
 - Fixed a crash when parsing ``#embed`` parameters with unmatched closing brackets. (#GH152829)
 - Fixed a crash when compiling ``__real__`` or ``__imag__`` unary operator on scalar value with type promotion. (#GH160583)
+- Fixed a crash when parsing invalid nested name specifier sequences
+  containing a single colon. (#GH167905)
 
 Improvements
 ^^^^^^^^^^^^
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 82f2294ff5bb7..75a582e70b244 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1063,7 +1063,8 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
       return TPResult::False;
     }
 
-    if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less)) {
+    if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less) &&
+        Next.isNot(tok::colon)) {
       // Determine whether this is a valid expression. If not, we will hit
       // a parse error one way or another. In that case, tell the caller that
       // this is ambiguous. Typo-correct to type and expression keywords and
diff --git a/clang/test/Parser/cxx-nested-name-spec.cpp b/clang/test/Parser/cxx-nested-name-spec.cpp
new file mode 100644
index 0000000000000..3a551a4f2221f
--- /dev/null
+++ b/clang/test/Parser/cxx-nested-name-spec.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+namespace a { b c ( a:c::
+// expected-error@-1 {{unknown type name 'b'}}
+// expected-error@-2 {{unexpected ':' in nested name specifier; did you mean '::'?}}
+// expected-error@-3 {{no member named 'c' in namespace 'a'}}
+// expected-error@-4 {{expected ';' after top level declarator}}
+// expected-note@-5 {{to match this '{'}}
+// expected-error@+1 {{expected unqualified-id}} \
+// expected-error@+1 {{expected '}'}}

@a-tarasyuk
Copy link
Member Author

@AaronBallman, whenever you have time, could you please review these changes? Thanks

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a suggestion but feel free to land without taking it because the changes do LG.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!

@a-tarasyuk a-tarasyuk merged commit c128fd9 into llvm:main Dec 3, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

segfault in clang::Lexer::Lex

5 participants