diff options
Diffstat (limited to 'clang-tools-extra/clangd/FindTarget.cpp')
| -rw-r--r-- | clang-tools-extra/clangd/FindTarget.cpp | 296 |
1 files changed, 295 insertions, 1 deletions
diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 2631185b47c..4d69318b579 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -10,22 +10,33 @@ #include "AST.h" #include "Logger.h" #include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" #include "clang/AST/TypeLocVisitor.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" +#include <utility> namespace clang { namespace clangd { namespace { +using ast_type_traits::DynTypedNode; LLVM_ATTRIBUTE_UNUSED std::string nodeToString(const ast_type_traits::DynTypedNode &N) { @@ -348,12 +359,274 @@ allTargetDecls(const ast_type_traits::DynTypedNode &N) { llvm::SmallVector<const Decl *, 1> targetDecl(const ast_type_traits::DynTypedNode &N, DeclRelationSet Mask) { llvm::SmallVector<const Decl *, 1> Result; - for (const auto &Entry : allTargetDecls(N)) + for (const auto &Entry : allTargetDecls(N)) { if (!(Entry.second & ~Mask)) Result.push_back(Entry.first); + } return Result; } +namespace { +/// Find declarations explicitly referenced in the source code defined by \p N. +/// For templates, will prefer to return a template instantiation whenever +/// possible. However, can also return a template pattern if the specialization +/// cannot be picked, e.g. in dependent code or when there is no corresponding +/// Decl for a template instantitation, e.g. for templated using decls: +/// template <class T> using Ptr = T*; +/// Ptr<int> x; +/// ^~~ there is no Decl for 'Ptr<int>', so we return the template pattern. +llvm::SmallVector<const NamedDecl *, 1> +explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask = {}) { + assert(!(Mask & (DeclRelation::TemplatePattern | + DeclRelation::TemplateInstantiation)) && + "explicitRefenceTargets handles templates on its own"); + auto Decls = allTargetDecls(N); + + // We prefer to return template instantiation, but fallback to template + // pattern if instantiation is not available. + Mask |= DeclRelation::TemplatePattern | DeclRelation::TemplateInstantiation; + + llvm::SmallVector<const NamedDecl *, 1> TemplatePatterns; + llvm::SmallVector<const NamedDecl *, 1> Targets; + bool SeenTemplateInstantiations = false; + for (auto &D : Decls) { + if (D.second & ~Mask) + continue; + if (D.second & DeclRelation::TemplatePattern) { + TemplatePatterns.push_back(llvm::cast<NamedDecl>(D.first)); + continue; + } + if (D.second & DeclRelation::TemplateInstantiation) + SeenTemplateInstantiations = true; + Targets.push_back(llvm::cast<NamedDecl>(D.first)); + } + if (!SeenTemplateInstantiations) + Targets.insert(Targets.end(), TemplatePatterns.begin(), + TemplatePatterns.end()); + return Targets; +} + +Optional<ReferenceLoc> refInDecl(const Decl *D) { + struct Visitor : ConstDeclVisitor<Visitor> { + llvm::Optional<ReferenceLoc> Ref; + + void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + Ref = ReferenceLoc{D->getQualifierLoc(), + D->getIdentLocation(), + {D->getNominatedNamespaceAsWritten()}}; + } + + void VisitUsingDecl(const UsingDecl *D) { + Ref = ReferenceLoc{D->getQualifierLoc(), D->getLocation(), + explicitReferenceTargets(DynTypedNode::create(*D), + DeclRelation::Underlying)}; + } + + void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { + Ref = ReferenceLoc{D->getQualifierLoc(), + D->getTargetNameLoc(), + {D->getAliasedNamespace()}}; + } + }; + + Visitor V; + V.Visit(D); + return V.Ref; +} + +Optional<ReferenceLoc> refInExpr(const Expr *E) { + struct Visitor : ConstStmtVisitor<Visitor> { + // FIXME: handle more complicated cases, e.g. ObjC, designated initializers. + llvm::Optional<ReferenceLoc> Ref; + + void VisitDeclRefExpr(const DeclRefExpr *E) { + Ref = ReferenceLoc{ + E->getQualifierLoc(), E->getNameInfo().getLoc(), {E->getFoundDecl()}}; + } + + void VisitMemberExpr(const MemberExpr *E) { + Ref = ReferenceLoc{E->getQualifierLoc(), + E->getMemberNameInfo().getLoc(), + {E->getFoundDecl()}}; + } + }; + + Visitor V; + V.Visit(E); + return V.Ref; +} + +Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) { + struct Visitor : TypeLocVisitor<Visitor> { + llvm::Optional<ReferenceLoc> Ref; + + void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { + // We only know about qualifier, rest if filled by inner locations. + Visit(L.getNamedTypeLoc().getUnqualifiedLoc()); + // Fill in the qualifier. + if (!Ref) + return; + assert(!Ref->Qualifier.hasQualifier() && "qualifier already set"); + Ref->Qualifier = L.getQualifierLoc(); + } + + void VisitDeducedTemplateSpecializationTypeLoc( + DeducedTemplateSpecializationTypeLoc L) { + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()))}; + } + + void VisitTagTypeLoc(TagTypeLoc L) { + Ref = + ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + } + + void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) { + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getTemplateNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()))}; + } + + void VisitDependentTemplateSpecializationTypeLoc( + DependentTemplateSpecializationTypeLoc L) { + Ref = ReferenceLoc{ + L.getQualifierLoc(), L.getTemplateNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()))}; + } + + void VisitDependentNameTypeLoc(DependentNameTypeLoc L) { + Ref = ReferenceLoc{ + L.getQualifierLoc(), L.getNameLoc(), + explicitReferenceTargets(DynTypedNode::create(L.getType()))}; + } + + void VisitTypedefTypeLoc(TypedefTypeLoc L) { + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getNameLoc(), {L.getTypedefNameDecl()}}; + } + }; + + Visitor V; + V.Visit(L.getUnqualifiedLoc()); + return V.Ref; +} + +class ExplicitReferenceColletor + : public RecursiveASTVisitor<ExplicitReferenceColletor> { +public: + ExplicitReferenceColletor(llvm::function_ref<void(ReferenceLoc)> Out) + : Out(Out) { + assert(Out); + } + + bool VisitTypeLoc(TypeLoc TTL) { + if (TypeLocsToSkip.count(TTL.getBeginLoc().getRawEncoding())) + return true; + visitNode(DynTypedNode::create(TTL)); + return true; + } + + bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc L) { + // ElaboratedTypeLoc will reports information for its inner type loc. + // Otherwise we loose information about inner types loc's qualifier. + TypeLoc Inner = L.getNamedTypeLoc().getUnqualifiedLoc(); + TypeLocsToSkip.insert(Inner.getBeginLoc().getRawEncoding()); + return RecursiveASTVisitor::TraverseElaboratedTypeLoc(L); + } + + bool VisitExpr(Expr *E) { + visitNode(DynTypedNode::create(*E)); + return true; + } + + bool VisitDecl(Decl *D) { + visitNode(DynTypedNode::create(*D)); + return true; + } + + // We have to use Traverse* because there is no corresponding Visit*. + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc L) { + if (!L.getNestedNameSpecifier()) + return true; + visitNode(DynTypedNode::create(L)); + // Inner type is missing information about its qualifier, skip it. + if (auto TL = L.getTypeLoc()) + TypeLocsToSkip.insert(TL.getBeginLoc().getRawEncoding()); + return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L); + } + +private: + /// Obtain information about a reference directly defined in \p N. Does not + /// recurse into child nodes, e.g. do not expect references for constructor + /// initializers + /// + /// Any of the fields in the returned structure can be empty, but not all of + /// them, e.g. + /// - for implicitly generated nodes (e.g. MemberExpr from range-based-for), + /// source location information may be missing, + /// - for dependent code, targets may be empty. + /// + /// (!) For the purposes of this function declarations are not considered to + /// be references. However, declarations can have references inside them, + /// e.g. 'namespace foo = std' references namespace 'std' and this + /// function will return the corresponding reference. + llvm::Optional<ReferenceLoc> explicitReference(DynTypedNode N) { + if (auto *D = N.get<Decl>()) + return refInDecl(D); + if (auto *E = N.get<Expr>()) + return refInExpr(E); + if (auto *NNSL = N.get<NestedNameSpecifierLoc>()) + return ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), + explicitReferenceTargets(DynTypedNode::create( + *NNSL->getNestedNameSpecifier()))}; + if (const TypeLoc *TL = N.get<TypeLoc>()) + return refInTypeLoc(*TL); + if (const CXXCtorInitializer *CCI = N.get<CXXCtorInitializer>()) { + if (CCI->isBaseInitializer()) + return refInTypeLoc(CCI->getBaseClassLoc()); + assert(CCI->isAnyMemberInitializer()); + return ReferenceLoc{NestedNameSpecifierLoc(), + CCI->getMemberLocation(), + {CCI->getAnyMember()}}; + } + // We do not have location information for other nodes (QualType, etc) + return llvm::None; + } + + void visitNode(DynTypedNode N) { + auto Ref = explicitReference(N); + if (!Ref) + return; + // Our promise is to return only references from the source code. If we lack + // location information, skip these nodes. + // Normally this should not happen in practice, unless there are bugs in the + // traversals or users started the traversal at an implicit node. + if (Ref->NameLoc.isInvalid()) { + dlog("invalid location at node {0}", nodeToString(N)); + return; + } + Out(*Ref); + } + + llvm::function_ref<void(ReferenceLoc)> Out; + /// TypeLocs starting at these locations must be skipped, see + /// TraverseElaboratedTypeSpecifierLoc for details. + llvm::DenseSet</*SourceLocation*/ unsigned> TypeLocsToSkip; +}; +} // namespace + +void findExplicitReferences(Stmt *S, + llvm::function_ref<void(ReferenceLoc)> Out) { + assert(S); + ExplicitReferenceColletor(Out).TraverseStmt(S); +} +void findExplicitReferences(Decl *D, + llvm::function_ref<void(ReferenceLoc)> Out) { + assert(D); + ExplicitReferenceColletor(Out).TraverseDecl(D); +} + llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelation R) { switch (R) { #define REL_CASE(X) \ @@ -378,5 +651,26 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelationSet RS) { return OS; } +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R) { + // note we cannot print R.NameLoc without a source manager. + OS << "targets = {"; + bool First = true; + for (const NamedDecl *T : R.Targets) { + if (!First) + OS << ", "; + else + First = false; + OS << printQualifiedName(*T) << printTemplateSpecializationArgs(*T); + } + OS << "}"; + if (R.Qualifier) { + OS << ", qualifier = '"; + R.Qualifier.getNestedNameSpecifier()->print(OS, + PrintingPolicy(LangOptions())); + OS << "'"; + } + return OS; +} + } // namespace clangd } // namespace clang |

