diff --git a/clang-tools-extra/clangd/URI.h b/clang-tools-extra/clangd/URI.h --- a/clang-tools-extra/clangd/URI.h +++ b/clang-tools-extra/clangd/URI.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATHURI_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATHURI_H +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/Registry.h" @@ -102,6 +103,18 @@ std::string Body; }; +// Returns a URI of \p Path. Firstly, this makes the \p Path absolute using the +// current working directory of the given SourceManager if the Path is not an +// absolute path. If failed, this resolves relative paths against \p FallbackDir +// to get an absolute path. Then, this tries creating an URI for the absolute +// path with schemes specified in \p Opts. This returns an URI with the first +// working scheme, if there is any; otherwise, this returns None. +// +// The Path can be a path relative to the build directory, or retrieved from +// the SourceManager. +URI toURI(llvm::StringRef Path, const SourceManager &SM, + const std::string &FallbackDir); + /// URIScheme is an extension point for teaching clangd to recognize a custom /// URI scheme. This is expected to be implemented and exposed via the /// URISchemeRegistry. diff --git a/clang-tools-extra/clangd/URI.cpp b/clang-tools-extra/clangd/URI.cpp --- a/clang-tools-extra/clangd/URI.cpp +++ b/clang-tools-extra/clangd/URI.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "URI.h" +#include "SourceCode.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" @@ -265,5 +266,21 @@ return S->get()->getIncludeSpelling(Uri); } +URI toURI(llvm::StringRef Path, const SourceManager &SM, + const std::string &FallbackDir) { + llvm::SmallString<128> AbsolutePath(Path); + if (auto File = SM.getFileManager().getFile(Path)) { + if (auto CanonPath = getCanonicalPath(*File, SM)) { + AbsolutePath = *CanonPath; + } + } + // We don't perform is_absolute check in an else branch because makeAbsolute + // might return a relative path on some InMemoryFileSystems. + if (!llvm::sys::path::is_absolute(AbsolutePath) && !FallbackDir.empty()) + llvm::sys::fs::make_absolute(FallbackDir, AbsolutePath); + llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true); + return URI::create(AbsolutePath); +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/FileIndex.h b/clang-tools-extra/clangd/index/FileIndex.h --- a/clang-tools-extra/clangd/index/FileIndex.h +++ b/clang-tools-extra/clangd/index/FileIndex.h @@ -140,7 +140,7 @@ /// Exposed to assist in unit tests. SlabTuple indexMainDecls(ParsedAST &AST); -/// Idex declarations from \p AST and macros from \p PP that are declared in +/// Index declarations from \p AST and macros from \p PP that are declared in /// included headers. SlabTuple indexHeaderSymbols(ASTContext &AST, std::shared_ptr PP, const CanonicalIncludes &Includes); diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp b/clang-tools-extra/clangd/index/FileIndex.cpp --- a/clang-tools-extra/clangd/index/FileIndex.cpp +++ b/clang-tools-extra/clangd/index/FileIndex.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "FileIndex.h" +#include "CollectMacros.h" #include "Logger.h" #include "ParsedAST.h" #include "SymbolCollector.h" @@ -32,6 +33,7 @@ static SlabTuple indexSymbols(ASTContext &AST, std::shared_ptr PP, llvm::ArrayRef DeclsToIndex, + const MainFileMacros *MacroRefsToIndex, const CanonicalIncludes &Includes, bool IsIndexMainAST) { SymbolCollector::Options CollectorOpts; @@ -68,6 +70,34 @@ auto Syms = Collector.takeSymbols(); auto Refs = Collector.takeRefs(); auto Relations = Collector.takeRelations(); + + if (MacroRefsToIndex && MainFileEntry) { + const auto MainFileURI = + toURI(MainFileEntry->getName(), SM, CollectorOpts.FallbackDir) + .toString(); + // Copy previous references. + RefSlab::Builder RefsBuilder; + for (const auto &IDAndRefs : Refs) { + for (const auto &Ref : IDAndRefs.second) { + RefsBuilder.insert(IDAndRefs.first, Ref); + } + } + // Add macro references. + for (const auto &IDToRefs : MacroRefsToIndex->MacroRefs) { + for (const auto &Range : IDToRefs.second) { + Ref R; + R.Location.Start.setLine(Range.start.line); + R.Location.Start.setColumn(Range.start.character); + R.Location.End.setLine(Range.end.line); + R.Location.End.setColumn(Range.end.character); + R.Location.FileURI = MainFileURI.c_str(); + // FIXME: Add correct RefKind information to MainFileMacros. + R.Kind = RefKind::Reference; + RefsBuilder.insert(IDToRefs.first, R); + } + } + Refs = std::move(RefsBuilder).build(); + } vlog("index AST for {0} (main={1}): \n" " symbol slab: {2} symbols, {3} bytes\n" " ref slab: {4} symbols, {5} refs, {6} bytes\n" @@ -80,7 +110,8 @@ SlabTuple indexMainDecls(ParsedAST &AST) { return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls(), AST.getCanonicalIncludes(), + AST.getLocalTopLevelDecls(), &AST.getMacros(), + AST.getCanonicalIncludes(), /*IsIndexMainAST=*/true); } @@ -89,7 +120,8 @@ std::vector DeclsToIndex( AST.getTranslationUnitDecl()->decls().begin(), AST.getTranslationUnitDecl()->decls().end()); - return indexSymbols(AST, std::move(PP), DeclsToIndex, Includes, + return indexSymbols(AST, std::move(PP), DeclsToIndex, + /*MainFileMacros=*/nullptr, Includes, /*IsIndexMainAST=*/false); } diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -45,31 +45,6 @@ return ND; } -// Returns a URI of \p Path. Firstly, this makes the \p Path absolute using the -// current working directory of the given SourceManager if the Path is not an -// absolute path. If failed, this resolves relative paths against \p FallbackDir -// to get an absolute path. Then, this tries creating an URI for the absolute -// path with schemes specified in \p Opts. This returns an URI with the first -// working scheme, if there is any; otherwise, this returns None. -// -// The Path can be a path relative to the build directory, or retrieved from -// the SourceManager. -std::string toURI(const SourceManager &SM, llvm::StringRef Path, - const SymbolCollector::Options &Opts) { - llvm::SmallString<128> AbsolutePath(Path); - if (auto File = SM.getFileManager().getFile(Path)) { - if (auto CanonPath = getCanonicalPath(*File, SM)) { - AbsolutePath = *CanonPath; - } - } - // We don't perform is_absolute check in an else branch because makeAbsolute - // might return a relative path on some InMemoryFileSystems. - if (!llvm::sys::path::is_absolute(AbsolutePath) && !Opts.FallbackDir.empty()) - llvm::sys::fs::make_absolute(Opts.FallbackDir, AbsolutePath); - llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true); - return URI::create(AbsolutePath).toString(); -} - // All proto generated headers should start with this line. static const char *PROTO_HEADER_COMMENT = "// Generated by the protocol buffer compiler. DO NOT EDIT!"; @@ -157,7 +132,7 @@ auto Path = SM.getFilename(TokLoc); if (Path.empty()) return None; - FileURIStorage = toURI(SM, Path, Opts); + FileURIStorage = toURI(Path, SM, Opts.FallbackDir).toString(); SymbolLocation Result; Result.FileURI = FileURIStorage.c_str(); auto Range = getTokenRange(TokLoc, SM, LangOpts); @@ -517,7 +492,7 @@ auto Found = URICache.find(FID); if (Found == URICache.end()) { if (auto *FileEntry = SM.getFileEntryForID(FID)) { - auto FileURI = toURI(SM, FileEntry->getName(), Opts); + auto FileURI = toURI(FileEntry->getName(), SM, Opts.FallbackDir).toString(); Found = URICache.insert({FID, FileURI}).first; } else { // Ignore cases where we can not find a corresponding file entry @@ -679,7 +654,7 @@ if (Canonical.startswith("<") || Canonical.startswith("\"")) return Canonical.str(); if (Canonical != Filename) - return toURI(SM, Canonical, Opts); + return toURI(Canonical, SM, Opts.FallbackDir).toString(); } if (!isSelfContainedHeader(FID)) { // A .inc or .def file is often included into a real header to define @@ -690,7 +665,7 @@ return llvm::None; } // Standard case: just insert the file itself. - return toURI(SM, Filename, Opts); + return toURI(Filename, SM, Opts.FallbackDir).toString(); } bool SymbolCollector::isSelfContainedHeader(FileID FID) { diff --git a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp --- a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp @@ -345,6 +345,41 @@ FileURI("unittest:///test2.cc"))})); } +TEST(FileIndexTest, MacroRefs) { + Annotations HeaderCode(R"cpp( + #define $macro[[MACRO]](X) (X+1) + )cpp"); + Annotations MainCode(R"cpp( + void f() { + int a = $macro[[MACRO]](1); + } + )cpp"); + + auto Macro = findSymbol( + TestTU::withHeaderCode(HeaderCode.code()).headerSymbols(), "MACRO"); + FileIndex Index; + // Add test.cc + TestTU Test; + Test.HeaderCode = HeaderCode.code(); + Test.Code = MainCode.code(); + Test.Filename = "test.cc"; + auto AST = Test.build(); + Index.updateMain(Test.Filename, AST); + // Add test2.cc + TestTU Test2; + Test2.HeaderCode = HeaderCode.code(); + Test2.Code = MainCode.code(); + Test2.Filename = "test2.cc"; + AST = Test2.build(); + Index.updateMain(Test2.Filename, AST); + + EXPECT_THAT(getRefs(Index, Macro.ID), + RefsAre({AllOf(RefRange(MainCode.range("macro")), + FileURI("unittest:///test.cc")), + AllOf(RefRange(MainCode.range("macro")), + FileURI("unittest:///test2.cc"))})); +} + TEST(FileIndexTest, CollectMacros) { FileIndex M; update(M, "f", "#define CLANGD 1");