Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -759,9 +759,9 @@ return true; } -// Should we perform index-based completion in this context? +// Should we perform index-based completion in a context of the specified kind? // FIXME: consider allowing completion, but restricting the result types. -bool allowIndex(enum CodeCompletionContext::Kind K) { +bool contextAllowsIndex(enum CodeCompletionContext::Kind K) { switch (K) { case CodeCompletionContext::CCC_TopLevel: case CodeCompletionContext::CCC_ObjCInterface: @@ -803,6 +803,33 @@ llvm_unreachable("unknown code completion context"); } +// Should we allow index completions in the specified context? +bool allowIndex(CodeCompletionContext &CC) { + if (!contextAllowsIndex(CC.getKind())) + return false; + // We also avoid ClassName::bar (but allow namespace::bar). + auto Scope = CC.getCXXScopeSpecifier(); + if (!Scope) + return true; + NestedNameSpecifier *NameSpec = (*Scope)->getScopeRep(); + if (!NameSpec) + return true; + // We only query the index when qualifier is a namespace. + // If it's a class, we rely solely on sema completions. + switch (NameSpec->getKind()) { + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Namespace: + case NestedNameSpecifier::NamespaceAlias: + return true; + case NestedNameSpecifier::Super: + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + // Unresolved inside a template. + case NestedNameSpecifier::Identifier: + return false; + } +} + } // namespace clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { @@ -918,7 +945,7 @@ } SymbolSlab queryIndex() { - if (!Opts.Index || !allowIndex(Recorder->CCContext.getKind())) + if (!Opts.Index || !allowIndex(Recorder->CCContext)) return SymbolSlab(); trace::Span Tracer("Query index"); SPAN_ATTACH(Tracer, "limit", Opts.Limit); Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -825,6 +825,67 @@ UnorderedElementsAre("")))); } +TEST(CompletionTest, NoIndexCompletionsInsideClasses) { + auto Completions = completions( + R"cpp( + struct Foo { + int SomeNameOfField; + typedef int SomeNameOfTypedefField; + }; + + Foo::^)cpp", + {func("::SomeNameInTheIndex"), func("::Foo::SomeNameInTheIndex")}); + + EXPECT_THAT(Completions.items, + AllOf(Contains(Labeled("SomeNameOfField")), + Contains(Labeled("SomeNameOfTypedefField")), + Not(Contains(Labeled("SomeNameInTheIndex"))))); +} + +TEST(CompletionTest, NoIndexCompletionsInsideDependentCode) { + { + auto Completions = completions( + R"cpp( + template + void foo() { + T::^ + } + )cpp", + {func("::SomeNameInTheIndex")}); + + EXPECT_THAT(Completions.items, + Not(Contains(Labeled("SomeNameInTheIndex")))); + } + + { + auto Completions = completions( + R"cpp( + template + void foo() { + T::template Y::^ + } + )cpp", + {func("::SomeNameInTheIndex")}); + + EXPECT_THAT(Completions.items, + Not(Contains(Labeled("SomeNameInTheIndex")))); + } + + { + auto Completions = completions( + R"cpp( + template + void foo() { + T::foo::^ + } + )cpp", + {func("::SomeNameInTheIndex")}); + + EXPECT_THAT(Completions.items, + Not(Contains(Labeled("SomeNameInTheIndex")))); + } +} + } // namespace } // namespace clangd } // namespace clang