diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -297,6 +297,22 @@ return R; } +std::vector getConstructors(const NamedDecl *ND) { + std::vector Ctors; + if (const auto *RD = dyn_cast(ND)) { + if (!RD->hasUserDeclaredConstructor()) + return {}; + Ctors = {RD->ctors().begin(), RD->ctors().end()}; + for (const auto *D : RD->decls()) { + if (const auto *FTD = dyn_cast(D)) + if (const auto *Ctor = + dyn_cast(FTD->getTemplatedDecl())) + Ctors.push_back(Ctor); + } + } + return Ctors; +} + // Return all rename occurrences (using the index) outside of the main file, // grouped by the absolute file path. llvm::Expected>> @@ -304,6 +320,14 @@ llvm::StringRef MainFile, const SymbolIndex &Index) { RefsRequest RQuest; RQuest.IDs.insert(*getSymbolID(&RenameDecl)); + // Classes and their constructors are different symbols, and have different + // symbol ID. + // When querying references for a class, clangd's own index will also return + // references of the corresponding class constructors, but this is not true + // for all index backends, e.g. kythe, so we add all constructors to the query + // request. + for (const auto *Ctor : getConstructors(&RenameDecl)) + RQuest.IDs.insert(*getSymbolID(Ctor)); // Absolute file path => rename occurrences in that file. llvm::StringMap> AffectedFiles; diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -768,6 +768,50 @@ testing::HasSubstr("too many occurrences")); } +TEST(CrossFileRename, QueryCtorInIndex) { + const auto MainCode = Annotations("F^oo f;"); + auto TU = TestTU::withCode(MainCode.code()); + TU.HeaderCode = R"cpp( + class Foo { + public: + Foo() = default; + }; + )cpp"; + auto AST = TU.build(); + + RefsRequest Req; + class RecordIndex : public SymbolIndex { + public: + RecordIndex(RefsRequest *R) : Out(R) {} + bool refs(const RefsRequest &Req, + llvm::function_ref Callback) const override { + *Out = Req; + return false; + } + + bool fuzzyFind(const FuzzyFindRequest &, + llvm::function_ref) const override { + return false; + } + void lookup(const LookupRequest &, + llvm::function_ref) const override {} + + void relations(const RelationsRequest &, + llvm::function_ref) + const override {} + size_t estimateMemoryUsage() const override { return 0; } + + RefsRequest *Out; + } RIndex(&Req); + auto Results = + rename({MainCode.point(), "NewName", AST, testPath("main.cc"), &RIndex, + /*CrossFile=*/true}); + ASSERT_TRUE(bool(Results)) << Results.takeError(); + const auto HeaderSymbols = TU.headerSymbols(); + EXPECT_THAT(Req.IDs, + testing::Contains(findSymbol(HeaderSymbols, "Foo::Foo").ID)); +} + TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) { auto MainCode = Annotations("int [[^x]] = 2;"); auto MainFilePath = testPath("main.cc");