From: Haojian Wu Date: Tue, 26 Jul 2022 20:27:09 +0000 (+0200) Subject: [pseudo] Eliminate the false `::` nested-name-specifier ambiguity X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6f6c40a875c84443f255f3a6b4efc0bc0f2fb67a;p=platform%2Fupstream%2Fllvm.git [pseudo] Eliminate the false `::` nested-name-specifier ambiguity The solution is to favor the longest possible nest-name-specifier, and drop other alternatives by using the guard, per per C++ [basic.lookup.qual.general]. Motivated cases: ``` Foo::Foo() {}; // the constructor can be parsed as: // - Foo ::Foo(); // where the first Foo is return-type, and ::Foo is the function declarator // + Foo::Foo(); // where Foo::Foo is the function declarator ``` ``` void test() { // a very slow parsing case when there are many qualifers! X::Y::Z; // The statement can be parsed as: // - X ::Y::Z; // ::Y::Z is the declarator // - X::Y ::Z; // ::Z is the declarator // + X::Y::Z; // a declaration without declarator (X::Y::Z is decl-specifier-seq) // + X::Y::Z; // a qualifed-id expression } ``` Differential Revision: https://reviews.llvm.org/D130511 --- diff --git a/clang-tools-extra/pseudo/include/clang-pseudo/Token.h b/clang-tools-extra/pseudo/include/clang-pseudo/Token.h index e4a8659..22b72c7 100644 --- a/clang-tools-extra/pseudo/include/clang-pseudo/Token.h +++ b/clang-tools-extra/pseudo/include/clang-pseudo/Token.h @@ -90,6 +90,11 @@ struct Token { while (T->Kind == tok::comment); return *T; } + /// Returns the previous token in the stream. this may not be a sentinel. + const Token &prev() const { + assert(Kind != tok::eof); + return *(this - 1); + } /// Returns the bracket paired with this one, if any. const Token *pair() const { return Pair == 0 ? nullptr : this + Pair; } diff --git a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp index a0a252c..668c4d1 100644 --- a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp +++ b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp @@ -312,6 +312,14 @@ llvm::DenseMap buildGuards() { IF__CONSTEXPR__L_PAREN__init_statement__condition__R_PAREN__statement, guardNextTokenNotElse}, + // Implement C++ [basic.lookup.qual.general]: + // If a name, template-id, or decltype-specifier is followed by a + // ​::​, it shall designate a namespace, class, enumeration, or + // dependent type, and the ​::​ is never interpreted as a complete + // nested-name-specifier. + {rule::nested_name_specifier::COLONCOLON, + TOKEN_GUARD(coloncolon, Tok.prev().Kind != tok::identifier)}, + // The grammar distinguishes (only) user-defined vs plain string literals, // where the clang lexer distinguishes (only) encoding types. {rule::user_defined_string_literal_chunk::STRING_LITERAL, diff --git a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf index 8dfab8b..e1667fe 100644 --- a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf +++ b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf @@ -68,7 +68,7 @@ unqualified-id := ~ type-name unqualified-id := ~ decltype-specifier unqualified-id := template-id qualified-id := nested-name-specifier TEMPLATE_opt unqualified-id -nested-name-specifier := :: +nested-name-specifier := :: [guard] nested-name-specifier := type-name :: nested-name-specifier := namespace-name :: nested-name-specifier := decltype-specifier :: diff --git a/clang-tools-extra/pseudo/test/cxx/nested-name-specifier.cpp b/clang-tools-extra/pseudo/test/cxx/nested-name-specifier.cpp new file mode 100644 index 0000000..41d0fa1 --- /dev/null +++ b/clang-tools-extra/pseudo/test/cxx/nested-name-specifier.cpp @@ -0,0 +1,28 @@ +// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s + +// Verify that we don't form a complete `::` nested-name-specifier if there is +// an identifier preceding it. +Foo::Foo() {} // No "Foo ::Foo()" false parse +// CHECK: ├─declaration-seq~function-definition := function-declarator function-body +// CHECK-NEXT: │ ├─function-declarator~noptr-declarator := noptr-declarator parameters-and-qualifiers + +int ::x; +// CHECK: declaration~simple-declaration := decl-specifier-seq init-declarator-list ; +// CHECK-NEXT: ├─decl-specifier-seq~INT + +void test() { + X::Y::Z; // No false qualified-declarator parses "X ::Y::Z" and "X::Y ::Z". +// CHECK: statement-seq~statement := +// CHECK: statement~expression-statement := expression ; +// CHECK: statement~simple-declaration := decl-specifier-seq ; +// CHECK-NOT: simple-declaration := decl-specifier-seq init-declarator-list ; + + // FIXME: eliminate the false `a ::c` declaration parse. + a::c; +// CHECK: statement := +// CHECK-NEXT: ├─statement~expression-statement := expression ; +// CHECK-NEXT: │ ├─expression~relational-expression := +// CHECK: └─statement~simple-declaration := +// CHECK-NEXT: ├─simple-declaration := decl-specifier-seq ; +// CHECK: └─simple-declaration := decl-specifier-seq init-declarator-list ; +}