[clang] 8abb5fb - [SyntaxTree] Use simplified grammar rule for `NestedNameSpecifier` grammar nodes

Eduardo Caldas via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 7 11:05:58 PDT 2020


Author: Eduardo Caldas
Date: 2020-08-07T18:05:47Z
New Revision: 8abb5fb68f81b0e42d824bf080b1cef9a61559d6

URL: https://github.com/llvm/llvm-project/commit/8abb5fb68f81b0e42d824bf080b1cef9a61559d6
DIFF: https://github.com/llvm/llvm-project/commit/8abb5fb68f81b0e42d824bf080b1cef9a61559d6.diff

LOG: [SyntaxTree] Use simplified grammar rule for `NestedNameSpecifier` grammar nodes

This is our grammar rule for nested-name-specifiers:
globalbal-specifier:
  /*empty*/
simple-template-specifier:
  template_opt simple-template-id
name-specifier:
  global-specifier
  decltype-specifier
  identifier
  simple-template-specifier
nested-name-specifier:
  list(name-specifier, ::, non-empty, terminated)

It is a relaxed version of C++ [expr.prim.id] and quite simpler to map to our API.

TODO: refine name specifiers, `simple-template-name-specifier` and
decltype-name-specifier` are token soup for now.

Added: 
    

Modified: 
    clang/include/clang/Tooling/Syntax/Nodes.h
    clang/lib/Tooling/Syntax/BuildTree.cpp
    clang/lib/Tooling/Syntax/Nodes.cpp
    clang/unittests/Tooling/Syntax/TreeTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h
index a5972a394583..2273cb70307a 100644
--- a/clang/include/clang/Tooling/Syntax/Nodes.h
+++ b/clang/include/clang/Tooling/Syntax/Nodes.h
@@ -95,9 +95,13 @@ enum class NodeKind : uint16_t {
   TrailingReturnType,
   ParametersAndQualifiers,
   MemberPointer,
+  UnqualifiedId,
+  // Nested Name Specifiers.
   NestedNameSpecifier,
-  NameSpecifier,
-  UnqualifiedId
+  GlobalNameSpecifier,
+  DecltypeNameSpecifier,
+  IdentifierNameSpecifier,
+  SimpleTemplateNameSpecifier
 };
 /// For debugging purposes.
 raw_ostream &operator<<(raw_ostream &OS, NodeKind K);
@@ -138,6 +142,7 @@ enum class NodeRole : uint8_t {
   /// Tokens or Keywords
   ArrowToken,
   ExternKeyword,
+  TemplateKeyword,
   /// An inner statement for those that have only a single child of kind
   /// statement, e.g. loop body for while, for, etc; inner statement for case,
   /// default, etc.
@@ -167,6 +172,7 @@ enum class NodeRole : uint8_t {
   IdExpression_id,
   IdExpression_qualifier,
   NestedNameSpecifier_specifier,
+  NestedNameSpecifier_delimiter,
   ParenExpression_subExpression
 };
 /// For debugging purposes.
@@ -195,12 +201,60 @@ class Expression : public Tree {
 };
 
 /// A sequence of these specifiers make a `nested-name-specifier`.
-/// e.g. the `std::` or `vector<int>::` in `std::vector<int>::size`.
-class NameSpecifier final : public Tree {
+/// e.g. the `std` or `vector<int>` in `std::vector<int>::size`.
+class NameSpecifier : public Tree {
 public:
-  NameSpecifier() : Tree(NodeKind::NameSpecifier) {}
+  NameSpecifier(NodeKind K) : Tree(K) {}
   static bool classof(const Node *N) {
-    return N->kind() == NodeKind::NameSpecifier;
+    return N->kind() == NodeKind::GlobalNameSpecifier ||
+           N->kind() == NodeKind::DecltypeNameSpecifier ||
+           N->kind() == NodeKind::IdentifierNameSpecifier ||
+           N->kind() == NodeKind::SimpleTemplateNameSpecifier;
+  }
+};
+
+/// The global namespace name specifier, this specifier doesn't correspond to a
+/// token instead an absence of tokens before a `::` characterizes it, in
+/// `::std::vector<int>` it would be characterized by the absence of a token
+/// before the first `::`
+class GlobalNameSpecifier final : public NameSpecifier {
+public:
+  GlobalNameSpecifier() : NameSpecifier(NodeKind::GlobalNameSpecifier) {}
+  static bool classof(const Node *N) {
+    return N->kind() == NodeKind::GlobalNameSpecifier;
+  }
+};
+
+/// A name specifier holding a decltype, of the form: `decltype ( expression ) `
+/// e.g. the `decltype(s)` in `decltype(s)::size`.
+class DecltypeNameSpecifier final : public NameSpecifier {
+public:
+  DecltypeNameSpecifier() : NameSpecifier(NodeKind::DecltypeNameSpecifier) {}
+  static bool classof(const Node *N) {
+    return N->kind() == NodeKind::DecltypeNameSpecifier;
+  }
+};
+
+/// A identifier name specifier, of the form `identifier`
+/// e.g. the `std` in `std::vector<int>::size`.
+class IdentifierNameSpecifier final : public NameSpecifier {
+public:
+  IdentifierNameSpecifier()
+      : NameSpecifier(NodeKind::IdentifierNameSpecifier) {}
+  static bool classof(const Node *N) {
+    return N->kind() == NodeKind::IdentifierNameSpecifier;
+  }
+};
+
+/// A name specifier with a simple-template-id, of the form `template_opt
+/// identifier < template-args >` e.g. the `vector<int>` in
+/// `std::vector<int>::size`.
+class SimpleTemplateNameSpecifier final : public NameSpecifier {
+public:
+  SimpleTemplateNameSpecifier()
+      : NameSpecifier(NodeKind::SimpleTemplateNameSpecifier) {}
+  static bool classof(const Node *N) {
+    return N->kind() == NodeKind::SimpleTemplateNameSpecifier;
   }
 };
 
