[clang] 88bf9b3 - [Syntax] Build template declaration nodes
Dmitri Gribenko via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 19 09:46:40 PDT 2020
Author: Marcel Hlopko
Date: 2020-03-19T17:43:07+01:00
New Revision: 88bf9b3d26f06eaf33972d1f371edc4cb187ff1a
URL: https://github.com/llvm/llvm-project/commit/88bf9b3d26f06eaf33972d1f371edc4cb187ff1a
DIFF: https://github.com/llvm/llvm-project/commit/88bf9b3d26f06eaf33972d1f371edc4cb187ff1a.diff
LOG: [Syntax] Build template declaration nodes
Summary:
Rollforward of
https://reviews.llvm.org/rGdd12826808f9079e164b82e64b0697a077379241 after
temporarily adding -fno-delayed-template-parsing to the TreeTest.
Original summary:
> Copy of https://reviews.llvm.org/D72334, submitting with Ilya's permission.
>
> Handles template declaration of all kinds.
>
> Also builds template declaration nodes for specializations and explicit
> instantiations of classes.
>
> Some missing things will be addressed in the follow-up patches:
>
> * specializations of functions and variables,
> * template parameters.
Reviewers: gribozavr2
Subscribers: cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D76418
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 82fcac33f99b..f4d482bb848c 100644
--- a/clang/include/clang/Tooling/Syntax/Nodes.h
+++ b/clang/include/clang/Tooling/Syntax/Nodes.h
@@ -64,6 +64,8 @@ enum class NodeKind : uint16_t {
StaticAssertDeclaration,
LinkageSpecificationDeclaration,
SimpleDeclaration,
+ TemplateDeclaration,
+ ExplicitTemplateInstantiation,
NamespaceDefinition,
NamespaceAliasDefinition,
UsingNamespaceDirective,
@@ -112,6 +114,9 @@ enum class NodeRole : uint8_t {
StaticAssertDeclaration_condition,
StaticAssertDeclaration_message,
SimpleDeclaration_declarator,
+ TemplateDeclaration_declaration,
+ ExplicitTemplateInstantiation_externKeyword,
+ ExplicitTemplateInstantiation_declaration,
ArraySubscript_sizeExpression,
TrailingReturnType_arrow,
TrailingReturnType_declarator,
@@ -396,6 +401,34 @@ class SimpleDeclaration final : public Declaration {
std::vector<syntax::SimpleDeclarator *> declarators();
};
+/// template <template-parameters> <declaration>
+class TemplateDeclaration final : public Declaration {
+public:
+ TemplateDeclaration() : Declaration(NodeKind::TemplateDeclaration) {}
+ static bool classof(const Node *N) {
+ return N->kind() == NodeKind::TemplateDeclaration;
+ }
+ syntax::Leaf *templateKeyword();
+ syntax::Declaration *declaration();
+};
+
+/// template <declaration>
+/// Examples:
+/// template struct X<int>
+/// template void foo<int>()
+/// template int var<double>
+class ExplicitTemplateInstantiation final : public Declaration {
+public:
+ ExplicitTemplateInstantiation()
+ : Declaration(NodeKind::ExplicitTemplateInstantiation) {}
+ static bool classof(const Node *N) {
+ return N->kind() == NodeKind::ExplicitTemplateInstantiation;
+ }
+ syntax::Leaf *templateKeyword();
+ syntax::Leaf *externKeyword();
+ syntax::Declaration *declaration();
+};
+
/// namespace <name> { <decls> }
class NamespaceDefinition final : public Declaration {
public:
diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp
index 9ebf7d29d8ed..a09ac1c53e34 100644
--- a/clang/lib/Tooling/Syntax/BuildTree.cpp
+++ b/clang/lib/Tooling/Syntax/BuildTree.cpp
@@ -18,6 +18,7 @@
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Syntax/Nodes.h"
@@ -189,7 +190,6 @@ class syntax::TreeBuilder {
/// Should be called for expressions in non-statement position to avoid
/// wrapping into expression statement.
void markExprChild(Expr *Child, NodeRole Role);
-
/// Set role for a token starting at \p Loc.
void markChildToken(SourceLocation Loc, NodeRole R);
/// Set role for \p T.
@@ -199,6 +199,9 @@ class syntax::TreeBuilder {
void markChild(llvm::ArrayRef<syntax::Token> Range, NodeRole R);
/// Set role for the delayed node that spans exactly \p Range.
void markDelayedChild(llvm::ArrayRef<syntax::Token> Range, NodeRole R);
+ /// Set role for the node that may or may not be delayed. Node must span
+ /// exactly \p Range.
+ void markMaybeDelayedChild(llvm::ArrayRef<syntax::Token> Range, NodeRole R);
/// Finish building the tree and consume the root node.
syntax::TranslationUnit *finalize() && {
@@ -215,6 +218,9 @@ class syntax::TreeBuilder {
return TU;
}
+ /// Finds a token starting at \p L. The token must exist if \p L is valid.
+ const syntax::Token *findToken(SourceLocation L) const;
+
/// getRange() finds the syntax tokens corresponding to the passed source
/// locations.
/// \p First is the start position of the first token and \p Last is the start
@@ -227,15 +233,22 @@ class syntax::TreeBuilder {
Arena.sourceManager().isBeforeInTranslationUnit(First, Last));
return llvm::makeArrayRef(findToken(First), std::next(findToken(Last)));
}
- llvm::ArrayRef<syntax::Token> getRange(const Decl *D) const {
- auto Tokens = getRange(D->getBeginLoc(), D->getEndLoc());
- if (llvm::isa<NamespaceDecl>(D))
- return Tokens;
- if (DeclsWithoutSemicolons.count(D))
- return Tokens;
- // FIXME: do not consume trailing semicolon on function definitions.
- // Most declarations own a semicolon in syntax trees, but not in clang AST.
- return withTrailingSemicolon(Tokens);
+
+ llvm::ArrayRef<syntax::Token>
+ getTemplateRange(const ClassTemplateSpecializationDecl *D) const {
+ auto R = D->getSourceRange();
+ auto Tokens = getRange(R.getBegin(), R.getEnd());
+ return maybeAppendSemicolon(Tokens, D);
+ }
+
+ llvm::ArrayRef<syntax::Token> getDeclRange(const Decl *D) const {
+ llvm::ArrayRef<clang::syntax::Token> Tokens;
+ // We want to drop the template parameters for specializations.
+ if (const auto *S = llvm::dyn_cast<TagDecl>(D))
+ Tokens = getRange(S->TypeDecl::getBeginLoc(), S->getEndLoc());
+ else
+ Tokens = getRange(D->getBeginLoc(), D->getEndLoc());
+ return maybeAppendSemicolon(Tokens, D);
}
llvm::ArrayRef<syntax::Token> getExprRange(const Expr *E) const {
return getRange(E->getBeginLoc(), E->getEndLoc());
@@ -255,6 +268,18 @@ class syntax::TreeBuilder {
}
private:
+ llvm::ArrayRef<syntax::Token>
+ maybeAppendSemicolon(llvm::ArrayRef<syntax::Token> Tokens,
+ const Decl *D) const {
+ if (llvm::isa<NamespaceDecl>(D))
+ return Tokens;
+ if (DeclsWithoutSemicolons.count(D))
+ return Tokens;
+ // FIXME: do not consume trailing semicolon on function definitions.
+ // Most declarations own a semicolon in syntax trees, but not in clang AST.
+ return withTrailingSemicolon(Tokens);
+ }
+
llvm::ArrayRef<syntax::Token>
withTrailingSemicolon(llvm::ArrayRef<syntax::Token> Tokens) const {
assert(!Tokens.empty());
@@ -265,9 +290,6 @@ class syntax::TreeBuilder {
return Tokens;
}
- /// Finds a token starting at \p L. The token must exist.
- const syntax::Token *findToken(SourceLocation L) const;
-
/// A collection of trees covering the input tokens.
/// When created, each tree corresponds to a single token in the file.
/// Clients call 'foldChildren' to attach one or more subtrees to a parent
@@ -298,6 +320,15 @@ class syntax::TreeBuilder {
It->second.Role = Role;
}
+ void assignRoleMaybeDelayed(llvm::ArrayRef<syntax::Token> Range,
+ syntax::NodeRole Role) {
+ auto It = DelayedFolds.find(Range.begin());
+ if (It == DelayedFolds.end())
+ return assignRole(Range, Role);
+ assert(It->second.End == Range.end());
+ It->second.Role = Role;
+ }
+
void assignRole(llvm::ArrayRef<syntax::Token> Range,
syntax::NodeRole Role) {
assert(!Range.empty());
@@ -460,7 +491,7 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
bool WalkUpFromDeclaratorDecl(DeclaratorDecl *DD) {
// Ensure declarators are covered by SimpleDeclaration.
- Builder.noticeDeclRange(Builder.getRange(DD));
+ Builder.noticeDeclRange(Builder.getDeclRange(DD));
// Build the declarator node.
SourceRange Initializer;
@@ -485,7 +516,7 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
bool WalkUpFromTypedefNameDecl(TypedefNameDecl *D) {
// Ensure declarators are covered by SimpleDeclaration.
- Builder.noticeDeclRange(Builder.getRange(D));
+ Builder.noticeDeclRange(Builder.getDeclRange(D));
auto R = getDeclaratorRange(
Builder.sourceManager(), D->getTypeSourceInfo()->getTypeLoc(),
@@ -500,19 +531,59 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
bool VisitDecl(Decl *D) {
assert(!D->isImplicit());
- Builder.foldNode(Builder.getRange(D),
+ Builder.foldNode(Builder.getDeclRange(D),
new (allocator()) syntax::UnknownDeclaration());
return true;
}
+ // RAV does not call WalkUpFrom* on explicit instantiations, so we have to
+ // override Traverse.
+ // FIXME: make RAV call WalkUpFrom* instead.
+ bool
+ TraverseClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *C) {
+ if (!RecursiveASTVisitor::TraverseClassTemplateSpecializationDecl(C))
+ return false;
+ if (C->isExplicitSpecialization())
+ return true; // we are only interested in explicit instantiations.
+ if (!WalkUpFromClassTemplateSpecializationDecl(C))
+ return false;
+ foldExplicitTemplateInstantiation(
+ Builder.getTemplateRange(C), Builder.findToken(C->getExternLoc()),
+ Builder.findToken(C->getTemplateKeywordLoc()), Builder.getDeclRange(C));
+ return true;
+ }
+
+ bool WalkUpFromTemplateDecl(TemplateDecl *S) {
+ foldTemplateDeclaration(
+ Builder.getDeclRange(S),
+ Builder.findToken(S->getTemplateParameters()->getTemplateLoc()),
+ Builder.getDeclRange(S->getTemplatedDecl()));
+ return true;
+ }
+
bool WalkUpFromTagDecl(TagDecl *C) {
// FIXME: build the ClassSpecifier node.
- if (C->isFreeStanding()) {
- // Class is a declaration specifier and needs a spanning declaration node.
- Builder.foldNode(Builder.getRange(C),
- new (allocator()) syntax::SimpleDeclaration);
+ if (!C->isFreeStanding()) {
+ assert(C->getNumTemplateParameterLists() == 0);
return true;
}
+ // Class is a declaration specifier and needs a spanning declaration node.
+ auto DeclarationRange = Builder.getDeclRange(C);
+ Builder.foldNode(DeclarationRange,
+ new (allocator()) syntax::SimpleDeclaration);
+
+ // Build TemplateDeclaration nodes if we had template parameters.
+ auto ConsumeTemplateParameters = [&](const TemplateParameterList &L) {
+ const auto *TemplateKW = Builder.findToken(L.getTemplateLoc());
+ auto R = llvm::makeArrayRef(TemplateKW, DeclarationRange.end());
+ foldTemplateDeclaration(R, TemplateKW, DeclarationRange);
+
+ DeclarationRange = R;
+ };
+ if (auto *S = llvm::dyn_cast<ClassTemplatePartialSpecializationDecl>(C))
+ ConsumeTemplateParameters(*S->getTemplateParameters());
+ for (unsigned I = C->getNumTemplateParameterLists(); 0 < I; --I)
+ ConsumeTemplateParameters(*C->getTemplateParameterList(I - 1));
return true;
}
@@ -581,7 +652,7 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
}
bool WalkUpFromNamespaceDecl(NamespaceDecl *S) {
- auto Tokens = Builder.getRange(S);
+ auto Tokens = Builder.getDeclRange(S);
if (Tokens.front().kind() == tok::coloncolon) {
// Handle nested namespace definitions. Those start at '::' token, e.g.
// namespace a^::b {}
@@ -622,7 +693,7 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
Builder.markChildToken(L.getLParenLoc(), syntax::NodeRole::OpenParen);
for (auto *P : L.getParams())
Builder.markDelayedChild(
- Builder.getRange(P),
+ Builder.getDeclRange(P),
syntax::NodeRole::ParametersAndQualifiers_parameter);
Builder.markChildToken(L.getRParenLoc(), syntax::NodeRole::CloseParen);
Builder.foldNode(Builder.getRange(L.getLParenLoc(), L.getEndLoc()),
@@ -756,7 +827,7 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
}
bool WalkUpFromEmptyDecl(EmptyDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::EmptyDeclaration);
return true;
}
@@ -766,49 +837,49 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
syntax::NodeRole::StaticAssertDeclaration_condition);
Builder.markExprChild(S->getMessage(),
syntax::NodeRole::StaticAssertDeclaration_message);
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::StaticAssertDeclaration);
return true;
}
bool WalkUpFromLinkageSpecDecl(LinkageSpecDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::LinkageSpecificationDeclaration);
return true;
}
bool WalkUpFromNamespaceAliasDecl(NamespaceAliasDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::NamespaceAliasDefinition);
return true;
}
bool WalkUpFromUsingDirectiveDecl(UsingDirectiveDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::UsingNamespaceDirective);
return true;
}
bool WalkUpFromUsingDecl(UsingDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::UsingDeclaration);
return true;
}
bool WalkUpFromUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::UsingDeclaration);
return true;
}
bool WalkUpFromUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::UsingDeclaration);
return true;
}
bool WalkUpFromTypeAliasDecl(TypeAliasDecl *S) {
- Builder.foldNode(Builder.getRange(S),
+ Builder.foldNode(Builder.getDeclRange(S),
new (allocator()) syntax::TypeAliasDeclaration);
return true;
}
@@ -845,6 +916,36 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
Builder.foldNode(Tokens, new (allocator()) syntax::TrailingReturnType);
return Tokens;
}
+
+ void
+ foldExplicitTemplateInstantiation(ArrayRef<syntax::Token> Range,
+ const syntax::Token *ExternKW,
+ const syntax::Token *TemplateKW,
+ ArrayRef<syntax::Token> InnerDeclaration) {
+ assert(!ExternKW || ExternKW->kind() == tok::kw_extern);
+ assert(TemplateKW && TemplateKW->kind() == tok::kw_template);
+ Builder.markChildToken(
+ ExternKW,
+ syntax::NodeRole::ExplicitTemplateInstantiation_externKeyword);
+ Builder.markChildToken(TemplateKW, syntax::NodeRole::IntroducerKeyword);
+ Builder.markChild(
+ InnerDeclaration,
+ syntax::NodeRole::ExplicitTemplateInstantiation_declaration);
+ Builder.foldNode(Range,
+ new (allocator()) syntax::ExplicitTemplateInstantiation);
+ }
+
+ void foldTemplateDeclaration(ArrayRef<syntax::Token> Range,
+ const syntax::Token *TemplateKW,
+ ArrayRef<syntax::Token> TemplatedDeclaration) {
+ assert(TemplateKW && TemplateKW->kind() == tok::kw_template);
+ Builder.markChildToken(TemplateKW, syntax::NodeRole::IntroducerKeyword);
+ Builder.markMaybeDelayedChild(
+ TemplatedDeclaration,
+ syntax::NodeRole::TemplateDeclaration_declaration);
+ Builder.foldNode(Range, new (allocator()) syntax::TemplateDeclaration);
+ }
+
/// A small helper to save some typing.
llvm::BumpPtrAllocator &allocator() { return Builder.allocator(); }
@@ -891,6 +992,11 @@ void syntax::TreeBuilder::markDelayedChild(llvm::ArrayRef<syntax::Token> Range,
Pending.assignRoleDelayed(Range, R);
}
+void syntax::TreeBuilder::markMaybeDelayedChild(
+ llvm::ArrayRef<syntax::Token> Range, NodeRole R) {
+ Pending.assignRoleMaybeDelayed(Range, R);
+}
+
void syntax::TreeBuilder::markStmtChild(Stmt *Child, NodeRole Role) {
if (!Child)
return;
@@ -916,6 +1022,8 @@ void syntax::TreeBuilder::markExprChild(Expr *Child, NodeRole Role) {
}
const syntax::Token *syntax::TreeBuilder::findToken(SourceLocation L) const {
+ if (L.isInvalid())
+ return nullptr;
auto It = LocationToToken.find(L.getRawEncoding());
assert(It != LocationToToken.end());
return It->second;
diff --git a/clang/lib/Tooling/Syntax/Nodes.cpp b/clang/lib/Tooling/Syntax/Nodes.cpp
index 4f86007e39bb..75f025e5f853 100644
--- a/clang/lib/Tooling/Syntax/Nodes.cpp
+++ b/clang/lib/Tooling/Syntax/Nodes.cpp
@@ -58,6 +58,10 @@ llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeKind K) {
return OS << "LinkageSpecificationDeclaration";
case NodeKind::SimpleDeclaration:
return OS << "SimpleDeclaration";
+ case NodeKind::TemplateDeclaration:
+ return OS << "TemplateDeclaration";
+ case NodeKind::ExplicitTemplateInstantiation:
+ return OS << "ExplicitTemplateInstantiation";
case NodeKind::NamespaceDefinition:
return OS << "NamespaceDefinition";
case NodeKind::NamespaceAliasDefinition:
@@ -118,6 +122,12 @@ llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeRole R) {
return OS << "StaticAssertDeclaration_message";
case syntax::NodeRole::SimpleDeclaration_declarator:
return OS << "SimpleDeclaration_declarator";
+ case syntax::NodeRole::TemplateDeclaration_declaration:
+ return OS << "TemplateDeclaration_declaration";
+ case syntax::NodeRole::ExplicitTemplateInstantiation_externKeyword:
+ return OS << "ExplicitTemplateInstantiation_externKeyword";
+ case syntax::NodeRole::ExplicitTemplateInstantiation_declaration:
+ return OS << "ExplicitTemplateInstantiation_declaration";
case syntax::NodeRole::ArraySubscript_sizeExpression:
return OS << "ArraySubscript_sizeExpression";
case syntax::NodeRole::TrailingReturnType_arrow:
@@ -281,6 +291,31 @@ syntax::SimpleDeclaration::declarators() {
return Children;
}
+syntax::Leaf *syntax::TemplateDeclaration::templateKeyword() {
+ return llvm::cast_or_null<syntax::Leaf>(
+ findChild(syntax::NodeRole::IntroducerKeyword));
+}
+
+syntax::Declaration *syntax::TemplateDeclaration::declaration() {
+ return llvm::cast_or_null<syntax::Declaration>(
+ findChild(syntax::NodeRole::TemplateDeclaration_declaration));
+}
+
+syntax::Leaf *syntax::ExplicitTemplateInstantiation::templateKeyword() {
+ return llvm::cast_or_null<syntax::Leaf>(
+ findChild(syntax::NodeRole::IntroducerKeyword));
+}
+
+syntax::Leaf *syntax::ExplicitTemplateInstantiation::externKeyword() {
+ return llvm::cast_or_null<syntax::Leaf>(
+ findChild(syntax::NodeRole::ExplicitTemplateInstantiation_externKeyword));
+}
+
+syntax::Declaration *syntax::ExplicitTemplateInstantiation::declaration() {
+ return llvm::cast_or_null<syntax::Declaration>(
+ findChild(syntax::NodeRole::ExplicitTemplateInstantiation_declaration));
+}
+
syntax::Leaf *syntax::ParenDeclarator::lparen() {
return llvm::cast_or_null<syntax::Leaf>(
findChild(syntax::NodeRole::OpenParen));
diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp
index 6e914b6378c8..cfed1eefbfe3 100644
--- a/clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -99,6 +99,7 @@ class SyntaxTreeTest : public ::testing::Test {
Diags->setClient(new IgnoringDiagConsumer);
// Prepare to run a compiler.
std::vector<const char *> Args = {"syntax-test", "-std=c++11",
+ "-fno-delayed-template-parsing",
"-fsyntax-only", FileName};
Invocation = createInvocationFromCommandLine(Args, Diags, FS);
assert(Invocation);
@@ -678,6 +679,212 @@ struct {} *a1;
`-;
)txt"},
{R"cpp(
+template <class T> struct cls {};
+template <class T> int var = 10;
+template <class T> int fun() {}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-TemplateDeclaration
+| |-template
+| |-<
+| |-UnknownDeclaration
+| | |-class
+| | `-T
+| |->
+| `-SimpleDeclaration
+| |-struct
+| |-cls
+| |-{
+| |-}
+| `-;
+|-TemplateDeclaration
+| |-template
+| |-<
+| |-UnknownDeclaration
+| | |-class
+| | `-T
+| |->
+| `-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-var
+| | |-=
+| | `-UnknownExpression
+| | `-10
+| `-;
+`-TemplateDeclaration
+ |-template
+ |-<
+ |-UnknownDeclaration
+ | |-class
+ | `-T
+ |->
+ `-SimpleDeclaration
+ |-int
+ |-SimpleDeclarator
+ | |-fun
+ | `-ParametersAndQualifiers
+ | |-(
+ | `-)
+ `-CompoundStatement
+ |-{
+ `-}
+)txt"},
+ {R"cpp(
+template <class T>
+struct X {
+ template <class U>
+ U foo();
+};
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-TemplateDeclaration
+ |-template
+ |-<
+ |-UnknownDeclaration
+ | |-class
+ | `-T
+ |->
+ `-SimpleDeclaration
+ |-struct
+ |-X
+ |-{
+ |-TemplateDeclaration
+ | |-template
+ | |-<
+ | |-UnknownDeclaration
+ | | |-class
+ | | `-U
+ | |->
+ | `-SimpleDeclaration
+ | |-U
+ | |-SimpleDeclarator
+ | | |-foo
+ | | `-ParametersAndQualifiers
+ | | |-(
+ | | `-)
+ | `-;
+ |-}
+ `-;
+)txt"},
+ {R"cpp(
+template <class T> struct X {};
+template <class T> struct X<T*> {};
+template <> struct X<int> {};
+
+template struct X<double>;
+extern template struct X<float>;
+)cpp",
+ R"txt(
+*: TranslationUnit
+|-TemplateDeclaration
+| |-template
+| |-<
+| |-UnknownDeclaration
+| | |-class
+| | `-T
+| |->
+| `-SimpleDeclaration
+| |-struct
+| |-X
+| |-{
+| |-}
+| `-;
+|-TemplateDeclaration
+| |-template
+| |-<
+| |-UnknownDeclaration
+| | |-class
+| | `-T
+| |->
+| `-SimpleDeclaration
+| |-struct
+| |-X
+| |-<
+| |-T
+| |-*
+| |->
+| |-{
+| |-}
+| `-;
+|-TemplateDeclaration
+| |-template
+| |-<
+| |->
+| `-SimpleDeclaration
+| |-struct
+| |-X
+| |-<
+| |-int
+| |->
+| |-{
+| |-}
+| `-;
+|-ExplicitTemplateInstantiation
+| |-template
+| `-SimpleDeclaration
+| |-struct
+| |-X
+| |-<
+| |-double
+| |->
+| `-;
+`-ExplicitTemplateInstantiation
+ |-extern
+ |-template
+ `-SimpleDeclaration
+ |-struct
+ |-X
+ |-<
+ |-float
+ |->
+ `-;
+)txt"},
+ {R"cpp(
+template <class T> struct X { struct Y; };
+template <class T> struct X<T>::Y {};
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-TemplateDeclaration
+| |-template
+| |-<
+| |-UnknownDeclaration
+| | |-class
+| | `-T
+| |->
+| `-SimpleDeclaration
+| |-struct
+| |-X
+| |-{
+| |-SimpleDeclaration
+| | |-struct
+| | |-Y
+| | `-;
+| |-}
+| `-;
+`-TemplateDeclaration
+ |-template
+ |-<
+ |-UnknownDeclaration
+ | |-class
+ | `-T
+ |->
+ `-SimpleDeclaration
+ |-struct
+ |-X
+ |-<
+ |-T
+ |->
+ |-::
+ |-Y
+ |-{
+ |-}
+ `-;
+ )txt"},
+ {R"cpp(
namespace ns {}
using namespace ::ns;
)cpp",
@@ -726,7 +933,7 @@ template <class T> struct X {
)cpp",
R"txt(
*: TranslationUnit
-`-UnknownDeclaration
+`-TemplateDeclaration
|-template
|-<
|-UnknownDeclaration
More information about the cfe-commits
mailing list