forked from OSchip/llvm-project
[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
This commit is contained in:
parent
7d7771f34d
commit
5a5be4044f
|
@ -184,6 +184,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
|
|||
TEMPLATE_KIND(DependentTemplate);
|
||||
TEMPLATE_KIND(SubstTemplateTemplateParm);
|
||||
TEMPLATE_KIND(SubstTemplateTemplateParmPack);
|
||||
TEMPLATE_KIND(UsingTemplate);
|
||||
#undef TEMPLATE_KIND
|
||||
}
|
||||
llvm_unreachable("Unhandled NameKind enum");
|
||||
|
|
|
@ -762,6 +762,7 @@ public:
|
|||
case TemplateName::QualifiedTemplate:
|
||||
case TemplateName::SubstTemplateTemplateParm:
|
||||
case TemplateName::SubstTemplateTemplateParmPack:
|
||||
case TemplateName::UsingTemplate:
|
||||
// Names that could be resolved to a TemplateDecl are handled elsewhere.
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -620,6 +620,16 @@ let Class = PropertyTypeCase<TemplateName, "Template"> in {
|
|||
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() }];
|
||||
|
|
|
@ -39,6 +39,7 @@ class SubstTemplateTemplateParmStorage;
|
|||
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.
|
||||
|
@ -190,7 +191,8 @@ public:
|
|||
class TemplateName {
|
||||
using StorageType =
|
||||
llvm::PointerUnion<TemplateDecl *, UncommonTemplateNameStorage *,
|
||||
QualifiedTemplateName *, DependentTemplateName *>;
|
||||
QualifiedTemplateName *, DependentTemplateName *,
|
||||
UsingShadowDecl *>;
|
||||
|
||||
StorageType Storage;
|
||||
|
||||
|
@ -224,7 +226,11 @@ public:
|
|||
/// 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 +241,7 @@ public:
|
|||
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 +294,13 @@ public:
|
|||
/// structure, if any.
|
||||
DependentTemplateName *getAsDependentTemplateName() const;
|
||||
|
||||
/// Retrieve the using shadow declaration through which the underlying
|
||||
/// template declaration is introduced.
|
||||
///
|
||||
/// The underlying template declaration is not stored in the template name, it
|
||||
/// can be retrieved via the using shadow declaration.
|
||||
UsingShadowDecl *getAsUsingShadowDecl() const;
|
||||
|
||||
TemplateName getUnderlying() const;
|
||||
|
||||
/// Get the template name to substitute when this template name is used as a
|
||||
|
|
|
@ -317,6 +317,8 @@ public:
|
|||
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);
|
||||
|
|
|
@ -6125,6 +6125,9 @@ ASTContext::getNameForTemplate(TemplateName Name,
|
|||
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 @@ ASTContext::getNameForTemplate(TemplateName Name,
|
|||
TemplateName
|
||||
ASTContext::getCanonicalTemplateName(const TemplateName &Name) const {
|
||||
switch (Name.getKind()) {
|
||||
case TemplateName::UsingTemplate:
|
||||
case TemplateName::QualifiedTemplate:
|
||||
case TemplateName::Template: {
|
||||
TemplateDecl *Template = Name.getAsTemplateDecl();
|
||||
|
|
|
@ -9193,6 +9193,12 @@ Expected<TemplateName> ASTImporter::Import(TemplateName From) {
|
|||
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");
|
||||
|
|
|
@ -517,6 +517,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||
case TemplateName::Template:
|
||||
case TemplateName::QualifiedTemplate:
|
||||
case TemplateName::SubstTemplateTemplateParm:
|
||||
case TemplateName::UsingTemplate:
|
||||
// It is sufficient to check value of getAsTemplateDecl.
|
||||
break;
|
||||
|
||||
|
|
|
@ -2207,6 +2207,7 @@ void CXXNameMangler::mangleType(TemplateName TN) {
|
|||
TD = TN.getAsQualifiedTemplateName()->getTemplateDecl();
|
||||
goto HaveDecl;
|
||||
|
||||
case TemplateName::UsingTemplate:
|
||||
case TemplateName::Template:
|
||||
TD = TN.getAsTemplateDecl();
|
||||
goto HaveDecl;
|
||||
|
@ -2383,6 +2384,12 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
|
|||
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
|
||||
|
|
|
@ -150,6 +150,7 @@ void ODRHash::AddTemplateName(TemplateName Name) {
|
|||
case TemplateName::DependentTemplate:
|
||||
case TemplateName::SubstTemplateTemplateParm:
|
||||
case TemplateName::SubstTemplateTemplateParmPack:
|
||||
case TemplateName::UsingTemplate:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +77,7 @@ TemplateName::TemplateName(SubstTemplateTemplateParmPackStorage *Storage)
|
|||
: 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(); }
|
||||
|
||||
|
@ -86,6 +88,8 @@ TemplateName::NameKind TemplateName::getKind() const {
|
|||
return DependentTemplate;
|
||||
if (Storage.is<QualifiedTemplateName *>())
|
||||
return QualifiedTemplate;
|
||||
if (Storage.is<UsingShadowDecl *>())
|
||||
return UsingTemplate;
|
||||
|
||||
UncommonTemplateNameStorage *uncommon
|
||||
= Storage.get<UncommonTemplateNameStorage*>();
|
||||
|
@ -108,6 +112,9 @@ TemplateDecl *TemplateName::getAsTemplateDecl() const {
|
|||
if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm())
|
||||
return sub->getReplacement().getAsTemplateDecl();
|
||||
|
||||
if (UsingShadowDecl *USD = getAsUsingShadowDecl())
|
||||
return cast<TemplateDecl>(USD->getTargetDecl());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -153,6 +160,10 @@ DependentTemplateName *TemplateName::getAsDependentTemplateName() const {
|
|||
return Storage.dyn_cast<DependentTemplateName *>();
|
||||
}
|
||||
|
||||
UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const {
|
||||
return Storage.dyn_cast<UsingShadowDecl *>();
|
||||
}
|
||||
|
||||
TemplateName TemplateName::getNameToSubstitute() const {
|
||||
TemplateDecl *Decl = getAsTemplateDecl();
|
||||
|
||||
|
@ -222,7 +233,20 @@ bool TemplateName::containsUnexpandedParameterPack() const {
|
|||
|
||||
void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
||||
Qualified Qual) const {
|
||||
if (TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>())
|
||||
TemplateDecl *Template = Storage.dyn_cast<TemplateDecl *>();
|
||||
if (getKind() == 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 qualifier name of the underlying template decl)
|
||||
//
|
||||
// Similar to the UsingType behavior, std::vector is much more common, and
|
||||
// provides more information in practice, we print the underlying template
|
||||
// decl of the using-shadow decl.
|
||||
Template = getAsTemplateDecl();
|
||||
}
|
||||
if (Template)
|
||||
if (Policy.CleanUglifiedParameters &&
|
||||
isa<TemplateTemplateParmDecl>(Template) && Template->getIdentifier())
|
||||
OS << Template->getIdentifier()->deuglifiedName();
|
||||
|
@ -262,6 +286,7 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
|||
else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) {
|
||||
Assumed->getDeclName().print(OS, Policy);
|
||||
} else {
|
||||
assert(getKind() == TemplateName::OverloadedTemplate);
|
||||
OverloadedTemplateStorage *OTS = getAsOverloadedTemplate();
|
||||
(*OTS->begin())->printName(OS);
|
||||
}
|
||||
|
|
|
@ -900,12 +900,17 @@ void TextNodeDumper::VisitIntegralTemplateArgument(const TemplateArgument &TA) {
|
|||
}
|
||||
|
||||
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::VisitAutoType(const AutoType *T) {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -3686,7 +3686,8 @@ TemplateSpecializationType::TemplateSpecializationType(
|
|||
"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);
|
||||
|
|
|
@ -504,9 +504,11 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
|
|||
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 @@ Corrected:
|
|||
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.
|
||||
|
|
|
@ -11023,6 +11023,8 @@ void Sema::CheckDeductionGuideDeclarator(Declarator &D, QualType &R,
|
|||
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 {
|
||||
|
|
|
@ -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 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
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 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
AnyFunctionTemplates = true;
|
||||
else {
|
||||
D = FoundTemplate;
|
||||
FoundUsingShadow = dyn_cast<UsingShadowDecl>(FoundD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -283,10 +287,14 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
|
||||
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)) {
|
||||
|
|
|
@ -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
|
|
@ -55,6 +55,9 @@ namespace WrongScope {
|
|||
}
|
||||
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 {
|
||||
|
|
|
@ -1442,6 +1442,7 @@ bool CursorVisitor::VisitTemplateParameters(
|
|||
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:
|
||||
|
|
|
@ -890,6 +890,18 @@ TEST_P(ImportDecl, ImportUsingDecl) {
|
|||
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 }; }"
|
||||
|
|
|
@ -31,6 +31,7 @@ add_clang_unittest(ASTTests
|
|||
SourceLocationTest.cpp
|
||||
StmtPrinterTest.cpp
|
||||
StructuralEquivalenceTest.cpp
|
||||
TemplateNameTest.cpp
|
||||
TypePrinterTest.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//===- 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 X in X<vector>.
|
||||
auto Matcher = templateArgumentLoc().bind("id");
|
||||
|
||||
// !TemplateArgumentLoc is a local storage of the MatchCallback!
|
||||
internal::CollectMatchesCallback StorageCB;
|
||||
MatchFinder Finder;
|
||||
Finder.addMatcher(Matcher, &StorageCB);
|
||||
Finder.matchAST(Ctx);
|
||||
const auto *Template =
|
||||
selectFirst<TemplateArgumentLoc>("id", StorageCB.Nodes);
|
||||
assert(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
|
Loading…
Reference in New Issue