Index: clang-tools-extra/trunk/clangd/XRefs.h =================================================================== --- clang-tools-extra/trunk/clangd/XRefs.h +++ clang-tools-extra/trunk/clangd/XRefs.h @@ -17,8 +17,8 @@ #include "Path.h" #include "Protocol.h" #include "index/Index.h" -#include "clang/AST/Type.h" #include "index/SymbolLocation.h" +#include "clang/AST/Type.h" #include "clang/Format/Format.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/Optional.h" @@ -158,6 +158,9 @@ /// SourceLocationBeg must point to the first character of the token bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg); +/// Returns all decls that are referenced in the \p FD except local symbols. +llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, + const FunctionDecl *FD); } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/clangd/XRefs.cpp =================================================================== --- clang-tools-extra/trunk/clangd/XRefs.cpp +++ clang-tools-extra/trunk/clangd/XRefs.cpp @@ -9,6 +9,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "FindSymbols.h" +#include "FindTarget.h" #include "FormattedString.h" #include "Logger.h" #include "ParsedAST.h" @@ -1299,5 +1300,18 @@ return OS; } +llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, + const FunctionDecl *FD) { + if (!FD->hasBody()) + return {}; + llvm::DenseSet DeclRefs; + findExplicitReferences(FD, [&](ReferenceLoc Ref) { + for (const Decl *D : Ref.Targets) { + if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter()) + DeclRefs.insert(D); + } + }); + return DeclRefs; +} } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/clangd/unittests/XRefsTests.cpp =================================================================== --- clang-tools-extra/trunk/clangd/unittests/XRefsTests.cpp +++ clang-tools-extra/trunk/clangd/unittests/XRefsTests.cpp @@ -10,6 +10,7 @@ #include "Matchers.h" #include "ParsedAST.h" #include "Protocol.h" +#include "SourceCode.h" #include "SyncAPI.h" #include "TestFS.h" #include "TestIndex.h" @@ -18,13 +19,19 @@ #include "index/FileIndex.h" #include "index/MemIndex.h" #include "index/SymbolCollector.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Index/IndexingAction.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include namespace clang { namespace clangd { @@ -2187,6 +2194,84 @@ } } +TEST(GetNonLocalDeclRefs, All) { + struct Case { + llvm::StringRef AnnotatedCode; + std::vector ExpectedDecls; + } Cases[] = { + { + // VarDecl and ParamVarDecl + R"cpp( + void bar(); + void ^foo(int baz) { + int x = 10; + bar(); + })cpp", + {"bar"}, + }, + { + // Method from class + R"cpp( + class Foo { public: void foo(); }; + class Bar { + void foo(); + void bar(); + }; + void Bar::^foo() { + Foo f; + bar(); + f.foo(); + })cpp", + {"Bar", "Bar::bar", "Foo", "Foo::foo"}, + }, + { + // Local types + R"cpp( + void ^foo() { + class Foo { public: void foo() {} }; + class Bar { public: void bar() {} }; + Foo f; + Bar b; + b.bar(); + f.foo(); + })cpp", + {}, + }, + { + // Template params + R"cpp( + template class Q> + void ^foo() { + T x; + Q y; + })cpp", + {}, + }, + }; + for (const Case &C : Cases) { + Annotations File(C.AnnotatedCode); + auto AST = TestTU::withCode(File.code()).build(); + ASSERT_TRUE(AST.getDiagnostics().empty()) + << AST.getDiagnostics().begin()->Message; + SourceLocation SL = llvm::cantFail( + sourceLocationInMainFile(AST.getSourceManager(), File.point())); + + const FunctionDecl *FD = + llvm::dyn_cast(&findDecl(AST, [SL](const NamedDecl &ND) { + return ND.getLocation() == SL && llvm::isa(ND); + })); + ASSERT_NE(FD, nullptr); + + auto NonLocalDeclRefs = getNonLocalDeclRefs(AST, FD); + std::vector Names; + for (const Decl *D : NonLocalDeclRefs) { + if (const auto *ND = llvm::dyn_cast(D)) + Names.push_back(ND->getQualifiedNameAsString()); + } + EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)); + } +} + } // namespace } // namespace clangd } // namespace clang