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. @@ -1625,6 +1628,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; @@ -1656,6 +1660,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 @@ -164,6 +164,29 @@ } raw_ostream &operator<<(raw_ostream &, SymbolOrigin); +enum class SymbolFlag : uint8_t { + None = 0, + /// Whether or not this symbol is meant to be used for the code completion. + /// See also isIndexedForCodeCompletion(). + IsIndexedForCodeCompletion = 1 << 0, + /// Indicates if the symbol is deprecated. + Deprecated = 1 << 1, +}; +inline SymbolFlag operator|(SymbolFlag A, SymbolFlag B) { + return static_cast(static_cast(A) | + static_cast(B)); +} +inline SymbolFlag &operator|=(SymbolFlag &A, SymbolFlag B) { return A = A | B; } +inline SymbolFlag operator&(SymbolFlag A, SymbolFlag B) { + return static_cast(static_cast(A) & + static_cast(B)); +} +inline SymbolFlag &operator&=(SymbolFlag &A, SymbolFlag B) { return A = A & B; } +inline SymbolFlag operator~(SymbolFlag F) { + return static_cast(~static_cast(F)); +} +raw_ostream &operator<<(raw_ostream &, SymbolFlag); + // The class presents a C++ symbol, e.g. class, function. // // WARNING: Symbols do not own much of their underlying data - typically strings @@ -201,9 +224,6 @@ // 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 this symbol is meant to be used for the code completion. - /// See also isIndexedForCodeCompletion(). - bool IsIndexedForCodeCompletion = false; /// Where this symbol came from. Usually an index provides a constant value. SymbolOrigin Origin = SymbolOrigin::Unknown; /// A brief description of the symbol that can be appended in the completion @@ -244,7 +264,14 @@ /// any definition. llvm::SmallVector IncludeHeaders; - // FIXME: add extra fields for index scoring signals. + /// FIXME: also add deprecation message and fixit? + bool Deprecated() const { + return static_cast(Flags & SymbolFlag::Deprecated); + } + bool IsIndexedForCodeCompletion() const { + return static_cast(Flags & SymbolFlag::IsIndexedForCodeCompletion); + } + SymbolFlag Flags = SymbolFlag::None; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); Index: clangd/index/Index.cpp =================================================================== --- clangd/index/Index.cpp +++ clangd/index/Index.cpp @@ -55,6 +55,17 @@ return OS; } +raw_ostream &operator<<(raw_ostream &OS, SymbolFlag F) { + if (F == SymbolFlag::None) + return OS << "None"; + std::string s; + if (static_cast(F & SymbolFlag::Deprecated)) + s += "deprecated|"; + if (static_cast(F & SymbolFlag::IsIndexedForCodeCompletion)) + s += "completion|"; + return OS << StringRef(s).rtrim('|'); +} + raw_ostream &operator<<(raw_ostream &OS, const Symbol &S) { return OS << S.Scope << S.Name; } Index: clangd/index/MemIndex.cpp =================================================================== --- clangd/index/MemIndex.cpp +++ clangd/index/MemIndex.cpp @@ -35,7 +35,7 @@ // Exact match against all possible scopes. if (!Req.Scopes.empty() && !llvm::is_contained(Req.Scopes, Sym->Scope)) continue; - if (Req.RestrictForCodeCompletion && !Sym->IsIndexedForCodeCompletion) + if (Req.RestrictForCodeCompletion && !Sym->IsIndexedForCodeCompletion()) continue; if (auto Score = Filter.match(Sym->Name)) { Index: clangd/index/Serialization.cpp =================================================================== --- clangd/index/Serialization.cpp +++ clangd/index/Serialization.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Serialization.h" #include "../RIFF.h" +#include "Index.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -200,7 +201,7 @@ } } writeVar(Sym.References, OS); - OS.write(Sym.IsIndexedForCodeCompletion); + OS.write(static_cast(Sym.Flags)); OS.write(static_cast(Sym.Origin)); writeVar(Strings.index(Sym.Signature), OS); writeVar(Strings.index(Sym.CompletionSnippetSuffix), OS); @@ -274,7 +275,7 @@ } } Sym.References = consumeVar(Data); - Sym.IsIndexedForCodeCompletion = consume8(Data); + Sym.Flags = static_cast(consume8(Data)); Sym.Origin = static_cast(consume8(Data)); READ_STRING(Sym.Signature); READ_STRING(Sym.CompletionSnippetSuffix); Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -396,7 +396,7 @@ Symbol S; S.ID = std::move(*ID); S.Name = Name->getName(); - S.IsIndexedForCodeCompletion = true; + S.Flags |= SymbolFlag::IsIndexedForCodeCompletion; S.SymInfo = index::getSymbolInfoForMacro(*MI); std::string FileURI; if (auto DeclLoc = getTokenLocation(MI->getDefinitionLoc(), SM, Opts, @@ -491,7 +491,8 @@ // FIXME: this returns foo:bar: for objective-C methods, we prefer only foo: // for consistency with CodeCompletionString and a clean name/signature split. - S.IsIndexedForCodeCompletion = isIndexedForCodeCompletion(ND, Ctx); + if (isIndexedForCodeCompletion(ND, Ctx)) + S.Flags |= SymbolFlag::IsIndexedForCodeCompletion; S.SymInfo = index::getSymbolInfo(&ND); std::string FileURI; if (auto DeclLoc = getTokenLocation(findNameLoc(&ND), SM, Opts, @@ -531,6 +532,8 @@ S.IncludeHeaders.emplace_back(Include, 1); S.Origin = Opts.Origin; + if (ND.getAvailability() == AR_Deprecated) + S.Flags |= SymbolFlag::Deprecated; Symbols.insert(S); return Symbols.find(S.ID); } Index: clangd/index/SymbolYAML.cpp =================================================================== --- clangd/index/SymbolYAML.cpp +++ clangd/index/SymbolYAML.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" +#include LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(clang::clangd::Symbol) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) @@ -48,6 +49,19 @@ std::string HexString; }; +struct NormalizedSymbolFlag { + NormalizedSymbolFlag(IO &) {} + NormalizedSymbolFlag(IO &, clang::clangd::SymbolFlag F) { + Flag = static_cast(F); + } + + clang::clangd::SymbolFlag denormalize(IO &) { + return static_cast(Flag); + } + + uint8_t Flag = 0; +}; + template <> struct MappingTraits { static void mapping(IO &IO, SymbolLocation::Position &Value) { IO.mapRequired("Line", Value.Line); @@ -83,6 +97,8 @@ template <> struct MappingTraits { static void mapping(IO &IO, Symbol &Sym) { MappingNormalization NSymbolID(IO, Sym.ID); + MappingNormalization + NSymbolFlag(IO, Sym.Flags); IO.mapRequired("ID", NSymbolID->HexString); IO.mapRequired("Name", Sym.Name); IO.mapRequired("Scope", Sym.Scope); @@ -91,8 +107,7 @@ SymbolLocation()); IO.mapOptional("Definition", Sym.Definition, SymbolLocation()); IO.mapOptional("References", Sym.References, 0u); - IO.mapOptional("IsIndexedForCodeCompletion", Sym.IsIndexedForCodeCompletion, - false); + IO.mapOptional("Flags", NSymbolFlag->Flag); IO.mapOptional("Signature", Sym.Signature); IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); IO.mapOptional("Documentation", Sym.Documentation); 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; @@ -161,7 +162,7 @@ USR += Regex("^.*$").sub(USRFormat, Sym.Name); // e.g. func -> @F@func# Sym.ID = SymbolID(USR); Sym.SymInfo.Kind = Kind; - Sym.IsIndexedForCodeCompletion = true; + Sym.Flags |= SymbolFlag::IsIndexedForCodeCompletion; Sym.Origin = SymbolOrigin::Static; return Sym; } @@ -720,9 +721,9 @@ TEST(CompletionTest, GlobalCompletionFiltering) { Symbol Class = cls("XYZ"); - Class.IsIndexedForCodeCompletion = false; + Class.Flags &= ~(SymbolFlag::IsIndexedForCodeCompletion); Symbol Func = func("XYZ::foooo"); - Func.IsIndexedForCodeCompletion = false; + Func.Flags &= ~(SymbolFlag::IsIndexedForCodeCompletion); auto Results = completions(R"(// void f() { XYZ::foooo^ @@ -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); @@ -1887,12 +1890,24 @@ Sym.Name = "Clangd_Macro_Test"; Sym.ID = SymbolID("c:foo.cpp@8@macro@Clangd_Macro_Test"); Sym.SymInfo.Kind = index::SymbolKind::Macro; - Sym.IsIndexedForCodeCompletion = true; + Sym.Flags |= SymbolFlag::IsIndexedForCodeCompletion; EXPECT_THAT(completions("#define Clangd_Macro_Test\nClangd_Macro_T^", {Sym}) .Completions, UnorderedElementsAre(Named("Clangd_Macro_Test"))); } +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); Index: unittests/clangd/SerializationTests.cpp =================================================================== --- unittests/clangd/SerializationTests.cpp +++ unittests/clangd/SerializationTests.cpp @@ -35,7 +35,7 @@ End: Line: 1 Column: 1 -IsIndexedForCodeCompletion: true +Flags: 1 Documentation: 'Foo doc' ReturnType: 'int' IncludeHeaders: @@ -62,7 +62,7 @@ End: Line: 1 Column: 1 -IsIndexedForCodeCompletion: false +Flags: 2 Signature: '-sig' CompletionSnippetSuffix: '-snippet' ... @@ -82,7 +82,8 @@ EXPECT_EQ(Sym1.Documentation, "Foo doc"); EXPECT_EQ(Sym1.ReturnType, "int"); EXPECT_EQ(Sym1.CanonicalDeclaration.FileURI, "file:///path/foo.h"); - EXPECT_TRUE(Sym1.IsIndexedForCodeCompletion); + EXPECT_TRUE(Sym1.IsIndexedForCodeCompletion()); + EXPECT_FALSE(Sym1.Deprecated()); EXPECT_THAT(Sym1.IncludeHeaders, UnorderedElementsAre(IncludeHeaderWithRef("include1", 7u), IncludeHeaderWithRef("include2", 3u))); @@ -94,7 +95,8 @@ EXPECT_EQ(Sym2.Signature, "-sig"); EXPECT_EQ(Sym2.ReturnType, ""); EXPECT_EQ(Sym2.CanonicalDeclaration.FileURI, "file:///path/bar.h"); - EXPECT_FALSE(Sym2.IsIndexedForCodeCompletion); + EXPECT_FALSE(Sym2.IsIndexedForCodeCompletion()); + EXPECT_TRUE(Sym2.Deprecated()); std::string ConcatenatedYAML; { Index: unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- unittests/clangd/SymbolCollectorTests.cpp +++ unittests/clangd/SymbolCollectorTests.cpp @@ -80,8 +80,9 @@ } MATCHER_P(RefCount, R, "") { return int(arg.References) == R; } MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") { - return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion; + return arg.IsIndexedForCodeCompletion() == IsIndexedForCodeCompletion; } +MATCHER(Deprecated, "") { return arg.Deprecated(); } MATCHER(RefRange, "") { const Ref &Pos = testing::get<0>(arg); const Range &Range = testing::get<1>(arg); @@ -1014,6 +1015,17 @@ DeclRange(Header.range("used"))))); } +TEST_F(SymbolCollectorTest, DeprecatedSymbols) { + const std::string Header = R"( + void TestClangc() __attribute__((deprecated("", ""))); + void TestClangd(); + )"; + runSymbolCollector(Header, /**/ ""); + EXPECT_THAT(Symbols, UnorderedElementsAre( + AllOf(QName("TestClangc"), Deprecated()), + AllOf(QName("TestClangd"), Not(Deprecated())))); +} + } // namespace } // namespace clangd } // namespace clang