aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaojian Wu <hokein@google.com>2019-10-18 12:07:19 +0000
committerHaojian Wu <hokein@google.com>2019-10-18 12:07:19 +0000
commitf57e6b037bc0b33d791008aafe7f6b34a77fcbc0 (patch)
tree8c5354efff4e2a9e1ded8a5666e57be289894740
parent4e1c1408f1a54e3cf318279bc194f52807d732f8 (diff)
[clangd] Report declaration references in findExplicitReferences.
Reviewers: ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D68977 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@375226 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--clangd/AST.cpp14
-rw-r--r--clangd/AST.h6
-rw-r--r--clangd/FindTarget.cpp127
-rw-r--r--clangd/FindTarget.h2
-rw-r--r--clangd/XRefs.cpp3
-rw-r--r--clangd/unittests/FindTargetTests.cpp114
-rw-r--r--clangd/unittests/XRefsTests.cpp3
7 files changed, 172 insertions, 97 deletions
diff --git a/clangd/AST.cpp b/clangd/AST.cpp
index 4242721d..002f2ef0 100644
--- a/clangd/AST.cpp
+++ b/clangd/AST.cpp
@@ -103,16 +103,12 @@ static bool isAnonymous(const DeclarationName &N) {
return N.isIdentifier() && !N.getAsIdentifierInfo();
}
-/// Returns a nested name specifier of \p ND if it was present in the source,
-/// e.g.
-/// void ns::something::foo() -> returns 'ns::something'
-/// void foo() -> returns null
-static NestedNameSpecifier *getQualifier(const NamedDecl &ND) {
+NestedNameSpecifierLoc getQualifierLoc(const NamedDecl &ND) {
if (auto *V = llvm::dyn_cast<DeclaratorDecl>(&ND))
- return V->getQualifier();
+ return V->getQualifierLoc();
if (auto *T = llvm::dyn_cast<TagDecl>(&ND))
- return T->getQualifier();
- return nullptr;
+ return T->getQualifierLoc();
+ return NestedNameSpecifierLoc();
}
std::string printUsingNamespaceName(const ASTContext &Ctx,
@@ -153,7 +149,7 @@ std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
}
// Print nested name qualifier if it was written in the source code.
- if (auto *Qualifier = getQualifier(ND))
+ if (auto *Qualifier = getQualifierLoc(ND).getNestedNameSpecifier())
Qualifier->print(Out, PP);
// Print the name itself.
ND.getDeclName().print(Out, PP);
diff --git a/clangd/AST.h b/clangd/AST.h
index eddabdaf..b05e0a22 100644
--- a/clangd/AST.h
+++ b/clangd/AST.h
@@ -104,6 +104,12 @@ bool isImplicitTemplateInstantiation(const NamedDecl *D);
/// explicit specialization.
bool isExplicitTemplateSpecialization(const NamedDecl *D);
+/// Returns a nested name specifier loc of \p ND if it was present in the
+/// source, e.g.
+/// void ns::something::foo() -> returns 'ns::something'
+/// void foo() -> returns null
+NestedNameSpecifierLoc getQualifierLoc(const NamedDecl &ND);
+
} // namespace clangd
} // namespace clang
diff --git a/clangd/FindTarget.cpp b/clangd/FindTarget.cpp
index ffe808d5..49191053 100644
--- a/clangd/FindTarget.cpp
+++ b/clangd/FindTarget.cpp
@@ -412,63 +412,86 @@ explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask = {}) {
return Targets;
}
-Optional<ReferenceLoc> refInDecl(const Decl *D) {
+llvm::SmallVector<ReferenceLoc, 2> refInDecl(const Decl *D) {
struct Visitor : ConstDeclVisitor<Visitor> {
- llvm::Optional<ReferenceLoc> Ref;
+ llvm::SmallVector<ReferenceLoc, 2> Refs;
void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
- Ref = ReferenceLoc{D->getQualifierLoc(),
- D->getIdentLocation(),
- {D->getNominatedNamespaceAsWritten()}};
+ // We want to keep it as non-declaration references, as the
+ // "using namespace" declaration doesn't have a name.
+ Refs.push_back(ReferenceLoc{D->getQualifierLoc(),
+ D->getIdentLocation(),
+ /*IsDecl=*/false,
+ {D->getNominatedNamespaceAsWritten()}});
}
void VisitUsingDecl(const UsingDecl *D) {
- Ref = ReferenceLoc{D->getQualifierLoc(), D->getLocation(),
- explicitReferenceTargets(DynTypedNode::create(*D),
- DeclRelation::Underlying)};
+ // "using ns::identifer;" is a non-declaration reference.
+ Refs.push_back(
+ ReferenceLoc{D->getQualifierLoc(), D->getLocation(), /*IsDecl=*/false,
+ explicitReferenceTargets(DynTypedNode::create(*D),
+ DeclRelation::Underlying)});
}
void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) {
- Ref = ReferenceLoc{D->getQualifierLoc(),
- D->getTargetNameLoc(),
- {D->getAliasedNamespace()}};
+ // For namespace alias, "namespace Foo = Target;", we add two references.
+ // Add a declaration reference for Foo.
+ VisitNamedDecl(D);
+ // Add a non-declaration reference for Target.
+ Refs.push_back(ReferenceLoc{D->getQualifierLoc(),
+ D->getTargetNameLoc(),
+ /*IsDecl=*/false,
+ {D->getAliasedNamespace()}});
+ }
+
+ void VisitNamedDecl(const NamedDecl *ND) {
+ // FIXME: decide on how to surface destructors when we need them.
+ if (llvm::isa<CXXDestructorDecl>(ND))
+ return;
+ Refs.push_back(ReferenceLoc{
+ getQualifierLoc(*ND), ND->getLocation(), /*IsDecl=*/true, {ND}});
}
};
Visitor V;
V.Visit(D);
- return V.Ref;
+ return V.Refs;
}
-Optional<ReferenceLoc> refInExpr(const Expr *E) {
+llvm::SmallVector<ReferenceLoc, 2> refInExpr(const Expr *E) {
struct Visitor : ConstStmtVisitor<Visitor> {
// FIXME: handle more complicated cases, e.g. ObjC, designated initializers.
- llvm::Optional<ReferenceLoc> Ref;
+ llvm::SmallVector<ReferenceLoc, 2> Refs;
void VisitDeclRefExpr(const DeclRefExpr *E) {
- Ref = ReferenceLoc{
- E->getQualifierLoc(), E->getNameInfo().getLoc(), {E->getFoundDecl()}};
+ Refs.push_back(ReferenceLoc{E->getQualifierLoc(),
+ E->getNameInfo().getLoc(),
+ /*IsDecl=*/false,
+ {E->getFoundDecl()}});
}
void VisitMemberExpr(const MemberExpr *E) {
- Ref = ReferenceLoc{E->getQualifierLoc(),
- E->getMemberNameInfo().getLoc(),
- {E->getFoundDecl()}};
+ Refs.push_back(ReferenceLoc{E->getQualifierLoc(),
+ E->getMemberNameInfo().getLoc(),
+ /*IsDecl=*/false,
+ {E->getFoundDecl()}});
}
void VisitOverloadExpr(const OverloadExpr *E) {
- Ref = ReferenceLoc{E->getQualifierLoc(), E->getNameInfo().getLoc(),
- llvm::SmallVector<const NamedDecl *, 1>(
- E->decls().begin(), E->decls().end())};
+ Refs.push_back(ReferenceLoc{E->getQualifierLoc(),
+ E->getNameInfo().getLoc(),
+ /*IsDecl=*/false,
+ llvm::SmallVector<const NamedDecl *, 1>(
+ E->decls().begin(), E->decls().end())});
}
};
Visitor V;
V.Visit(E);
- return V.Ref;
+ return V.Refs;
}
-Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) {
+llvm::SmallVector<ReferenceLoc, 2> refInTypeLoc(TypeLoc L) {
struct Visitor : TypeLocVisitor<Visitor> {
llvm::Optional<ReferenceLoc> Ref;
@@ -483,13 +506,17 @@ Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) {
}
void VisitTagTypeLoc(TagTypeLoc L) {
- Ref =
- ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}};
+ Ref = ReferenceLoc{NestedNameSpecifierLoc(),
+ L.getNameLoc(),
+ /*IsDecl=*/false,
+ {L.getDecl()}};
}
void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) {
- Ref =
- ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}};
+ Ref = ReferenceLoc{NestedNameSpecifierLoc(),
+ L.getNameLoc(),
+ /*IsDecl=*/false,
+ {L.getDecl()}};
}
void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
@@ -502,14 +529,14 @@ Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) {
// 2. 'vector<int>' with mask 'Underlying'.
// we want to return only #1 in this case.
Ref = ReferenceLoc{
- NestedNameSpecifierLoc(), L.getTemplateNameLoc(),
+ NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()),
DeclRelation::Alias)};
}
void VisitDeducedTemplateSpecializationTypeLoc(
DeducedTemplateSpecializationTypeLoc L) {
Ref = ReferenceLoc{
- NestedNameSpecifierLoc(), L.getNameLoc(),
+ NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()),
DeclRelation::Alias)};
}
@@ -517,25 +544,29 @@ Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) {
void VisitDependentTemplateSpecializationTypeLoc(
DependentTemplateSpecializationTypeLoc L) {
Ref = ReferenceLoc{
- L.getQualifierLoc(), L.getTemplateNameLoc(),
+ L.getQualifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()))};
}
void VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
Ref = ReferenceLoc{
- L.getQualifierLoc(), L.getNameLoc(),
+ L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()))};
}
void VisitTypedefTypeLoc(TypedefTypeLoc L) {
- Ref = ReferenceLoc{
- NestedNameSpecifierLoc(), L.getNameLoc(), {L.getTypedefNameDecl()}};
+ Ref = ReferenceLoc{NestedNameSpecifierLoc(),
+ L.getNameLoc(),
+ /*IsDecl=*/false,
+ {L.getTypedefNameDecl()}};
}
};
Visitor V;
V.Visit(L.getUnqualifiedLoc());
- return V.Ref;
+ if (!V.Ref)
+ return {};
+ return {*V.Ref};
}
class ExplicitReferenceColletor
@@ -575,6 +606,7 @@ public:
case TemplateArgument::TemplateExpansion:
reportReference(ReferenceLoc{A.getTemplateQualifierLoc(),
A.getTemplateNameLoc(),
+ /*IsDecl=*/false,
{A.getArgument()
.getAsTemplateOrTemplatePattern()
.getAsTemplateDecl()}},
@@ -625,34 +657,33 @@ private:
/// 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) {
+ llvm::SmallVector<ReferenceLoc, 2> 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()))};
+ return {ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), false,
+ 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()}};
+ return {ReferenceLoc{NestedNameSpecifierLoc(),
+ CCI->getMemberLocation(),
+ /*IsDecl=*/false,
+ {CCI->getAnyMember()}}};
}
// We do not have location information for other nodes (QualType, etc)
- return llvm::None;
+ return {};
}
void visitNode(DynTypedNode N) {
- auto Ref = explicitReference(N);
- if (!Ref)
- return;
- reportReference(*Ref, N);
+ for (const auto &R : explicitReference(N))
+ reportReference(R, N);
}
void reportReference(const ReferenceLoc &Ref, DynTypedNode N) {
@@ -727,6 +758,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R) {
PrintingPolicy(LangOptions()));
OS << "'";
}
+ if (R.IsDecl)
+ OS << ", decl";
return OS;
}
diff --git a/clangd/FindTarget.h b/clangd/FindTarget.h
index 80a8e787..9ddd24ad 100644
--- a/clangd/FindTarget.h
+++ b/clangd/FindTarget.h
@@ -86,6 +86,8 @@ struct ReferenceLoc {
NestedNameSpecifierLoc Qualifier;
/// Start location of the last name part, i.e. 'foo' in 'ns::foo<int>'.
SourceLocation NameLoc;
+ /// True if the reference is a declaration or definition;
+ bool IsDecl = false;
// FIXME: add info about template arguments.
/// A list of targets referenced by this name. Normally this has a single
/// element, but multiple is also possible, e.g. in case of using declarations
diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp
index 43904cdd..2e75e8c8 100644
--- a/clangd/XRefs.cpp
+++ b/clangd/XRefs.cpp
@@ -1308,7 +1308,8 @@ llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
llvm::DenseSet<const Decl *> DeclRefs;
findExplicitReferences(FD, [&](ReferenceLoc Ref) {
for (const Decl *D : Ref.Targets) {
- if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter())
+ if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
+ !Ref.IsDecl)
DeclRefs.insert(D);
}
});
diff --git a/clangd/unittests/FindTargetTests.cpp b/clangd/unittests/FindTargetTests.cpp
index 9e1f1c18..bf74b45a 100644
--- a/clangd/unittests/FindTargetTests.cpp
+++ b/clangd/unittests/FindTargetTests.cpp
@@ -622,53 +622,61 @@ TEST_F(FindExplicitReferencesTest, All) {
struct Struct { int a; };
using Typedef = int;
void foo() {
- $0^Struct x;
- $1^Typedef y;
- static_cast<$2^Struct*>(0);
+ $0^Struct $1^x;
+ $2^Typedef $3^y;
+ static_cast<$4^Struct*>(0);
}
)cpp",
"0: targets = {Struct}\n"
- "1: targets = {Typedef}\n"
- "2: targets = {Struct}\n"},
+ "1: targets = {x}, decl\n"
+ "2: targets = {Typedef}\n"
+ "3: targets = {y}, decl\n"
+ "4: targets = {Struct}\n"},
// Name qualifiers.
{R"cpp(
namespace a { namespace b { struct S { typedef int type; }; } }
void foo() {
- $0^a::$1^b::$2^S x;
- using namespace $3^a::$4^b;
- $5^S::$6^type y;
+ $0^a::$1^b::$2^S $3^x;
+ using namespace $4^a::$5^b;
+ $6^S::$7^type $8^y;
}
)cpp",
"0: targets = {a}\n"
"1: targets = {a::b}, qualifier = 'a::'\n"
"2: targets = {a::b::S}, qualifier = 'a::b::'\n"
- "3: targets = {a}\n"
- "4: targets = {a::b}, qualifier = 'a::'\n"
- "5: targets = {a::b::S}\n"
- "6: targets = {a::b::S::type}, qualifier = 'struct S::'\n"},
+ "3: targets = {x}, decl\n"
+ "4: targets = {a}\n"
+ "5: targets = {a::b}, qualifier = 'a::'\n"
+ "6: targets = {a::b::S}\n"
+ "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n"
+ "8: targets = {y}, decl\n"},
// Simple templates.
{R"cpp(
template <class T> struct vector { using value_type = T; };
template <> struct vector<bool> { using value_type = bool; };
void foo() {
- $0^vector<int> vi;
- $1^vector<bool> vb;
+ $0^vector<int> $1^vi;
+ $2^vector<bool> $3^vb;
}
)cpp",
"0: targets = {vector<int>}\n"
- "1: targets = {vector<bool>}\n"},
+ "1: targets = {vi}, decl\n"
+ "2: targets = {vector<bool>}\n"
+ "3: targets = {vb}, decl\n"},
// Template type aliases.
{R"cpp(
template <class T> struct vector { using value_type = T; };
template <> struct vector<bool> { using value_type = bool; };
template <class T> using valias = vector<T>;
void foo() {
- $0^valias<int> vi;
- $1^valias<bool> vb;
+ $0^valias<int> $1^vi;
+ $2^valias<bool> $3^vb;
}
)cpp",
"0: targets = {valias}\n"
- "1: targets = {valias}\n"},
+ "1: targets = {vi}, decl\n"
+ "2: targets = {valias}\n"
+ "3: targets = {vb}, decl\n"},
// MemberExpr should know their using declaration.
{R"cpp(
struct X { void func(int); }
@@ -710,13 +718,14 @@ TEST_F(FindExplicitReferencesTest, All) {
};
void foo() {
- for (int x : $0^vector()) {
- $1^x = 10;
+ for (int $0^x : $1^vector()) {
+ $2^x = 10;
}
}
)cpp",
- "0: targets = {vector}\n"
- "1: targets = {x}\n"},
+ "0: targets = {x}, decl\n"
+ "1: targets = {vector}\n"
+ "2: targets = {x}\n"},
// Handle UnresolvedLookupExpr.
{R"cpp(
namespace ns1 { void func(char*); }
@@ -752,39 +761,42 @@ TEST_F(FindExplicitReferencesTest, All) {
void foo() {
static_cast<$0^T>(0);
$1^T();
- $2^T t;
+ $2^T $3^t;
}
)cpp",
"0: targets = {T}\n"
"1: targets = {T}\n"
- "2: targets = {T}\n"},
+ "2: targets = {T}\n"
+ "3: targets = {t}, decl\n"},
// Non-type template parameters.
{R"cpp(
template <int I>
void foo() {
- int x = $0^I;
+ int $0^x = $1^I;
}
)cpp",
- "0: targets = {I}\n"},
+ "0: targets = {x}, decl\n"
+ "1: targets = {I}\n"},
// Template template parameters.
{R"cpp(
template <class T> struct vector {};
template <template<class> class TT, template<class> class ...TP>
void foo() {
- $0^TT<int> x;
- $1^foo<$2^TT>();
- $3^foo<$4^vector>()
- $5^foo<$6^TP...>();
+ $0^TT<int> $1^x;
+ $2^foo<$3^TT>();
+ $4^foo<$5^vector>()
+ $6^foo<$7^TP...>();
}
)cpp",
"0: targets = {TT}\n"
- "1: targets = {foo}\n"
- "2: targets = {TT}\n"
- "3: targets = {foo}\n"
- "4: targets = {vector}\n"
- "5: targets = {foo}\n"
- "6: targets = {TP}\n"},
+ "1: targets = {x}, decl\n"
+ "2: targets = {foo}\n"
+ "3: targets = {TT}\n"
+ "4: targets = {foo}\n"
+ "5: targets = {vector}\n"
+ "6: targets = {foo}\n"
+ "7: targets = {TP}\n"},
// Non-type template parameters with declarations.
{R"cpp(
int func();
@@ -792,13 +804,37 @@ TEST_F(FindExplicitReferencesTest, All) {
template <int(*FuncParam)()>
void foo() {
- $0^wrapper<$1^func> w;
- $2^FuncParam();
+ $0^wrapper<$1^func> $2^w;
+ $3^FuncParam();
}
)cpp",
"0: targets = {wrapper<&func>}\n"
"1: targets = {func}\n"
- "2: targets = {FuncParam}\n"},
+ "2: targets = {w}, decl\n"
+ "3: targets = {FuncParam}\n"},
+ {R"cpp(
+ namespace ns {}
+ class S {};
+ void foo() {
+ class $0^Foo { $1^Foo(); ~$2^Foo(); int $3^field; };
+ int $4^Var;
+ enum $5^E { $6^ABC };
+ typedef int $7^INT;
+ using $8^INT2 = int;
+ namespace $9^NS = $10^ns;
+ }
+ )cpp",
+ "0: targets = {Foo}, decl\n"
+ "1: targets = {foo()::Foo::Foo}, decl\n"
+ "2: targets = {Foo}\n"
+ "3: targets = {foo()::Foo::field}, decl\n"
+ "4: targets = {Var}, decl\n"
+ "5: targets = {E}, decl\n"
+ "6: targets = {foo()::ABC}, decl\n"
+ "7: targets = {INT}, decl\n"
+ "8: targets = {INT2}, decl\n"
+ "9: targets = {NS}, decl\n"
+ "10: targets = {ns}\n"},
};
for (const auto &C : Cases) {
diff --git a/clangd/unittests/XRefsTests.cpp b/clangd/unittests/XRefsTests.cpp
index aafcb475..c8b00553 100644
--- a/clangd/unittests/XRefsTests.cpp
+++ b/clangd/unittests/XRefsTests.cpp
@@ -2284,7 +2284,8 @@ TEST(GetNonLocalDeclRefs, All) {
if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
Names.push_back(ND->getQualifiedNameAsString());
}
- EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls));
+ EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls))
+ << File.code();
}
}