@@ -213,6 +267,7 @@ class NestedNameSpecifier final : public Tree {
     return N->kind() <= NodeKind::NestedNameSpecifier;
   }
   std::vector<NameSpecifier *> specifiers();
+  std::vector<Leaf *> delimiters();
 };
 
 /// Models an `unqualified-id`. C++ [expr.prim.id.unqual]
@@ -239,8 +294,7 @@ class IdExpression final : public Expression {
     return N->kind() == NodeKind::IdExpression;
   }
   NestedNameSpecifier *qualifier();
-  // TODO after expose `id-expression` from `DependentScopeDeclRefExpr`:
-  // Add accessor for `template_opt`.
+  Leaf *templateKeyword();
   UnqualifiedId *unqualifiedId();
 };
 

diff  --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp
index 29b1a9d7394f..21ac6b8bda62 100644
--- a/clang/lib/Tooling/Syntax/BuildTree.cpp
+++ b/clang/lib/Tooling/Syntax/BuildTree.cpp
@@ -286,6 +286,11 @@ class syntax::TreeBuilder {
     foldNode(Range, New, nullptr);
   }
 
+  void foldNode(ArrayRef<syntax::Token> Range, syntax::Tree *New,
+                NestedNameSpecifierLoc L) {
+    // FIXME: add mapping for NestedNameSpecifierLoc
+    foldNode(Range, New, nullptr);
+  }
   /// Notifies that we should not consume trailing semicolon when computing
   /// token range of \p D.
   void noticeDeclWithoutSemicolon(Decl *D);
@@ -690,21 +695,6 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
     return true;
   }
 
-  syntax::NestedNameSpecifier *
-  BuildNestedNameSpecifier(NestedNameSpecifierLoc QualifierLoc) {
-    if (!QualifierLoc)
-      return nullptr;
-    for (auto it = QualifierLoc; it; it = it.getPrefix()) {
-      auto *NS = new (allocator()) syntax::NameSpecifier;
-      Builder.foldNode(Builder.getRange(it.getLocalSourceRange()), NS, nullptr);
-      Builder.markChild(NS, syntax::NodeRole::NestedNameSpecifier_specifier);
-    }
-    auto *NNS = new (allocator()) syntax::NestedNameSpecifier;
-    Builder.foldNode(Builder.getRange(QualifierLoc.getSourceRange()), NNS,
-                     nullptr);
-    return NNS;
-  }
-
   bool TraverseUserDefinedLiteral(UserDefinedLiteral *S) {
     // The semantic AST node `UserDefinedLiteral` (UDL) may have one child node
     // referencing the location of the UDL suffix (`_w` in `1.2_w`). The
@@ -754,23 +744,118 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
     return true;
   }
 
