[clang-tools-extra] r341458 - [clangd] Implement findReferences function
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 5 03:33:36 PDT 2018
Author: sammccall
Date: Wed Sep 5 03:33:36 2018
New Revision: 341458
URL: http://llvm.org/viewvc/llvm-project?rev=341458&view=rev
Log:
[clangd] Implement findReferences function
clangd will use findReferences to provide LSP's reference feature.
Modified:
clang-tools-extra/trunk/clangd/XRefs.cpp
clang-tools-extra/trunk/clangd/XRefs.h
clang-tools-extra/trunk/unittests/clangd/TestTU.cpp
clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp
Modified: clang-tools-extra/trunk/clangd/XRefs.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.cpp?rev=341458&r1=341457&r2=341458&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.cpp (original)
+++ clang-tools-extra/trunk/clangd/XRefs.cpp Wed Sep 5 03:33:36 2018
@@ -174,30 +174,27 @@ IdentifiedSymbol getSymbolAtPosition(Par
return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()};
}
-llvm::Optional<Location>
-makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
+Range getTokenRange(ParsedAST &AST, SourceLocation TokLoc) {
const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
- const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
- SourceLocation LocStart = ValSourceRange.getBegin();
+ SourceLocation LocEnd = Lexer::getLocForEndOfToken(
+ TokLoc, 0, SourceMgr, AST.getASTContext().getLangOpts());
+ return {sourceLocToPosition(SourceMgr, TokLoc),
+ sourceLocToPosition(SourceMgr, LocEnd)};
+}
- const FileEntry *F =
- SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart));
+llvm::Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc) {
+ const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+ const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc));
if (!F)
return llvm::None;
- SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
- SourceMgr, LangOpts);
- Position Begin = sourceLocToPosition(SourceMgr, LocStart);
- Position End = sourceLocToPosition(SourceMgr, LocEnd);
- Range R = {Begin, End};
- Location L;
-
auto FilePath = getRealPath(F, SourceMgr);
if (!FilePath) {
log("failed to get path!");
return llvm::None;
}
+ Location L;
L.uri = URIForFile(*FilePath);
- L.range = R;
+ L.range = getTokenRange(AST, TokLoc);
return L;
}
@@ -223,7 +220,7 @@ std::vector<Location> findDefinitions(Pa
for (auto Item : Symbols.Macros) {
auto Loc = Item.Info->getDefinitionLoc();
- auto L = makeLocation(AST, SourceRange(Loc, Loc));
+ auto L = makeLocation(AST, Loc);
if (L)
Result.push_back(*L);
}
@@ -266,7 +263,7 @@ std::vector<Location> findDefinitions(Pa
auto &Candidate = ResultCandidates[Key];
auto Loc = findNameLoc(D);
- auto L = makeLocation(AST, SourceRange(Loc, Loc));
+ auto L = makeLocation(AST, Loc);
// The declaration in the identified symbols is a definition if possible
// otherwise it is declaration.
bool IsDef = getDefinition(D) == D;
@@ -316,24 +313,36 @@ std::vector<Location> findDefinitions(Pa
namespace {
-/// Finds document highlights that a given list of declarations refers to.
-class DocumentHighlightsFinder : public index::IndexDataConsumer {
- std::vector<const Decl *> &Decls;
- std::vector<DocumentHighlight> DocumentHighlights;
- const ASTContext &AST;
-
+/// Collects references to symbols within the main file.
+class ReferenceFinder : public index::IndexDataConsumer {
public:
- DocumentHighlightsFinder(ASTContext &AST, Preprocessor &PP,
- std::vector<const Decl *> &Decls)
- : Decls(Decls), AST(AST) {}
- std::vector<DocumentHighlight> takeHighlights() {
- // Don't keep the same highlight multiple times.
- // This can happen when nodes in the AST are visited twice.
- std::sort(DocumentHighlights.begin(), DocumentHighlights.end());
- auto Last =
- std::unique(DocumentHighlights.begin(), DocumentHighlights.end());
- DocumentHighlights.erase(Last, DocumentHighlights.end());
- return std::move(DocumentHighlights);
+ struct Reference {
+ const Decl *Target;
+ SourceLocation Loc;
+ index::SymbolRoleSet Role;
+ };
+
+ ReferenceFinder(ASTContext &AST, Preprocessor &PP,
+ const std::vector<const Decl *> &TargetDecls)
+ : AST(AST) {
+ for (const Decl *D : TargetDecls)
+ Targets.insert(D);
+ }
+
+ std::vector<Reference> take() && {
+ std::sort(References.begin(), References.end(),
+ [](const Reference &L, const Reference &R) {
+ return std::tie(L.Loc, L.Target, L.Role) <
+ std::tie(R.Loc, R.Target, R.Role);
+ });
+ // We sometimes see duplicates when parts of the AST get traversed twice.
+ References.erase(std::unique(References.begin(), References.end(),
+ [](const Reference &L, const Reference &R) {
+ return std::tie(L.Target, L.Loc, L.Role) ==
+ std::tie(R.Target, R.Loc, R.Role);
+ }),
+ References.end());
+ return std::move(References);
}
bool
@@ -341,63 +350,53 @@ public:
ArrayRef<index::SymbolRelation> Relations,
SourceLocation Loc,
index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
- const SourceManager &SourceMgr = AST.getSourceManager();
- SourceLocation HighlightStartLoc = SourceMgr.getFileLoc(Loc);
- if (SourceMgr.getMainFileID() != SourceMgr.getFileID(HighlightStartLoc) ||
- std::find(Decls.begin(), Decls.end(), D) == Decls.end()) {
- return true;
- }
- SourceLocation End;
- const LangOptions &LangOpts = AST.getLangOpts();
- End = Lexer::getLocForEndOfToken(HighlightStartLoc, 0, SourceMgr, LangOpts);
- SourceRange SR(HighlightStartLoc, End);
-
- DocumentHighlightKind Kind = DocumentHighlightKind::Text;
- if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Write) & Roles)
- Kind = DocumentHighlightKind::Write;
- else if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Read) & Roles)
- Kind = DocumentHighlightKind::Read;
-
- DocumentHighlights.push_back(getDocumentHighlight(SR, Kind));
+ const SourceManager &SM = AST.getSourceManager();
+ Loc = SM.getFileLoc(Loc);
+ if (SM.isWrittenInMainFile(Loc) && Targets.count(D))
+ References.push_back({D, Loc, Roles});
return true;
}
private:
- DocumentHighlight getDocumentHighlight(SourceRange SR,
- DocumentHighlightKind Kind) {
- const SourceManager &SourceMgr = AST.getSourceManager();
- Position Begin = sourceLocToPosition(SourceMgr, SR.getBegin());
- Position End = sourceLocToPosition(SourceMgr, SR.getEnd());
- Range R = {Begin, End};
- DocumentHighlight DH;
- DH.range = R;
- DH.kind = Kind;
- return DH;
- }
+ llvm::SmallSet<const Decl *, 4> Targets;
+ std::vector<Reference> References;
+ const ASTContext &AST;
};
-} // namespace
-
-std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
- Position Pos) {
- const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
- SourceLocation SourceLocationBeg =
- getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
-
- auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
- std::vector<const Decl *> SelectedDecls = Symbols.Decls;
-
- DocumentHighlightsFinder DocHighlightsFinder(
- AST.getASTContext(), AST.getPreprocessor(), SelectedDecls);
-
+std::vector<ReferenceFinder::Reference>
+findRefs(const std::vector<const Decl *> &Decls, ParsedAST &AST) {
+ ReferenceFinder RefFinder(AST.getASTContext(), AST.getPreprocessor(), Decls);
index::IndexingOptions IndexOpts;
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = true;
indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
- DocHighlightsFinder, IndexOpts);
+ RefFinder, IndexOpts);
+ return std::move(RefFinder).take();
+}
+
+} // namespace
+
+std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
+ Position Pos) {
+ const SourceManager &SM = AST.getASTContext().getSourceManager();
+ auto Symbols = getSymbolAtPosition(
+ AST, getBeginningOfIdentifier(AST, Pos, SM.getMainFileID()));
+ auto References = findRefs(Symbols.Decls, AST);
- return DocHighlightsFinder.takeHighlights();
+ std::vector<DocumentHighlight> Result;
+ for (const auto &Ref : References) {
+ DocumentHighlight DH;
+ DH.range = getTokenRange(AST, Ref.Loc);
+ if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
+ DH.kind = DocumentHighlightKind::Write;
+ else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
+ DH.kind = DocumentHighlightKind::Read;
+ else
+ DH.kind = DocumentHighlightKind::Text;
+ Result.push_back(std::move(DH));
+ }
+ return Result;
}
static PrintingPolicy printingPolicyForDecls(PrintingPolicy Base) {
@@ -659,5 +658,51 @@ Optional<Hover> getHover(ParsedAST &AST,
return None;
}
+std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
+ const SymbolIndex *Index) {
+ std::vector<Location> Results;
+ const SourceManager &SM = AST.getASTContext().getSourceManager();
+ auto MainFilePath = getRealPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
+ if (!MainFilePath) {
+ elog("Failed to get a path for the main file, so no references");
+ return Results;
+ }
+ auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID());
+ auto Symbols = getSymbolAtPosition(AST, Loc);
+
+ // We traverse the AST to find references in the main file.
+ // TODO: should we handle macros, too?
+ auto MainFileRefs = findRefs(Symbols.Decls, AST);
+ for (const auto &Ref : MainFileRefs) {
+ Location Result;
+ Result.range = getTokenRange(AST, Ref.Loc);
+ Result.uri = URIForFile(*MainFilePath);
+ Results.push_back(std::move(Result));
+ }
+
+ // Now query the index for references from other files.
+ if (!Index)
+ return Results;
+ RefsRequest Req;
+ for (const Decl *D : Symbols.Decls) {
+ // Not all symbols can be referenced from outside (e.g. function-locals).
+ // TODO: we could skip TU-scoped symbols here (e.g. static functions) if
+ // we know this file isn't a header. The details might be tricky.
+ if (D->getParentFunctionOrMethod())
+ continue;
+ if (auto ID = getSymbolID(D))
+ Req.IDs.insert(*ID);
+ }
+ if (Req.IDs.empty())
+ return Results;
+ Index->refs(Req, [&](const Ref &R) {
+ auto LSPLoc = toLSPLocation(R.Location, /*HintPath=*/*MainFilePath);
+ // Avoid indexed results for the main file - the AST is authoritative.
+ if (LSPLoc && LSPLoc->uri.file() != *MainFilePath)
+ Results.push_back(std::move(*LSPLoc));
+ });
+ return Results;
+}
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/clangd/XRefs.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.h?rev=341458&r1=341457&r2=341458&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.h (original)
+++ clang-tools-extra/trunk/clangd/XRefs.h Wed Sep 5 03:33:36 2018
@@ -34,6 +34,10 @@ std::vector<DocumentHighlight> findDocum
/// Get the hover information when hovering at \p Pos.
llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos);
+/// Returns reference locations of the symbol at a specified \p Pos.
+std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
+ const SymbolIndex *Index = nullptr);
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/unittests/clangd/TestTU.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TestTU.cpp?rev=341458&r1=341457&r2=341458&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/TestTU.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/TestTU.cpp Wed Sep 5 03:33:36 2018
@@ -49,8 +49,10 @@ SymbolSlab TestTU::headerSymbols() const
}
std::unique_ptr<SymbolIndex> TestTU::index() const {
- // FIXME: we should generate proper refs for TestTU.
- return MemIndex::build(headerSymbols(), RefSlab());
+ auto AST = build();
+ auto Content = indexAST(AST.getASTContext(), AST.getPreprocessorPtr(),
+ AST.getLocalTopLevelDecls());
+ return MemIndex::build(std::move(Content.first), std::move(Content.second));
}
const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) {
Modified: clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp?rev=341458&r1=341457&r2=341458&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp Wed Sep 5 03:33:36 2018
@@ -1068,6 +1068,143 @@ TEST(GoToDefinition, WithPreamble) {
ElementsAre(Location{FooCppUri, FooWithoutHeader.range()}));
}
+TEST(FindReferences, WithinAST) {
+ const char *Tests[] = {
+ R"cpp(// Local variable
+ int main() {
+ int $foo[[foo]];
+ $foo[[^foo]] = 2;
+ int test1 = $foo[[foo]];
+ }
+ )cpp",
+
+ R"cpp(// Struct
+ namespace ns1 {
+ struct $foo[[Foo]] {};
+ } // namespace ns1
+ int main() {
+ ns1::$foo[[Fo^o]]* Params;
+ }
+ )cpp",
+
+ R"cpp(// Function
+ int $foo[[foo]](int) {}
+ int main() {
+ auto *X = &$foo[[^foo]];
+ $foo[[foo]](42)
+ }
+ )cpp",
+
+ R"cpp(// Field
+ struct Foo {
+ int $foo[[foo]];
+ Foo() : $foo[[foo]](0) {}
+ };
+ int main() {
+ Foo f;
+ f.$foo[[f^oo]] = 1;
+ }
+ )cpp",
+
+ R"cpp(// Method call
+ struct Foo { int [[foo]](); };
+ int Foo::[[foo]]() {}
+ int main() {
+ Foo f;
+ f.^foo();
+ }
+ )cpp",
+
+ R"cpp(// Typedef
+ typedef int $foo[[Foo]];
+ int main() {
+ $foo[[^Foo]] bar;
+ }
+ )cpp",
+
+ R"cpp(// Namespace
+ namespace $foo[[ns]] {
+ struct Foo {};
+ } // namespace ns
+ int main() { $foo[[^ns]]::Foo foo; }
+ )cpp",
+ };
+ for (const char *Test : Tests) {
+ Annotations T(Test);
+ auto AST = TestTU::withCode(T.code()).build();
+ std::vector<Matcher<Location>> ExpectedLocations;
+ for (const auto &R : T.ranges("foo"))
+ ExpectedLocations.push_back(RangeIs(R));
+ EXPECT_THAT(findReferences(AST, T.point()),
+ ElementsAreArray(ExpectedLocations))
+ << Test;
+ }
+}
+
+TEST(FindReferences, NeedsIndex) {
+ const char *Header = "int foo();";
+ Annotations Main("int main() { [[f^oo]](); }");
+ TestTU TU;
+ TU.Code = Main.code();
+ TU.HeaderCode = Header;
+ auto AST = TU.build();
+
+ // References in main file are returned without index.
+ EXPECT_THAT(findReferences(AST, Main.point(), /*Index=*/nullptr),
+ ElementsAre(RangeIs(Main.range())));
+ Annotations IndexedMain(R"cpp(
+ int main() { [[f^oo]](); }
+ )cpp");
+
+ // References from indexed files are included.
+ TestTU IndexedTU;
+ IndexedTU.Code = IndexedMain.code();
+ IndexedTU.Filename = "Indexed.cpp";
+ IndexedTU.HeaderCode = Header;
+ EXPECT_THAT(findReferences(AST, Main.point(), IndexedTU.index().get()),
+ ElementsAre(RangeIs(Main.range()), RangeIs(IndexedMain.range())));
+
+ // If the main file is in the index, we don't return duplicates.
+ // (even if the references are in a different location)
+ TU.Code = ("\n\n" + Main.code()).str();
+ EXPECT_THAT(findReferences(AST, Main.point(), TU.index().get()),
+ ElementsAre(RangeIs(Main.range())));
+};
+
+TEST(FindReferences, NoQueryForLocalSymbols) {
+ struct RecordingIndex : public MemIndex {
+ mutable Optional<DenseSet<SymbolID>> RefIDs;
+ void refs(const RefsRequest &Req,
+ llvm::function_ref<void(const Ref &)>) const override {
+ RefIDs = Req.IDs;
+ }
+ };
+
+ struct Test {
+ StringRef AnnotatedCode;
+ bool WantQuery;
+ } Tests[] = {
+ {"int ^x;", true},
+ // For now we don't assume header structure which would allow skipping.
+ {"namespace { int ^x; }", true},
+ {"static int ^x;", true},
+ // Anything in a function certainly can't be referenced though.
+ {"void foo() { int ^x; }", false},
+ {"void foo() { struct ^x{}; }", false},
+ {"auto lambda = []{ int ^x; };", false},
+ };
+ for (Test T : Tests) {
+ Annotations File(T.AnnotatedCode);
+ RecordingIndex Rec;
+ auto AST = TestTU::withCode(File.code()).build();
+ findReferences(AST, File.point(), &Rec);
+ if (T.WantQuery)
+ EXPECT_NE(Rec.RefIDs, llvm::None) << T.AnnotatedCode;
+ else
+ EXPECT_EQ(Rec.RefIDs, llvm::None) << T.AnnotatedCode;
+ }
+};
+
} // namespace
} // namespace clangd
} // namespace clang
More information about the cfe-commits
mailing list