Skip to content

Commit 734aa1d

Browse files
committed
[clangd] Publish xref for macros from Index and AST.
Summary: With this patch the `findReferences` API will return Xref for macros. If the symbol under the cursor is a macro then we collect the references to it from: 1. Main file by looking at the ParsedAST. (These were added to the ParsedAST in https://reviews.llvm.org/D70008) 2. Files other than the mainfile by looking at the: * static index (Added in https://reviews.llvm.org/D70489) * file index (Added in https://reviews.llvm.org/D71406) This patch collects all the xref from the above places and outputs it in `findReferences` API. Reviewers: kadircet Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D72395
1 parent c9babcb commit 734aa1d

File tree

2 files changed

+106
-42
lines changed

2 files changed

+106
-42
lines changed

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -432,52 +432,73 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
432432
elog("Failed to get a path for the main file, so no references");
433433
return Results;
434434
}
435+
auto URIMainFile = URIForFile::canonicalize(*MainFilePath, *MainFilePath);
435436
auto Loc = SM.getMacroArgExpandedLocation(
436437
getBeginningOfIdentifier(Pos, SM, AST.getLangOpts()));
437-
// TODO: should we handle macros, too?
438-
// We also show references to the targets of using-decls, so we include
439-
// DeclRelation::Underlying.
440-
DeclRelationSet Relations = DeclRelation::TemplatePattern |
441-
DeclRelation::Alias | DeclRelation::Underlying;
442-
auto Decls = getDeclAtPosition(AST, Loc, Relations);
443-
444-
// We traverse the AST to find references in the main file.
445-
auto MainFileRefs = findRefs(Decls, AST);
446-
// We may get multiple refs with the same location and different Roles, as
447-
// cross-reference is only interested in locations, we deduplicate them
448-
// by the location to avoid emitting duplicated locations.
449-
MainFileRefs.erase(std::unique(MainFileRefs.begin(), MainFileRefs.end(),
450-
[](const ReferenceFinder::Reference &L,
451-
const ReferenceFinder::Reference &R) {
452-
return L.Loc == R.Loc;
453-
}),
454-
MainFileRefs.end());
455-
for (const auto &Ref : MainFileRefs) {
456-
if (auto Range = getTokenRange(SM, AST.getLangOpts(), Ref.Loc)) {
457-
Location Result;
458-
Result.range = *Range;
459-
Result.uri = URIForFile::canonicalize(*MainFilePath, *MainFilePath);
460-
Results.References.push_back(std::move(Result));
438+
RefsRequest Req;
439+
440+
if (auto Macro = locateMacroAt(Loc, AST.getPreprocessor())) {
441+
// Handle references to macro.
442+
if (auto MacroSID = getSymbolID(Macro->Name, Macro->Info, SM)) {
443+
// Collect macro references from main file.
444+
const auto &IDToRefs = AST.getMacros().MacroRefs;
445+
auto Refs = IDToRefs.find(*MacroSID);
446+
if (Refs != IDToRefs.end()) {
447+
for (const auto Ref : Refs->second) {
448+
Location Result;
449+
Result.range = Ref;
450+
Result.uri = URIMainFile;
451+
Results.References.push_back(std::move(Result));
452+
}
453+
}
454+
Req.IDs.insert(*MacroSID);
455+
}
456+
} else {
457+
// Handle references to Decls.
458+
459+
// We also show references to the targets of using-decls, so we include
460+
// DeclRelation::Underlying.
461+
DeclRelationSet Relations = DeclRelation::TemplatePattern |
462+
DeclRelation::Alias | DeclRelation::Underlying;
463+
auto Decls = getDeclAtPosition(AST, Loc, Relations);
464+
465+
// We traverse the AST to find references in the main file.
466+
auto MainFileRefs = findRefs(Decls, AST);
467+
// We may get multiple refs with the same location and different Roles, as
468+
// cross-reference is only interested in locations, we deduplicate them
469+
// by the location to avoid emitting duplicated locations.
470+
MainFileRefs.erase(std::unique(MainFileRefs.begin(), MainFileRefs.end(),
471+
[](const ReferenceFinder::Reference &L,
472+
const ReferenceFinder::Reference &R) {
473+
return L.Loc == R.Loc;
474+
}),
475+
MainFileRefs.end());
476+
for (const auto &Ref : MainFileRefs) {
477+
if (auto Range = getTokenRange(SM, AST.getLangOpts(), Ref.Loc)) {
478+
Location Result;
479+
Result.range = *Range;
480+
Result.uri = URIMainFile;
481+
Results.References.push_back(std::move(Result));
482+
}
483+
}
484+
if (Index && Results.References.size() <= Limit) {
485+
for (const Decl *D : Decls) {
486+
// Not all symbols can be referenced from outside (e.g.
487+
// function-locals).
488+
// TODO: we could skip TU-scoped symbols here (e.g. static functions) if
489+
// we know this file isn't a header. The details might be tricky.
490+
if (D->getParentFunctionOrMethod())
491+
continue;
492+
if (auto ID = getSymbolID(D))
493+
Req.IDs.insert(*ID);
494+
}
461495
}
462496
}
463497
// Now query the index for references from other files.
464-
if (Index && Results.References.size() <= Limit) {
465-
RefsRequest Req;
498+
if (!Req.IDs.empty() && Index && Results.References.size() <= Limit) {
466499
Req.Limit = Limit;
467-
468-
for (const Decl *D : Decls) {
469-
// Not all symbols can be referenced from outside (e.g. function-locals).
470-
// TODO: we could skip TU-scoped symbols here (e.g. static functions) if
471-
// we know this file isn't a header. The details might be tricky.
472-
if (D->getParentFunctionOrMethod())
473-
continue;
474-
if (auto ID = getSymbolID(D))
475-
Req.IDs.insert(*ID);
476-
}
477-
if (Req.IDs.empty())
478-
return Results;
479500
Results.HasMore |= Index->refs(Req, [&](const Ref &R) {
480-
// no need to continue process if we reach the limit.
501+
// No need to continue process if we reach the limit.
481502
if (Results.References.size() > Limit)
482503
return;
483504
auto LSPLoc = toLSPLocation(R.Location, *MainFilePath);

clang-tools-extra/clangd/unittests/XRefsTests.cpp

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,13 @@ TEST(FindReferences, WithinAST) {
937937
[[CAT]](Fo, o) foo4;
938938
}
939939
)cpp",
940+
941+
R"cpp(// Macros
942+
#define [[MA^CRO]](X) (X+1)
943+
void test() {
944+
int x = [[MACRO]]([[MACRO]](1));
945+
}
946+
)cpp",
940947
};
941948
for (const char *Test : Tests) {
942949
Annotations T(Test);
@@ -1011,7 +1018,7 @@ TEST(FindReferences, ExplicitSymbols) {
10111018
}
10121019
}
10131020

1014-
TEST(FindReferences, NeedsIndex) {
1021+
TEST(FindReferences, NeedsIndexForSymbols) {
10151022
const char *Header = "int foo();";
10161023
Annotations Main("int main() { [[f^oo]](); }");
10171024
TestTU TU;
@@ -1040,13 +1047,49 @@ TEST(FindReferences, NeedsIndex) {
10401047
EXPECT_EQ(1u, LimitRefs.References.size());
10411048
EXPECT_TRUE(LimitRefs.HasMore);
10421049

1043-
// If the main file is in the index, we don't return duplicates.
1044-
// (even if the references are in a different location)
1050+
// Avoid indexed results for the main file. Use AST for the mainfile.
10451051
TU.Code = ("\n\n" + Main.code()).str();
10461052
EXPECT_THAT(findReferences(AST, Main.point(), 0, TU.index().get()).References,
10471053
ElementsAre(RangeIs(Main.range())));
10481054
}
10491055

1056+
TEST(FindReferences, NeedsIndexForMacro) {
1057+
const char *Header = "#define MACRO(X) (X+1)";
1058+
Annotations Main(R"cpp(
1059+
int main() {
1060+
int a = [[MA^CRO]](1);
1061+
}
1062+
)cpp");
1063+
TestTU TU;
1064+
TU.Code = Main.code();
1065+
TU.HeaderCode = Header;
1066+
auto AST = TU.build();
1067+
1068+
// References in main file are returned without index.
1069+
EXPECT_THAT(
1070+
findReferences(AST, Main.point(), 0, /*Index=*/nullptr).References,
1071+
ElementsAre(RangeIs(Main.range())));
1072+
1073+
Annotations IndexedMain(R"cpp(
1074+
int indexed_main() {
1075+
int a = [[MACRO]](1);
1076+
}
1077+
)cpp");
1078+
1079+
// References from indexed files are included.
1080+
TestTU IndexedTU;
1081+
IndexedTU.Code = IndexedMain.code();
1082+
IndexedTU.Filename = "Indexed.cpp";
1083+
IndexedTU.HeaderCode = Header;
1084+
EXPECT_THAT(
1085+
findReferences(AST, Main.point(), 0, IndexedTU.index().get()).References,
1086+
ElementsAre(RangeIs(Main.range()), RangeIs(IndexedMain.range())));
1087+
auto LimitRefs =
1088+
findReferences(AST, Main.point(), /*Limit*/ 1, IndexedTU.index().get());
1089+
EXPECT_EQ(1u, LimitRefs.References.size());
1090+
EXPECT_TRUE(LimitRefs.HasMore);
1091+
}
1092+
10501093
TEST(FindReferences, NoQueryForLocalSymbols) {
10511094
struct RecordingIndex : public MemIndex {
10521095
mutable Optional<llvm::DenseSet<SymbolID>> RefIDs;

0 commit comments

Comments
 (0)