[clang] [Clang] prevent crash on invalid nested name specifiers with a single colon (PR #169246)
Oleksandr T. via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 2 13:52:46 PST 2025
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/169246
>From 0645128caa40e835866720d7ea481be21e007cff Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sun, 23 Nov 2025 22:56:50 +0200
Subject: [PATCH 1/2] [Clang] prevent crash on invalid nested name specifiers
with a single colon
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/lib/Parse/ParseTentative.cpp | 3 ++-
clang/test/Parser/cxx-nested-name-spec.cpp | 10 ++++++++++
3 files changed, 14 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Parser/cxx-nested-name-spec.cpp
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 at -1 {{unknown type name 'b'}}
+// expected-error at -2 {{unexpected ':' in nested name specifier; did you mean '::'?}}
+// expected-error at -3 {{no member named 'c' in namespace 'a'}}
+// expected-error at -4 {{expected ';' after top level declarator}}
+// expected-note at -5 {{to match this '{'}}
+// expected-error at +1 {{expected unqualified-id}} \
+// expected-error at +1 {{expected '}'}}
>From 8de744bef76eaf97883225965d4f501cf8e33ac5 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 2 Dec 2025 23:52:32 +0200
Subject: [PATCH 2/2] add isNoneOf to simplify chained isNot checks
---
clang/include/clang/Lex/Token.h | 8 +++++++-
clang/lib/Parse/ParseTentative.cpp | 3 +--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index d9dc5a562d802..43091a6f3a8c6 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -100,13 +100,19 @@ class Token {
/// is/isNot - Predicates to check if this token is a specific kind, as in
/// "if (Tok.is(tok::l_brace)) {...}".
bool is(tok::TokenKind K) const { return Kind == K; }
- bool isNot(tok::TokenKind K) const { return Kind != K; }
template <typename... Ts> bool isOneOf(Ts... Ks) const {
static_assert(sizeof...(Ts) > 0,
"requires at least one tok::TokenKind specified");
return (is(Ks) || ...);
}
+ bool isNot(tok::TokenKind K) const { return Kind != K; }
+ template <typename... Ts> bool isNoneOf(Ts... Ks) const {
+ static_assert(sizeof...(Ts) > 0,
+ "requires at least one tok::TokenKind specified");
+ return (isNot(Ks) && ...);
+ }
+
/// Return true if this is a raw identifier (when lexing
/// in raw mode) or a non-keyword identifier (when lexing in non-raw mode).
bool isAnyIdentifier() const {
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 75a582e70b244..9622a00687ca5 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1063,8 +1063,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
return TPResult::False;
}
- if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less) &&
- Next.isNot(tok::colon)) {
+ if (Next.isNoneOf(tok::coloncolon, tok::less, 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
More information about the cfe-commits
mailing list