DraftStore.cpp
ExpectedTypes.cpp
FindSymbols.cpp
+ FindTarget.cpp
FileDistance.cpp
Format.cpp
FS.cpp
--- /dev/null
+//===--- FindTarget.cpp - What does an AST node refer to? -----------------===//
+//
+// 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 "FindTarget.h"
+#include "AST.h"
+#include "Logger.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLocVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+LLVM_DUMP_METHOD std::string
+nodeToString(const ast_type_traits::DynTypedNode &N) {
+ std::string S = N.getNodeKind().asStringRef();
+ {
+ llvm::raw_string_ostream OS(S);
+ OS << ": ";
+ N.print(OS, PrintingPolicy(LangOptions()));
+ }
+ std::replace(S.begin(), S.end(), '\n', ' ');
+ return S;
+}
+
+// TargetFinder locates the entities that an AST node refers to.
+//
+// Typically this is (possibly) one declaration and (possibly) one type, but
+// may be more:
+// - for ambiguous nodes like OverloadExpr
+// - if we want to include e.g. both typedefs and the underlying type
+//
+// This is organized as a set of mutually recursive helpers for particular node
+// types, but for most nodes this is a short walk rather than a deep traversal.
+//
+// It's tempting to do e.g. typedef resolution as a second normalization step,
+// after finding the 'primary' decl etc. But we do this monolithically instead
+// because:
+// - normalization may require these traversals again (e.g. unwrapping a
+// typedef reveals a decltype which must be traversed)
+// - it doesn't simplify that much, e.g. the first stage must still be able
+// to yield multiple decls to handle OverloadExpr
+// - there are cases where it's required for correctness. e.g:
+// template<class X> using pvec = vector<x*>; pvec<int> x;
+// There's no Decl `pvec<int>`, we must choose `pvec<X>` or `vector<int*>`
+// and both are lossy. We must know upfront what the caller ultimately wants.
+//
+// FIXME: improve common dependent scope using name lookup in primary templates.
+// e.g. template<typename T> int foo() { return std::vector<T>().size(); }
+// formally size() is unresolved, but the primary template is a good guess.
+// This affects:
+// - DependentTemplateSpecializationType,
+// - DependentScopeMemberExpr
+// - DependentScopeDeclRefExpr
+// - DependentNameType
+struct TargetFinder {
+ using RelSet = DeclRelationSet;
+ using Rel = DeclRelation;
+ llvm::SmallDenseMap<const Decl *, RelSet> Decls;
+ RelSet Flags;
+
+ static const Decl *getTemplatePattern(const Decl *D) {
+ if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
+ return CRD->getTemplateInstantiationPattern();
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ return FD->getTemplateInstantiationPattern();
+ } else if (auto *VD = dyn_cast<VarDecl>(D)) {
+ // Hmm: getTIP returns its arg if it's not an instantiation?!
+ VarDecl *T = VD->getTemplateInstantiationPattern();
+ return (T == D) ? nullptr : T;
+ } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
+ return ED->getInstantiatedFromMemberEnum();
+ } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {
+ const auto *ND = cast<NamedDecl>(D);
+ if (const DeclContext *Parent = dyn_cast_or_null<DeclContext>(
+ getTemplatePattern(llvm::cast<Decl>(ND->getDeclContext()))))
+ for (const NamedDecl *BaseND : Parent->lookup(ND->getDeclName()))
+ if (!BaseND->isImplicit() && BaseND->getKind() == ND->getKind())
+ return BaseND;
+ } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
+ if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
+ if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
+ for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))
+ return BaseECD;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ template <typename T> void debug(T &Node, RelSet Flags) {
+ dlog("visit [{0}] {1}", Flags,
+ nodeToString(ast_type_traits::DynTypedNode::create(Node)));
+ }
+
+ void report(const Decl *D, RelSet Flags) {
+ dlog("--> [{0}] {1}", Flags,
+ nodeToString(ast_type_traits::DynTypedNode::create(*D)));
+ Decls[D] |= Flags;
+ }
+
+public:
+ void add(const Decl *D, RelSet Flags) {
+ if (!D)
+ return;
+ debug(*D, Flags);
+ if (const UsingDirectiveDecl *UDD = llvm::dyn_cast<UsingDirectiveDecl>(D))
+ D = UDD->getNominatedNamespaceAsWritten();
+
+ if (const TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(D)) {
+ add(TND->getUnderlyingType(), Flags | Rel::Underlying);
+ Flags |= Rel::Alias; // continue with the alias.
+ } else if (const UsingDecl *UD = dyn_cast<UsingDecl>(D)) {
+ for (const UsingShadowDecl *S : UD->shadows())
+ add(S->getUnderlyingDecl(), Flags | Rel::Underlying);
+ Flags |= Rel::Alias; // continue with the alias.
+ } else if (const auto *NAD = dyn_cast<NamespaceAliasDecl>(D)) {
+ add(NAD->getUnderlyingDecl(), Flags | Rel::Underlying);
+ Flags |= Rel::Alias; // continue with the alias
+ } else if (const UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(D)) {
+ // Include the using decl, but don't traverse it. This may end up
+ // including *all* shadows, which we don't want.
+ report(USD->getUsingDecl(), Flags | Rel::Alias);
+ // Shadow decls are synthetic and not themselves interesting.
+ // Record the underlying decl instead, if allowed.
+ D = USD->getTargetDecl();
+ Flags |= Rel::Underlying; // continue with the underlying decl.
+ }
+
+ if (const Decl *Pat = getTemplatePattern(D)) {
+ assert(Pat != D);
+ add(Pat, Flags | Rel::TemplatePattern);
+ // Now continue with the instantiation.
+ Flags |= Rel::TemplateInstantiation;
+ }
+
+ report(D, Flags);
+ }
+
+ void add(const Stmt *S, RelSet Flags) {
+ if (!S)
+ return;
+ debug(*S, Flags);
+ struct Visitor : public ConstStmtVisitor<Visitor> {
+ TargetFinder &Outer;
+ RelSet Flags;
+ Visitor(TargetFinder &Outer, RelSet Flags) : Outer(Outer), Flags(Flags) {}
+
+ void VisitDeclRefExpr(const DeclRefExpr *DRE) {
+ const Decl *D = DRE->getDecl();
+ // UsingShadowDecl allows us to record the UsingDecl.
+ // getFoundDecl() returns the wrong thing in other cases (templates).
+ if (auto *USD = llvm::dyn_cast<UsingShadowDecl>(DRE->getFoundDecl()))
+ D = USD;
+ Outer.add(D, Flags);
+ }
+ void VisitMemberExpr(const MemberExpr *ME) {
+ const Decl *D = ME->getMemberDecl();
+ if (auto *USD =
+ llvm::dyn_cast<UsingShadowDecl>(ME->getFoundDecl().getDecl()))
+ D = USD;
+ Outer.add(D, Flags);
+ }
+ void VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
+ Outer.add(CCE->getConstructor(), Flags);
+ }
+ void VisitDesignatedInitExpr(const DesignatedInitExpr *DIE) {
+ for (const DesignatedInitExpr::Designator &D :
+ llvm::reverse(DIE->designators()))
+ if (D.isFieldDesignator()) {
+ Outer.add(D.getField(), Flags);
+ // We don't know which designator was intended, we assume the outer.
+ break;
+ }
+ }
+ void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *OIRE) {
+ Outer.add(OIRE->getDecl(), Flags);
+ }
+ void VisitObjCMessageExpr(const ObjCMessageExpr *OME) {
+ Outer.add(OME->getMethodDecl(), Flags);
+ }
+ void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *OPRE) {
+ if (OPRE->isExplicitProperty())
+ Outer.add(OPRE->getExplicitProperty(), Flags);
+ else {
+ if (OPRE->isMessagingGetter())
+ Outer.add(OPRE->getImplicitPropertyGetter(), Flags);
+ if (OPRE->isMessagingSetter())
+ Outer.add(OPRE->getImplicitPropertySetter(), Flags);
+ }
+ }
+ void VisitObjCProtocolExpr(const ObjCProtocolExpr *OPE) {
+ Outer.add(OPE->getProtocol(), Flags);
+ }
+ };
+ Visitor(*this, Flags).Visit(S);
+ }
+
+ void add(QualType T, RelSet Flags) {
+ if (T.isNull())
+ return;
+ debug(T, Flags);
+ struct Visitor : public TypeVisitor<Visitor> {
+ TargetFinder &Outer;
+ RelSet Flags;
+ Visitor(TargetFinder &Outer, RelSet Flags) : Outer(Outer), Flags(Flags) {}
+
+ void VisitTagType(const TagType *TT) {
+ Outer.add(TT->getAsTagDecl(), Flags);
+ }
+ void VisitDecltypeType(const DecltypeType *DTT) {
+ Outer.add(DTT->getUnderlyingType(), Flags | Rel::Underlying);
+ }
+ void VisitDeducedType(const DeducedType *DT) {
+ // FIXME: In practice this doesn't work: the AutoType you find inside
+ // TypeLoc never has a deduced type. https://llvm.org/PR42914
+ Outer.add(DT->getDeducedType(), Flags | Rel::Underlying);
+ }
+ void VisitTypedefType(const TypedefType *TT) {
+ Outer.add(TT->getDecl(), Flags);
+ }
+ void
+ VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
+ // Have to handle these case-by-case.
+
+ // templated type aliases: there's no specialized/instantiated using
+ // decl to point to. So try to find a decl for the underlying type
+ // (after substitution), and failing that point to the (templated) using
+ // decl.
+ if (TST->isTypeAlias()) {
+ Outer.add(TST->getAliasedType(), Flags | Rel::Underlying);
+ // Don't *traverse* the alias, which would result in traversing the
+ // template of the underlying type.
+ Outer.report(
+ TST->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(),
+ Flags | Rel::Alias | Rel::TemplatePattern);
+ }
+ // specializations of template template parameters aren't instantiated
+ // into decls, so they must refer to the parameter itself.
+ else if (const auto *Parm =
+ llvm::dyn_cast_or_null<TemplateTemplateParmDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
+ Outer.add(Parm, Flags);
+ // class template specializations have a (specialized) CXXRecordDecl.
+ else if (const CXXRecordDecl *RD = TST->getAsCXXRecordDecl())
+ Outer.add(RD, Flags); // add(Decl) will despecialize if needed.
+ else {
+ // fallback: the (un-specialized) declaration from primary template.
+ if (auto *TD = TST->getTemplateName().getAsTemplateDecl())
+ Outer.add(TD->getTemplatedDecl(), Flags | Rel::TemplatePattern);
+ }
+ }
+ void VisitTemplateTypeParmType(const TemplateTypeParmType *TTPT) {
+ Outer.add(TTPT->getDecl(), Flags);
+ }
+ void VisitObjCInterfaceType(const ObjCInterfaceType *OIT) {
+ Outer.add(OIT->getDecl(), Flags);
+ }
+ void VisitObjCObjectType(const ObjCObjectType *OOT) {
+ // FIXME: ObjCObjectTypeLoc has no children for the protocol list, so
+ // there is no node in id<Foo> that refers to ObjCProtocolDecl Foo.
+ if (OOT->isObjCQualifiedId() && OOT->getNumProtocols() == 1)
+ Outer.add(OOT->getProtocol(0), Flags);
+ }
+ };
+ Visitor(*this, Flags).Visit(T.getTypePtr());
+ }
+
+ void add(const NestedNameSpecifier *NNS, RelSet Flags) {
+ if (!NNS)
+ return;
+ debug(*NNS, Flags);
+ switch (NNS->getKind()) {
+ case NestedNameSpecifier::Identifier:
+ return;
+ case NestedNameSpecifier::Namespace:
+ add(NNS->getAsNamespace(), Flags);
+ return;
+ case NestedNameSpecifier::NamespaceAlias:
+ add(NNS->getAsNamespaceAlias(), Flags);
+ return;
+ case NestedNameSpecifier::TypeSpec:
+ case NestedNameSpecifier::TypeSpecWithTemplate:
+ add(QualType(NNS->getAsType(), 0), Flags);
+ return;
+ case NestedNameSpecifier::Global:
+ // This should be TUDecl, but we can't get a pointer to it!
+ return;
+ case NestedNameSpecifier::Super:
+ add(NNS->getAsRecordDecl(), Flags);
+ return;
+ }
+ llvm_unreachable("unhandled NestedNameSpecifier::SpecifierKind");
+ }
+
+ void add(const CXXCtorInitializer *CCI, RelSet Flags) {
+ if (!CCI)
+ return;
+ debug(*CCI, Flags);
+
+ if (CCI->isAnyMemberInitializer())
+ add(CCI->getAnyMember(), Flags);
+ // Constructor calls contain a TypeLoc node, so we don't handle them here.
+ }
+};
+
+} // namespace
+
+llvm::SmallVector<std::pair<const Decl *, DeclRelationSet>, 1>
+allTargetDecls(const ast_type_traits::DynTypedNode &N) {
+ dlog("allTargetDecls({0})", nodeToString(N));
+ TargetFinder Finder;
+ DeclRelationSet Flags;
+ if (const Decl *D = N.get<Decl>())
+ Finder.add(D, Flags);
+ else if (const Stmt *S = N.get<Stmt>())
+ Finder.add(S, Flags);
+ else if (const NestedNameSpecifierLoc *NNSL = N.get<NestedNameSpecifierLoc>())
+ Finder.add(NNSL->getNestedNameSpecifier(), Flags);
+ else if (const NestedNameSpecifier *NNS = N.get<NestedNameSpecifier>())
+ Finder.add(NNS, Flags);
+ else if (const TypeLoc *TL = N.get<TypeLoc>())
+ Finder.add(TL->getType(), Flags);
+ else if (const QualType *QT = N.get<QualType>())
+ Finder.add(*QT, Flags);
+ else if (const CXXCtorInitializer *CCI = N.get<CXXCtorInitializer>())
+ Finder.add(CCI, Flags);
+
+ return {Finder.Decls.begin(), Finder.Decls.end()};
+}
+
+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))
+ if (!(Entry.second & ~Mask))
+ Result.push_back(Entry.first);
+ return Result;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelation R) {
+ switch (R) {
+#define REL_CASE(X) \
+ case DeclRelation::X: \
+ return OS << #X;
+ REL_CASE(Alias);
+ REL_CASE(Underlying);
+ REL_CASE(TemplateInstantiation);
+ REL_CASE(TemplatePattern);
+#undef REL_CASE
+ };
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelationSet RS) {
+ const char *Sep = "";
+ for (unsigned I = 0; I < RS.S.size(); ++I) {
+ if (RS.S.test(I)) {
+ OS << Sep << static_cast<DeclRelation>(I);
+ Sep = "|";
+ }
+ }
+ return OS;
+}
+
+} // namespace clangd
+} // namespace clang
--- /dev/null
+//===--- FindTarget.h - What does an AST node refer to? ---------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Many clangd features are concerned with references in the AST:
+// - xrefs, go-to-definition, explicitly talk about references
+// - hover and code actions relate to things you "target" in the editor
+// - refactoring actions need to know about entities that are referenced
+// to determine whether/how the edit can be applied.
+//
+// Historically, we have used libIndex (IndexDataConsumer) to tie source
+// locations to referenced declarations. This file defines a more decoupled
+// approach based around AST nodes (DynTypedNode), and can be combined with
+// SelectionTree or other traversals.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+#include <bitset>
+
+namespace clang {
+namespace clangd {
+/// Describes the link between an AST node and a Decl it refers to.
+enum class DeclRelation : unsigned;
+/// A bitfield of DeclRelations.
+class DeclRelationSet;
+
+/// targetDecl() finds the declaration referred to by an AST node.
+/// For example a RecordTypeLoc refers to the RecordDecl for the type.
+///
+/// In some cases there are multiple results, e.g. a dependent unresolved
+/// OverloadExpr may have several candidates. All will be returned:
+///
+/// void foo(int); <-- candidate
+/// void foo(double); <-- candidate
+/// template <typename T> callFoo() { foo(T()); }
+/// ^ OverloadExpr
+///
+/// In other cases, there may be choices about what "referred to" means.
+/// e.g. does naming a typedef refer to the underlying type?
+/// The results are marked with a set of DeclRelations, and can be filtered.
+///
+/// struct S{}; <-- candidate (underlying)
+/// using T = S{}; <-- candidate (alias)
+/// T x;
+/// ^ TypedefTypeLoc
+///
+/// Formally, we walk a graph starting at the provided node, and return the
+/// decls that were found. Certain edges in the graph have labels, and for each
+/// decl we return the set of labels seen on a path to the decl.
+/// For the previous example:
+///
+/// TypedefTypeLoc T
+/// |
+/// TypedefType T
+/// / \
+/// [underlying] [alias]
+/// / \
+/// RecordDecl S TypeAliasDecl T
+///
+/// FIXME: some AST nodes cannot be DynTypedNodes, these cannot be specified.
+llvm::SmallVector<const Decl *, 1>
+targetDecl(const ast_type_traits::DynTypedNode &, DeclRelationSet Mask);
+
+/// 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<std::pair<const Decl *, DeclRelationSet>, 1>
+allTargetDecls(const ast_type_traits::DynTypedNode &);
+
+enum class DeclRelation : unsigned {
+ // Template options apply when the declaration is an instantiated template.
+ // e.g. [[vector<int>]] vec;
+
+ /// This is the template instantiation that was referred to.
+ /// e.g. template<> class vector<int> (the implicit specialization)
+ TemplateInstantiation,
+ /// This is the pattern the template specialization was instantiated from.
+ /// e.g. class vector<T> (the pattern within the primary template)
+ TemplatePattern,
+
+ // Alias options apply when the declaration is an alias.
+ // e.g. namespace clang { [[StringRef]] S; }
+
+ /// This declaration is an alias that was referred to.
+ /// e.g. using llvm::StringRef (the UsingDecl directly referenced).
+ Alias,
+ /// This is the underlying declaration for an alias, decltype etc.
+ /// e.g. class llvm::StringRef (the underlying declaration referenced).
+ Underlying,
+};
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelation);
+
+class DeclRelationSet {
+ using Set = std::bitset<static_cast<unsigned>(DeclRelation::Underlying) + 1>;
+ Set S;
+ DeclRelationSet(Set S) : S(S) {}
+
+public:
+ DeclRelationSet() = default;
+ DeclRelationSet(DeclRelation R) { S.set(static_cast<unsigned>(R)); }
+
+ explicit operator bool() const { return S.any(); }
+ friend DeclRelationSet operator&(DeclRelationSet L, DeclRelationSet R) {
+ return L.S & R.S;
+ }
+ friend DeclRelationSet operator|(DeclRelationSet L, DeclRelationSet R) {
+ return L.S | R.S;
+ }
+ friend bool operator==(DeclRelationSet L, DeclRelationSet R) {
+ return L.S == R.S;
+ }
+ friend DeclRelationSet operator~(DeclRelationSet R) { return ~R.S; }
+ DeclRelationSet &operator|=(DeclRelationSet Other) {
+ S |= Other.S;
+ return *this;
+ }
+ DeclRelationSet &operator&=(DeclRelationSet Other) {
+ S &= Other.S;
+ return *this;
+ }
+ friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelationSet);
+};
+// The above operators can't be looked up if both sides are enums.
+// over.match.oper.html#3.2
+inline DeclRelationSet operator|(DeclRelation L, DeclRelation R) {
+ return DeclRelationSet(L) | DeclRelationSet(R);
+}
+inline DeclRelationSet operator&(DeclRelation L, DeclRelation R) {
+ return DeclRelationSet(L) & DeclRelationSet(R);
+}
+inline DeclRelationSet operator~(DeclRelation R) { return ~DeclRelationSet(R); }
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelationSet);
+
+} // namespace clangd
+} // namespace clang
"testns1::TestClass<testns1::OtherClass>", "testns1"));
}
-
} // namespace
} // namespace clangd
} // namespace clang
FileDistanceTests.cpp
FileIndexTests.cpp
FindSymbolsTests.cpp
+ FindTargetTests.cpp
FormattedStringTests.cpp
FormatTests.cpp
FSTests.cpp
--- /dev/null
+//===-- FindSymbolsTests.cpp -------------------------*- C++ -*------------===//
+//
+// 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 "FindTarget.h"
+
+#include "Selection.h"
+#include "TestTU.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Annotations.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <initializer_list>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+// A referenced Decl together with its DeclRelationSet, for assertions.
+//
+// There's no great way to assert on the "content" of a Decl in the general case
+// that's both expressive and unambiguous (e.g. clearly distinguishes between
+// templated decls and their specializations).
+//
+// We use the result of pretty-printing the decl, with the {body} truncated.
+struct PrintedDecl {
+ PrintedDecl(const char *Name, DeclRelationSet Relations = {})
+ : Name(Name), Relations(Relations) {}
+ PrintedDecl(const Decl *D, DeclRelationSet Relations = {})
+ : Relations(Relations) {
+ std::string S;
+ llvm::raw_string_ostream OS(S);
+ D->print(OS);
+ llvm::StringRef FirstLine =
+ llvm::StringRef(OS.str()).take_until([](char C) { return C == '\n'; });
+ FirstLine = FirstLine.rtrim(" {");
+ Name = FirstLine.rtrim(" {");
+ }
+
+ std::string Name;
+ DeclRelationSet Relations;
+};
+bool operator==(const PrintedDecl &L, const PrintedDecl &R) {
+ return std::tie(L.Name, L.Relations) == std::tie(R.Name, R.Relations);
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PrintedDecl &D) {
+ return OS << D.Name << " Rel=" << D.Relations;
+}
+
+// The test cases in for targetDecl() take the form
+// - a piece of code (Code = "...")
+// - Code should have a single AST node marked as a [[range]]
+// - an EXPECT_DECLS() assertion that verify the type of node selected, and
+// all the decls that targetDecl() considers it to reference
+// Despite the name, these cases actually test allTargetDecls() for brevity.
+class TargetDeclTest : public ::testing::Test {
+protected:
+ using Rel = DeclRelation;
+ std::string Code;
+ std::vector<const char *> Flags;
+
+ // Asserts that `Code` has a marked selection of a node `NodeType`,
+ // and returns allTargetDecls() as PrintedDecl structs.
+ // Use via EXPECT_DECLS().
+ std::vector<PrintedDecl> assertNodeAndPrintDecls(const char *NodeType) {
+ llvm::Annotations A(Code);
+ auto TU = TestTU::withCode(A.code());
+ TU.ExtraArgs = Flags;
+ auto AST = TU.build();
+ EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty()) << Code;
+ llvm::Annotations::Range R = A.range();
+ SelectionTree Selection(AST.getASTContext(), AST.getTokens(), R.Begin,
+ R.End);
+ const SelectionTree::Node *N = Selection.commonAncestor();
+ if (!N) {
+ ADD_FAILURE() << "No node selected!\n" << Code;
+ return {};
+ }
+ EXPECT_EQ(N->kind(), NodeType) << Selection;
+
+ std::vector<PrintedDecl> ActualDecls;
+ for (const auto &Entry : allTargetDecls(N->ASTNode))
+ ActualDecls.emplace_back(Entry.first, Entry.second);
+ return ActualDecls;
+ }
+};
+
+// This is a macro to preserve line numbers in assertion failures.
+// It takes the expected decls as varargs to work around comma-in-macro issues.
+#define EXPECT_DECLS(NodeType, ...) \
+ EXPECT_THAT(assertNodeAndPrintDecls(NodeType), \
+ ::testing::UnorderedElementsAreArray( \
+ std::vector<PrintedDecl>({__VA_ARGS__}))) \
+ << Code
+using ExpectedDecls = std::vector<PrintedDecl>;
+
+TEST_F(TargetDeclTest, Exprs) {
+ Code = R"cpp(
+ int f();
+ int x = [[f]]();
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", "int f()");
+
+ Code = R"cpp(
+ struct S { S operator+(S) const; };
+ auto X = S() [[+]] S();
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", "S operator+(S) const");
+}
+
+TEST_F(TargetDeclTest, UsingDecl) {
+ Code = R"cpp(
+ namespace foo {
+ int f(int);
+ int f(char);
+ }
+ using foo::f;
+ int x = [[f]](42);
+ )cpp";
+ // f(char) is not referenced!
+ EXPECT_DECLS("DeclRefExpr", {"using foo::f", Rel::Alias},
+ {"int f(int)", Rel::Underlying});
+
+ Code = R"cpp(
+ namespace foo {
+ int f(int);
+ int f(char);
+ }
+ [[using foo::f]];
+ )cpp";
+ // All overloads are referenced.
+ EXPECT_DECLS("UsingDecl", {"using foo::f", Rel::Alias},
+ {"int f(int)", Rel::Underlying},
+ {"int f(char)", Rel::Underlying});
+
+ Code = R"cpp(
+ struct X {
+ int foo();
+ };
+ struct Y : X {
+ using X::foo;
+ };
+ int x = Y().[[foo]]();
+ )cpp";
+ EXPECT_DECLS("MemberExpr", {"using X::foo", Rel::Alias},
+ {"int foo()", Rel::Underlying});
+}
+
+TEST_F(TargetDeclTest, ConstructorInitList) {
+ Code = R"cpp(
+ struct X {
+ int a;
+ X() : [[a]](42) {}
+ };
+ )cpp";
+ EXPECT_DECLS("CXXCtorInitializer", "int a");
+
+ Code = R"cpp(
+ struct X {
+ X() : [[X]](1) {}
+ X(int);
+ };
+ )cpp";
+ EXPECT_DECLS("RecordTypeLoc", "struct X");
+}
+
+TEST_F(TargetDeclTest, DesignatedInit) {
+ Flags = {"-xc"}; // array designators are a C99 extension.
+ Code = R"c(
+ struct X { int a; };
+ struct Y { int b; struct X c[2]; };
+ struct Y y = { .c[0].[[a]] = 1 };
+ )c";
+ EXPECT_DECLS("DesignatedInitExpr", "int a");
+}
+
+TEST_F(TargetDeclTest, NestedNameSpecifier) {
+ Code = R"cpp(
+ namespace a { namespace b { int c; } }
+ int x = a::[[b::]]c;
+ )cpp";
+ EXPECT_DECLS("NestedNameSpecifierLoc", "namespace b");
+
+ Code = R"cpp(
+ namespace a { struct X { enum { y }; }; }
+ int x = a::[[X::]]y;
+ )cpp";
+ EXPECT_DECLS("NestedNameSpecifierLoc", "struct X");
+
+ Code = R"cpp(
+ template <typename T>
+ int x = [[T::]]y;
+ )cpp";
+ // FIXME: We don't do a good job printing TemplateTypeParmDecls, apparently!
+ EXPECT_DECLS("NestedNameSpecifierLoc", "");
+
+ Code = R"cpp(
+ namespace a { int x; }
+ namespace b = a;
+ int y = [[b]]::x;
+ )cpp";
+ EXPECT_DECLS("NestedNameSpecifierLoc", {"namespace b = a", Rel::Alias},
+ {"namespace a", Rel::Underlying});
+}
+
+TEST_F(TargetDeclTest, Types) {
+ Code = R"cpp(
+ struct X{};
+ [[X]] x;
+ )cpp";
+ EXPECT_DECLS("RecordTypeLoc", "struct X");
+
+ Code = R"cpp(
+ struct S{};
+ typedef S X;
+ [[X]] x;
+ )cpp";
+ EXPECT_DECLS("TypedefTypeLoc", {"typedef S X", Rel::Alias},
+ {"struct S", Rel::Underlying});
+
+ Code = R"cpp(
+ template<class T>
+ void foo() { [[T]] x; }
+ )cpp";
+ // FIXME: We don't do a good job printing TemplateTypeParmDecls, apparently!
+ EXPECT_DECLS("TemplateTypeParmTypeLoc", "");
+
+ Code = R"cpp(
+ template<template<typename> class T>
+ void foo() { [[T<int>]] x; }
+ )cpp";
+ EXPECT_DECLS("TemplateSpecializationTypeLoc", "template <typename> class T");
+
+ Code = R"cpp(
+ struct S{};
+ S X;
+ [[decltype]](X) Y;
+ )cpp";
+ EXPECT_DECLS("DecltypeTypeLoc", {"struct S", Rel::Underlying});
+
+ Code = R"cpp(
+ struct S{};
+ [[auto]] X = S{};
+ )cpp";
+ // FIXME: deduced type missing in AST. https://llvm.org/PR42914
+ EXPECT_DECLS("AutoTypeLoc");
+}
+
+TEST_F(TargetDeclTest, ClassTemplate) {
+ Code = R"cpp(
+ // Implicit specialization.
+ template<int x> class Foo{};
+ [[Foo<42>]] B;
+ )cpp";
+ EXPECT_DECLS("TemplateSpecializationTypeLoc",
+ {"template<> class Foo<42>", Rel::TemplateInstantiation},
+ {"class Foo", Rel::TemplatePattern});
+
+ Code = R"cpp(
+ // Explicit specialization.
+ template<int x> class Foo{};
+ template<> class Foo<42>{};
+ [[Foo<42>]] B;
+ )cpp";
+ EXPECT_DECLS("TemplateSpecializationTypeLoc", "template<> class Foo<42>");
+
+ Code = R"cpp(
+ // Partial specialization.
+ template<typename T> class Foo{};
+ template<typename T> class Foo<T*>{};
+ [[Foo<int*>]] B;
+ )cpp";
+ EXPECT_DECLS("TemplateSpecializationTypeLoc",
+ {"template<> class Foo<int *>", Rel::TemplateInstantiation},
+ {"template <typename T> class Foo<type-parameter-0-0 *>",
+ Rel::TemplatePattern});
+}
+
+TEST_F(TargetDeclTest, FunctionTemplate) {
+ Code = R"cpp(
+ // Implicit specialization.
+ template<typename T> bool foo(T) { return false; };
+ bool x = [[foo]](42);
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr",
+ {"template<> bool foo<int>(int)", Rel::TemplateInstantiation},
+ {"bool foo(T)", Rel::TemplatePattern});
+
+ Code = R"cpp(
+ // Explicit specialization.
+ template<typename T> bool foo(T) { return false; };
+ template<> bool foo<int>(int) { return false; };
+ bool x = [[foo]](42);
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", "template<> bool foo<int>(int)");
+}
+
+TEST_F(TargetDeclTest, VariableTemplate) {
+ // Pretty-printer doesn't do a very good job of variable templates :-(
+ Code = R"cpp(
+ // Implicit specialization.
+ template<typename T> int foo;
+ int x = [[foo]]<char>;
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", {"int foo", Rel::TemplateInstantiation},
+ {"int foo", Rel::TemplatePattern});
+
+ Code = R"cpp(
+ // Explicit specialization.
+ template<typename T> int foo;
+ template <> bool foo<char>;
+ int x = [[foo]]<char>;
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", "bool foo");
+
+ Code = R"cpp(
+ // Partial specialization.
+ template<typename T> int foo;
+ template<typename T> bool foo<T*>;
+ bool x = [[foo]]<char*>;
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", {"bool foo", Rel::TemplateInstantiation},
+ {"bool foo", Rel::TemplatePattern});
+}
+
+TEST_F(TargetDeclTest, TypeAliasTemplate) {
+ Code = R"cpp(
+ template<typename T, int X> class SmallVector {};
+ template<typename U> using TinyVector = SmallVector<U, 1>;
+ [[TinyVector<int>]] X;
+ )cpp";
+ EXPECT_DECLS("TemplateSpecializationTypeLoc",
+ {"template<> class SmallVector<int, 1>",
+ Rel::TemplateInstantiation | Rel::Underlying},
+ {"class SmallVector", Rel::TemplatePattern | Rel::Underlying},
+ {"using TinyVector = SmallVector<U, 1>",
+ Rel::Alias | Rel::TemplatePattern});
+}
+
+TEST_F(TargetDeclTest, MemberOfTemplate) {
+ Code = R"cpp(
+ template <typename T> struct Foo {
+ int x(T);
+ };
+ int y = Foo<int>().[[x]](42);
+ )cpp";
+ EXPECT_DECLS("MemberExpr", {"int x(int)", Rel::TemplateInstantiation},
+ {"int x(T)", Rel::TemplatePattern});
+
+ Code = R"cpp(
+ template <typename T> struct Foo {
+ template <typename U>
+ int x(T, U);
+ };
+ int y = Foo<char>().[[x]]('c', 42);
+ )cpp";
+ EXPECT_DECLS("MemberExpr",
+ {"template<> int x<int>(char, int)", Rel::TemplateInstantiation},
+ {"int x(T, U)", Rel::TemplatePattern});
+}
+
+TEST_F(TargetDeclTest, Lambda) {
+ Code = R"cpp(
+ void foo(int x = 42) {
+ auto l = [ [[x]] ]{ return x + 1; };
+ };
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", "int x = 42");
+
+ // It seems like this should refer to another var, with the outer param being
+ // an underlying decl. But it doesn't seem to exist.
+ Code = R"cpp(
+ void foo(int x = 42) {
+ auto l = [x]{ return [[x]] + 1; };
+ };
+ )cpp";
+ EXPECT_DECLS("DeclRefExpr", "int x = 42");
+
+ Code = R"cpp(
+ void foo() {
+ auto l = [x = 1]{ return [[x]] + 1; };
+ };
+ )cpp";
+ // FIXME: why both auto and int?
+ EXPECT_DECLS("DeclRefExpr", "auto int x = 1");
+}
+
+TEST_F(TargetDeclTest, ObjC) {
+ Flags = {"-xobjective-c"};
+ Code = R"cpp(
+ @interface Foo {}
+ -(void)bar;
+ @end
+ void test(Foo *f) {
+ [f [[bar]] ];
+ }
+ )cpp";
+ EXPECT_DECLS("ObjCMessageExpr", "- (void)bar");
+
+ Code = R"cpp(
+ @interface Foo { @public int bar; }
+ @end
+ int test(Foo *f) {
+ return [[f->bar]];
+ }
+ )cpp";
+ EXPECT_DECLS("ObjCIvarRefExpr", "int bar");
+
+ Code = R"cpp(
+ @interface Foo {}
+ -(int) x;
+ -(void) setX:(int)x;
+ @end
+ void test(Foo *f) {
+ [[f.x]] = 42;
+ }
+ )cpp";
+ EXPECT_DECLS("ObjCPropertyRefExpr", "- (void)setX:(int)x");
+
+ Code = R"cpp(
+ @interface Foo {}
+ @property int x;
+ @end
+ void test(Foo *f) {
+ [[f.x]] = 42;
+ }
+ )cpp";
+ EXPECT_DECLS("ObjCPropertyRefExpr",
+ "@property(atomic, assign, unsafe_unretained, readwrite) int x");
+
+ Code = R"cpp(
+ @protocol Foo
+ @end
+ id test() {
+ return [[@protocol(Foo)]];
+ }
+ )cpp";
+ EXPECT_DECLS("ObjCProtocolExpr", "@protocol Foo");
+
+ Code = R"cpp(
+ @interface Foo
+ @end
+ void test([[Foo]] *p);
+ )cpp";
+ EXPECT_DECLS("ObjCInterfaceTypeLoc", "@interface Foo");
+
+ Code = R"cpp(
+ @protocol Foo
+ @end
+ void test([[id<Foo>]] p);
+ )cpp";
+ EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo");
+
+ Code = R"cpp(
+ @class C;
+ @protocol Foo
+ @end
+ void test(C<[[Foo]]> *p);
+ )cpp";
+ // FIXME: there's no AST node corresponding to 'Foo', so we're stuck.
+ EXPECT_DECLS("ObjCObjectTypeLoc");
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang