Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -919,6 +919,7 @@ if (Opts.Limit) Req.MaxCandidateCount = Opts.Limit; Req.Query = Filter->pattern(); + Req.CompletionMatchesOnly = true; Req.Scopes = getQueryScopes(Recorder->CCContext, Recorder->CCSema->getSourceManager()); log(llvm::formatv("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])", Index: clangd/index/Index.h =================================================================== --- clangd/index/Index.h +++ clangd/index/Index.h @@ -138,9 +138,10 @@ // The number of translation units that reference this symbol from their main // file. This number is only meaningful if aggregated in an index. unsigned References = 0; - + // Whether or not the symbol should be considered for completion. + bool ForCompletion = false; /// A brief description of the symbol that can be displayed in the completion - /// candidate list. For example, "Foo(X x, Y y) const" is a labal for a + /// candidate list. For example, "Foo(X x, Y y) const" is a label for a /// function. llvm::StringRef CompletionLabel; /// The piece of text that the user is expected to type to match the @@ -250,6 +251,8 @@ /// \brief The number of top candidates to return. The index may choose to /// return more than this, e.g. if it doesn't know which candidates are best. size_t MaxCandidateCount = UINT_MAX; + /// A flag to restrict the results to completion matches. + bool CompletionMatchesOnly = false; }; struct LookupRequest { Index: clangd/index/MemIndex.cpp =================================================================== --- clangd/index/MemIndex.cpp +++ clangd/index/MemIndex.cpp @@ -45,6 +45,8 @@ // Exact match against all possible scopes. if (!Req.Scopes.empty() && !llvm::is_contained(Req.Scopes, Sym->Scope)) continue; + if (Req.CompletionMatchesOnly && !Sym->ForCompletion) + continue; if (auto Score = Filter.match(Sym->Name)) { Top.emplace(-*Score, Sym); Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -92,13 +92,6 @@ bool shouldFilterDecl(const NamedDecl *ND, ASTContext *ASTCtx, const SymbolCollector::Options &Opts) { - using namespace clang::ast_matchers; - if (ND->isImplicit()) - return true; - // Skip anonymous declarations, e.g (anonymous enum/class/struct). - if (ND->getDeclName().isEmpty()) - return true; - // FIXME: figure out a way to handle internal linkage symbols (e.g. static // variables, function) defined in the .cc files. Also we skip the symbols // in anonymous namespace as the qualifier names of these symbols are like @@ -110,27 +103,41 @@ if (ND->isInAnonymousNamespace()) return true; + using namespace clang::ast_matchers; + if (ND->isImplicit()) + return true; + // Skip anonymous declarations, e.g (anonymous enum/class/struct). + if (ND->getDeclName().isEmpty()) + return true; + + // Don't index template specializations. + auto IsSpecialization = + anyOf(functionDecl(isExplicitTemplateSpecialization()), + cxxRecordDecl(isExplicitTemplateSpecialization()), + varDecl(isExplicitTemplateSpecialization())); + if (!match(decl(IsSpecialization), *ND, *ASTCtx).empty()) + return true; + + return false; +} + +bool isForCompletion(const NamedDecl *ND, ASTContext *ASTCtx) { + using namespace clang::ast_matchers; // We only want: // * symbols in namespaces or translation unit scopes (e.g. no class // members) // * enum constants in unscoped enum decl (e.g. "red" in "enum {red};") auto InTopLevelScope = hasDeclContext( anyOf(namespaceDecl(), translationUnitDecl(), linkageSpecDecl())); - // Don't index template specializations. - auto IsSpecialization = - anyOf(functionDecl(isExplicitTemplateSpecialization()), - cxxRecordDecl(isExplicitTemplateSpecialization()), - varDecl(isExplicitTemplateSpecialization())); if (match(decl(allOf(unless(isExpansionInMainFile()), anyOf(InTopLevelScope, hasDeclContext(enumDecl(InTopLevelScope, - unless(isScoped())))), - unless(IsSpecialization))), + unless(isScoped())))))), *ND, *ASTCtx) .empty()) - return true; + return false; - return false; + return true; } // We only collect #include paths for symbols that are suitable for global code @@ -314,18 +321,8 @@ *ASTCtx, *PP, CodeCompletionContext::CCC_Name, *CompletionAllocator, *CompletionTUInfo, /*IncludeBriefComments*/ true); - std::string Label; - std::string SnippetInsertText; - std::string IgnoredLabel; - std::string PlainInsertText; - getLabelAndInsertText(*CCS, &Label, &SnippetInsertText, - /*EnableSnippets=*/true); - getLabelAndInsertText(*CCS, &IgnoredLabel, &PlainInsertText, - /*EnableSnippets=*/false); - std::string FilterText = getFilterText(*CCS); - std::string Documentation = getDocumentation(*CCS); - std::string CompletionDetail = getDetail(*CCS); + std::string Documentation = getDocumentation(*CCS); std::string Include; if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) { // Use the expansion location to get the #include header since this is @@ -334,14 +331,29 @@ QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts)) Include = std::move(*Header); } - S.CompletionFilterText = FilterText; - S.CompletionLabel = Label; - S.CompletionPlainInsertText = PlainInsertText; - S.CompletionSnippetInsertText = SnippetInsertText; + Symbol::Details Detail; Detail.Documentation = Documentation; - Detail.CompletionDetail = CompletionDetail; Detail.IncludeHeader = Include; + + std::string Label, SnippetInsertText, IgnoredLabel, PlainInsertText, + CompletionDetail, FilterText; + if (isForCompletion(&ND, ASTCtx)) { + S.ForCompletion = true; + getLabelAndInsertText(*CCS, &Label, &SnippetInsertText, + /*EnableSnippets=*/true); + getLabelAndInsertText(*CCS, &IgnoredLabel, &PlainInsertText, + /*EnableSnippets=*/false); + CompletionDetail = getDetail(*CCS); + FilterText = getFilterText(*CCS); + + Detail.CompletionDetail = CompletionDetail; + S.CompletionFilterText = FilterText; + S.CompletionLabel = Label; + S.CompletionPlainInsertText = PlainInsertText; + S.CompletionSnippetInsertText = SnippetInsertText; + } + S.Detail = &Detail; Symbols.insert(S); Index: clangd/index/SymbolYAML.cpp =================================================================== --- clangd/index/SymbolYAML.cpp +++ clangd/index/SymbolYAML.cpp @@ -101,6 +101,7 @@ SymbolLocation()); IO.mapOptional("Definition", Sym.Definition, SymbolLocation()); IO.mapOptional("References", Sym.References, 0u); + IO.mapOptional("ForCompletion", Sym.ForCompletion, false); IO.mapRequired("CompletionLabel", Sym.CompletionLabel); IO.mapRequired("CompletionFilterText", Sym.CompletionFilterText); IO.mapRequired("CompletionPlainInsertText", Sym.CompletionPlainInsertText); Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -57,6 +57,7 @@ using ::testing::Each; using ::testing::ElementsAre; using ::testing::Field; +using ::testing::IsEmpty; using ::testing::Not; using ::testing::UnorderedElementsAre; @@ -156,6 +157,7 @@ } USR += Regex("^.*$").sub(USRFormat, Sym.Name); // e.g. func -> @F@func# Sym.ID = SymbolID(USR); + Sym.ForCompletion = true; Sym.CompletionPlainInsertText = Sym.Name; Sym.CompletionSnippetInsertText = Sym.Name; Sym.CompletionLabel = Sym.Name; @@ -645,6 +647,109 @@ EXPECT_THAT(Results.items, Not(Contains(Labeled("param_in_bar")))); } +TEST(CompletionTest, Enums) { + EXPECT_THAT(completions(R"cpp( + enum class Color2 { + Yellow + }; + void foo() { + Color2::^ + })cpp") + .items, + Has("Yellow", CompletionItemKind::Value)); + EXPECT_THAT(completions(R"cpp( + enum { + Red + }; + void foo() { + Re^ + })cpp") + .items, + Has("Red", CompletionItemKind::Value)); + EXPECT_THAT(completions(R"cpp( + enum Color { + Green + }; + void foo() { + Gr^ + })cpp") + .items, + Has("Green", CompletionItemKind::Value)); + EXPECT_THAT(completions(R"cpp( + namespace ns { + enum { + Black + }; + } + void foo() { + ns::B^ + })cpp") + .items, + Has("Black", CompletionItemKind::Value)); + EXPECT_THAT(completions(R"cpp( + void foo() { + ns::B^ + })cpp") + .items, + IsEmpty()); +} + +TEST(CompletionTest, AnonymousNamespace) { + + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + auto Opts = ClangdServer::optsForTest(); + Opts.BuildDynamicSymbolIndex = true; + ClangdServer Server(CDB, FS, DiagConsumer, Opts); + auto File = testPath("bar.cpp"); + Server.addDocument(File, R"( + namespace { + void inAnymous() { + } + } // namespace + )"); + + File = testPath("bar2.cpp"); + Annotations Test(R"( + void bar() { + inAnym^ + } + )"); + + Server.addDocument(File, Test.code()); + ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for preamble"; + auto Results = cantFail(runCodeComplete(Server, File, Test.point(), {})); + EXPECT_THAT(Results.items, IsEmpty()); +} + +TEST(CompletionTest, InMainFile) { + + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + auto Opts = ClangdServer::optsForTest(); + Opts.BuildDynamicSymbolIndex = true; + ClangdServer Server(CDB, FS, DiagConsumer, Opts); + auto File = testPath("main.cpp"); + Server.addDocument(File, R"( + void funcInMain() { + } + )"); + + File = testPath("bar2.cpp"); + Annotations Test(R"( + void bar() { + funcInMa^ + } + )"); + + Server.addDocument(File, Test.code()); + ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for preamble"; + auto Results = cantFail(runCodeComplete(Server, File, Test.point(), {})); + EXPECT_THAT(Results.items, IsEmpty()); +} + SignatureHelp signatures(StringRef Text) { MockFSProvider FS; MockCompilationDatabase CDB; Index: unittests/clangd/FileIndexTests.cpp =================================================================== --- unittests/clangd/FileIndexTests.cpp +++ unittests/clangd/FileIndexTests.cpp @@ -170,7 +170,7 @@ EXPECT_THAT(match(M, FuzzyFindRequest()), UnorderedElementsAre()); } -TEST(FileIndexTest, IgnoreClassMembers) { +TEST(FileIndexTest, ClassMembers) { FileIndex M; M.update("f1", build("f1", "class X { static int m1; int m2; static void f(); };") @@ -178,7 +178,8 @@ FuzzyFindRequest Req; Req.Query = ""; - EXPECT_THAT(match(M, Req), UnorderedElementsAre("X")); + EXPECT_THAT(match(M, Req), + UnorderedElementsAre("X", "X::m1", "X::m2", "X::f")); } TEST(FileIndexTest, NoIncludeCollected) { Index: unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- unittests/clangd/SymbolCollectorTests.cpp +++ unittests/clangd/SymbolCollectorTests.cpp @@ -60,6 +60,9 @@ arg.Definition.EndOffset == Offsets.second; } MATCHER_P(Refs, R, "") { return int(arg.References) == R; } +MATCHER_P(ForCompletion, ForCompletion, "") { + return arg.ForCompletion == ForCompletion; +} namespace clang { namespace clangd { @@ -191,25 +194,29 @@ } // namespace foo )"; runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAreArray( - {QName("Foo"), QName("f1"), QName("f2"), QName("KInt"), - QName("kStr"), QName("foo"), QName("foo::bar"), - QName("foo::int32"), QName("foo::int32_t"), QName("foo::v1"), - QName("foo::bar::v2"), QName("foo::baz")})); + EXPECT_THAT( + Symbols, + UnorderedElementsAreArray( + {QName("Foo"), QName("Foo::f"), QName("f1"), QName("f2"), + QName("KInt"), QName("kStr"), QName("foo"), QName("foo::bar"), + QName("foo::int32"), QName("foo::int32_t"), QName("foo::v1"), + QName("foo::bar::v2"), QName("foo::baz")})); } TEST_F(SymbolCollectorTest, Template) { Annotations Header(R"( // Template is indexed, specialization and instantiation is not. - template struct [[Tmpl]] {T x = 0;}; + template struct [[Tmpl]] {T $xdecl[[x]] = 0;}; template <> struct Tmpl {}; extern template struct Tmpl; template struct Tmpl; )"); runSymbolCollector(Header.code(), /*Main=*/""); - EXPECT_THAT(Symbols, UnorderedElementsAreArray({AllOf( - QName("Tmpl"), DeclRange(Header.offsetRange()))})); + EXPECT_THAT( + Symbols, + UnorderedElementsAreArray( + {AllOf(QName("Tmpl"), DeclRange(Header.offsetRange())), + AllOf(QName("Tmpl::x"), DeclRange(Header.offsetRange("xdecl")))})); } TEST_F(SymbolCollectorTest, Locations) { @@ -228,7 +235,7 @@ void $printdef[[print]]() {} // Declared/defined in main only. - int Y; + int $ydecl[[Y]]; )cpp"); runSymbolCollector(Header.code(), Main.code()); EXPECT_THAT( @@ -240,7 +247,8 @@ DefRange(Main.offsetRange("clsdef"))), AllOf(QName("print"), DeclRange(Header.offsetRange("printdecl")), DefRange(Main.offsetRange("printdef"))), - AllOf(QName("Z"), DeclRange(Header.offsetRange("zdecl"))))); + AllOf(QName("Z"), DeclRange(Header.offsetRange("zdecl"))), + AllOf(QName("Y"), DeclRange(Main.offsetRange("ydecl"))))); } TEST_F(SymbolCollectorTest, References) { @@ -262,10 +270,11 @@ CollectorOpts.CountReferences = true; runSymbolCollector(Header, Main); EXPECT_THAT(Symbols, - UnorderedElementsAre(AllOf(QName("W"), Refs(1)), - AllOf(QName("X"), Refs(1)), - AllOf(QName("Y"), Refs(0)), - AllOf(QName("Z"), Refs(0)), QName("y"))); + UnorderedElementsAre( + AllOf(QName("W"), Refs(1)), AllOf(QName("X"), Refs(1)), + AllOf(QName("Y"), Refs(0)), AllOf(QName("Z"), Refs(0)), + QName("y"), QName("w"), QName("w2"), QName("x"), QName("V"), + QName("v"))); } TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) { @@ -320,7 +329,7 @@ Green }; enum class Color2 { - Yellow // ignore + Yellow }; namespace ns { enum { @@ -329,20 +338,22 @@ } )"; runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Red"), QName("Color"), - QName("Green"), QName("Color2"), - QName("ns"), QName("ns::Black"))); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("Red"), QName("Color"), QName("Green"), + QName("Color2"), QName("Color2::Yellow"), + QName("ns"), QName("ns::Black"))); } -TEST_F(SymbolCollectorTest, IgnoreNamelessSymbols) { +TEST_F(SymbolCollectorTest, NamelessSymbols) { const std::string Header = R"( struct { int a; } Foo; )"; runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("Foo"))); + EXPECT_THAT(Symbols, UnorderedElementsAre( + QName("Foo"), AllOf(QName("(anonymous struct)::a"), + ForCompletion(false)))); } TEST_F(SymbolCollectorTest, SymbolFormedFromMacro) { @@ -384,7 +395,7 @@ DeclURI(TestHeaderURI)))); } -TEST_F(SymbolCollectorTest, IgnoreSymbolsInMainFile) { +TEST_F(SymbolCollectorTest, SymbolsInMainFile) { const std::string Header = R"( class Foo {}; void f1(); @@ -394,15 +405,16 @@ namespace { void ff() {} // ignore } - void main_f() {} // ignore + void main_f() {} // not for completion void f1() {} )"; runSymbolCollector(Header, Main); - EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2"))); + EXPECT_THAT(Symbols, UnorderedElementsAre( + QName("Foo"), QName("f1"), QName("f2"), + AllOf(QName("main_f"), ForCompletion(false)))); } -TEST_F(SymbolCollectorTest, IgnoreClassMembers) { +TEST_F(SymbolCollectorTest, ClassMembers) { const std::string Header = R"( class Foo { void f() {} @@ -417,7 +429,13 @@ void Foo::ssf() {} )"; runSymbolCollector(Header, Main); - EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"))); + EXPECT_THAT(Symbols, + UnorderedElementsAre( + QName("Foo"), AllOf(QName("Foo::f"), ForCompletion(false)), + AllOf(QName("Foo::g"), ForCompletion(false)), + AllOf(QName("Foo::sf"), ForCompletion(false)), + AllOf(QName("Foo::ssf"), ForCompletion(false)), + AllOf(QName("Foo::x"), ForCompletion(false)))); } TEST_F(SymbolCollectorTest, Scopes) { @@ -512,6 +530,7 @@ StartOffset: 0 EndOffset: 1 FileURI: file:///path/foo.h +ForCompletion: true CompletionLabel: 'Foo1-label' CompletionFilterText: 'filter' CompletionPlainInsertText: 'plain' @@ -532,6 +551,7 @@ StartOffset: 10 EndOffset: 12 FileURI: file:///path/bar.h +ForCompletion: true CompletionLabel: 'Foo2-label' CompletionFilterText: 'filter' CompletionPlainInsertText: 'plain' @@ -545,9 +565,10 @@ QName("clang::Foo1"), Labeled("Foo1-label"), Doc("Foo doc"), Detail("int"), DeclURI("file:///path/foo.h")))); auto Symbols2 = SymbolsFromYAML(YAML2); - EXPECT_THAT(Symbols2, UnorderedElementsAre(AllOf( - QName("clang::Foo2"), Labeled("Foo2-label"), - Not(HasDetail()), DeclURI("file:///path/bar.h")))); + EXPECT_THAT(Symbols2, + UnorderedElementsAre(AllOf( + QName("clang::Foo2"), Labeled("Foo2-label"), Not(HasDetail()), + DeclURI("file:///path/bar.h"), ForCompletion(true)))); std::string ConcatenatedYAML; { @@ -638,23 +659,30 @@ // Canonical declarations. class $cdecl[[C]] {}; struct $sdecl[[S]] {}; - union $udecl[[U]] {int x; bool y;}; + union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];}; )"); runSymbolCollector(Header.code(), /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre( - AllOf(QName("C"), DeclURI(TestHeaderURI), - DeclRange(Header.offsetRange("cdecl")), - IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.offsetRange("cdecl"))), - AllOf(QName("S"), DeclURI(TestHeaderURI), - DeclRange(Header.offsetRange("sdecl")), - IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.offsetRange("sdecl"))), - AllOf(QName("U"), DeclURI(TestHeaderURI), - DeclRange(Header.offsetRange("udecl")), - IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.offsetRange("udecl"))))); + EXPECT_THAT( + Symbols, + UnorderedElementsAre( + AllOf(QName("C"), DeclURI(TestHeaderURI), + DeclRange(Header.offsetRange("cdecl")), + IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), + DefRange(Header.offsetRange("cdecl"))), + AllOf(QName("S"), DeclURI(TestHeaderURI), + DeclRange(Header.offsetRange("sdecl")), + IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), + DefRange(Header.offsetRange("sdecl"))), + AllOf(QName("U"), DeclURI(TestHeaderURI), + DeclRange(Header.offsetRange("udecl")), + IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), + DefRange(Header.offsetRange("udecl"))), + AllOf(QName("U::x"), DeclURI(TestHeaderURI), + DeclRange(Header.offsetRange("xdecl")), DefURI(TestHeaderURI), + DefRange(Header.offsetRange("xdecl"))), + AllOf(QName("U::y"), DeclURI(TestHeaderURI), + DeclRange(Header.offsetRange("ydecl")), DefURI(TestHeaderURI), + DefRange(Header.offsetRange("ydecl"))))); } TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {