diff --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h --- a/clang-tools-extra/clangd/FindTarget.h +++ b/clang-tools-extra/clangd/FindTarget.h @@ -86,6 +86,8 @@ NestedNameSpecifierLoc Qualifier; /// Start location of the last name part, i.e. 'foo' in 'ns::foo'. SourceLocation NameLoc; + /// True if the reference is a declaration or definition; + bool IsDecl; // 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/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 @@ -415,15 +415,16 @@ Optional refInDecl(const Decl *D) { struct Visitor : ConstDeclVisitor { llvm::Optional Ref; - + bool DeclRef = true; void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { Ref = ReferenceLoc{D->getQualifierLoc(), D->getIdentLocation(), + DeclRef, {D->getNominatedNamespaceAsWritten()}}; } void VisitUsingDecl(const UsingDecl *D) { - Ref = ReferenceLoc{D->getQualifierLoc(), D->getLocation(), + Ref = ReferenceLoc{D->getQualifierLoc(), D->getLocation(), DeclRef, explicitReferenceTargets(DynTypedNode::create(*D), DeclRelation::Underlying)}; } @@ -431,8 +432,24 @@ void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { Ref = ReferenceLoc{D->getQualifierLoc(), D->getTargetNameLoc(), + DeclRef, {D->getAliasedNamespace()}}; } + + void VisitTagDecl(const TagDecl *ND) { + Ref = + ReferenceLoc{ND->getQualifierLoc(), ND->getLocation(), DeclRef, {ND}}; + } + + void VisitDeclaratorDecl(const DeclaratorDecl *ND) { + Ref = + ReferenceLoc{ND->getQualifierLoc(), ND->getLocation(), DeclRef, {ND}}; + } + + void VisitNamedDecl(const NamedDecl *ND) { + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), ND->getLocation(), DeclRef, {ND}}; + } }; Visitor V; @@ -444,20 +461,24 @@ struct Visitor : ConstStmtVisitor { // FIXME: handle more complicated cases, e.g. ObjC, designated initializers. llvm::Optional Ref; - + bool NotDeclRef = false; void VisitDeclRefExpr(const DeclRefExpr *E) { - Ref = ReferenceLoc{ - E->getQualifierLoc(), E->getNameInfo().getLoc(), {E->getFoundDecl()}}; + Ref = ReferenceLoc{E->getQualifierLoc(), + E->getNameInfo().getLoc(), + NotDeclRef, + {E->getFoundDecl()}}; } void VisitMemberExpr(const MemberExpr *E) { Ref = ReferenceLoc{E->getQualifierLoc(), E->getMemberNameInfo().getLoc(), + NotDeclRef, {E->getFoundDecl()}}; } void VisitOverloadExpr(const OverloadExpr *E) { Ref = ReferenceLoc{E->getQualifierLoc(), E->getNameInfo().getLoc(), + NotDeclRef, llvm::SmallVector( E->decls().begin(), E->decls().end())}; } @@ -471,7 +492,7 @@ Optional refInTypeLoc(TypeLoc L) { struct Visitor : TypeLocVisitor { llvm::Optional Ref; - + bool NotDeclRef = false; void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { // We only know about qualifier, rest if filled by inner locations. Visit(L.getNamedTypeLoc().getUnqualifiedLoc()); @@ -483,13 +504,13 @@ } void VisitTagTypeLoc(TagTypeLoc L) { - Ref = - ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getNameLoc(), NotDeclRef, {L.getDecl()}}; } void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) { - Ref = - ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + Ref = ReferenceLoc{ + NestedNameSpecifierLoc(), L.getNameLoc(), NotDeclRef, {L.getDecl()}}; } void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) { @@ -502,14 +523,14 @@ // 2. 'vector' with mask 'Underlying'. // we want to return only #1 in this case. Ref = ReferenceLoc{ - NestedNameSpecifierLoc(), L.getTemplateNameLoc(), + NestedNameSpecifierLoc(), L.getTemplateNameLoc(), NotDeclRef, explicitReferenceTargets(DynTypedNode::create(L.getType()), DeclRelation::Alias)}; } void VisitDeducedTemplateSpecializationTypeLoc( DeducedTemplateSpecializationTypeLoc L) { Ref = ReferenceLoc{ - NestedNameSpecifierLoc(), L.getNameLoc(), + NestedNameSpecifierLoc(), L.getNameLoc(), NotDeclRef, explicitReferenceTargets(DynTypedNode::create(L.getType()), DeclRelation::Alias)}; } @@ -517,19 +538,21 @@ void VisitDependentTemplateSpecializationTypeLoc( DependentTemplateSpecializationTypeLoc L) { Ref = ReferenceLoc{ - L.getQualifierLoc(), L.getTemplateNameLoc(), + L.getQualifierLoc(), L.getTemplateNameLoc(), NotDeclRef, explicitReferenceTargets(DynTypedNode::create(L.getType()))}; } void VisitDependentNameTypeLoc(DependentNameTypeLoc L) { Ref = ReferenceLoc{ - L.getQualifierLoc(), L.getNameLoc(), + L.getQualifierLoc(), L.getNameLoc(), NotDeclRef, explicitReferenceTargets(DynTypedNode::create(L.getType()))}; } void VisitTypedefTypeLoc(TypedefTypeLoc L) { - Ref = ReferenceLoc{ - NestedNameSpecifierLoc(), L.getNameLoc(), {L.getTypedefNameDecl()}}; + Ref = ReferenceLoc{NestedNameSpecifierLoc(), + L.getNameLoc(), + NotDeclRef, + {L.getTypedefNameDecl()}}; } }; @@ -575,6 +598,7 @@ case TemplateArgument::TemplateExpansion: reportReference(ReferenceLoc{A.getTemplateQualifierLoc(), A.getTemplateNameLoc(), + false, {A.getArgument() .getAsTemplateOrTemplatePattern() .getAsTemplateDecl()}}, @@ -631,7 +655,7 @@ if (auto *E = N.get()) return refInExpr(E); if (auto *NNSL = N.get()) - return ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), + return ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), false, explicitReferenceTargets(DynTypedNode::create( *NNSL->getNestedNameSpecifier()))}; if (const TypeLoc *TL = N.get()) @@ -642,6 +666,7 @@ assert(CCI->isAnyMemberInitializer()); return ReferenceLoc{NestedNameSpecifierLoc(), CCI->getMemberLocation(), + false, {CCI->getAnyMember()}}; } // We do not have location information for other nodes (QualType, etc) @@ -727,6 +752,8 @@ PrintingPolicy(LangOptions())); OS << "'"; } + if (R.IsDecl) + OS << ", decl ref"; return OS; } diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1307,7 +1307,8 @@ llvm::DenseSet 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/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -520,8 +520,28 @@ findExplicitReferences(Func.getBody(), [&Refs](ReferenceLoc R) { Refs.push_back(std::move(R)); }); + return dumpReferences(std::move(Refs), Code, AST.getSourceManager()); + } + + /// Similar to annotateReferencesInFoo, but finds all references in the main + /// AST. + AllRefs annotateReferencesInMainAST(llvm::StringRef Code) { + TestTU TU; + TU.Code = Code; - auto &SM = AST.getSourceManager(); + auto AST = TU.build(); + std::vector Refs; + for (const auto *ToplLevelDecl : AST.getLocalTopLevelDecls()) { + findExplicitReferences(ToplLevelDecl, [&Refs](ReferenceLoc R) { + Refs.push_back(std::move(R)); + }); + } + return dumpReferences(std::move(Refs), Code, AST.getSourceManager()); + } + +private: + AllRefs dumpReferences(std::vector Refs, llvm::StringRef Code, + const SourceManager &SM) { llvm::sort(Refs, [&](const ReferenceLoc &L, const ReferenceLoc &R) { return SM.isBeforeInTranslationUnit(L.NameLoc, R.NameLoc); }); @@ -590,8 +610,8 @@ using namespace $1^alias; } )cpp", - "0: targets = {ns}\n" - "1: targets = {alias}\n"}, + "0: targets = {ns}, decl ref\n" + "1: targets = {alias}, decl ref\n"}, // Using declarations. {R"cpp( namespace ns { int global; } @@ -600,59 +620,67 @@ } )cpp", "0: targets = {ns}\n" - "1: targets = {ns::global}, qualifier = 'ns::'\n"}, + "1: targets = {ns::global}, qualifier = 'ns::', decl ref\n"}, // Simple types. {R"cpp( 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 ref\n" + "2: targets = {Typedef}\n" + "3: targets = {y}, decl ref\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 ref\n" + "4: targets = {a}\n" + "5: targets = {a::b}, qualifier = 'a::', decl ref\n" + "6: targets = {a::b::S}\n" + "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" + "8: targets = {y}, decl ref\n"}, // Simple templates. {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; void foo() { - $0^vector vi; - $1^vector vb; + $0^vector $1^vi; + $2^vector $3^vb; } )cpp", "0: targets = {vector}\n" - "1: targets = {vector}\n"}, + "1: targets = {vi}, decl ref\n" + "2: targets = {vector}\n" + "3: targets = {vb}, decl ref\n"}, // Template type aliases. {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; template using valias = vector; void foo() { - $0^valias vi; - $1^valias vb; + $0^valias $1^vi; + $2^valias $3^vb; } )cpp", "0: targets = {valias}\n" - "1: targets = {valias}\n"}, + "1: targets = {vi}, decl ref\n" + "2: targets = {valias}\n" + "3: targets = {vb}, decl ref\n"}, // MemberExpr should know their using declaration. {R"cpp( struct X { void func(int); } @@ -694,13 +722,14 @@ }; 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 ref\n" + "1: targets = {vector}\n" + "2: targets = {x}\n"}, // Handle UnresolvedLookupExpr. {R"cpp( namespace ns1 { void func(char*); } @@ -736,39 +765,42 @@ 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 ref\n"}, // Non-type template parameters. {R"cpp( template void foo() { - int x = $0^I; + int $0^x = $1^I; } )cpp", - "0: targets = {I}\n"}, + "0: targets = {x}, decl ref\n" + "1: targets = {I}\n"}, // Template template parameters. {R"cpp( template struct vector {}; template class TT, template class ...TP> void foo() { - $0^TT x; - $1^foo<$2^TT>(); - $3^foo<$4^vector>() - $5^foo<$6^TP...>(); + $0^TT $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 ref\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(); @@ -776,13 +808,14 @@ template 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 ref\n" + "3: targets = {FuncParam}\n"}, }; for (const auto &C : Cases) { @@ -796,6 +829,70 @@ } } +TEST_F(FindExplicitReferencesTest, DeclReference) { + std::pair Cases[] = + { + // simple declarations. + {R"cpp( + class $0^Foo { $1^Foo(); $2^~$3^Foo(); int $4^field; }; + int $5^Var; + void $6^Fun() {} + enum $7^E { $8^ABC }; + typedef int $9^INT; + using $10^INT2 = int; + template + // FIXME: the duplicated locations are from FunctionTemplateDecl and + // the underlying FunctionDecl. we should report just one. + void $12^$13^F() {} + )cpp", + "0: targets = {Foo}, decl ref\n" + "1: targets = {Foo::Foo}, decl ref\n" + "2: targets = {Foo::~Foo}, decl ref\n" + "3: targets = {Foo}\n" + "4: targets = {Foo::field}, decl ref\n" + "5: targets = {Var}, decl ref\n" + "6: targets = {Fun}, decl ref\n" + "7: targets = {E}, decl ref\n" + "8: targets = {ABC}, decl ref\n" + "9: targets = {INT}, decl ref\n" + "10: targets = {INT2}, decl ref\n" + "11: targets = {T}, decl ref\n" + "12: targets = {F}, decl ref\n" + "13: targets = {F}, decl ref\n"}, + // declarations with qualifiers. + {R"cpp( + namespace $0^ns { + class $1^Foo { + void $2^method(); + }; + void $3^func(); + } + void $4^ns::$5^Foo::$6^method() {} + void $7^ns::$8^func() {} + )cpp", + "0: targets = {ns}, decl ref\n" + "1: targets = {ns::Foo}, decl ref\n" + "2: targets = {ns::Foo::method}, decl ref\n" + "3: targets = {ns::func}, decl ref\n" + "4: targets = {ns}\n" + "5: targets = {ns::Foo}, qualifier = 'ns::'\n" + "6: targets = {ns::Foo::method}, qualifier = 'ns::class Foo::', " + "decl ref\n" + "7: targets = {ns}\n" + "8: targets = {ns::func}, qualifier = 'ns::', decl ref\n"}, + }; + + for (const auto &C : Cases) { + llvm::StringRef ExpectedCode = C.first; + llvm::StringRef ExpectedRefs = C.second; + + auto Actual = + annotateReferencesInMainAST(llvm::Annotations(ExpectedCode).code()); + EXPECT_EQ(ExpectedCode, Actual.AnnotatedCode); + EXPECT_EQ(ExpectedRefs, Actual.DumpedReferences) << ExpectedCode; + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -2277,7 +2277,8 @@ if (const auto *ND = llvm::dyn_cast(D)) Names.push_back(ND->getQualifiedNameAsString()); } - EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)); + EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)) + << File.code(); } }