[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