+  syntax::NameSpecifier *BuildNameSpecifier(const NestedNameSpecifier &NNS) {
+    switch (NNS.getKind()) {
+    case NestedNameSpecifier::Global:
+      return new (allocator()) syntax::GlobalNameSpecifier;
+    case NestedNameSpecifier::Namespace:
+    case NestedNameSpecifier::NamespaceAlias:
+    case NestedNameSpecifier::Identifier:
+      return new (allocator()) syntax::IdentifierNameSpecifier;
+    case NestedNameSpecifier::TypeSpecWithTemplate:
+      return new (allocator()) syntax::SimpleTemplateNameSpecifier;
+    case NestedNameSpecifier::TypeSpec: {
+      const auto *NNSType = NNS.getAsType();
+      assert(NNSType);
+      if (isa<DecltypeType>(NNSType))
+        return new (allocator()) syntax::DecltypeNameSpecifier;
+      if (isa<TemplateSpecializationType, DependentTemplateSpecializationType>(
+              NNSType))
+        return new (allocator()) syntax::SimpleTemplateNameSpecifier;
+      return new (allocator()) syntax::IdentifierNameSpecifier;
+    }
+    case NestedNameSpecifier::Super:
+      // FIXME: Support Microsoft's __super
+      llvm::report_fatal_error("We don't yet support the __super specifier",
+                               true);
+    }
+  }
+
+  // FIXME: Fix `NestedNameSpecifierLoc::getLocalSourceRange` for the
+  // `DependentTemplateSpecializationType` case.
+  /// Given a nested-name-specifier return the range for the last name specifier
+  ///
+  /// e.g. `std::T::template X<U>::` => `template X<U>::`
+  SourceRange getLocalSourceRange(const NestedNameSpecifierLoc &NNSLoc) {
+    auto SR = NNSLoc.getLocalSourceRange();
+
+    // The method `NestedNameSpecifierLoc::getLocalSourceRange` *should* return
+    // the desired `SourceRange`, but there is a corner
+    // case. For a `DependentTemplateSpecializationType` this method returns its
+    // qualifiers as well, in other words in the example above this method
+    // returns `T::template X<U>::` instead of only `template X<U>::`
+    if (auto TL = NNSLoc.getTypeLoc()) {
+      if (auto DependentTL =
+              TL.getAs<DependentTemplateSpecializationTypeLoc>()) {
+        // The 'template' keyword is always present in dependent template
+        // specializations. Except in the case of incorrect code
+        // TODO: Treat the case of incorrect code.
+        SR.setBegin(DependentTL.getTemplateKeywordLoc());
+      }
+    }
+
+    return SR;
+  }
+
+  syntax::NestedNameSpecifier *
+  BuildNestedNameSpecifier(const NestedNameSpecifierLoc &QualifierLoc) {
+    if (!QualifierLoc)
+      return nullptr;
+    for (auto it = QualifierLoc; it; it = it.getPrefix()) {
+      assert(it.hasQualifier());
+      auto *NS = BuildNameSpecifier(*it.getNestedNameSpecifier());
+      assert(NS);
+      if (!isa<syntax::GlobalNameSpecifier>(NS))
+        Builder.foldNode(Builder.getRange(getLocalSourceRange(it)).drop_back(),
+                         NS, it);
+      Builder.markChild(NS, syntax::NodeRole::NestedNameSpecifier_specifier);
+      Builder.markChildToken(it.getEndLoc(),
+                             syntax::NodeRole::NestedNameSpecifier_delimiter);
+    }
+    auto *NNS = new (allocator()) syntax::NestedNameSpecifier;
+    Builder.foldNode(Builder.getRange(QualifierLoc.getSourceRange()), NNS,
+                     QualifierLoc);
+    return NNS;
+  }
+
   bool WalkUpFromDeclRefExpr(DeclRefExpr *S) {
-    if (auto *NNS = BuildNestedNameSpecifier(S->getQualifierLoc()))
-      Builder.markChild(NNS, syntax::NodeRole::IdExpression_qualifier);
+    auto *Qualifier = BuildNestedNameSpecifier(S->getQualifierLoc());
+    if (Qualifier)
+      Builder.markChild(Qualifier, syntax::NodeRole::IdExpression_qualifier);
+
+    auto TemplateKeywordLoc = S->getTemplateKeywordLoc();
+    if (TemplateKeywordLoc.isValid())
+      Builder.markChildToken(TemplateKeywordLoc,
+                             syntax::NodeRole::TemplateKeyword);
 
     auto *unqualifiedId = new (allocator()) syntax::UnqualifiedId;
-    // Get `UnqualifiedId` from `DeclRefExpr`.
-    // FIXME: Extract this logic so that it can be used by `MemberExpr`,
-    // and other semantic constructs, now it is tied to `DeclRefExpr`.
-    if (!S->hasExplicitTemplateArgs()) {
-      Builder.foldNode(Builder.getRange(S->getNameInfo().getSourceRange()),
-                       unqualifiedId, nullptr);
-    } else {
-      auto templateIdSourceRange =
-          SourceRange(S->getNameInfo().getBeginLoc(), S->getRAngleLoc());
-      Builder.foldNode(Builder.getRange(templateIdSourceRange), unqualifiedId,
-                       nullptr);
-    }
+
+    Builder.foldNode(Builder.getRange(S->getLocation(), S->getEndLoc()),
+                     unqualifiedId, nullptr);
+
+    Builder.markChild(unqualifiedId, syntax::NodeRole::IdExpression_id);
+
+    Builder.foldNode(Builder.getExprRange(S),
+                     new (allocator()) syntax::IdExpression, S);
+    return true;
+  }
+
+  // Same logic as DeclRefExpr.
+  bool WalkUpFromDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) {
+    auto *Qualifier = BuildNestedNameSpecifier(S->getQualifierLoc());
+    if (Qualifier)
+      Builder.markChild(Qualifier, syntax::NodeRole::IdExpression_qualifier);
+
+    auto TemplateKeywordLoc = S->getTemplateKeywordLoc();
+    if (TemplateKeywordLoc.isValid())
+      Builder.markChildToken(TemplateKeywordLoc,
+                             syntax::NodeRole::TemplateKeyword);
+
+    auto *unqualifiedId = new (allocator()) syntax::UnqualifiedId;
+
+    Builder.foldNode(Builder.getRange(S->getLocation(), S->getEndLoc()),
+                     unqualifiedId, nullptr);
+
     Builder.markChild(unqualifiedId, syntax::NodeRole::IdExpression_id);
 
     Builder.foldNode(Builder.getExprRange(S),

diff  --git a/clang/lib/Tooling/Syntax/Nodes.cpp b/clang/lib/Tooling/Syntax/Nodes.cpp
index 47af2c467b1d..b5a4c50b2875 100644
--- a/clang/lib/Tooling/Syntax/Nodes.cpp
+++ b/clang/lib/Tooling/Syntax/Nodes.cpp
@@ -116,8 +116,14 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeKind K) {
     return OS << "ParametersAndQualifiers";
   case NodeKind::MemberPointer:
     return OS << "MemberPointer";
-  case NodeKind::NameSpecifier:
-    return OS << "NameSpecifier";
+  case NodeKind::GlobalNameSpecifier:
+    return OS << "GlobalNameSpecifier";
+  case NodeKind::DecltypeNameSpecifier:
+    return OS << "DecltypeNameSpecifier";
+  case NodeKind::IdentifierNameSpecifier:
+    return OS << "IdentifierNameSpecifier";
+  case NodeKind::SimpleTemplateNameSpecifier:
+    return OS << "SimpleTemplateNameSpecifier";
   case NodeKind::NestedNameSpecifier:
     return OS << "NestedNameSpecifier";
   }
@@ -142,6 +148,8 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
     return OS << "ArrowToken";
   case syntax::NodeRole::ExternKeyword:
     return OS << "ExternKeyword";
+  case syntax::NodeRole::TemplateKeyword:
+    return OS << "TemplateKeyword";
   case syntax::NodeRole::BodyStatement:
     return OS << "BodyStatement";
   case syntax::NodeRole::CaseStatement_value:
@@ -190,12 +198,23 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
     return OS << "IdExpression_qualifier";
   case syntax::NodeRole::NestedNameSpecifier_specifier:
     return OS << "NestedNameSpecifier_specifier";
+  case syntax::NodeRole::NestedNameSpecifier_delimiter:
+    return OS << "NestedNameSpecifier_delimiter";
   case syntax::NodeRole::ParenExpression_subExpression:
     return OS << "ParenExpression_subExpression";
   }
   llvm_unreachable("invalid role");
 }
 
