diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index 5b81ec213ff98..d42dbf316364a 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -16,12 +16,14 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Stmt.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" @@ -636,7 +638,7 @@ static NamedDecl *getOnlyInstantiationImpl(TemplateDeclTy *TD) { return Only; } -NamedDecl *getOnlyInstantiation(NamedDecl *TemplatedDecl) { +NamedDecl *getOnlyInstantiation(const NamedDecl *TemplatedDecl) { if (TemplateDecl *TD = TemplatedDecl->getDescribedTemplate()) { if (auto *CTD = llvm::dyn_cast(TD)) return getOnlyInstantiationImpl(CTD); @@ -648,6 +650,197 @@ NamedDecl *getOnlyInstantiation(NamedDecl *TemplatedDecl) { return nullptr; } +NamedDecl *getOnlyInstantiatedDecls(const NamedDecl *DependentDecl) { + if (auto *Instantiation = getOnlyInstantiation(DependentDecl)) + return Instantiation; + NamedDecl *OuterTemplate = nullptr; + for (auto *DC = DependentDecl->getDeclContext(); isa(DC); + DC = DC->getParent()) { + auto *RD = cast(DC); + if (auto *I = getOnlyInstantiation(RD)) { + OuterTemplate = I; + break; + } + } + + if (!OuterTemplate) + return nullptr; + + struct Visitor : DeclVisitor { + const NamedDecl *TemplatedDecl; + Visitor(const NamedDecl *TemplatedDecl) : TemplatedDecl(TemplatedDecl) {} + + NamedDecl *VisitCXXRecordDecl(CXXRecordDecl *RD) { + if (RD->getTemplateInstantiationPattern() == TemplatedDecl) + return RD; + for (auto *F : RD->decls()) { + if (auto *Injected = llvm::dyn_cast(F); + Injected && Injected->isInjectedClassName()) + continue; + if (NamedDecl *ND = Visit(F)) + return ND; + } + return nullptr; + } + + NamedDecl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) { + unsigned Size = llvm::range_size(CTD->specializations()); + if (Size != 1) + return nullptr; + return Visit(*CTD->spec_begin()); + } + + NamedDecl *VisitFunctionTemplateDecl(FunctionTemplateDecl *FTD) { + unsigned Size = llvm::range_size(FTD->specializations()); + if (Size != 1) + return nullptr; + return Visit(*FTD->spec_begin()); + } + + NamedDecl *VisitFunctionDecl(FunctionDecl *FD) { + if (FD->getTemplateInstantiationPattern() == TemplatedDecl) + return FD; + return nullptr; + } + + NamedDecl *VisitVarDecl(VarDecl *VD) { + if (VD->getCanonicalDecl()->getSourceRange() == + TemplatedDecl->getCanonicalDecl()->getSourceRange()) + return VD; + return nullptr; + } + + NamedDecl *VisitFieldDecl(FieldDecl *FD) { + if (FD->getCanonicalDecl()->getSourceRange() == + TemplatedDecl->getCanonicalDecl()->getSourceRange()) + return FD; + return nullptr; + } + }; + return Visitor(DependentDecl).Visit(OuterTemplate); +} + +std::optional +getOnlyInstantiatedNode(const DeclContext *StartingPoint, + const DynTypedNode &DependentNode) { + if (auto *CTD = DependentNode.get()) + return getOnlyInstantiatedNode( + StartingPoint, DynTypedNode::create(*CTD->getTemplatedDecl())); + if (auto *FTD = DependentNode.get()) + return getOnlyInstantiatedNode( + StartingPoint, DynTypedNode::create(*FTD->getTemplatedDecl())); + + if (auto *FD = DependentNode.get()) { + auto *ID = getOnlyInstantiatedDecls(FD); + if (!ID) + return std::nullopt; + return DynTypedNode::create(*ID); + } + if (auto *RD = DependentNode.get()) { + auto *ID = getOnlyInstantiatedDecls(RD); + if (!ID) + return std::nullopt; + return DynTypedNode::create(*ID); + } + + NamedDecl *InstantiatedEnclosingDecl = nullptr; + for (auto *DC = StartingPoint; DC; + DC = DC->getParent()) { + auto *ND = llvm::dyn_cast(DC); + if (!ND) + continue; + InstantiatedEnclosingDecl = getOnlyInstantiatedDecls(ND); + if (InstantiatedEnclosingDecl) + break; + } + + if (!InstantiatedEnclosingDecl) + return std::nullopt; + + auto *InstantiatedFunctionDecl = + llvm::dyn_cast(InstantiatedEnclosingDecl); + if (!InstantiatedFunctionDecl) + return std::nullopt; + + struct FullExprVisitor : RecursiveASTVisitor { + const DynTypedNode &DependentNode; + Stmt *Result; + FullExprVisitor(const DynTypedNode &DependentNode) + : DependentNode(DependentNode), Result(nullptr) {} + + bool shouldVisitTemplateInstantiations() const { return true; } + + bool shouldVisitImplicitCode() const { return true; } + + bool VisitStmt(Stmt *S) { + if (S->getSourceRange() == DependentNode.getSourceRange()) { + Result = S; + return false; + } + return true; + } + }; + + FullExprVisitor Visitor(DependentNode); + Visitor.TraverseFunctionDecl(InstantiatedFunctionDecl); + if (Visitor.Result) + return DynTypedNode::create(*Visitor.Result); + return std::nullopt; +} + +NamedDecl * +getOnlyInstantiationForMemberFunction(const CXXMethodDecl *TemplatedDecl) { + if (auto *MemberInstantiation = getOnlyInstantiation(TemplatedDecl)) + return MemberInstantiation; + NamedDecl *OuterTemplate = nullptr; + for (auto *DC = TemplatedDecl->getDeclContext(); isa(DC); + DC = DC->getParent()) { + auto *RD = cast(DC); + if (auto *I = getOnlyInstantiation(RD)) { + OuterTemplate = I; + break; + } + } + if (!OuterTemplate) + return nullptr; + struct Visitor : DeclVisitor { + const CXXMethodDecl *TD; + Visitor(const CXXMethodDecl *TemplatedDecl) : TD(TemplatedDecl) {} + NamedDecl *VisitCXXRecordDecl(CXXRecordDecl *RD) { + for (auto *F : RD->decls()) { + if (!isa(F)) + continue; + if (NamedDecl *ND = Visit(F)) + return ND; + } + return nullptr; + } + + NamedDecl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) { + unsigned Size = llvm::range_size(CTD->specializations()); + if (Size != 1) + return nullptr; + return Visit(*CTD->spec_begin()); + } + + NamedDecl *VisitFunctionTemplateDecl(FunctionTemplateDecl *FTD) { + unsigned Size = llvm::range_size(FTD->specializations()); + if (Size != 1) + return nullptr; + return Visit(*FTD->spec_begin()); + } + + NamedDecl *VisitCXXMethodDecl(CXXMethodDecl *MD) { + auto *Pattern = MD->getTemplateInstantiationPattern(); + if (Pattern == TD) + return MD; + return nullptr; + } + + }; + return Visitor(TemplatedDecl).Visit(OuterTemplate); +} + std::vector getAttributes(const DynTypedNode &N) { std::vector Result; if (const auto *TL = N.get()) { diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h index fb0722d697cd0..74a43fdce2690 100644 --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -177,7 +177,14 @@ TemplateTypeParmTypeLoc getContainedAutoParamType(TypeLoc TL); // If TemplatedDecl is the generic body of a template, and the template has // exactly one visible instantiation, return the instantiated body. -NamedDecl *getOnlyInstantiation(NamedDecl *TemplatedDecl); +NamedDecl *getOnlyInstantiation(const NamedDecl *TemplatedDecl); + +NamedDecl * +getOnlyInstantiationForMemberFunction(const CXXMethodDecl *TemplatedDecl); + +std::optional +getOnlyInstantiatedNode(const DeclContext *StartingPoint, + const DynTypedNode &DependentNode); /// Return attributes attached directly to a node. std::vector getAttributes(const DynTypedNode &); diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 839cf6332fe8b..0045c4e64c6f7 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -133,7 +133,7 @@ struct TargetFinder { using Rel = DeclRelation; private: - const HeuristicResolver *Resolver; + const HeuristicResolver &Resolver; llvm::SmallDenseMap> Decls; @@ -153,7 +153,7 @@ struct TargetFinder { } public: - TargetFinder(const HeuristicResolver *Resolver) : Resolver(Resolver) {} + TargetFinder(const HeuristicResolver &Resolver) : Resolver(Resolver) {} llvm::SmallVector, 1> takeDecls() const { using ValTy = std::pair; @@ -197,10 +197,8 @@ struct TargetFinder { Flags |= Rel::Alias; // continue with the alias } else if (const UnresolvedUsingValueDecl *UUVD = dyn_cast(D)) { - if (Resolver) { - for (const NamedDecl *Target : Resolver->resolveUsingValueDecl(UUVD)) { - add(Target, Flags); // no Underlying as this is a non-renaming alias - } + for (const NamedDecl *Target : Resolver.resolveUsingValueDecl(UUVD)) { + add(Target, Flags); // no Underlying as this is a non-renaming alias } Flags |= Rel::Alias; // continue with the alias } else if (isa(D)) { @@ -304,17 +302,13 @@ struct TargetFinder { } void VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) { - if (Outer.Resolver) { - for (const NamedDecl *D : Outer.Resolver->resolveMemberExpr(E)) { - Outer.add(D, Flags); - } + for (const NamedDecl *D : Outer.Resolver.resolveMemberExpr(E)) { + Outer.add(D, Flags); } } void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) { - if (Outer.Resolver) { - for (const NamedDecl *D : Outer.Resolver->resolveDeclRefExpr(E)) { - Outer.add(D, Flags); - } + for (const NamedDecl *D : Outer.Resolver.resolveDeclRefExpr(E)) { + Outer.add(D, Flags); } } void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *OIRE) { @@ -407,20 +401,16 @@ struct TargetFinder { Outer.add(TD->getTemplatedDecl(), Flags | Rel::TemplatePattern); } void VisitDependentNameType(const DependentNameType *DNT) { - if (Outer.Resolver) { - for (const NamedDecl *ND : - Outer.Resolver->resolveDependentNameType(DNT)) { - Outer.add(ND, Flags); - } + for (const NamedDecl *ND : + Outer.Resolver.resolveDependentNameType(DNT)) { + Outer.add(ND, Flags); } } void VisitDependentTemplateSpecializationType( const DependentTemplateSpecializationType *DTST) { - if (Outer.Resolver) { - for (const NamedDecl *ND : - Outer.Resolver->resolveTemplateSpecializationType(DTST)) { - Outer.add(ND, Flags); - } + for (const NamedDecl *ND : + Outer.Resolver.resolveTemplateSpecializationType(DTST)) { + Outer.add(ND, Flags); } } void VisitTypedefType(const TypedefType *TT) { @@ -488,10 +478,7 @@ struct TargetFinder { add(NNS->getAsNamespaceAlias(), Flags); return; case NestedNameSpecifier::Identifier: - if (Resolver) { - add(QualType(Resolver->resolveNestedNameSpecifierToType(NNS), 0), - Flags); - } + add(QualType(Resolver.resolveNestedNameSpecifierToType(NNS), 0), Flags); return; case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: @@ -542,7 +529,7 @@ struct TargetFinder { } // namespace llvm::SmallVector, 1> -allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver) { +allTargetDecls(const DynTypedNode &N, const HeuristicResolver &Resolver) { dlog("allTargetDecls({0})", nodeToString(N)); TargetFinder Finder(Resolver); DeclRelationSet Flags; @@ -573,7 +560,7 @@ allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver) { llvm::SmallVector targetDecl(const DynTypedNode &N, DeclRelationSet Mask, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { llvm::SmallVector Result; for (const auto &Entry : allTargetDecls(N, Resolver)) { if (!(Entry.second & ~Mask)) @@ -584,7 +571,7 @@ targetDecl(const DynTypedNode &N, DeclRelationSet Mask, llvm::SmallVector explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { assert(!(Mask & (DeclRelation::TemplatePattern | DeclRelation::TemplateInstantiation)) && "explicitReferenceTargets handles templates on its own"); @@ -616,11 +603,11 @@ explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask, namespace { llvm::SmallVector refInDecl(const Decl *D, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { struct Visitor : ConstDeclVisitor { - Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {} + Visitor(const HeuristicResolver &Resolver) : Resolver(Resolver) {} - const HeuristicResolver *Resolver; + const HeuristicResolver &Resolver; llvm::SmallVector Refs; void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { @@ -741,11 +728,11 @@ llvm::SmallVector refInDecl(const Decl *D, } llvm::SmallVector refInStmt(const Stmt *S, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { struct Visitor : ConstStmtVisitor { - Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {} + Visitor(const HeuristicResolver &Resolver) : Resolver(Resolver) {} - const HeuristicResolver *Resolver; + const HeuristicResolver &Resolver; // FIXME: handle more complicated cases: more ObjC, designated initializers. llvm::SmallVector Refs; @@ -852,11 +839,11 @@ llvm::SmallVector refInStmt(const Stmt *S, } llvm::SmallVector -refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) { +refInTypeLoc(TypeLoc L, const HeuristicResolver &Resolver) { struct Visitor : TypeLocVisitor { - Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {} + Visitor(const HeuristicResolver &Resolver) : Resolver(Resolver) {} - const HeuristicResolver *Resolver; + const HeuristicResolver &Resolver; llvm::SmallVector Refs; void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { @@ -966,7 +953,7 @@ class ExplicitReferenceCollector : public RecursiveASTVisitor { public: ExplicitReferenceCollector(llvm::function_ref Out, - const HeuristicResolver *Resolver) + const HeuristicResolver &Resolver) : Out(Out), Resolver(Resolver) { assert(Out); } @@ -1144,7 +1131,7 @@ class ExplicitReferenceCollector } llvm::function_ref Out; - const HeuristicResolver *Resolver; + const HeuristicResolver &Resolver; /// TypeLocs starting at these locations must be skipped, see /// TraverseElaboratedTypeSpecifierLoc for details. llvm::DenseSet TypeLocsToSkip; @@ -1153,19 +1140,19 @@ class ExplicitReferenceCollector void findExplicitReferences(const Stmt *S, llvm::function_ref Out, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { assert(S); ExplicitReferenceCollector(Out, Resolver).TraverseStmt(const_cast(S)); } void findExplicitReferences(const Decl *D, llvm::function_ref Out, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { assert(D); ExplicitReferenceCollector(Out, Resolver).TraverseDecl(const_cast(D)); } void findExplicitReferences(const ASTContext &AST, llvm::function_ref Out, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { ExplicitReferenceCollector(Out, Resolver) .TraverseAST(const_cast(AST)); } diff --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h index b41c547095100..b5d10ea8be189 100644 --- a/clang-tools-extra/clangd/FindTarget.h +++ b/clang-tools-extra/clangd/FindTarget.h @@ -81,14 +81,14 @@ class DeclRelationSet; /// FIXME: some AST nodes cannot be DynTypedNodes, these cannot be specified. llvm::SmallVector targetDecl(const DynTypedNode &, DeclRelationSet Mask, - const HeuristicResolver *Resolver); + const HeuristicResolver &Resolver); /// Similar to targetDecl(), however instead of applying a filter, all possible /// decls are returned along with their DeclRelationSets. /// This is suitable for indexing, where everything is recorded and filtering /// is applied later. llvm::SmallVector, 1> -allTargetDecls(const DynTypedNode &, const HeuristicResolver *); +allTargetDecls(const DynTypedNode &, const HeuristicResolver &); enum class DeclRelation : unsigned { // Template options apply when the declaration is an instantiated template. @@ -147,13 +147,13 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R); /// FIXME: extend to report location information about declaration names too. void findExplicitReferences(const Stmt *S, llvm::function_ref Out, - const HeuristicResolver *Resolver); + const HeuristicResolver &Resolver); void findExplicitReferences(const Decl *D, llvm::function_ref Out, - const HeuristicResolver *Resolver); + const HeuristicResolver &Resolver); void findExplicitReferences(const ASTContext &AST, llvm::function_ref Out, - const HeuristicResolver *Resolver); + const HeuristicResolver &Resolver); /// Find declarations explicitly referenced in the source code defined by \p N. /// For templates, will prefer to return a template instantiation whenever @@ -166,7 +166,7 @@ void findExplicitReferences(const ASTContext &AST, /// \p Mask should not contain TemplatePattern or TemplateInstantiation. llvm::SmallVector explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask, - const HeuristicResolver *Resolver); + const HeuristicResolver &Resolver); // Boring implementation details of bitfield. diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp index 3c147b6b582bf..072ff4cf13d10 100644 --- a/clang-tools-extra/clangd/HeuristicResolver.cpp +++ b/clang-tools-extra/clangd/HeuristicResolver.cpp @@ -7,10 +7,15 @@ //===----------------------------------------------------------------------===// #include "HeuristicResolver.h" +#include "AST.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTTypeTraits.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" namespace clang { @@ -34,7 +39,7 @@ const auto TemplateFilter = [](const NamedDecl *D) { namespace { const Type *resolveDeclsToType(const std::vector &Decls, - ASTContext &Ctx) { + const ASTContext &Ctx) { if (Decls.size() != 1) // Names an overload set -- just bail. return nullptr; if (const auto *TD = dyn_cast(Decls[0])) { @@ -46,6 +51,106 @@ const Type *resolveDeclsToType(const std::vector &Decls, return nullptr; } +// Visitor that helps to extract deduced type from instantiated entities. +// This merely performs the source location comparison against each Decl +// until it finds a Decl with the same location as the +// dependent one. Its associated type will then be extracted. +struct InstantiatedDeclVisitor : RecursiveASTVisitor { + + InstantiatedDeclVisitor(NamedDecl *DependentDecl) + : DependentDecl(DependentDecl) {} + + bool shouldVisitTemplateInstantiations() const { return true; } + + bool shouldVisitLambdaBody() const { return true; } + + bool shouldVisitImplicitCode() const { return true; } + + template bool onDeclVisited(D *MaybeInstantiated) { + if (MaybeInstantiated->getDeclContext()->isDependentContext()) + return true; + auto *Dependent = dyn_cast(DependentDecl); + if (!Dependent) + return true; + auto LHS = MaybeInstantiated->getTypeSourceInfo(), + RHS = Dependent->getTypeSourceInfo(); + if (!LHS || !RHS) + return true; + if (LHS->getTypeLoc().getSourceRange() != + RHS->getTypeLoc().getSourceRange()) + return true; + DeducedType = MaybeInstantiated->getType(); + return false; + } + + bool VisitFieldDecl(FieldDecl *FD) { return onDeclVisited(FD); } + + bool VisitVarDecl(VarDecl *VD) { return onDeclVisited(VD); } + + NamedDecl *DependentDecl; + QualType DeducedType; +}; + +/// Attempt to resolve the dependent type from the surrounding context for which +/// a single instantiation is available. +const Type * +resolveTypeFromInstantiatedTemplate(const DeclContext *DC, + const CXXDependentScopeMemberExpr *Expr) { + + std::optional Node = + getOnlyInstantiatedNode(DC, DynTypedNode::create(*Expr)); + if (!Node) + return nullptr; + + if (auto *ME = Node->get()) + return ME->getBase()->getType().getTypePtrOrNull(); + + return nullptr; + + if (Expr->isImplicitAccess()) + return nullptr; + + auto *Base = Expr->getBase(); + NamedDecl *ND = nullptr; + if (auto *CXXMember = dyn_cast(Base)) + ND = CXXMember->getMemberDecl(); + + if (auto *DRExpr = dyn_cast(Base)) + ND = DRExpr->getFoundDecl(); + + // FIXME: Handle CXXUnresolvedConstructExpr. This kind of type doesn't have + // available Decls to be matched against. Which inhibits the current heuristic + // from resolving expressions such as `T().fo^o()`, where T is a + // single-instantiated template parameter. + if (!ND) + return nullptr; + + NamedDecl *Instantiation = nullptr; + + // Find out a single instantiation that we can start with. The enclosing + // context for the current Decl might not be a templated entity (e.g. a member + // function inside a class template), hence we shall walk up the decl + // contexts first. + for (auto *EnclosingContext = ND->getDeclContext(); EnclosingContext; + EnclosingContext = EnclosingContext->getParent()) { + if (auto *ND = dyn_cast(EnclosingContext)) { + Instantiation = getOnlyInstantiation(ND); + if (Instantiation) + break; + } + } + + if (!Instantiation) + return nullptr; + + // This will traverse down the instantiation entity, visit each Decl, and + // extract the deduced type for the undetermined Decl `ND`. + InstantiatedDeclVisitor Visitor(ND); + Visitor.TraverseDecl(Instantiation); + + return Visitor.DeducedType.getTypePtrOrNull(); +} + } // namespace // Helper function for HeuristicResolver::resolveDependentMember() @@ -150,8 +255,14 @@ std::vector HeuristicResolver::resolveMemberExpr( if (ME->isArrow()) { BaseType = getPointeeType(BaseType); } + if (!BaseType) return {}; + + if (BaseType->isDependentType()) + if (auto *MaybeResolved = resolveTypeFromInstantiatedTemplate(EnclosingDecl, ME)) + BaseType = MaybeResolved; + if (const auto *BT = BaseType->getAs()) { // If BaseType is the type of a dependent expression, it's just // represented as BuiltinType::Dependent which gives us no information. We diff --git a/clang-tools-extra/clangd/HeuristicResolver.h b/clang-tools-extra/clangd/HeuristicResolver.h index dc04123d37593..16650e503a1d0 100644 --- a/clang-tools-extra/clangd/HeuristicResolver.h +++ b/clang-tools-extra/clangd/HeuristicResolver.h @@ -45,7 +45,8 @@ namespace clangd { // not a specialization. More advanced heuristics may be added in the future. class HeuristicResolver { public: - HeuristicResolver(ASTContext &Ctx) : Ctx(Ctx) {} + HeuristicResolver(const ASTContext &Ctx, const DeclContext *EnclosingDecl = nullptr) + : Ctx(Ctx), EnclosingDecl(EnclosingDecl) {} // Try to heuristically resolve certain types of expressions, declarations, or // types to one or more likely-referenced declarations. @@ -76,7 +77,8 @@ class HeuristicResolver { const Type *getPointeeType(const Type *T) const; private: - ASTContext &Ctx; + const ASTContext &Ctx; + const DeclContext *EnclosingDecl; // Given a tag-decl type and a member name, heuristically resolve the // name to one or more declarations. diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index a868d3bb4e3fa..32b34d1703c31 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -1358,8 +1358,9 @@ std::optional getHover(ParsedAST &AST, Position Pos, SelectionTree::createRight(AST.getASTContext(), TB, Offset, Offset); if (const SelectionTree::Node *N = ST.commonAncestor()) { // FIXME: Fill in HighlightRange with range coming from N->ASTNode. - auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Alias, - AST.getHeuristicResolver()); + auto Decls = explicitReferenceTargets( + N->ASTNode, DeclRelation::Alias, + AST.getHeuristicResolver(&N->getDeclContext())); if (const auto *DeclToUse = pickDeclToUse(Decls)) { HoverCountMetric.record(1, "decl"); HI = getHoverContents(DeclToUse, PP, Index, TB); diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp index b540c273cbd59..fc08d29cd3776 100644 --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -627,7 +627,7 @@ class InlayHintVisitor : public RecursiveASTVisitor { isa(E)) return true; - auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E); + auto CalleeDecls = Resolver.resolveCalleeOfCallExpr(E); if (CalleeDecls.size() != 1) return true; @@ -1274,7 +1274,7 @@ class InlayHintVisitor : public RecursiveASTVisitor { std::optional RestrictRange; FileID MainFileID; StringRef MainFileBuf; - const HeuristicResolver *Resolver; + HeuristicResolver Resolver; PrintingPolicy TypeHintPolicy; }; diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index edd0f77b1031e..741e9e96f3437 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -835,7 +835,6 @@ ParsedAST::ParsedAST(PathRef TUPath, llvm::StringRef Version, Marks(std::move(Marks)), Diags(std::move(Diags)), LocalTopLevelDecls(std::move(LocalTopLevelDecls)), Includes(std::move(Includes)) { - Resolver = std::make_unique(getASTContext()); assert(this->Clang); assert(this->Action); } diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h index c68fdba6bd26c..1f1425a995005 100644 --- a/clang-tools-extra/clangd/ParsedAST.h +++ b/clang-tools-extra/clangd/ParsedAST.h @@ -30,6 +30,7 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Syntax/Tokens.h" +#include "HeuristicResolver.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include @@ -118,8 +119,9 @@ class ParsedAST { /// AST. Might be std::nullopt if no Preamble is used. std::optional preambleVersion() const; - const HeuristicResolver *getHeuristicResolver() const { - return Resolver.get(); + HeuristicResolver + getHeuristicResolver(const DeclContext *EnclosingDecl = nullptr) const { + return HeuristicResolver(getASTContext(), EnclosingDecl); } private: @@ -159,7 +161,6 @@ class ParsedAST { // top-level decls from the preamble. std::vector LocalTopLevelDecls; IncludeStructure Includes; - std::unique_ptr Resolver; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index 49e479abf4562..d5cd8a3c089a8 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -95,9 +95,9 @@ bool isUniqueDefinition(const NamedDecl *Decl) { } std::optional kindForType(const Type *TP, - const HeuristicResolver *Resolver); + const HeuristicResolver &Resolver); std::optional kindForDecl(const NamedDecl *D, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { if (auto *USD = dyn_cast(D)) { if (auto *Target = USD->getTargetDecl()) D = Target; @@ -169,7 +169,7 @@ std::optional kindForDecl(const NamedDecl *D, if (isa(D)) return HighlightingKind::Label; if (const auto *UUVD = dyn_cast(D)) { - auto Targets = Resolver->resolveUsingValueDecl(UUVD); + auto Targets = Resolver.resolveUsingValueDecl(UUVD); if (!Targets.empty() && Targets[0] != UUVD) { return kindForDecl(Targets[0], Resolver); } @@ -178,7 +178,7 @@ std::optional kindForDecl(const NamedDecl *D, return std::nullopt; } std::optional kindForType(const Type *TP, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { if (!TP) return std::nullopt; if (TP->isBuiltinType()) // Builtins are special, they do not have decls. @@ -416,9 +416,11 @@ class HighlightingFilter { /// Consumes source locations and maps them to text ranges for highlightings. class HighlightingsBuilder { public: - HighlightingsBuilder(const ParsedAST &AST, const HighlightingFilter &Filter) + HighlightingsBuilder(const ParsedAST &AST, const HighlightingFilter &Filter, + const HeuristicResolver &Resolver) : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()), - LangOpts(AST.getLangOpts()), Filter(Filter) {} + LangOpts(AST.getLangOpts()), Filter(Filter), + Resolver(Resolver) {} HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) { auto Range = getRangeForSourceLocation(Loc); @@ -567,7 +569,7 @@ class HighlightingsBuilder { return WithInactiveLines; } - const HeuristicResolver *getResolver() const { return Resolver; } + const HeuristicResolver &getResolver() const { return Resolver; } private: std::optional getRangeForSourceLocation(SourceLocation Loc) { @@ -589,7 +591,7 @@ class HighlightingsBuilder { HighlightingFilter Filter; std::vector Tokens; std::map> ExtraModifiers; - const HeuristicResolver *Resolver = nullptr; + const HeuristicResolver &Resolver; // returned from addToken(InvalidLoc) HighlightingToken InvalidHighlightingToken; }; @@ -1150,7 +1152,7 @@ getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens) { if (!IncludeInactiveRegionTokens) Filter.disableKind(HighlightingKind::InactiveCode); // Add highlightings for AST nodes. - HighlightingsBuilder Builder(AST, Filter); + HighlightingsBuilder Builder(AST, Filter, AST.getHeuristicResolver()); // Highlight 'decltype' and 'auto' as their underlying types. CollectExtraHighlightings(Builder).TraverseAST(C); // Highlight all decls and references coming from the AST. diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index f55d2a5623956..835cc9292fd92 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -188,7 +188,8 @@ getDeclAtPositionWithRelations(ParsedAST &AST, SourceLocation Pos, // This makes the `override` hack work. if (N->ASTNode.get() && N->Parent) N = N->Parent; - llvm::copy_if(allTargetDecls(N->ASTNode, AST.getHeuristicResolver()), + llvm::copy_if(allTargetDecls(N->ASTNode, AST.getHeuristicResolver( + &N->getDeclContext())), std::back_inserter(Result), [&](auto &Entry) { return !(Entry.second & ~Relations); }); } @@ -1243,7 +1244,8 @@ std::vector findDocumentHighlights(ParsedAST &AST, DeclRelationSet Relations = DeclRelation::TemplatePattern | DeclRelation::Alias; auto TargetDecls = - targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver()); + targetDecl(N->ASTNode, Relations, + AST.getHeuristicResolver(&N->getDeclContext())); if (!TargetDecls.empty()) { // FIXME: we may get multiple DocumentHighlights with the same location // and different kinds, deduplicate them. @@ -2014,8 +2016,8 @@ static QualType typeForNode(const SelectionTree::Node *N) { // Given a type targeted by the cursor, return one or more types that are more interesting // to target. -static void unwrapFindType( - QualType T, const HeuristicResolver* H, llvm::SmallVector& Out) { +static void unwrapFindType(QualType T, const HeuristicResolver &H, + llvm::SmallVector &Out) { if (T.isNull()) return; @@ -2043,18 +2045,18 @@ static void unwrapFindType( } // For smart pointer types, add the underlying type - if (H) - if (const auto* PointeeType = H->getPointeeType(T.getNonReferenceType().getTypePtr())) { - unwrapFindType(QualType(PointeeType, 0), H, Out); - return Out.push_back(T); - } + if (const auto *PointeeType = + H.getPointeeType(T.getNonReferenceType().getTypePtr())) { + unwrapFindType(QualType(PointeeType, 0), H, Out); + return Out.push_back(T); + } return Out.push_back(T); } // Convenience overload, to allow calling this without the out-parameter -static llvm::SmallVector unwrapFindType( - QualType T, const HeuristicResolver* H) { +static llvm::SmallVector unwrapFindType(QualType T, + const HeuristicResolver &H) { llvm::SmallVector Result; unwrapFindType(T, H, Result); return Result; @@ -2076,10 +2078,11 @@ std::vector findType(ParsedAST &AST, Position Pos, std::vector LocatedSymbols; // NOTE: unwrapFindType might return duplicates for something like - // unique_ptr>. Let's *not* remove them, because it gives you some - // information about the type you may have not known before - // (since unique_ptr> != unique_ptr). - for (const QualType& Type : unwrapFindType(typeForNode(N), AST.getHeuristicResolver())) + // unique_ptr>. Let's *not* remove them, because it gives you + // some information about the type you may have not known before (since + // unique_ptr> != unique_ptr). + for (const QualType &Type : unwrapFindType( + typeForNode(N), AST.getHeuristicResolver(&N->getDeclContext()))) llvm::copy(locateSymbolForType(AST, Type, Index), std::back_inserter(LocatedSymbols)); diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index 11f9e4627af76..9f1e04ce9af84 100644 --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -168,7 +168,7 @@ llvm::DenseSet locateDeclAt(ParsedAST &AST, for (const NamedDecl *D : targetDecl(SelectedNode->ASTNode, DeclRelation::Alias | DeclRelation::TemplatePattern, - AST.getHeuristicResolver())) { + AST.getHeuristicResolver(&SelectedNode->getDeclContext()))) { D = pickInterestingTarget(D); Result.insert(canonicalRenameDecl(D)); } diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp index d6556bba14725..7dc4053c401ab 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp @@ -123,7 +123,7 @@ bool checkDeclsAreVisible(const llvm::DenseSet &DeclRefs, // still valid in context of Target. llvm::Expected qualifyAllDecls(const FunctionDecl *FD, const FunctionDecl *Target, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { // There are three types of spellings that needs to be qualified in a function // body: // - Types: Foo -> ns::Foo @@ -221,7 +221,7 @@ llvm::Expected qualifyAllDecls(const FunctionDecl *FD, /// \p Dest to be the same as in \p Source. llvm::Expected renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { llvm::DenseMap ParamToNewName; llvm::DenseMap> RefLocs; auto HandleParam = [&](const NamedDecl *DestParam, diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp index b84ae04072f2c..d44f0ef13c772 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp @@ -181,7 +181,7 @@ deleteTokensWithKind(const syntax::TokenBuffer &TokBuf, tok::TokenKind Kind, llvm::Expected getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace, const syntax::TokenBuffer &TokBuf, - const HeuristicResolver *Resolver) { + const HeuristicResolver &Resolver) { auto &AST = FD->getASTContext(); auto &SM = AST.getSourceManager(); auto TargetContext = findContextForNS(TargetNamespace, FD->getDeclContext()); diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp index 0302839c58252..28c19e097ec98 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp @@ -176,7 +176,7 @@ struct ExtractionZone { // This performs a partial AST traversal proportional to the size of the // enclosing function, so it is possibly expensive. bool requiresHoisting(const SourceManager &SM, - const HeuristicResolver *Resolver) const { + const HeuristicResolver &Resolver) const { // First find all the declarations that happened inside extraction zone. llvm::SmallSet DeclsInExtZone; for (auto *RootStmt : RootStmts) { diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp index 3101bf34acd71..229c7aa6681c4 100644 --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/AttrKinds.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/StringRef.h" @@ -320,6 +321,90 @@ TEST(ClangdAST, GetOnlyInstantiation) { } } +TEST(ClangdAST, DISABLED_GetOnlyInstantiationForMemberFunction) { + struct { + const char *Code; + const char *MemberTemplate; + const char *ExpectedInstantiation; + } Cases[] = { + { + R"cpp( + template struct Foo { + template struct Bar { + U Baz(T); + }; + }; + double X = Foo::Bar().Baz(3); + )cpp", + "Baz", + "double Baz(int)", + }, + { + R"cpp( + template struct Foo { + struct Bar { + template U Baz(T); + }; + }; + double X = Foo::Bar().Baz(3); + )cpp", + "Baz", + "template<> double Baz(int)", + }, + { + R"cpp( + struct Foo { + struct Bar { + template T Baz(T); + }; + }; + int X = Foo::Bar().Baz(3); + )cpp", + "Baz", + "template<> int Baz(int)", + }, + { + R"cpp( + struct Foo { + template struct Bar { + template static U Baz(T); + }; + }; + int X = Foo::Bar::Baz(3); + )cpp", + "Baz", + "template<> static double Baz(int)", + }, + + }; + for (const auto &Case : Cases) { + SCOPED_TRACE(Case.Code); + auto TU = TestTU::withCode(Case.Code); + TU.ExtraArgs.push_back("-std=c++20"); + auto AST = TU.build(); + PrintingPolicy PP = AST.getASTContext().getPrintingPolicy(); + PP.TerseOutput = true; + std::string Name; + if (auto *Result = getOnlyInstantiationForMemberFunction( + dyn_cast_if_present( + &findDecl(AST, [&](const NamedDecl &D) { + auto *MD = dyn_cast(&D); + IdentifierInfo *Id = D.getIdentifier(); + if (!MD || !Id) + return false; + return MD->isDependentContext() && + Id->getName() == Case.MemberTemplate; + })))) { + llvm::raw_string_ostream OS(Name); + Result->print(OS, PP); + } + if (Case.ExpectedInstantiation) + EXPECT_EQ(Case.ExpectedInstantiation, Name); + else + EXPECT_THAT(Name, IsEmpty()); + } +} + TEST(ClangdAST, GetContainedAutoParamType) { auto TU = TestTU::withCode(R"cpp( int withAuto( diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index fbd10c4a47a79..e4bb157b7b3d0 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -86,8 +86,8 @@ class TargetDeclTest : public ::testing::Test { EXPECT_EQ(N->kind(), NodeType) << Selection; std::vector ActualDecls; - for (const auto &Entry : - allTargetDecls(N->ASTNode, AST.getHeuristicResolver())) + for (const auto &Entry : allTargetDecls( + N->ASTNode, AST.getHeuristicResolver(&N->getDeclContext()))) ActualDecls.emplace_back(Entry.first, Entry.second); return ActualDecls; } diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index f53cbf01b7992..1c800ea791613 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -1222,6 +1222,42 @@ TEST(LocateSymbol, TextualSmoke) { hasID(getSymbolID(&findDecl(AST, "MyClass")))))); } +TEST(LocateSymbol, DISABLED_DeduceDependentTypeFromSingleInstantiation) { + Annotations T(R"cpp( + struct Widget { + int $range_1[[method]](int); + }; + template struct A { + template struct B { + template T foo(U, V arg) { + V copy; + int not_used = copy.$point_1^method(T{}); + not_used = V().$point_2^method(T{}); + auto lambda = [](auto w) { + return w.$point_3^method(T{}); + }; + lambda(copy); + arg.$point_4^method(T{}); + } + }; + }; + int main() { + int X = A::B().foo(3.14, Widget{}); + } + )cpp"); + + auto TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + EXPECT_THAT(locateSymbolAt(AST, T.point("point_1")), + ElementsAre(sym("method", T.range("range_1"), std::nullopt))); + EXPECT_THAT(locateSymbolAt(AST, T.point("point_2")), + ElementsAre(sym("method", T.range("range_1"), std::nullopt))); + EXPECT_THAT(locateSymbolAt(AST, T.point("point_3")), + ElementsAre(sym("method", T.range("range_1"), std::nullopt))); + EXPECT_THAT(locateSymbolAt(AST, T.point("point_4")), + ElementsAre(sym("method", T.range("range_1"), std::nullopt))); +} + TEST(LocateSymbol, Textual) { const char *Tests[] = { R"cpp(// Comment