| //===-- YAMLSerialization.cpp ------------------------------------*- C++-*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // A YAML index file is a sequence of tagged entries. |
| // Each entry either encodes a Symbol or the list of references to a symbol |
| // (a "ref bundle"). |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Index.h" |
| #include "Relation.h" |
| #include "Serialization.h" |
| #include "SymbolLocation.h" |
| #include "SymbolOrigin.h" |
| #include "dex/Dex.h" |
| #include "support/Logger.h" |
| #include "support/Trace.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/StringSaver.h" |
| #include "llvm/Support/YAMLTraits.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cstdint> |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) |
| LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref) |
| |
| namespace { |
| using RefBundle = |
| std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>; |
| // This is a pale imitation of std::variant<Symbol, RefBundle, Relation> |
| struct VariantEntry { |
| llvm::Optional<clang::clangd::Symbol> Symbol; |
| llvm::Optional<RefBundle> Refs; |
| llvm::Optional<clang::clangd::Relation> Relation; |
| llvm::Optional<clang::clangd::IncludeGraphNode> Source; |
| llvm::Optional<clang::tooling::CompileCommand> Cmd; |
| }; |
| // A class helps YAML to serialize the 32-bit encoded position (Line&Column), |
| // as YAMLIO can't directly map bitfields. |
| struct YPosition { |
| uint32_t Line; |
| uint32_t Column; |
| }; |
| |
| // avoid ODR violation of specialization for non-owned CompileCommand |
| struct CompileCommandYAML : clang::tooling::CompileCommand {}; |
| |
| } // namespace |
| namespace llvm { |
| namespace yaml { |
| |
| using clang::clangd::FileDigest; |
| using clang::clangd::IncludeGraph; |
| using clang::clangd::IncludeGraphNode; |
| using clang::clangd::Ref; |
| using clang::clangd::RefKind; |
| using clang::clangd::Relation; |
| using clang::clangd::RelationKind; |
| using clang::clangd::Symbol; |
| using clang::clangd::SymbolID; |
| using clang::clangd::SymbolLocation; |
| using clang::clangd::SymbolOrigin; |
| using clang::index::SymbolInfo; |
| using clang::index::SymbolKind; |
| using clang::index::SymbolLanguage; |
| using clang::tooling::CompileCommand; |
| |
| // Helper to (de)serialize the SymbolID. We serialize it as a hex string. |
| struct NormalizedSymbolID { |
| NormalizedSymbolID(IO &) {} |
| NormalizedSymbolID(IO &, const SymbolID &ID) { |
| llvm::raw_string_ostream OS(HexString); |
| OS << ID; |
| } |
| |
| SymbolID denormalize(IO &I) { |
| auto ID = SymbolID::fromStr(HexString); |
| if (!ID) { |
| I.setError(llvm::toString(ID.takeError())); |
| return SymbolID(); |
| } |
| return *ID; |
| } |
| |
| std::string HexString; |
| }; |
| |
| struct NormalizedSymbolFlag { |
| NormalizedSymbolFlag(IO &) {} |
| NormalizedSymbolFlag(IO &, Symbol::SymbolFlag F) { |
| Flag = static_cast<uint8_t>(F); |
| } |
| |
| Symbol::SymbolFlag denormalize(IO &) { |
| return static_cast<Symbol::SymbolFlag>(Flag); |
| } |
| |
| uint8_t Flag = 0; |
| }; |
| |
| struct NormalizedSymbolOrigin { |
| NormalizedSymbolOrigin(IO &) {} |
| NormalizedSymbolOrigin(IO &, SymbolOrigin O) { |
| Origin = static_cast<uint8_t>(O); |
| } |
| |
| SymbolOrigin denormalize(IO &) { return static_cast<SymbolOrigin>(Origin); } |
| |
| uint8_t Origin = 0; |
| }; |
| |
| template <> struct MappingTraits<YPosition> { |
| static void mapping(IO &IO, YPosition &Value) { |
| IO.mapRequired("Line", Value.Line); |
| IO.mapRequired("Column", Value.Column); |
| } |
| }; |
| |
| struct NormalizedPosition { |
| using Position = clang::clangd::SymbolLocation::Position; |
| NormalizedPosition(IO &) {} |
| NormalizedPosition(IO &, const Position &Pos) { |
| P.Line = Pos.line(); |
| P.Column = Pos.column(); |
| } |
| |
| Position denormalize(IO &) { |
| Position Pos; |
| Pos.setLine(P.Line); |
| Pos.setColumn(P.Column); |
| return Pos; |
| } |
| YPosition P; |
| }; |
| |
| struct NormalizedFileURI { |
| NormalizedFileURI(IO &) {} |
| NormalizedFileURI(IO &, const char *FileURI) { URI = FileURI; } |
| |
| const char *denormalize(IO &IO) { |
| assert(IO.getContext() && |
| "Expecting an UniqueStringSaver to allocate data"); |
| return static_cast<llvm::UniqueStringSaver *>(IO.getContext()) |
| ->save(URI) |
| .data(); |
| } |
| |
| std::string URI; |
| }; |
| |
| template <> struct MappingTraits<SymbolLocation> { |
| static void mapping(IO &IO, SymbolLocation &Value) { |
| MappingNormalization<NormalizedFileURI, const char *> NFile(IO, |
| Value.FileURI); |
| IO.mapRequired("FileURI", NFile->URI); |
| MappingNormalization<NormalizedPosition, SymbolLocation::Position> NStart( |
| IO, Value.Start); |
| IO.mapRequired("Start", NStart->P); |
| MappingNormalization<NormalizedPosition, SymbolLocation::Position> NEnd( |
| IO, Value.End); |
| IO.mapRequired("End", NEnd->P); |
| } |
| }; |
| |
| template <> struct MappingTraits<SymbolInfo> { |
| static void mapping(IO &io, SymbolInfo &SymInfo) { |
| // FIXME: expose other fields? |
| io.mapRequired("Kind", SymInfo.Kind); |
| io.mapRequired("Lang", SymInfo.Lang); |
| } |
| }; |
| |
| template <> |
| struct MappingTraits<clang::clangd::Symbol::IncludeHeaderWithReferences> { |
| static void mapping(IO &io, |
| clang::clangd::Symbol::IncludeHeaderWithReferences &Inc) { |
| io.mapRequired("Header", Inc.IncludeHeader); |
| io.mapRequired("References", Inc.References); |
| } |
| }; |
| |
| template <> struct MappingTraits<Symbol> { |
| static void mapping(IO &IO, Symbol &Sym) { |
| MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, Sym.ID); |
| MappingNormalization<NormalizedSymbolFlag, Symbol::SymbolFlag> NSymbolFlag( |
| IO, Sym.Flags); |
| MappingNormalization<NormalizedSymbolOrigin, SymbolOrigin> NSymbolOrigin( |
| IO, Sym.Origin); |
| IO.mapRequired("ID", NSymbolID->HexString); |
| IO.mapRequired("Name", Sym.Name); |
| IO.mapRequired("Scope", Sym.Scope); |
| IO.mapRequired("SymInfo", Sym.SymInfo); |
| IO.mapOptional("CanonicalDeclaration", Sym.CanonicalDeclaration, |
| SymbolLocation()); |
| IO.mapOptional("Definition", Sym.Definition, SymbolLocation()); |
| IO.mapOptional("References", Sym.References, 0u); |
| IO.mapOptional("Origin", NSymbolOrigin->Origin); |
| IO.mapOptional("Flags", NSymbolFlag->Flag); |
| IO.mapOptional("Signature", Sym.Signature); |
| IO.mapOptional("TemplateSpecializationArgs", |
| Sym.TemplateSpecializationArgs); |
| IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); |
| IO.mapOptional("Documentation", Sym.Documentation); |
| IO.mapOptional("ReturnType", Sym.ReturnType); |
| IO.mapOptional("Type", Sym.Type); |
| IO.mapOptional("IncludeHeaders", Sym.IncludeHeaders); |
| } |
| }; |
| |
| template <> struct ScalarEnumerationTraits<SymbolLanguage> { |
| static void enumeration(IO &IO, SymbolLanguage &Value) { |
| IO.enumCase(Value, "C", SymbolLanguage::C); |
| IO.enumCase(Value, "Cpp", SymbolLanguage::CXX); |
| IO.enumCase(Value, "ObjC", SymbolLanguage::ObjC); |
| IO.enumCase(Value, "Swift", SymbolLanguage::Swift); |
| } |
| }; |
| |
| template <> struct ScalarEnumerationTraits<SymbolKind> { |
| static void enumeration(IO &IO, SymbolKind &Value) { |
| #define DEFINE_ENUM(name) IO.enumCase(Value, #name, SymbolKind::name) |
| |
| DEFINE_ENUM(Unknown); |
| DEFINE_ENUM(Function); |
| DEFINE_ENUM(Module); |
| DEFINE_ENUM(Namespace); |
| DEFINE_ENUM(NamespaceAlias); |
| DEFINE_ENUM(Macro); |
| DEFINE_ENUM(Enum); |
| DEFINE_ENUM(Struct); |
| DEFINE_ENUM(Class); |
| DEFINE_ENUM(Protocol); |
| DEFINE_ENUM(Extension); |
| DEFINE_ENUM(Union); |
| DEFINE_ENUM(TypeAlias); |
| DEFINE_ENUM(Function); |
| DEFINE_ENUM(Variable); |
| DEFINE_ENUM(Field); |
| DEFINE_ENUM(EnumConstant); |
| DEFINE_ENUM(InstanceMethod); |
| DEFINE_ENUM(ClassMethod); |
| DEFINE_ENUM(StaticMethod); |
| DEFINE_ENUM(InstanceProperty); |
| DEFINE_ENUM(ClassProperty); |
| DEFINE_ENUM(StaticProperty); |
| DEFINE_ENUM(Constructor); |
| DEFINE_ENUM(Destructor); |
| DEFINE_ENUM(ConversionFunction); |
| DEFINE_ENUM(Parameter); |
| DEFINE_ENUM(Using); |
| |
| #undef DEFINE_ENUM |
| } |
| }; |
| |
| template <> struct MappingTraits<RefBundle> { |
| static void mapping(IO &IO, RefBundle &Refs) { |
| MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, |
| Refs.first); |
| IO.mapRequired("ID", NSymbolID->HexString); |
| IO.mapRequired("References", Refs.second); |
| } |
| }; |
| |
| struct NormalizedRefKind { |
| NormalizedRefKind(IO &) {} |
| NormalizedRefKind(IO &, RefKind O) { Kind = static_cast<uint8_t>(O); } |
| |
| RefKind denormalize(IO &) { return static_cast<RefKind>(Kind); } |
| |
| uint8_t Kind = 0; |
| }; |
| |
| template <> struct MappingTraits<Ref> { |
| static void mapping(IO &IO, Ref &R) { |
| MappingNormalization<NormalizedRefKind, RefKind> NKind(IO, R.Kind); |
| IO.mapRequired("Kind", NKind->Kind); |
| IO.mapRequired("Location", R.Location); |
| } |
| }; |
| |
| struct NormalizedSymbolRole { |
| NormalizedSymbolRole(IO &) {} |
| NormalizedSymbolRole(IO &IO, RelationKind R) { |
| Kind = static_cast<uint8_t>(R); |
| } |
| |
| RelationKind denormalize(IO &IO) { return static_cast<RelationKind>(Kind); } |
| |
| uint8_t Kind = 0; |
| }; |
| |
| template <> struct MappingTraits<SymbolID> { |
| static void mapping(IO &IO, SymbolID &ID) { |
| MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, ID); |
| IO.mapRequired("ID", NSymbolID->HexString); |
| } |
| }; |
| |
| template <> struct MappingTraits<Relation> { |
| static void mapping(IO &IO, Relation &Relation) { |
| MappingNormalization<NormalizedSymbolRole, RelationKind> NRole( |
| IO, Relation.Predicate); |
| IO.mapRequired("Subject", Relation.Subject); |
| IO.mapRequired("Predicate", NRole->Kind); |
| IO.mapRequired("Object", Relation.Object); |
| } |
| }; |
| |
| struct NormalizedSourceFlag { |
| NormalizedSourceFlag(IO &) {} |
| NormalizedSourceFlag(IO &, IncludeGraphNode::SourceFlag O) { |
| Flag = static_cast<uint8_t>(O); |
| } |
| |
| IncludeGraphNode::SourceFlag denormalize(IO &) { |
| return static_cast<IncludeGraphNode::SourceFlag>(Flag); |
| } |
| |
| uint8_t Flag = 0; |
| }; |
| |
| struct NormalizedFileDigest { |
| NormalizedFileDigest(IO &) {} |
| NormalizedFileDigest(IO &, const FileDigest &Digest) { |
| HexString = llvm::toHex(Digest); |
| } |
| |
| FileDigest denormalize(IO &I) { |
| FileDigest Digest; |
| if (HexString.size() == Digest.size() * 2 && |
| llvm::all_of(HexString, llvm::isHexDigit)) { |
| memcpy(Digest.data(), llvm::fromHex(HexString).data(), Digest.size()); |
| } else { |
| I.setError(std::string("Bad hex file digest: ") + HexString); |
| } |
| return Digest; |
| } |
| |
| std::string HexString; |
| }; |
| |
| template <> struct MappingTraits<IncludeGraphNode> { |
| static void mapping(IO &IO, IncludeGraphNode &Node) { |
| IO.mapRequired("URI", Node.URI); |
| MappingNormalization<NormalizedSourceFlag, IncludeGraphNode::SourceFlag> |
| NSourceFlag(IO, Node.Flags); |
| IO.mapRequired("Flags", NSourceFlag->Flag); |
| MappingNormalization<NormalizedFileDigest, FileDigest> NDigest(IO, |
| Node.Digest); |
| IO.mapRequired("Digest", NDigest->HexString); |
| IO.mapRequired("DirectIncludes", Node.DirectIncludes); |
| } |
| }; |
| |
| template <> struct MappingTraits<CompileCommandYAML> { |
| static void mapping(IO &IO, CompileCommandYAML &Cmd) { |
| IO.mapRequired("Directory", Cmd.Directory); |
| IO.mapRequired("CommandLine", Cmd.CommandLine); |
| } |
| }; |
| |
| template <> struct MappingTraits<VariantEntry> { |
| static void mapping(IO &IO, VariantEntry &Variant) { |
| if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) { |
| if (!IO.outputting()) |
| Variant.Symbol.emplace(); |
| MappingTraits<Symbol>::mapping(IO, *Variant.Symbol); |
| } else if (IO.mapTag("!Refs", Variant.Refs.hasValue())) { |
| if (!IO.outputting()) |
| Variant.Refs.emplace(); |
| MappingTraits<RefBundle>::mapping(IO, *Variant.Refs); |
| } else if (IO.mapTag("!Relations", Variant.Relation.hasValue())) { |
| if (!IO.outputting()) |
| Variant.Relation.emplace(); |
| MappingTraits<Relation>::mapping(IO, *Variant.Relation); |
| } else if (IO.mapTag("!Source", Variant.Source.hasValue())) { |
| if (!IO.outputting()) |
| Variant.Source.emplace(); |
| MappingTraits<IncludeGraphNode>::mapping(IO, *Variant.Source); |
| } else if (IO.mapTag("!Cmd", Variant.Cmd.hasValue())) { |
| if (!IO.outputting()) |
| Variant.Cmd.emplace(); |
| MappingTraits<CompileCommandYAML>::mapping( |
| IO, static_cast<CompileCommandYAML &>(*Variant.Cmd)); |
| } |
| } |
| }; |
| |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace clang { |
| namespace clangd { |
| |
| void writeYAML(const IndexFileOut &O, llvm::raw_ostream &OS) { |
| llvm::yaml::Output Yout(OS); |
| for (const auto &Sym : *O.Symbols) { |
| VariantEntry Entry; |
| Entry.Symbol = Sym; |
| Yout << Entry; |
| } |
| if (O.Refs) |
| for (auto &Sym : *O.Refs) { |
| VariantEntry Entry; |
| Entry.Refs = Sym; |
| Yout << Entry; |
| } |
| if (O.Relations) |
| for (auto &R : *O.Relations) { |
| VariantEntry Entry; |
| Entry.Relation = R; |
| Yout << Entry; |
| } |
| if (O.Sources) { |
| for (const auto &Source : *O.Sources) { |
| VariantEntry Entry; |
| Entry.Source = Source.getValue(); |
| Yout << Entry; |
| } |
| } |
| if (O.Cmd) { |
| VariantEntry Entry; |
| Entry.Cmd = *O.Cmd; |
| Yout << Entry; |
| } |
| } |
| |
| llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data) { |
| SymbolSlab::Builder Symbols; |
| RefSlab::Builder Refs; |
| RelationSlab::Builder Relations; |
| llvm::BumpPtrAllocator |
| Arena; // store the underlying data of Position::FileURI. |
| llvm::UniqueStringSaver Strings(Arena); |
| llvm::yaml::Input Yin(Data, &Strings); |
| IncludeGraph Sources; |
| llvm::Optional<tooling::CompileCommand> Cmd; |
| while (Yin.setCurrentDocument()) { |
| llvm::yaml::EmptyContext Ctx; |
| VariantEntry Variant; |
| yamlize(Yin, Variant, true, Ctx); |
| if (Yin.error()) |
| return llvm::errorCodeToError(Yin.error()); |
| |
| if (Variant.Symbol) |
| Symbols.insert(*Variant.Symbol); |
| if (Variant.Refs) |
| for (const auto &Ref : Variant.Refs->second) |
| Refs.insert(Variant.Refs->first, Ref); |
| if (Variant.Relation) |
| Relations.insert(*Variant.Relation); |
| if (Variant.Source) { |
| auto &IGN = Variant.Source.getValue(); |
| auto Entry = Sources.try_emplace(IGN.URI).first; |
| Entry->getValue() = std::move(IGN); |
| // Fixup refs to refer to map keys which will live on |
| Entry->getValue().URI = Entry->getKey(); |
| for (auto &Include : Entry->getValue().DirectIncludes) |
| Include = Sources.try_emplace(Include).first->getKey(); |
| } |
| if (Variant.Cmd) |
| Cmd = *Variant.Cmd; |
| Yin.nextDocument(); |
| } |
| |
| IndexFileIn Result; |
| Result.Symbols.emplace(std::move(Symbols).build()); |
| Result.Refs.emplace(std::move(Refs).build()); |
| Result.Relations.emplace(std::move(Relations).build()); |
| if (Sources.size()) |
| Result.Sources = std::move(Sources); |
| Result.Cmd = std::move(Cmd); |
| return std::move(Result); |
| } |
| |
| std::string toYAML(const Symbol &S) { |
| std::string Buf; |
| { |
| llvm::raw_string_ostream OS(Buf); |
| llvm::yaml::Output Yout(OS); |
| Symbol Sym = S; // copy: Yout<< requires mutability. |
| Yout << Sym; |
| } |
| return Buf; |
| } |
| |
| std::string toYAML(const std::pair<SymbolID, llvm::ArrayRef<Ref>> &Data) { |
| RefBundle Refs = {Data.first, Data.second}; |
| std::string Buf; |
| { |
| llvm::raw_string_ostream OS(Buf); |
| llvm::yaml::Output Yout(OS); |
| Yout << Refs; |
| } |
| return Buf; |
| } |
| |
| std::string toYAML(const Relation &R) { |
| std::string Buf; |
| { |
| llvm::raw_string_ostream OS(Buf); |
| llvm::yaml::Output Yout(OS); |
| Relation Rel = R; // copy: Yout<< requires mutability. |
| Yout << Rel; |
| } |
| return Buf; |
| } |
| |
| std::string toYAML(const Ref &R) { |
| std::string Buf; |
| { |
| llvm::raw_string_ostream OS(Buf); |
| llvm::yaml::Output Yout(OS); |
| Ref Reference = R; // copy: Yout<< requires mutability. |
| Yout << Reference; |
| } |
| return Buf; |
| } |
| |
| llvm::Expected<clangd::Symbol> |
| symbolFromYAML(StringRef YAML, llvm::UniqueStringSaver *Strings) { |
| clangd::Symbol Deserialized; |
| llvm::yaml::Input YAMLInput(YAML, Strings); |
| if (YAMLInput.error()) |
| return error("Unable to deserialize Symbol from YAML: {0}", YAML); |
| YAMLInput >> Deserialized; |
| return Deserialized; |
| } |
| |
| llvm::Expected<clangd::Ref> refFromYAML(StringRef YAML, |
| llvm::UniqueStringSaver *Strings) { |
| clangd::Ref Deserialized; |
| llvm::yaml::Input YAMLInput(YAML, Strings); |
| if (YAMLInput.error()) |
| return error("Unable to deserialize Symbol from YAML: {0}", YAML); |
| YAMLInput >> Deserialized; |
| return Deserialized; |
| } |
| |
| } // namespace clangd |
| } // namespace clang |