diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -450,6 +450,11 @@ void VisitTemplateTypeParmType(const TemplateTypeParmType *TTPT) { Outer.add(TTPT->getDecl(), Flags); } + void + VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *STTPT) { + Outer.add(STTPT->getReplacedParameter()->getDecl(), Flags | Rel::Alias); + Outer.add(STTPT->getReplacementType(), Flags | Rel::Underlying); + } void VisitObjCInterfaceType(const ObjCInterfaceType *OIT) { Outer.add(OIT->getDecl(), Flags); } diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -605,6 +605,8 @@ // For simple cases (not inside macros) we prune subtrees that don't intersect. class SelectionVisitor : public RecursiveASTVisitor { public: + bool shouldVisitSingleTemplateInstantiationInstead() const { return true; } + // Runs the visitor to gather selected nodes and their ancestors. // If there is any selection, the root (TUDecl) is the first node. static std::deque collect(ASTContext &AST, diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -94,6 +94,28 @@ SecondMethodPtrTy>::value>::isSameMethod(FirstMethodPtr, SecondMethodPtr); } +// Paper over API difference between Function templates and others. +template +TemplateSpecializationKind getTemplateSpecializationKind(TemplatedDeclTy *TD) { + return TD->getSpecializationKind(); +} + +inline TemplateSpecializationKind +getTemplateSpecializationKind(FunctionDecl *FD) { + return FD->getTemplateSpecializationKind(); +} + +template +auto getTemplatedDecl(TemplateTy *D, bool SingleInstantiationInstead) + -> decltype(D->getTemplatedDecl()) { + if (SingleInstantiationInstead && llvm::size(D->specializations()) == 1) { + auto *Candidate = *D->spec_begin(); + if (getTemplateSpecializationKind(Candidate) != TSK_ExplicitSpecialization) + return Candidate; + } + return D->getTemplatedDecl(); +} + } // end namespace detail /// A class that does preorder or postorder @@ -178,6 +200,11 @@ /// template instantiations. bool shouldVisitTemplateInstantiations() const { return false; } + /// When encountering a singly-instantiated template definition, + /// should we walk the instantiated body instead of the template body? + /// Ignored if shouldVisitTemplateInstantiations() is true. + bool shouldVisitSingleTemplateInstantiationInstead() const { return false; } + /// Return whether this visitor should recurse into the types of /// TypeLocs. bool shouldWalkTypesOfTypeLocs() const { return true; } @@ -1761,24 +1788,27 @@ // This macro unifies the traversal of class, variable and function // template declarations. -#define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND) \ - DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, { \ - TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \ - TRY_TO(TraverseDecl(D->getTemplatedDecl())); \ - \ - /* By default, we do not traverse the instantiations of \ - class templates since they do not appear in the user code. The \ - following code optionally traverses them. \ - \ - We only traverse the class instantiations when we see the canonical \ - declaration of the template, to ensure we only visit them once. */ \ - if (getDerived().shouldVisitTemplateInstantiations() && \ - D == D->getCanonicalDecl()) \ - TRY_TO(TraverseTemplateInstantiations(D)); \ - \ - /* Note that getInstantiatedFromMemberTemplate() is just a link \ - from a template instantiation back to the template from which \ - it was instantiated, and thus should not be traversed. */ \ +#define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND) \ + DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, { \ + TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \ + TRY_TO(TraverseDecl(detail::getTemplatedDecl( \ + D, \ + !getDerived().shouldVisitTemplateInstantiations() && \ + getDerived().shouldVisitSingleTemplateInstantiationInstead()))); \ + \ + /* By default, we do not traverse the instantiations of \ + class templates since they do not appear in the user code. The \ + following code optionally traverses them. \ + \ \ + We only traverse the class instantiations when we see the canonical \ + declaration of the template, to ensure we only visit them once. */ \ + if (getDerived().shouldVisitTemplateInstantiations() && \ + D == D->getCanonicalDecl()) \ + TRY_TO(TraverseTemplateInstantiations(D)); \ + \ + /* Note that getInstantiatedFromMemberTemplate() is just a link \ + from a template instantiation back to the template from which \ + it was instantiated, and thus should not be traversed. */ \ }) DEF_TRAVERSE_TMPL_DECL(Class) @@ -2547,7 +2577,14 @@ TRY_TO(TraverseDecl(S->getLambdaClass())); } else { // We need to poke around to find the bits that might be explicitly written. - TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + FunctionDecl *CallOperator = S->getCallOperator(); + if (FunctionTemplateDecl *CallTemplate = S->getDependentCallOperator()) { + CallOperator = detail::getTemplatedDecl( + CallTemplate, + !getDerived().shouldVisitTemplateInstantiations() && + getDerived().shouldVisitSingleTemplateInstantiationInstead()); + } + TypeLoc TL = CallOperator->getTypeSourceInfo()->getTypeLoc(); FunctionProtoTypeLoc Proto = TL.getAsAdjusted(); TRY_TO(TraverseTemplateParameterListHelper(S->getTemplateParameterList())); @@ -2568,7 +2605,7 @@ TRY_TO(TraverseTypeLoc(Proto.getReturnLoc())); TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getTrailingRequiresClause()); - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody()); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(CallOperator->getBody()); } ShouldVisitChildren = false; })