Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -177,6 +177,9 @@ }; Scores Score; + /// Indicates if this item is deprecated. + bool Deprecated = false; + // Serialize this to an LSP completion item. This is a lossy operation. CompletionItem render(const CodeCompleteOptions &) const; }; Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -353,6 +353,8 @@ return std::tie(X.range.start.line, X.range.start.character) < std::tie(Y.range.start.line, Y.range.start.character); }); + Completion.Deprecated |= + (C.SemaResult->Availability == CXAvailability_Deprecated); } if (C.IndexResult) { Completion.Origin |= C.IndexResult->Origin; @@ -362,6 +364,7 @@ Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind); if (Completion.Name.empty()) Completion.Name = C.IndexResult->Name; + Completion.Deprecated |= C.IndexResult->Deprecated; } // Turn absolute path into a literal string that can be #included. @@ -1623,6 +1626,7 @@ LSP.kind = Kind; LSP.detail = BundleSize > 1 ? llvm::formatv("[{0} overloads]", BundleSize) : ReturnType; + LSP.deprecated = Deprecated; if (InsertInclude) LSP.detail += "\n" + InsertInclude->Header; LSP.documentation = Documentation; @@ -1654,6 +1658,7 @@ : InsertTextFormat::PlainText; if (InsertInclude && InsertInclude->Insertion) LSP.additionalTextEdits.push_back(*InsertInclude->Insertion); + return LSP; } Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -768,6 +768,9 @@ /// themselves. std::vector additionalTextEdits; + /// Indicates if this item is deprecated. + bool deprecated = false; + // TODO(krasimir): The following optional fields defined by the language // server protocol are unsupported: // Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -517,6 +517,8 @@ Result["textEdit"] = *CI.textEdit; if (!CI.additionalTextEdits.empty()) Result["additionalTextEdits"] = json::Array(CI.additionalTextEdits); + if (CI.deprecated) + Result["deprecated"] = CI.deprecated; return std::move(Result); } Index: clangd/Quality.cpp =================================================================== --- clangd/Quality.cpp +++ clangd/Quality.cpp @@ -167,9 +167,7 @@ } void SymbolQualitySignals::merge(const CodeCompletionResult &SemaCCResult) { - if (SemaCCResult.Availability == CXAvailability_Deprecated) - Deprecated = true; - + Deprecated |= (SemaCCResult.Availability == CXAvailability_Deprecated); Category = categorize(SemaCCResult); if (SemaCCResult.Declaration) { @@ -180,6 +178,7 @@ } void SymbolQualitySignals::merge(const Symbol &IndexResult) { + Deprecated |= IndexResult.Deprecated; References = std::max(IndexResult.References, References); Category = categorize(IndexResult.SymInfo); ReservedName = ReservedName || isReserved(IndexResult.Name); Index: clangd/index/Index.h =================================================================== --- clangd/index/Index.h +++ clangd/index/Index.h @@ -244,7 +244,9 @@ /// any definition. llvm::SmallVector IncludeHeaders; - // FIXME: add extra fields for index scoring signals. + /// Indicates if the symbol is deprecated. + /// FIXME: also add deprecation message and fixit? + bool Deprecated = false; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -535,6 +535,7 @@ S.IncludeHeaders.emplace_back(Include, 1); S.Origin = Opts.Origin; + S.Deprecated = ND.getAvailability() == AR_Deprecated; Symbols.insert(S); return Symbols.find(S.ID); } Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -77,6 +77,7 @@ return Contains(AllOf(Named(std::move(Name)), Kind(K))); } MATCHER(IsDocumented, "") { return !arg.Documentation.empty(); } +MATCHER(Deprecated, "") { return arg.Deprecated; } std::unique_ptr memIndex(std::vector Symbols) { SymbolSlab::Builder Slab; @@ -1355,6 +1356,7 @@ C.Kind = CompletionItemKind::Method; C.Score.Total = 1.0; C.Origin = SymbolOrigin::AST | SymbolOrigin::Static; + C.Deprecated = true; CodeCompleteOptions Opts; Opts.IncludeIndicator.Insert = "^"; @@ -1370,6 +1372,7 @@ EXPECT_EQ(R.documentation, "This is x()."); EXPECT_THAT(R.additionalTextEdits, IsEmpty()); EXPECT_EQ(R.sortText, sortText(1.0, "x")); + EXPECT_TRUE(R.deprecated); Opts.EnableSnippets = true; R = C.render(Opts); @@ -1882,6 +1885,18 @@ AllOf(Named("Func"), HasInclude("\"foo.h\""), Not(InsertInclude())))); } +TEST(CompletionTest, DeprecatedResults) { + std::string Body = R"cpp( + void TestClangd(); + void TestClangc() __attribute__((deprecated("", ""))); + )cpp"; + + EXPECT_THAT( + completions(Body + "int main() { TestClang^ }").Completions, + UnorderedElementsAre(AllOf(Named("TestClangd"), Not(Deprecated())), + AllOf(Named("TestClangc"), Deprecated()))); +} + } // namespace } // namespace clangd } // namespace clang Index: unittests/clangd/QualityTests.cpp =================================================================== --- unittests/clangd/QualityTests.cpp +++ unittests/clangd/QualityTests.cpp @@ -59,7 +59,7 @@ F.References = 24; // TestTU doesn't count references, so fake it. Quality = {}; Quality.merge(F); - EXPECT_FALSE(Quality.Deprecated); // FIXME: Include deprecated bit in index. + EXPECT_TRUE(Quality.Deprecated); EXPECT_FALSE(Quality.ReservedName); EXPECT_EQ(Quality.References, 24u); EXPECT_EQ(Quality.Category, SymbolQualitySignals::Function);