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 @@ -432,52 +432,73 @@ elog("Failed to get a path for the main file, so no references"); return Results; } + auto URIMainFile = URIForFile::canonicalize(*MainFilePath, *MainFilePath); auto Loc = SM.getMacroArgExpandedLocation( getBeginningOfIdentifier(Pos, SM, AST.getLangOpts())); - // TODO: should we handle macros, too? - // We also show references to the targets of using-decls, so we include - // DeclRelation::Underlying. - DeclRelationSet Relations = DeclRelation::TemplatePattern | - DeclRelation::Alias | DeclRelation::Underlying; - auto Decls = getDeclAtPosition(AST, Loc, Relations); - - // We traverse the AST to find references in the main file. - auto MainFileRefs = findRefs(Decls, AST); - // We may get multiple refs with the same location and different Roles, as - // cross-reference is only interested in locations, we deduplicate them - // by the location to avoid emitting duplicated locations. - MainFileRefs.erase(std::unique(MainFileRefs.begin(), MainFileRefs.end(), - [](const ReferenceFinder::Reference &L, - const ReferenceFinder::Reference &R) { - return L.Loc == R.Loc; - }), - MainFileRefs.end()); - for (const auto &Ref : MainFileRefs) { - if (auto Range = getTokenRange(SM, AST.getLangOpts(), Ref.Loc)) { - Location Result; - Result.range = *Range; - Result.uri = URIForFile::canonicalize(*MainFilePath, *MainFilePath); - Results.References.push_back(std::move(Result)); + RefsRequest Req; + + if (auto Macro = locateMacroAt(Loc, AST.getPreprocessor())) { + // Handle references to macro. + if (auto MacroSID = getSymbolID(Macro->Name, Macro->Info, SM)) { + // Collect macro references from main file. + const auto &IDToRefs = AST.getMacros().MacroRefs; + auto Refs = IDToRefs.find(*MacroSID); + if (Refs != IDToRefs.end()) { + for (const auto Ref : Refs->second) { + Location Result; + Result.range = Ref; + Result.uri = URIMainFile; + Results.References.push_back(std::move(Result)); + } + } + Req.IDs.insert(*MacroSID); + } + } else { + // Handle references to Decls. + + // We also show references to the targets of using-decls, so we include + // DeclRelation::Underlying. + DeclRelationSet Relations = DeclRelation::TemplatePattern | + DeclRelation::Alias | DeclRelation::Underlying; + auto Decls = getDeclAtPosition(AST, Loc, Relations); + + // We traverse the AST to find references in the main file. + auto MainFileRefs = findRefs(Decls, AST); + // We may get multiple refs with the same location and different Roles, as + // cross-reference is only interested in locations, we deduplicate them + // by the location to avoid emitting duplicated locations. + MainFileRefs.erase(std::unique(MainFileRefs.begin(), MainFileRefs.end(), + [](const ReferenceFinder::Reference &L, + const ReferenceFinder::Reference &R) { + return L.Loc == R.Loc; + }), + MainFileRefs.end()); + for (const auto &Ref : MainFileRefs) { + if (auto Range = getTokenRange(SM, AST.getLangOpts(), Ref.Loc)) { + Location Result; + Result.range = *Range; + Result.uri = URIMainFile; + Results.References.push_back(std::move(Result)); + } + } + if (Index && Results.References.size() <= Limit) { + for (const Decl *D : 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); + } } } // Now query the index for references from other files. - if (Index && Results.References.size() <= Limit) { - RefsRequest Req; + if (!Req.IDs.empty() && Index && Results.References.size() <= Limit) { Req.Limit = Limit; - - for (const Decl *D : 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; Results.HasMore |= Index->refs(Req, [&](const Ref &R) { - // no need to continue process if we reach the limit. + // No need to continue process if we reach the limit. if (Results.References.size() > Limit) return; auto LSPLoc = toLSPLocation(R.Location, *MainFilePath); 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 @@ -937,6 +937,13 @@ [[CAT]](Fo, o) foo4; } )cpp", + + R"cpp(// Macros + #define [[MA^CRO]](X) (X+1) + void test() { + int x = [[MACRO]]([[MACRO]](1)); + } + )cpp", }; for (const char *Test : Tests) { Annotations T(Test); @@ -1011,7 +1018,7 @@ } } -TEST(FindReferences, NeedsIndex) { +TEST(FindReferences, NeedsIndexForSymbols) { const char *Header = "int foo();"; Annotations Main("int main() { [[f^oo]](); }"); TestTU TU; @@ -1040,13 +1047,49 @@ EXPECT_EQ(1u, LimitRefs.References.size()); EXPECT_TRUE(LimitRefs.HasMore); - // If the main file is in the index, we don't return duplicates. - // (even if the references are in a different location) + // Avoid indexed results for the main file. Use AST for the mainfile. TU.Code = ("\n\n" + Main.code()).str(); EXPECT_THAT(findReferences(AST, Main.point(), 0, TU.index().get()).References, ElementsAre(RangeIs(Main.range()))); } +TEST(FindReferences, NeedsIndexForMacro) { + const char *Header = "#define MACRO(X) (X+1)"; + Annotations Main(R"cpp( + int main() { + int a = [[MA^CRO]](1); + } + )cpp"); + 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(), 0, /*Index=*/nullptr).References, + ElementsAre(RangeIs(Main.range()))); + + Annotations IndexedMain(R"cpp( + int indexed_main() { + int a = [[MACRO]](1); + } + )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(), 0, IndexedTU.index().get()).References, + ElementsAre(RangeIs(Main.range()), RangeIs(IndexedMain.range()))); + auto LimitRefs = + findReferences(AST, Main.point(), /*Limit*/ 1, IndexedTU.index().get()); + EXPECT_EQ(1u, LimitRefs.References.size()); + EXPECT_TRUE(LimitRefs.HasMore); +} + TEST(FindReferences, NoQueryForLocalSymbols) { struct RecordingIndex : public MemIndex { mutable Optional> RefIDs;