Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -431,14 +431,8 @@ void ProcessCodeCompleteResults(class Sema &S, CodeCompletionContext Context, CodeCompletionResult *InResults, unsigned NumResults) override final { - if (CCSema) { - log(llvm::formatv( - "Multiple code complete callbacks (parser backtracked?). " - "Dropping results from context {0}, keeping results from {1}.", - getCompletionKindString(this->CCContext.getKind()), - getCompletionKindString(Context.getKind()))); - return; - } + log("Processing completion results in context " + + getCompletionKindString(Context.getKind())); // Record the completion context. CCSema = &S; CCContext = Context; @@ -460,6 +454,8 @@ continue; // We choose to never append '::' to completion results in clangd. Result.StartsNestedNameSpecifier = false; + // FIXME: the same result can be added multiple times as the callback can + // be called more than once. We may want to deduplicate identical results. Results.push_back(Result); } ResultsCallback(); @@ -725,6 +721,8 @@ return false; } Action.EndSourceFile(); + if (Includes) + Includes->reset(); // Make sure this doesn't out-live Clang. return true; } @@ -878,8 +876,10 @@ assert(Includes && "Includes is not set"); // If preprocessor was run, inclusions from preprocessor callback should // already be added to Inclusions. - Output = runWithSema(); - Includes.reset(); // Make sure this doesn't out-live Clang. + auto Items = runWithSema(); + Output.items.insert(Output.items.end(), + std::make_move_iterator(Items.begin()), + std::make_move_iterator(Items.end())); SPAN_ATTACH(Tracer, "sema_completion_kind", getCompletionKindString(Recorder->CCContext.getKind())); }); @@ -898,6 +898,7 @@ NSema, NIndex, NBoth, Output.items.size(), Output.isIncomplete ? " (incomplete)" : "")); assert(!Opts.Limit || Output.items.size() <= Opts.Limit); + Output.isIncomplete = Incomplete; // We don't assert that isIncomplete means we hit a limit. // Indexes may choose to impose their own limits even if we don't have one. return Output; @@ -906,7 +907,7 @@ private: // This is called by run() once Sema code completion is done, but before the // Sema data structures are torn down. It does all the real work. - CompletionList runWithSema() { + std::vector runWithSema() { Filter = FuzzyMatcher( Recorder->CCSema->getPreprocessor().getCodeCompletionFilter()); // Sema provides the needed context to query the index. @@ -918,10 +919,9 @@ // Merge Sema and Index results, score them, and pick the winners. auto Top = mergeResults(Recorder->Results, IndexResults); // Convert the results to the desired LSP structs. - CompletionList Output; + std::vector Output; for (auto &C : Top) - Output.items.push_back(toCompletionItem(C.first, C.second)); - Output.isIncomplete = Incomplete; + Output.push_back(toCompletionItem(C.first, C.second)); return Output; } Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -673,7 +673,6 @@ } TEST(CompletionTest, BacktrackCrashes) { - // Sema calls code completion callbacks twice in these cases. auto Results = completions(R"cpp( namespace ns { struct FooBarBaz {}; @@ -682,7 +681,9 @@ int foo(ns::FooBar^ )cpp"); - EXPECT_THAT(Results.items, ElementsAre(Labeled("FooBarBaz"))); + // Sema calls code completion callbacks twice in these cases. + // FIXME: deduplicate identical results. + EXPECT_THAT(Results.items, Contains(Labeled("FooBarBaz"))); // Check we don't crash in that case too. completions(R"cpp( @@ -693,6 +694,26 @@ )cpp"); } +TEST(CompletionTest, CompleteInMacroWithStringification) { + auto Results = completions(R"cpp( +void f(const char *, int x); +#define F(x) f(#x, x) + +namespace ns { +int X; +int Y; +} // namespace ns + +int f(int input_num) { + F(ns::^) +} + } +)cpp"); + + EXPECT_THAT(Results.items, + UnorderedElementsAre(Named("X"), Named("Y"))); +} + TEST(CompletionTest, CompleteInExcludedPPBranch) { auto Results = completions(R"cpp( int bar(int param_in_bar) {