Reland "[AST] Add a new TemplateKind for template decls found via a using decl.""
This is the template version of https://reviews.llvm.org/D114251.
This patch introduces a new template name kind (UsingTemplateName). The
UsingTemplateName stores the found using-shadow decl (and underlying
template can be retrieved from the using-shadow decl). With the new
template name, we can be able to find the using decl that a template
typeloc (e.g. TemplateSpecializationTypeLoc) found its underlying template,
which is useful for tooling use cases (include cleaner etc).
This patch merely focuses on adding the node to the AST.
Next steps:
- support using-decl in qualified template name;
- update the clangd and other tools to use this new node;
- add ast matchers for matching different kinds of template names;
Differential Revision: https://reviews.llvm.org/D123127
diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index 4c36de8..8f640ad 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -184,6 +184,7 @@
TEMPLATE_KIND(DependentTemplate);
TEMPLATE_KIND(SubstTemplateTemplateParm);
TEMPLATE_KIND(SubstTemplateTemplateParmPack);
+ TEMPLATE_KIND(UsingTemplate);
#undef TEMPLATE_KIND
}
llvm_unreachable("Unhandled NameKind enum");
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index fce83d1..489bb93 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -762,6 +762,7 @@
case TemplateName::QualifiedTemplate:
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
+ case TemplateName::UsingTemplate:
// Names that could be resolved to a TemplateDecl are handled elsewhere.
break;
}
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 3da7fd9..0ab18b6 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -620,6 +620,16 @@
return TemplateName(declaration);
}]>;
}
+
+let Class = PropertyTypeCase<TemplateName, "UsingTemplate"> in {
+ def : Property<"foundDecl", UsingShadowDeclRef> {
+ let Read = [{ node.getAsUsingShadowDecl() }];
+ }
+ def : Creator<[{
+ return TemplateName(foundDecl);
+ }]>;
+}
+
let Class = PropertyTypeCase<TemplateName, "OverloadedTemplate"> in {
def : Property<"overloads", Array<NamedDeclRef>> {
let Read = [{ node.getAsOverloadedTemplate()->decls() }];
diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index 26c64d0..1bd86b0 100644
--- a/clang/include/clang/AST/TemplateName.h
+++ b/clang/include/clang/AST/TemplateName.h
@@ -25,6 +25,7 @@
namespace clang {
class ASTContext;
+class Decl;
class DependentTemplateName;
class IdentifierInfo;
class NamedDecl;
@@ -39,6 +40,7 @@
class TemplateArgument;
class TemplateDecl;
class TemplateTemplateParmDecl;
+class UsingShadowDecl;
/// Implementation class used to describe either a set of overloaded
/// template names or an already-substituted template template parameter pack.
@@ -188,8 +190,12 @@
/// specifier in the typedef. "apply" is a nested template, and can
/// only be understood in the context of
class TemplateName {
+ // NameDecl is either a TemplateDecl or a UsingShadowDecl depending on the
+ // NameKind.
+ // !! There is no free low bits in 32-bit builds to discriminate more than 4
+ // pointer types in PointerUnion.
using StorageType =
- llvm::PointerUnion<TemplateDecl *, UncommonTemplateNameStorage *,
+ llvm::PointerUnion<Decl *, UncommonTemplateNameStorage *,
QualifiedTemplateName *, DependentTemplateName *>;
StorageType Storage;
@@ -224,7 +230,11 @@
/// A template template parameter pack that has been substituted for
/// a template template argument pack, but has not yet been expanded into
/// individual arguments.
- SubstTemplateTemplateParmPack
+ SubstTemplateTemplateParmPack,
+
+ /// A template name that refers to a template declaration found through a
+ /// specific using shadow declaration.
+ UsingTemplate,
};
TemplateName() = default;
@@ -235,6 +245,7 @@
explicit TemplateName(SubstTemplateTemplateParmPackStorage *Storage);
explicit TemplateName(QualifiedTemplateName *Qual);
explicit TemplateName(DependentTemplateName *Dep);
+ explicit TemplateName(UsingShadowDecl *Using);
/// Determine whether this template name is NULL.
bool isNull() const;
@@ -287,6 +298,10 @@
/// structure, if any.
DependentTemplateName *getAsDependentTemplateName() const;
+ /// Retrieve the using shadow declaration through which the underlying
+ /// template declaration is introduced, if any.
+ UsingShadowDecl *getAsUsingShadowDecl() const;
+
TemplateName getUnderlying() const;
/// Get the template name to substitute when this template name is used as a
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 41bbf2e..0ecb8a2 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -317,6 +317,8 @@
void VisitTagType(const TagType *T);
void VisitTemplateTypeParmType(const TemplateTypeParmType *T);
void VisitAutoType(const AutoType *T);
+ void VisitDeducedTemplateSpecializationType(
+ const DeducedTemplateSpecializationType *T);
void VisitTemplateSpecializationType(const TemplateSpecializationType *T);
void VisitInjectedClassNameType(const InjectedClassNameType *T);
void VisitObjCInterfaceType(const ObjCInterfaceType *T);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 036f970..f6bb757 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6125,6 +6125,9 @@
return DeclarationNameInfo(subst->getParameterPack()->getDeclName(),
NameLoc);
}
+ case TemplateName::UsingTemplate:
+ return DeclarationNameInfo(Name.getAsUsingShadowDecl()->getDeclName(),
+ NameLoc);
}
llvm_unreachable("bad template name kind!");
@@ -6133,6 +6136,7 @@
TemplateName
ASTContext::getCanonicalTemplateName(const TemplateName &Name) const {
switch (Name.getKind()) {
+ case TemplateName::UsingTemplate:
case TemplateName::QualifiedTemplate:
case TemplateName::Template: {
TemplateDecl *Template = Name.getAsTemplateDecl();
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index d0041b8..319f39b 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -9240,6 +9240,12 @@
return ToContext.getSubstTemplateTemplateParmPack(
cast<TemplateTemplateParmDecl>(*ParamOrErr), *ArgPackOrErr);
}
+ case TemplateName::UsingTemplate: {
+ auto UsingOrError = Import(From.getAsUsingShadowDecl());
+ if (!UsingOrError)
+ return UsingOrError.takeError();
+ return TemplateName(cast<UsingShadowDecl>(*UsingOrError));
+ }
}
llvm_unreachable("Invalid template name kind");
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index d4d20dd..05f3470 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -517,6 +517,7 @@
case TemplateName::Template:
case TemplateName::QualifiedTemplate:
case TemplateName::SubstTemplateTemplateParm:
+ case TemplateName::UsingTemplate:
// It is sufficient to check value of getAsTemplateDecl.
break;
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 50e110e..adab764 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2207,6 +2207,7 @@
TD = TN.getAsQualifiedTemplateName()->getTemplateDecl();
goto HaveDecl;
+ case TemplateName::UsingTemplate:
case TemplateName::Template:
TD = TN.getAsTemplateDecl();
goto HaveDecl;
@@ -2383,6 +2384,12 @@
Out << "_SUBSTPACK_";
break;
}
+ case TemplateName::UsingTemplate: {
+ TemplateDecl *TD = TN.getAsTemplateDecl();
+ assert(TD && !isa<TemplateTemplateParmDecl>(TD));
+ mangleSourceNameWithAbiTags(TD);
+ break;
+ }
}
// Note: we don't pass in the template name here. We are mangling the
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 735bcff..04cbb09 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -150,6 +150,7 @@
case TemplateName::DependentTemplate:
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
+ case TemplateName::UsingTemplate:
break;
}
}
diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp
index a6d8a7f..8e32c9c 100644
--- a/clang/lib/AST/TemplateName.cpp
+++ b/clang/lib/AST/TemplateName.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/TemplateName.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DependenceFlags.h"
#include "clang/AST/NestedNameSpecifier.h"
@@ -76,12 +77,18 @@
: Storage(Storage) {}
TemplateName::TemplateName(QualifiedTemplateName *Qual) : Storage(Qual) {}
TemplateName::TemplateName(DependentTemplateName *Dep) : Storage(Dep) {}
+TemplateName::TemplateName(UsingShadowDecl *Using) : Storage(Using) {}
bool TemplateName::isNull() const { return Storage.isNull(); }
TemplateName::NameKind TemplateName::getKind() const {
- if (Storage.is<TemplateDecl *>())
+ if (auto *ND = Storage.dyn_cast<Decl *>()) {
+ if (isa<UsingShadowDecl>(ND))
+ return UsingTemplate;
+ assert(isa<TemplateDecl>(ND));
return Template;
+ }
+
if (Storage.is<DependentTemplateName *>())
return DependentTemplate;
if (Storage.is<QualifiedTemplateName *>())
@@ -99,8 +106,13 @@
}
TemplateDecl *TemplateName::getAsTemplateDecl() const {
- if (TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>())
- return Template;
+ if (Decl *TemplateOrUsing = Storage.dyn_cast<Decl *>()) {
+ if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(TemplateOrUsing))
+ return cast<TemplateDecl>(USD->getTargetDecl());
+
+ assert(isa<TemplateDecl>(TemplateOrUsing));
+ return cast<TemplateDecl>(TemplateOrUsing);
+ }
if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName())
return QTN->getTemplateDecl();
@@ -108,6 +120,9 @@
if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm())
return sub->getReplacement().getAsTemplateDecl();
+ if (UsingShadowDecl *USD = getAsUsingShadowDecl())
+ return cast<TemplateDecl>(USD->getTargetDecl());
+
return nullptr;
}
@@ -153,6 +168,13 @@
return Storage.dyn_cast<DependentTemplateName *>();
}
+UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const {
+ if (Decl *D = Storage.dyn_cast<Decl *>())
+ if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(D))
+ return USD;
+ return nullptr;
+}
+
TemplateName TemplateName::getNameToSubstitute() const {
TemplateDecl *Decl = getAsTemplateDecl();
@@ -222,7 +244,22 @@
void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
Qualified Qual) const {
- if (TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>())
+ auto Kind = getKind();
+ TemplateDecl *Template = nullptr;
+ if (Kind == TemplateName::Template || Kind == TemplateName::UsingTemplate) {
+ // After `namespace ns { using std::vector }`, what is the fully-qualified
+ // name of the UsingTemplateName `vector` within ns?
+ //
+ // - ns::vector (the qualified name of the using-shadow decl)
+ // - std::vector (the qualified name of the underlying template decl)
+ //
+ // Similar to the UsingType behavior, using declarations are used to import
+ // names more often than to export them, thus using the original name is
+ // most useful in this case.
+ Template = getAsTemplateDecl();
+ }
+
+ if (Template)
if (Policy.CleanUglifiedParameters &&
isa<TemplateTemplateParmDecl>(Template) && Template->getIdentifier())
OS << Template->getIdentifier()->deuglifiedName();
@@ -262,6 +299,7 @@
else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) {
Assumed->getDeclName().print(OS, Policy);
} else {
+ assert(getKind() == TemplateName::OverloadedTemplate);
OverloadedTemplateStorage *OTS = getAsOverloadedTemplate();
(*OTS->begin())->printName(OS);
}
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index ba8b7b6..9131dfb 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -900,12 +900,17 @@
}
void TextNodeDumper::VisitTemplateTemplateArgument(const TemplateArgument &TA) {
+ if (TA.getAsTemplate().getKind() == TemplateName::UsingTemplate)
+ OS << " using";
OS << " template ";
TA.getAsTemplate().dump(OS);
}
void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
const TemplateArgument &TA) {
+ if (TA.getAsTemplateOrTemplatePattern().getKind() ==
+ TemplateName::UsingTemplate)
+ OS << " using";
OS << " template expansion ";
TA.getAsTemplateOrTemplatePattern().dump(OS);
}
@@ -1575,10 +1580,18 @@
}
}
+void TextNodeDumper::VisitDeducedTemplateSpecializationType(
+ const DeducedTemplateSpecializationType *T) {
+ if (T->getTemplateName().getKind() == TemplateName::UsingTemplate)
+ OS << " using";
+}
+
void TextNodeDumper::VisitTemplateSpecializationType(
const TemplateSpecializationType *T) {
if (T->isTypeAlias())
OS << " alias";
+ if (T->getTemplateName().getKind() == TemplateName::UsingTemplate)
+ OS << " using";
OS << " ";
T->getTemplateName().dump(OS);
}
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 936550d..bb9900e 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3686,7 +3686,8 @@
"Use DependentTemplateSpecializationType for dependent template-name");
assert((T.getKind() == TemplateName::Template ||
T.getKind() == TemplateName::SubstTemplateTemplateParm ||
- T.getKind() == TemplateName::SubstTemplateTemplateParmPack) &&
+ T.getKind() == TemplateName::SubstTemplateTemplateParmPack ||
+ T.getKind() == TemplateName::UsingTemplate) &&
"Unexpected template name for TemplateSpecializationType");
auto *TemplateArgs = reinterpret_cast<TemplateArgument *>(this + 1);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 706b3da..0708e68 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -504,9 +504,11 @@
FoundUsingShadow = nullptr;
} else if (AllowDeducedTemplate) {
if (auto *TD = getAsTypeTemplateDecl(IIDecl)) {
- // FIXME: TemplateName should include FoundUsingShadow sugar.
- T = Context.getDeducedTemplateSpecializationType(TemplateName(TD),
- QualType(), false);
+ assert(!FoundUsingShadow || FoundUsingShadow->getTargetDecl() == TD);
+ TemplateName Template =
+ FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD);
+ T = Context.getDeducedTemplateSpecializationType(Template, QualType(),
+ false);
// Don't wrap in a further UsingType.
FoundUsingShadow = nullptr;
}
@@ -1107,12 +1109,20 @@
IsFunctionTemplate = isa<FunctionTemplateDecl>(TD);
IsVarTemplate = isa<VarTemplateDecl>(TD);
- if (SS.isNotEmpty())
+ UsingShadowDecl *FoundUsingShadow =
+ dyn_cast<UsingShadowDecl>(*Result.begin());
+
+ if (SS.isNotEmpty()) {
+ // FIXME: support using shadow-declaration in qualified template name.
Template =
Context.getQualifiedTemplateName(SS.getScopeRep(),
/*TemplateKeyword=*/false, TD);
- else
- Template = TemplateName(TD);
+ } else {
+ assert(!FoundUsingShadow ||
+ TD == cast<TemplateDecl>(FoundUsingShadow->getTargetDecl()));
+ Template = FoundUsingShadow ? TemplateName(FoundUsingShadow)
+ : TemplateName(TD);
+ }
} else {
// All results were non-template functions. This is a function template
// name.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 0ce9ddd3..4467f23 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11023,6 +11023,8 @@
TemplateName SpecifiedName = RetTST.getTypePtr()->getTemplateName();
bool TemplateMatches =
Context.hasSameTemplateName(SpecifiedName, GuidedTemplate);
+ // FIXME: We should consider other template kinds (using, qualified),
+ // otherwise we will emit bogus diagnostics.
if (SpecifiedName.getKind() == TemplateName::Template && TemplateMatches)
AcceptableReturnType = true;
else {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bf65f11..e7195ed 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -11,11 +11,13 @@
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/TemplateName.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/LangOptions.h"
@@ -223,6 +225,7 @@
return TNK_Non_template;
NamedDecl *D = nullptr;
+ UsingShadowDecl *FoundUsingShadow = dyn_cast<UsingShadowDecl>(*R.begin());
if (R.isAmbiguous()) {
// If we got an ambiguity involving a non-function template, treat this
// as a template name, and pick an arbitrary template for error recovery.
@@ -233,6 +236,7 @@
AnyFunctionTemplates = true;
else {
D = FoundTemplate;
+ FoundUsingShadow = dyn_cast<UsingShadowDecl>(FoundD);
break;
}
}
@@ -283,10 +287,14 @@
if (SS.isSet() && !SS.isInvalid()) {
NestedNameSpecifier *Qualifier = SS.getScopeRep();
- Template = Context.getQualifiedTemplateName(Qualifier,
- hasTemplateKeyword, TD);
+ // FIXME: store the using TemplateName in QualifiedTemplateName if
+ // the TD is referred via a using-declaration.
+ Template =
+ Context.getQualifiedTemplateName(Qualifier, hasTemplateKeyword, TD);
} else {
- Template = TemplateName(TD);
+ Template =
+ FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD);
+ assert(!FoundUsingShadow || FoundUsingShadow->getTargetDecl() == TD);
}
if (isa<FunctionTemplateDecl>(TD)) {
diff --git a/clang/test/AST/ast-dump-using-template.cpp b/clang/test/AST/ast-dump-using-template.cpp
new file mode 100644
index 0000000..fbce09d
--- /dev/null
+++ b/clang/test/AST/ast-dump-using-template.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++17 -ast-dump %s | FileCheck -strict-whitespace %s
+
+// Tests to verify we construct correct using template names.
+// TemplateNames are not dumped, so the sugar here isn't obvious. However
+// the "using" on the TemplateSpecializationTypes shows that the
+// UsingTemplateName is present.
+namespace ns {
+template<typename T> class S {
+ public:
+ S(T);
+};
+}
+using ns::S;
+
+// TemplateName in TemplateSpecializationType.
+template<typename T>
+using A = S<T>;
+// CHECK: TypeAliasDecl
+// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'S<T>' dependent using S
+
+// TemplateName in TemplateArgument.
+template <template <typename> class T> class X {};
+using B = X<S>;
+// CHECK: TypeAliasDecl
+// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'X<ns::S>' sugar X
+// CHECK-NEXT: |-TemplateArgument using template S
+// CHECK-NEXT: `-RecordType {{.*}} 'X<ns::S>'
+// CHECK-NEXT: `-ClassTemplateSpecialization {{.*}} 'X'
+
+// TemplateName in DeducedTemplateSpecializationType.
+S DeducedTemplateSpecializationT(123);
+using C = decltype(DeducedTemplateSpecializationT);
+// CHECK: DecltypeType {{.*}}
+// CHECK-NEXT: |-DeclRefExpr {{.*}}
+// CHECK-NEXT: `-DeducedTemplateSpecializationType {{.*}} 'ns::S<int>' sugar using
diff --git a/clang/test/CXX/temp/temp.deduct.guide/p3.cpp b/clang/test/CXX/temp/temp.deduct.guide/p3.cpp
index ec39c0c..4ba6e8e 100644
--- a/clang/test/CXX/temp/temp.deduct.guide/p3.cpp
+++ b/clang/test/CXX/temp/temp.deduct.guide/p3.cpp
@@ -55,6 +55,9 @@
}
using N::NamedNS1;
NamedNS1(int) -> NamedNS1<int>; // expected-error {{deduction guide must be declared in the same scope as template}}
+ // FIXME: remove the following bogus diagnostic
+ // expected-error@-2{{deduction guide is not written as a specialization of template 'NamedNS1'}}
+
using namespace N;
NamedNS2(int) -> NamedNS2<int>; // expected-error {{deduction guide must be declared in the same scope as template}}
struct ClassMemberA {
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 9e15ca7..fc61770 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1442,6 +1442,7 @@
bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) {
switch (Name.getKind()) {
case TemplateName::Template:
+ case TemplateName::UsingTemplate:
return Visit(MakeCursorTemplateRef(Name.getAsTemplateDecl(), Loc, TU));
case TemplateName::OverloadedTemplate:
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index d9b6f49..856010c 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -890,6 +890,18 @@
functionDecl(hasDescendant(usingDecl(hasName("bar")))));
}
+TEST_P(ImportDecl, ImportUsingTemplate) {
+ MatchVerifier<Decl> Verifier;
+ testImport("namespace ns { template <typename T> struct S {}; }"
+ "template <template <typename> class T> class X {};"
+ "void declToImport() {"
+ "using ns::S; X<S> xi; }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(
+ hasDescendant(varDecl(hasTypeLoc(templateSpecializationTypeLoc(
+ hasAnyTemplateArgumentLoc(templateArgumentLoc())))))));
+}
+
TEST_P(ImportDecl, ImportUsingEnumDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { enum bar { baz, toto, quux }; }"
diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt
index 2dcef2d2f..48a610c 100644
--- a/clang/unittests/AST/CMakeLists.txt
+++ b/clang/unittests/AST/CMakeLists.txt
@@ -31,6 +31,7 @@
SourceLocationTest.cpp
StmtPrinterTest.cpp
StructuralEquivalenceTest.cpp
+ TemplateNameTest.cpp
TypePrinterTest.cpp
)
diff --git a/clang/unittests/AST/TemplateNameTest.cpp b/clang/unittests/AST/TemplateNameTest.cpp
new file mode 100644
index 0000000..3098692
--- /dev/null
+++ b/clang/unittests/AST/TemplateNameTest.cpp
@@ -0,0 +1,62 @@
+//===- unittests/AST/TemplateNameTest.cpp --- Tests for TemplateName ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTPrint.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace {
+using namespace ast_matchers;
+
+std::string printTemplateName(TemplateName TN, const PrintingPolicy &Policy,
+ TemplateName::Qualified Qual) {
+ std::string Result;
+ llvm::raw_string_ostream Out(Result);
+ TN.print(Out, Policy, Qual);
+ return Out.str();
+}
+
+TEST(TemplateName, PrintUsingTemplate) {
+ std::string Code = R"cpp(
+ namespace std {
+ template <typename> struct vector {};
+ }
+ namespace absl { using std::vector; }
+
+ template<template <typename> class T> class X;
+
+ using absl::vector;
+ using A = X<vector>;
+ )cpp";
+ auto AST = tooling::buildASTFromCode(Code);
+ ASTContext &Ctx = AST->getASTContext();
+ // Match the template argument vector in X<vector>.
+ auto MatchResults = match(templateArgumentLoc().bind("id"), Ctx);
+ const auto *Template = selectFirst<TemplateArgumentLoc>("id", MatchResults);
+ ASSERT_TRUE(Template);
+
+ TemplateName TN = Template->getArgument().getAsTemplate();
+ EXPECT_EQ(TN.getKind(), TemplateName::UsingTemplate);
+ EXPECT_EQ(TN.getAsUsingShadowDecl()->getTargetDecl(), TN.getAsTemplateDecl());
+
+ EXPECT_EQ(printTemplateName(TN, Ctx.getPrintingPolicy(),
+ TemplateName::Qualified::Fully),
+ "std::vector");
+ EXPECT_EQ(printTemplateName(TN, Ctx.getPrintingPolicy(),
+ TemplateName::Qualified::AsWritten),
+ "vector");
+ EXPECT_EQ(printTemplateName(TN, Ctx.getPrintingPolicy(),
+ TemplateName::Qualified::None),
+ "vector");
+}
+
+} // namespace
+} // namespace clang