Author: Haojian Wu Date: 2022-04-14T11:04:55+02:00 New Revision: 6ba1b9075dc1fef6c32eafa71495bfec803321e4
URL: https://github.com/llvm/llvm-project/commit/6ba1b9075dc1fef6c32eafa71495bfec803321e4 DIFF: https://github.com/llvm/llvm-project/commit/6ba1b9075dc1fef6c32eafa71495bfec803321e4.diff LOG: 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 Added: clang/test/AST/ast-dump-using-template.cpp clang/unittests/AST/TemplateNameTest.cpp Modified: clang-tools-extra/clangd/DumpAST.cpp clang-tools-extra/clangd/SemanticHighlighting.cpp clang/include/clang/AST/PropertiesBase.td clang/include/clang/AST/TemplateName.h clang/include/clang/AST/TextNodeDumper.h clang/lib/AST/ASTContext.cpp clang/lib/AST/ASTImporter.cpp clang/lib/AST/ASTStructuralEquivalence.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/ODRHash.cpp clang/lib/AST/TemplateName.cpp clang/lib/AST/TextNodeDumper.cpp clang/lib/AST/Type.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaTemplate.cpp clang/test/CXX/temp/temp.deduct.guide/p3.cpp clang/tools/libclang/CIndex.cpp clang/unittests/AST/ASTImporterTest.cpp clang/unittests/AST/CMakeLists.txt Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index 4c36de88aea1f..8f640ad821f8c 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -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"); diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index fce83d1834a50..489bb93856a04 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -762,6 +762,7 @@ class CollectExtraHighlightings 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 3da7fd9030aac..0ab18b6d59c3d 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -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() }]; diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index 26c64d0e277ff..1bd86b0bdcf11 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 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. @@ -188,8 +190,12 @@ class SubstTemplateTemplateParmPackStorage /// 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 @@ class TemplateName { /// 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 @@ class TemplateName { 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 @@ class TemplateName { /// 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 41bbf2ec593aa..0ecb8a23dfb36 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -317,6 +317,8 @@ class TextNodeDumper 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 036f970897180..f6bb7574bf71f 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -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(); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index d0041b83f7afb..319f39b43264c 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -9240,6 +9240,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"); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index d4d20dde75e33..05f3470a179d2 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -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; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 50e110ec1f57e..adab764487c7b 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -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 diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 735bcff8f1137..04cbb09356d7b 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -150,6 +150,7 @@ void ODRHash::AddTemplateName(TemplateName Name) { 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 a6d8a7fe7b3dd..8e32c9c6a6ad7 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 @@ 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(); } 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 @@ TemplateName::NameKind TemplateName::getKind() const { } 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 @@ 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 +168,13 @@ DependentTemplateName *TemplateName::getAsDependentTemplateName() const { 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 @@ bool TemplateName::containsUnexpandedParameterPack() const { 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 @@ 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); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index ba8b7b692d588..9131dfbdca778 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -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); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 936550ddc54ce..bb9900ea6eea4 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -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); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 706b3daf918dc..0708e687c5752 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, 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 0ce9ddd36d68c..4467f2357189e 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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 { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index bf65f115116f1..e7195edc9a268 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 @@ 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)) { 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 0000000000000..fbce09d116ed0 --- /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 ec39c0c5ac995..4ba6e8ef03732 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 @@ 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 { diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 9e15ca714ceac..fc6177036b08a 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -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: diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index d9b6f4969abf8..856010cd4d036 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -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 }; }" diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index 2dcef2d2fca0e..48a610c98138a 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_unittest(ASTTests 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 0000000000000..30986920ce28e --- /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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits