[clang-tools-extra] 6f6c40a - [pseudo] Eliminate the false `::` nested-name-specifier ambiguity

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 28 02:01:23 PDT 2022


Author: Haojian Wu
Date: 2022-07-28T11:01:15+02:00
New Revision: 6f6c40a875c84443f255f3a6b4efc0bc0f2fb67a

URL: https://github.com/llvm/llvm-project/commit/6f6c40a875c84443f255f3a6b4efc0bc0f2fb67a
DIFF: https://github.com/llvm/llvm-project/commit/6f6c40a875c84443f255f3a6b4efc0bc0f2fb67a.diff

LOG: [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

Added: 
    clang-tools-extra/pseudo/test/cxx/nested-name-specifier.cpp

Modified: 
    clang-tools-extra/pseudo/include/clang-pseudo/Token.h
    clang-tools-extra/pseudo/lib/cxx/CXX.cpp
    clang-tools-extra/pseudo/lib/cxx/cxx.bnf

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/pseudo/include/clang-pseudo/Token.h b/clang-tools-extra/pseudo/include/clang-pseudo/Token.h
index e4a8659f739cf..22b72c71cbbab 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 a0a252c50b985..668c4d1bfcb6a 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<ExtensionID, RuleGuard> 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 8dfab8b9788d9..e1667fe8ea732 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 0000000000000..41d0fa13ff6dd
--- /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 := <ambiguous>
+// 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<b> ::c` declaration parse.
+  a<b>::c;
+// CHECK: statement := <ambiguous>
+// CHECK-NEXT: ├─statement~expression-statement := expression ;
+// CHECK-NEXT: │ ├─expression~relational-expression :=
+// CHECK:      └─statement~simple-declaration := <ambiguous>
+// CHECK-NEXT:   ├─simple-declaration := decl-specifier-seq ;
+// CHECK:        └─simple-declaration := decl-specifier-seq init-declarator-list ;
+}


        


More information about the cfe-commits mailing list