+std::vector<syntax::Leaf *> syntax::NestedNameSpecifier::delimiters() {
+  std::vector<syntax::Leaf *> Children;
+  for (auto *C = firstChild(); C; C = C->nextSibling()) {
+    assert(C->role() == syntax::NodeRole::NestedNameSpecifier_delimiter);
+    Children.push_back(llvm::cast<syntax::Leaf>(C));
+  }
+  return Children;
+}
+
 std::vector<syntax::NameSpecifier *> syntax::NestedNameSpecifier::specifiers() {
   std::vector<syntax::NameSpecifier *> Children;
   for (auto *C = firstChild(); C; C = C->nextSibling()) {
@@ -210,6 +229,11 @@ syntax::NestedNameSpecifier *syntax::IdExpression::qualifier() {
       findChild(syntax::NodeRole::IdExpression_qualifier));
 }
 
+syntax::Leaf *syntax::IdExpression::templateKeyword() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::TemplateKeyword));
+}
+
 syntax::UnqualifiedId *syntax::IdExpression::unqualifiedId() {
   return cast_or_null<syntax::UnqualifiedId>(
       findChild(syntax::NodeRole::IdExpression_id));

diff  --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp
index f818959019f5..fa5da1f86daa 100644
--- a/clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -873,24 +873,47 @@ TEST_P(SyntaxTreeTest, QualifiedId) {
   }
   EXPECT_TRUE(treeDumpEqual(
       R"cpp(
-namespace a {
+namespace n {
   struct S {
     template<typename T>
-    static T f(){}
+    struct ST {
+      static void f();
+    };
   };
 }
+template<typename T>
+struct ST {
+  struct S {
+    template<typename U>
+    static U f();
+  };
+};
 void test() {
-  ::              // global-namespace-specifier
-  a::             // namespace-specifier
-  S::             // type-name-specifier
+  ::                 // global-namespace-specifier
+  n::                // namespace-specifier
+  S::                // type-name-specifier
+  template ST<int>:: // type-template-instantiation-specifier
+  f();
+
+  n::                // namespace-specifier
+  S::                // type-name-specifier
+  ST<int>::          // type-template-instantiation-specifier
+  f();
+
+  ST<int>::         // type-name-specifier
+  S::               // type-name-specifier
   f<int>();
+
+  ST<int>::         // type-name-specifier
+  S::               // type-name-specifier
+  template f<int>();
 }
 )cpp",
       R"txt(
 *: TranslationUnit
 |-NamespaceDefinition
 | |-namespace
-| |-a
+| |-n
 | |-{
 | |-SimpleDeclaration
 | | |-struct
@@ -904,19 +927,58 @@ void test() {
 | | | | `-T
 | | | |->
 | | | `-SimpleDeclaration
-| | |   |-static
-| | |   |-T
-| | |   |-SimpleDeclarator
-| | |   | |-f
-| | |   | `-ParametersAndQualifiers
-| | |   |   |-(
-| | |   |   `-)
-| | |   `-CompoundStatement
-| | |     |-{
-| | |     `-}
+| | |   |-struct
+| | |   |-ST
+| | |   |-{
+| | |   |-SimpleDeclaration
+| | |   | |-static
+| | |   | |-void
+| | |   | |-SimpleDeclarator
+| | |   | | |-f
+| | |   | | `-ParametersAndQualifiers
+| | |   | |   |-(
+| | |   | |   `-)
+| | |   | `-;
+| | |   |-}
+| | |   `-;
 | | |-}
 | | `-;
 | `-}
+|-TemplateDeclaration
+| |-template
+| |-<
+| |-UnknownDeclaration
+| | |-typename
+| | `-T
+| |->
+| `-SimpleDeclaration
+|   |-struct
+|   |-ST
+|   |-{
+|   |-SimpleDeclaration
+|   | |-struct
+|   | |-S
+|   | |-{
+|   | |-TemplateDeclaration
+|   | | |-template
+|   | | |-<
+|   | | |-UnknownDeclaration
+|   | | | |-typename
+|   | | | `-U
+|   | | |->
+|   | | `-SimpleDeclaration
+|   | |   |-static
+|   | |   |-U
+|   | |   |-SimpleDeclarator
+|   | |   | |-f
+|   | |   | `-ParametersAndQualifiers
+|   | |   |   |-(
+|   | |   |   `-)
+|   | |   `-;
+|   | |-}
+|   | `-;
+|   |-}
+|   `-;
 `-SimpleDeclaration
   |-void
   |-SimpleDeclarator
@@ -930,14 +992,81 @@ void test() {
     | |-UnknownExpression
     | | |-IdExpression
     | | | |-NestedNameSpecifier
-    | | | | |-NameSpecifier
-    | | | | | `-::
-    | | | | |-NameSpecifier
-    | | | | | |-a
-    | | | | | `-::
-    | | | | `-NameSpecifier
-    | | | |   |-S
-    | | | |   `-::
+    | | | | |-::
+    | | | | |-IdentifierNameSpecifier
+    | | | | | `-n
+    | | | | |-::
+    | | | | |-IdentifierNameSpecifier
+    | | | | | `-S
+    | | | | |-::
+    | | | | |-SimpleTemplateNameSpecifier
+    | | | | | |-template
+    | | | | | |-ST
+    | | | | | |-<
+    | | | | | |-int
+    | | | | | `->
+    | | | | `-::
+    | | | `-UnqualifiedId
+    | | |   `-f
+    | | |-(
+    | | `-)
+    | `-;
+    |-ExpressionStatement
+    | |-UnknownExpression
+    | | |-IdExpression
+    | | | |-NestedNameSpecifier
+    | | | | |-IdentifierNameSpecifier
+    | | | | | `-n
+    | | | | |-::
+    | | | | |-IdentifierNameSpecifier
+    | | | | | `-S
+    | | | | |-::
+    | | | | |-SimpleTemplateNameSpecifier
+    | | | | | |-ST
+    | | | | | |-<
+    | | | | | |-int
+    | | | | | `->
+    | | | | `-::
+    | | | `-UnqualifiedId
+    | | |   `-f
+    | | |-(
+    | | `-)
+    | `-;
+    |-ExpressionStatement
+    | |-UnknownExpression
+    | | |-IdExpression
+    | | | |-NestedNameSpecifier
+    | | | | |-SimpleTemplateNameSpecifier
+    | | | | | |-ST
+    | | | | | |-<
+    | | | | | |-int
+    | | | | | `->
+    | | | | |-::
+    | | | | |-IdentifierNameSpecifier
+    | | | | | `-S
+    | | | | `-::
+    | | | `-UnqualifiedId
+    | | |   |-f
+    | | |   |-<
+    | | |   |-int
+    | | |   `->
+    | | |-(
+    | | `-)
+    | `-;
+    |-ExpressionStatement
+    | |-UnknownExpression
+    | | |-IdExpression
+    | | | |-NestedNameSpecifier
+    | | | | |-SimpleTemplateNameSpecifier
+    | | | | | |-ST
+    | | | | | |-<
+    | | | | | |-int
+    | | | | | `->
+    | | | | |-::
+    | | | | |-IdentifierNameSpecifier
+    | | | | | `-S
+    | | | | `-::
+    | | | |-template
     | | | `-UnqualifiedId
     | | |   |-f
     | | |   |-<
@@ -950,7 +1079,7 @@ void test() {
 )txt"));
 }
 
-TEST_P(SyntaxTreeTest, QualifiedIdWithTemplateKeyword) {
+TEST_P(SyntaxTreeTest, QualifiedIdWithDependentType) {
   if (!GetParam().isCXX()) {
     return;
   }
@@ -961,63 +1090,17 @@ TEST_P(SyntaxTreeTest, QualifiedIdWithTemplateKeyword) {
   }
   EXPECT_TRUE(treeDumpEqual(
       R"cpp(
-struct X {
-  template<int> static void f();
-  template<int>
-  struct Y {
-    static void f();
-  };
-};
-template<typename T> void test() {
-  // TODO: Expose `id-expression` from `DependentScopeDeclRefExpr`
-  T::template f<0>();     // nested-name-specifier template unqualified-id
-  T::template Y<0>::f();  // nested-name-specifier template :: unqualified-id
+template <typename T>
+void test() {
+  T::template U<int>::f();
+
+  T::U::f();
+
+  T::template f<0>();
 }
 )cpp",
       R"txt(
 *: TranslationUnit
-|-SimpleDeclaration
-| |-struct
-| |-X
-| |-{
-| |-TemplateDeclaration
-| | |-template
-| | |-<
-| | |-SimpleDeclaration
-| | | `-int
-| | |->
-| | `-SimpleDeclaration
-| |   |-static
-| |   |-void
-| |   |-SimpleDeclarator
-| |   | |-f
-| |   | `-ParametersAndQualifiers
-| |   |   |-(
-| |   |   `-)
-| |   `-;
-| |-TemplateDeclaration
-| | |-template
-| | |-<
-| | |-SimpleDeclaration
-| | | `-int
-| | |->
-| | `-SimpleDeclaration
-| |   |-struct
-| |   |-Y
-| |   |-{
-| |   |-SimpleDeclaration
-| |   | |-static
-| |   | |-void
-| |   | |-SimpleDeclarator
-| |   | | |-f
-| |   | | `-ParametersAndQualifiers
-| |   | |   |-(
-| |   | |   `-)
-| |   | `-;
-| |   |-}
-| |   `-;
-| |-}
-| `-;
 `-TemplateDeclaration
   |-template
   |-<
@@ -1036,31 +1119,52 @@ template<typename T> void test() {
       |-{
       |-ExpressionStatement
       | |-UnknownExpression
-      | | |-UnknownExpression
-      | | | |-T
-      | | | |-::
-      | | | |-template
-      | | | |-f
-      | | | |-<
-      | | | |-IntegerLiteralExpression
-      | | | | `-0
-      | | | `->
+      | | |-IdExpression
+      | | | |-NestedNameSpecifier
+      | | | | |-IdentifierNameSpecifier
+      | | | | | `-T
+      | | | | |-::
+      | | | | |-SimpleTemplateNameSpecifier
+      | | | | | |-template
+      | | | | | |-U
+      | | | | | |-<
+      | | | | | |-int
+      | | | | | `->
+      | | | | `-::
+      | | | `-UnqualifiedId
+      | | |   `-f
+      | | |-(
+      | | `-)
+      | `-;
+      |-ExpressionStatement
+      | |-UnknownExpression
+      | | |-IdExpression
+      | | | |-NestedNameSpecifier
+      | | | | |-IdentifierNameSpecifier
+      | | | | | `-T
+      | | | | |-::
+      | | | | |-IdentifierNameSpecifier
+      | | | | | `-U
+      | | | | `-::
+      | | | `-UnqualifiedId
+      | | |   `-f
       | | |-(
       | | `-)
       | `-;
       |-ExpressionStatement
       | |-UnknownExpression
-      | | |-UnknownExpression
-      | | | |-T
-      | | | |-::
+      | | |-IdExpression
+      | | | |-NestedNameSpecifier
+      | | | | |-IdentifierNameSpecifier
+      | | | | | `-T
+      | | | | `-::
       | | | |-template
-      | | | |-Y
-      | | | |-<
-      | | | |-IntegerLiteralExpression
-      | | | | `-0
-      | | | |->
-      | | | |-::
-      | | | `-f
+      | | | `-UnqualifiedId
+      | | |   |-f
+      | | |   |-<
+      | | |   |-IntegerLiteralExpression
+      | | |   | `-0
+      | | |   `->
       | | |-(
       | | `-)
       | `-;
@@ -1118,14 +1222,14 @@ void test(S s) {
     | |-UnknownExpression
     | | |-IdExpression
     | | | |-NestedNameSpecifier
-    | | | | `-NameSpecifier
-    | | | |   |-decltype
-    | | | |   |-(
-    | | | |   |-IdExpression
-    | | | |   | `-UnqualifiedId
-    | | | |   |   `-s
-    | | | |   |-)
-    | | | |   `-::
+    | | | | |-DecltypeNameSpecifier
+    | | | | | |-decltype
+    | | | | | |-(
+    | | | | | |-IdExpression
+    | | | | | | `-UnqualifiedId
+    | | | | | |   `-s
+    | | | | | `-)
+    | | | | `-::
     | | | `-UnqualifiedId
     | | |   `-f
     | | |-(


        


More information about the cfe-commits mailing list