Index: clangd/index/dex/dexp/CMakeLists.txt =================================================================== --- clangd/index/dex/dexp/CMakeLists.txt +++ clangd/index/dex/dexp/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../) set(LLVM_LINK_COMPONENTS Index: clangd/index/dex/dexp/Dexp.cpp =================================================================== --- clangd/index/dex/dexp/Dexp.cpp +++ clangd/index/dex/dexp/Dexp.cpp @@ -12,13 +12,15 @@ // //===----------------------------------------------------------------------===// -#include "../../Serialization.h" -#include "../Dex.h" +#include "Dex.h" +#include "Serialization.h" +#include "SourceCode.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/LineEditor/LineEditor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Signals.h" using clang::clangd::FuzzyFindRequest; @@ -52,6 +54,29 @@ llvm::outs() << llvm::formatv("{0} took {1:ms+n}.\n", Name, Duration); } +llvm::Expected +getSymbolIDFromIndex(llvm::StringRef QualifiedName, const SymbolIndex *Index) { + FuzzyFindRequest Request; + Request.Scopes.emplace_back(); + std::tie(Request.Scopes.back(), Request.Query) = + clang::clangd::splitQualifiedName(QualifiedName); + bool Found = false; + clang::clangd::SymbolID SymID; + // We choose the first one if there are overloaded symbols. + Index->fuzzyFind(Request, [&](const Symbol &Sym) { + std::string SymQualifiedName = (Sym.Scope + Sym.Name).str(); + if (!Found && QualifiedName == SymQualifiedName) { + SymID = Sym.ID; + Found = true; + } + }); + if (Found) + return SymID; + + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Symbol not found in index."); +} + // REPL commands inherit from Command and contain their options as members. // Creating a Command populates parser options, parseAndRun() resets them. class Command { @@ -88,7 +113,6 @@ }; // FIXME(kbobyrev): Ideas for more commands: -// * find symbol references: print set of reference locations // * load/swap/reload index: this would make it possible to get rid of llvm::cl // usages in the tool driver and actually use llvm::cl library in the REPL. // * show posting list density histogram (our dump data somewhere so that user @@ -139,12 +163,21 @@ cl::opt ID{ "id", cl::Positional, - cl::Required, cl::desc("Symbol ID to look up (hex)"), }; + cl::opt Name{ + "name", cl::desc("Qualified name to look up."), + }; void run() override { - auto SID = clang::clangd::SymbolID::fromStr(ID); + if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) { + llvm::outs() + << "Missing required argument: please provide -id or -name.\n"; + return; + } + llvm::Expected SID = + ID.getNumOccurrences() ? clang::clangd::SymbolID::fromStr(ID) + : getSymbolIDFromIndex(Name, Index); if (!SID) { llvm::outs() << llvm::toString(SID.takeError()) << "\n"; return; @@ -162,13 +195,59 @@ } }; +class Refs : public Command { + cl::opt ID{ + "id", cl::Positional, + cl::desc("Symbol ID of the symbol being queried (hex)."), + }; + cl::opt Name{ + "name", cl::desc("Qualified name of the symbol being queried."), + }; + cl::opt Filter{ + "filter", + cl::init(".*"), + cl::desc( + "Print all results from files matching this regular expression."), + }; + + void run() override { + if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) { + llvm::outs() + << "Missing required argument: please provide -id or -name.\n"; + return; + } + llvm::Expected SID = + ID.getNumOccurrences() ? clang::clangd::SymbolID::fromStr(ID) + : getSymbolIDFromIndex(Name, Index); + if (!SID) { + llvm::outs() << llvm::toString(SID.takeError()) << "\n"; + return; + } + clang::clangd::RefsRequest RefRequest; + RefRequest.IDs.insert(*SID); + llvm::Regex RegexFilter(Filter); + Index->refs(RefRequest, [&RegexFilter](const clang::clangd::Ref &R) { + auto U = clang::clangd::URI::parse(R.Location.FileURI); + if (!U) { + llvm::outs() << U.takeError(); + return; + } + if (RegexFilter.match(U->body())) + llvm::outs() << R << "\n"; + }); + } +}; + struct { const char *Name; const char *Description; std::function()> Implementation; } CommandInfo[] = { {"find", "Search for symbols with fuzzyFind", llvm::make_unique}, - {"lookup", "Dump symbol details by ID", llvm::make_unique}, + {"lookup", "Dump symbol details by ID or qualified name", + llvm::make_unique}, + {"refs", "Find references by ID or qualified name", + llvm::make_unique}, }; } // namespace