Skip to content

Commit 517828a

Browse files
committed
[clangd] Bundle code completion items when the include paths differ, but resolve to the same file.
This can happen when, for example, merging results from an external index that generates IncludeHeaders with full URI rather than just literal include. Differential Revision: https://reviews.llvm.org/D92494
1 parent 2ce38b3 commit 517828a

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

clang-tools-extra/clangd/CodeComplete.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,22 @@ struct CompletionCandidate {
173173

174174
// Returns a token identifying the overload set this is part of.
175175
// 0 indicates it's not part of any overload set.
176-
size_t overloadSet(const CodeCompleteOptions &Opts) const {
176+
size_t overloadSet(const CodeCompleteOptions &Opts, llvm::StringRef FileName,
177+
IncludeInserter *Inserter) const {
177178
if (!Opts.BundleOverloads.getValueOr(false))
178179
return 0;
180+
181+
// Depending on the index implementation, we can see different header
182+
// strings (literal or URI) mapping to the same file. We still want to
183+
// bundle those, so we must resolve the header to be included here.
184+
std::string HeaderForHash;
185+
if (Inserter)
186+
if (auto Header = headerToInsertIfAllowed(Opts))
187+
if (auto HeaderFile = toHeaderFile(*Header, FileName))
188+
if (auto Spelled =
189+
Inserter->calculateIncludePath(*HeaderFile, FileName))
190+
HeaderForHash = *Spelled;
191+
179192
llvm::SmallString<256> Scratch;
180193
if (IndexResult) {
181194
switch (IndexResult->SymInfo.Kind) {
@@ -192,7 +205,7 @@ struct CompletionCandidate {
192205
// This could break #include insertion.
193206
return llvm::hash_combine(
194207
(IndexResult->Scope + IndexResult->Name).toStringRef(Scratch),
195-
headerToInsertIfAllowed(Opts).getValueOr(""));
208+
HeaderForHash);
196209
default:
197210
return 0;
198211
}
@@ -206,8 +219,7 @@ struct CompletionCandidate {
206219
llvm::raw_svector_ostream OS(Scratch);
207220
D->printQualifiedName(OS);
208221
}
209-
return llvm::hash_combine(Scratch,
210-
headerToInsertIfAllowed(Opts).getValueOr(""));
222+
return llvm::hash_combine(Scratch, HeaderForHash);
211223
}
212224
assert(IdentifierResult);
213225
return 0;
@@ -1570,7 +1582,8 @@ class CodeCompleteFlow {
15701582
assert(IdentifierResult);
15711583
C.Name = IdentifierResult->Name;
15721584
}
1573-
if (auto OverloadSet = C.overloadSet(Opts)) {
1585+
if (auto OverloadSet = C.overloadSet(
1586+
Opts, FileName, Inserter ? Inserter.getPointer() : nullptr)) {
15741587
auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size());
15751588
if (Ret.second)
15761589
Bundles.emplace_back();

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,6 +1628,29 @@ TEST(CompletionTest, OverloadBundling) {
16281628
EXPECT_EQ(A.SnippetSuffix, "($0)");
16291629
}
16301630

1631+
TEST(CompletionTest, OverloadBundlingSameFileDifferentURI) {
1632+
clangd::CodeCompleteOptions Opts;
1633+
Opts.BundleOverloads = true;
1634+
1635+
Symbol SymX = sym("ns::X", index::SymbolKind::Function, "@F@\\0#");
1636+
Symbol SymY = sym("ns::X", index::SymbolKind::Function, "@F@\\0#I#");
1637+
std::string BarHeader = testPath("bar.h");
1638+
auto BarURI = URI::create(BarHeader).toString();
1639+
SymX.CanonicalDeclaration.FileURI = BarURI.c_str();
1640+
SymY.CanonicalDeclaration.FileURI = BarURI.c_str();
1641+
// The include header is different, but really it's the same file.
1642+
SymX.IncludeHeaders.emplace_back("\"bar.h\"", 1);
1643+
SymY.IncludeHeaders.emplace_back(BarURI.c_str(), 1);
1644+
1645+
auto Results = completions("void f() { ::ns::^ }", {SymX, SymY}, Opts);
1646+
// Expect both results are bundled, despite the different-but-same
1647+
// IncludeHeader.
1648+
ASSERT_EQ(1u, Results.Completions.size());
1649+
const auto &R = Results.Completions.front();
1650+
EXPECT_EQ("X", R.Name);
1651+
EXPECT_EQ(2u, R.BundleSize);
1652+
}
1653+
16311654
TEST(CompletionTest, DocumentationFromChangedFileCrash) {
16321655
MockFS FS;
16331656
auto FooH = testPath("foo.h");

0 commit comments

Comments
 (0)