1 //===-- YAMLSerialization.cpp ------------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // A YAML index file is a sequence of tagged entries.
10 // Each entry either encodes a Symbol or the list of references to a symbol
11 // (a "ref bundle").
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "Headers.h"
16 #include "index/Ref.h"
17 #include "index/Relation.h"
18 #include "index/Serialization.h"
19 #include "index/Symbol.h"
20 #include "index/SymbolLocation.h"
21 #include "index/SymbolOrigin.h"
22 #include "clang/Tooling/CompilationDatabase.h"
23 #include "llvm/ADT/Optional.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/Support/Allocator.h"
26 #include "llvm/Support/StringSaver.h"
27 #include "llvm/Support/YAMLTraits.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <cstdint>
30 
31 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences)
32 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref)
33 
34 namespace {
35 using RefBundle =
36     std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>;
37 // This is a pale imitation of std::variant<Symbol, RefBundle, Relation>
38 struct VariantEntry {
39   llvm::Optional<clang::clangd::Symbol> Symbol;
40   llvm::Optional<RefBundle> Refs;
41   llvm::Optional<clang::clangd::Relation> Relation;
42   llvm::Optional<clang::clangd::IncludeGraphNode> Source;
43   llvm::Optional<clang::tooling::CompileCommand> Cmd;
44 };
45 // A class helps YAML to serialize the 32-bit encoded position (Line&Column),
46 // as YAMLIO can't directly map bitfields.
47 struct YPosition {
48   uint32_t Line;
49   uint32_t Column;
50 };
51 
52 // avoid ODR violation of specialization for non-owned CompileCommand
53 struct CompileCommandYAML : clang::tooling::CompileCommand {};
54 
55 } // namespace
56 namespace llvm {
57 namespace yaml {
58 
59 using clang::clangd::FileDigest;
60 using clang::clangd::IncludeGraph;
61 using clang::clangd::IncludeGraphNode;
62 using clang::clangd::Ref;
63 using clang::clangd::RefKind;
64 using clang::clangd::Relation;
65 using clang::clangd::RelationKind;
66 using clang::clangd::Symbol;
67 using clang::clangd::SymbolID;
68 using clang::clangd::SymbolLocation;
69 using clang::index::SymbolInfo;
70 using clang::index::SymbolKind;
71 using clang::index::SymbolLanguage;
72 using clang::tooling::CompileCommand;
73 
74 // Helper to (de)serialize the SymbolID. We serialize it as a hex string.
75 struct NormalizedSymbolID {
NormalizedSymbolIDllvm::yaml::NormalizedSymbolID76   NormalizedSymbolID(IO &) {}
NormalizedSymbolIDllvm::yaml::NormalizedSymbolID77   NormalizedSymbolID(IO &, const SymbolID &ID) {
78     llvm::raw_string_ostream OS(HexString);
79     OS << ID;
80   }
81 
denormalizellvm::yaml::NormalizedSymbolID82   SymbolID denormalize(IO &I) {
83     auto ID = SymbolID::fromStr(HexString);
84     if (!ID) {
85       I.setError(llvm::toString(ID.takeError()));
86       return SymbolID();
87     }
88     return *ID;
89   }
90 
91   std::string HexString;
92 };
93 
94 struct NormalizedSymbolFlag {
NormalizedSymbolFlagllvm::yaml::NormalizedSymbolFlag95   NormalizedSymbolFlag(IO &) {}
NormalizedSymbolFlagllvm::yaml::NormalizedSymbolFlag96   NormalizedSymbolFlag(IO &, Symbol::SymbolFlag F) {
97     Flag = static_cast<uint8_t>(F);
98   }
99 
denormalizellvm::yaml::NormalizedSymbolFlag100   Symbol::SymbolFlag denormalize(IO &) {
101     return static_cast<Symbol::SymbolFlag>(Flag);
102   }
103 
104   uint8_t Flag = 0;
105 };
106 
107 template <> struct MappingTraits<YPosition> {
mappingllvm::yaml::MappingTraits108   static void mapping(IO &IO, YPosition &Value) {
109     IO.mapRequired("Line", Value.Line);
110     IO.mapRequired("Column", Value.Column);
111   }
112 };
113 
114 struct NormalizedPosition {
115   using Position = clang::clangd::SymbolLocation::Position;
NormalizedPositionllvm::yaml::NormalizedPosition116   NormalizedPosition(IO &) {}
NormalizedPositionllvm::yaml::NormalizedPosition117   NormalizedPosition(IO &, const Position &Pos) {
118     P.Line = Pos.line();
119     P.Column = Pos.column();
120   }
121 
denormalizellvm::yaml::NormalizedPosition122   Position denormalize(IO &) {
123     Position Pos;
124     Pos.setLine(P.Line);
125     Pos.setColumn(P.Column);
126     return Pos;
127   }
128   YPosition P;
129 };
130 
131 struct NormalizedFileURI {
NormalizedFileURIllvm::yaml::NormalizedFileURI132   NormalizedFileURI(IO &) {}
NormalizedFileURIllvm::yaml::NormalizedFileURI133   NormalizedFileURI(IO &, const char *FileURI) { URI = FileURI; }
134 
denormalizellvm::yaml::NormalizedFileURI135   const char *denormalize(IO &IO) {
136     assert(IO.getContext() &&
137            "Expecting an UniqueStringSaver to allocate data");
138     return static_cast<llvm::UniqueStringSaver *>(IO.getContext())
139         ->save(URI)
140         .data();
141   }
142 
143   std::string URI;
144 };
145 
146 template <> struct MappingTraits<SymbolLocation> {
mappingllvm::yaml::MappingTraits147   static void mapping(IO &IO, SymbolLocation &Value) {
148     MappingNormalization<NormalizedFileURI, const char *> NFile(IO,
149                                                                 Value.FileURI);
150     IO.mapRequired("FileURI", NFile->URI);
151     MappingNormalization<NormalizedPosition, SymbolLocation::Position> NStart(
152         IO, Value.Start);
153     IO.mapRequired("Start", NStart->P);
154     MappingNormalization<NormalizedPosition, SymbolLocation::Position> NEnd(
155         IO, Value.End);
156     IO.mapRequired("End", NEnd->P);
157   }
158 };
159 
160 template <> struct MappingTraits<SymbolInfo> {
mappingllvm::yaml::MappingTraits161   static void mapping(IO &IO, SymbolInfo &SymInfo) {
162     // FIXME: expose other fields?
163     IO.mapRequired("Kind", SymInfo.Kind);
164     IO.mapRequired("Lang", SymInfo.Lang);
165   }
166 };
167 
168 template <>
169 struct MappingTraits<clang::clangd::Symbol::IncludeHeaderWithReferences> {
mappingllvm::yaml::MappingTraits170   static void mapping(IO &IO,
171                       clang::clangd::Symbol::IncludeHeaderWithReferences &Inc) {
172     IO.mapRequired("Header", Inc.IncludeHeader);
173     IO.mapRequired("References", Inc.References);
174   }
175 };
176 
177 template <> struct MappingTraits<Symbol> {
mappingllvm::yaml::MappingTraits178   static void mapping(IO &IO, Symbol &Sym) {
179     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, Sym.ID);
180     MappingNormalization<NormalizedSymbolFlag, Symbol::SymbolFlag> NSymbolFlag(
181         IO, Sym.Flags);
182     IO.mapRequired("ID", NSymbolID->HexString);
183     IO.mapRequired("Name", Sym.Name);
184     IO.mapRequired("Scope", Sym.Scope);
185     IO.mapRequired("SymInfo", Sym.SymInfo);
186     IO.mapOptional("CanonicalDeclaration", Sym.CanonicalDeclaration,
187                    SymbolLocation());
188     IO.mapOptional("Definition", Sym.Definition, SymbolLocation());
189     IO.mapOptional("References", Sym.References, 0u);
190     IO.mapOptional("Flags", NSymbolFlag->Flag);
191     IO.mapOptional("Signature", Sym.Signature);
192     IO.mapOptional("TemplateSpecializationArgs",
193                    Sym.TemplateSpecializationArgs);
194     IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix);
195     IO.mapOptional("Documentation", Sym.Documentation);
196     IO.mapOptional("ReturnType", Sym.ReturnType);
197     IO.mapOptional("Type", Sym.Type);
198     IO.mapOptional("IncludeHeaders", Sym.IncludeHeaders);
199   }
200 };
201 
202 template <> struct ScalarEnumerationTraits<SymbolLanguage> {
enumerationllvm::yaml::ScalarEnumerationTraits203   static void enumeration(IO &IO, SymbolLanguage &Value) {
204     IO.enumCase(Value, "C", SymbolLanguage::C);
205     IO.enumCase(Value, "Cpp", SymbolLanguage::CXX);
206     IO.enumCase(Value, "ObjC", SymbolLanguage::ObjC);
207     IO.enumCase(Value, "Swift", SymbolLanguage::Swift);
208   }
209 };
210 
211 template <> struct ScalarEnumerationTraits<SymbolKind> {
enumerationllvm::yaml::ScalarEnumerationTraits212   static void enumeration(IO &IO, SymbolKind &Value) {
213 #define DEFINE_ENUM(name) IO.enumCase(Value, #name, SymbolKind::name)
214 
215     DEFINE_ENUM(Unknown);
216     DEFINE_ENUM(Function);
217     DEFINE_ENUM(Module);
218     DEFINE_ENUM(Namespace);
219     DEFINE_ENUM(NamespaceAlias);
220     DEFINE_ENUM(Macro);
221     DEFINE_ENUM(Enum);
222     DEFINE_ENUM(Struct);
223     DEFINE_ENUM(Class);
224     DEFINE_ENUM(Protocol);
225     DEFINE_ENUM(Extension);
226     DEFINE_ENUM(Union);
227     DEFINE_ENUM(TypeAlias);
228     DEFINE_ENUM(Function);
229     DEFINE_ENUM(Variable);
230     DEFINE_ENUM(Field);
231     DEFINE_ENUM(EnumConstant);
232     DEFINE_ENUM(InstanceMethod);
233     DEFINE_ENUM(ClassMethod);
234     DEFINE_ENUM(StaticMethod);
235     DEFINE_ENUM(InstanceProperty);
236     DEFINE_ENUM(ClassProperty);
237     DEFINE_ENUM(StaticProperty);
238     DEFINE_ENUM(Constructor);
239     DEFINE_ENUM(Destructor);
240     DEFINE_ENUM(ConversionFunction);
241     DEFINE_ENUM(Parameter);
242     DEFINE_ENUM(Using);
243 
244 #undef DEFINE_ENUM
245   }
246 };
247 
248 template <> struct MappingTraits<RefBundle> {
mappingllvm::yaml::MappingTraits249   static void mapping(IO &IO, RefBundle &Refs) {
250     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO,
251                                                                  Refs.first);
252     IO.mapRequired("ID", NSymbolID->HexString);
253     IO.mapRequired("References", Refs.second);
254   }
255 };
256 
257 struct NormalizedRefKind {
NormalizedRefKindllvm::yaml::NormalizedRefKind258   NormalizedRefKind(IO &) {}
NormalizedRefKindllvm::yaml::NormalizedRefKind259   NormalizedRefKind(IO &, RefKind O) { Kind = static_cast<uint8_t>(O); }
260 
denormalizellvm::yaml::NormalizedRefKind261   RefKind denormalize(IO &) { return static_cast<RefKind>(Kind); }
262 
263   uint8_t Kind = 0;
264 };
265 
266 template <> struct MappingTraits<Ref> {
mappingllvm::yaml::MappingTraits267   static void mapping(IO &IO, Ref &R) {
268     MappingNormalization<NormalizedRefKind, RefKind> NKind(IO, R.Kind);
269     IO.mapRequired("Kind", NKind->Kind);
270     IO.mapRequired("Location", R.Location);
271   }
272 };
273 
274 struct NormalizedSymbolRole {
NormalizedSymbolRolellvm::yaml::NormalizedSymbolRole275   NormalizedSymbolRole(IO &) {}
NormalizedSymbolRolellvm::yaml::NormalizedSymbolRole276   NormalizedSymbolRole(IO &IO, RelationKind R) {
277     Kind = static_cast<uint8_t>(R);
278   }
279 
denormalizellvm::yaml::NormalizedSymbolRole280   RelationKind denormalize(IO &IO) { return static_cast<RelationKind>(Kind); }
281 
282   uint8_t Kind = 0;
283 };
284 
285 template <> struct MappingTraits<SymbolID> {
mappingllvm::yaml::MappingTraits286   static void mapping(IO &IO, SymbolID &ID) {
287     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, ID);
288     IO.mapRequired("ID", NSymbolID->HexString);
289   }
290 };
291 
292 template <> struct MappingTraits<Relation> {
mappingllvm::yaml::MappingTraits293   static void mapping(IO &IO, Relation &Relation) {
294     MappingNormalization<NormalizedSymbolRole, RelationKind> NRole(
295         IO, Relation.Predicate);
296     IO.mapRequired("Subject", Relation.Subject);
297     IO.mapRequired("Predicate", NRole->Kind);
298     IO.mapRequired("Object", Relation.Object);
299   }
300 };
301 
302 struct NormalizedSourceFlag {
NormalizedSourceFlagllvm::yaml::NormalizedSourceFlag303   NormalizedSourceFlag(IO &) {}
NormalizedSourceFlagllvm::yaml::NormalizedSourceFlag304   NormalizedSourceFlag(IO &, IncludeGraphNode::SourceFlag O) {
305     Flag = static_cast<uint8_t>(O);
306   }
307 
denormalizellvm::yaml::NormalizedSourceFlag308   IncludeGraphNode::SourceFlag denormalize(IO &) {
309     return static_cast<IncludeGraphNode::SourceFlag>(Flag);
310   }
311 
312   uint8_t Flag = 0;
313 };
314 
315 struct NormalizedFileDigest {
NormalizedFileDigestllvm::yaml::NormalizedFileDigest316   NormalizedFileDigest(IO &) {}
NormalizedFileDigestllvm::yaml::NormalizedFileDigest317   NormalizedFileDigest(IO &, const FileDigest &Digest) {
318     HexString = llvm::toHex(Digest);
319   }
320 
denormalizellvm::yaml::NormalizedFileDigest321   FileDigest denormalize(IO &I) {
322     FileDigest Digest;
323     if (HexString.size() == Digest.size() * 2 &&
324         llvm::all_of(HexString, llvm::isHexDigit)) {
325       memcpy(Digest.data(), llvm::fromHex(HexString).data(), Digest.size());
326     } else {
327       I.setError(std::string("Bad hex file digest: ") + HexString);
328     }
329     return Digest;
330   }
331 
332   std::string HexString;
333 };
334 
335 template <> struct MappingTraits<IncludeGraphNode> {
mappingllvm::yaml::MappingTraits336   static void mapping(IO &IO, IncludeGraphNode &Node) {
337     IO.mapRequired("URI", Node.URI);
338     MappingNormalization<NormalizedSourceFlag, IncludeGraphNode::SourceFlag>
339         NSourceFlag(IO, Node.Flags);
340     IO.mapRequired("Flags", NSourceFlag->Flag);
341     MappingNormalization<NormalizedFileDigest, FileDigest> NDigest(IO,
342                                                                    Node.Digest);
343     IO.mapRequired("Digest", NDigest->HexString);
344     IO.mapRequired("DirectIncludes", Node.DirectIncludes);
345   }
346 };
347 
348 template <> struct MappingTraits<CompileCommandYAML> {
mappingllvm::yaml::MappingTraits349   static void mapping(IO &IO, CompileCommandYAML &Cmd) {
350     IO.mapRequired("Directory", Cmd.Directory);
351     IO.mapRequired("CommandLine", Cmd.CommandLine);
352   }
353 };
354 
355 template <> struct MappingTraits<VariantEntry> {
mappingllvm::yaml::MappingTraits356   static void mapping(IO &IO, VariantEntry &Variant) {
357     if (IO.mapTag("!Symbol", Variant.Symbol.has_value())) {
358       if (!IO.outputting())
359         Variant.Symbol.emplace();
360       MappingTraits<Symbol>::mapping(IO, *Variant.Symbol);
361     } else if (IO.mapTag("!Refs", Variant.Refs.has_value())) {
362       if (!IO.outputting())
363         Variant.Refs.emplace();
364       MappingTraits<RefBundle>::mapping(IO, *Variant.Refs);
365     } else if (IO.mapTag("!Relations", Variant.Relation.has_value())) {
366       if (!IO.outputting())
367         Variant.Relation.emplace();
368       MappingTraits<Relation>::mapping(IO, *Variant.Relation);
369     } else if (IO.mapTag("!Source", Variant.Source.has_value())) {
370       if (!IO.outputting())
371         Variant.Source.emplace();
372       MappingTraits<IncludeGraphNode>::mapping(IO, *Variant.Source);
373     } else if (IO.mapTag("!Cmd", Variant.Cmd.has_value())) {
374       if (!IO.outputting())
375         Variant.Cmd.emplace();
376       MappingTraits<CompileCommandYAML>::mapping(
377           IO, static_cast<CompileCommandYAML &>(*Variant.Cmd));
378     }
379   }
380 };
381 
382 } // namespace yaml
383 } // namespace llvm
384 
385 namespace clang {
386 namespace clangd {
387 
writeYAML(const IndexFileOut & O,llvm::raw_ostream & OS)388 void writeYAML(const IndexFileOut &O, llvm::raw_ostream &OS) {
389   llvm::yaml::Output Yout(OS);
390   for (const auto &Sym : *O.Symbols) {
391     VariantEntry Entry;
392     Entry.Symbol = Sym;
393     Yout << Entry;
394   }
395   if (O.Refs)
396     for (auto &Sym : *O.Refs) {
397       VariantEntry Entry;
398       Entry.Refs = Sym;
399       Yout << Entry;
400     }
401   if (O.Relations)
402     for (auto &R : *O.Relations) {
403       VariantEntry Entry;
404       Entry.Relation = R;
405       Yout << Entry;
406     }
407   if (O.Sources) {
408     for (const auto &Source : *O.Sources) {
409       VariantEntry Entry;
410       Entry.Source = Source.getValue();
411       Yout << Entry;
412     }
413   }
414   if (O.Cmd) {
415     VariantEntry Entry;
416     Entry.Cmd = *O.Cmd;
417     Yout << Entry;
418   }
419 }
420 
readYAML(llvm::StringRef Data,SymbolOrigin Origin)421 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data,
422                                      SymbolOrigin Origin) {
423   SymbolSlab::Builder Symbols;
424   RefSlab::Builder Refs;
425   RelationSlab::Builder Relations;
426   llvm::BumpPtrAllocator
427       Arena; // store the underlying data of Position::FileURI.
428   llvm::UniqueStringSaver Strings(Arena);
429   llvm::yaml::Input Yin(Data, &Strings);
430   IncludeGraph Sources;
431   llvm::Optional<tooling::CompileCommand> Cmd;
432   while (Yin.setCurrentDocument()) {
433     llvm::yaml::EmptyContext Ctx;
434     VariantEntry Variant;
435     yamlize(Yin, Variant, true, Ctx);
436     if (Yin.error())
437       return llvm::errorCodeToError(Yin.error());
438 
439     if (Variant.Symbol) {
440       Variant.Symbol->Origin = Origin;
441       Symbols.insert(*Variant.Symbol);
442     }
443     if (Variant.Refs)
444       for (const auto &Ref : Variant.Refs->second)
445         Refs.insert(Variant.Refs->first, Ref);
446     if (Variant.Relation)
447       Relations.insert(*Variant.Relation);
448     if (Variant.Source) {
449       auto &IGN = *Variant.Source;
450       auto Entry = Sources.try_emplace(IGN.URI).first;
451       Entry->getValue() = std::move(IGN);
452       // Fixup refs to refer to map keys which will live on
453       Entry->getValue().URI = Entry->getKey();
454       for (auto &Include : Entry->getValue().DirectIncludes)
455         Include = Sources.try_emplace(Include).first->getKey();
456     }
457     if (Variant.Cmd)
458       Cmd = *Variant.Cmd;
459     Yin.nextDocument();
460   }
461 
462   IndexFileIn Result;
463   Result.Symbols.emplace(std::move(Symbols).build());
464   Result.Refs.emplace(std::move(Refs).build());
465   Result.Relations.emplace(std::move(Relations).build());
466   if (Sources.size())
467     Result.Sources = std::move(Sources);
468   Result.Cmd = std::move(Cmd);
469   return std::move(Result);
470 }
471 
toYAML(const Symbol & S)472 std::string toYAML(const Symbol &S) {
473   std::string Buf;
474   {
475     llvm::raw_string_ostream OS(Buf);
476     llvm::yaml::Output Yout(OS);
477     Symbol Sym = S; // copy: Yout<< requires mutability.
478     Yout << Sym;
479   }
480   return Buf;
481 }
482 
toYAML(const std::pair<SymbolID,llvm::ArrayRef<Ref>> & Data)483 std::string toYAML(const std::pair<SymbolID, llvm::ArrayRef<Ref>> &Data) {
484   RefBundle Refs = {Data.first, Data.second};
485   std::string Buf;
486   {
487     llvm::raw_string_ostream OS(Buf);
488     llvm::yaml::Output Yout(OS);
489     Yout << Refs;
490   }
491   return Buf;
492 }
493 
toYAML(const Relation & R)494 std::string toYAML(const Relation &R) {
495   std::string Buf;
496   {
497     llvm::raw_string_ostream OS(Buf);
498     llvm::yaml::Output Yout(OS);
499     Relation Rel = R; // copy: Yout<< requires mutability.
500     Yout << Rel;
501   }
502   return Buf;
503 }
504 
toYAML(const Ref & R)505 std::string toYAML(const Ref &R) {
506   std::string Buf;
507   {
508     llvm::raw_string_ostream OS(Buf);
509     llvm::yaml::Output Yout(OS);
510     Ref Reference = R; // copy: Yout<< requires mutability.
511     Yout << Reference;
512   }
513   return Buf;
514 }
515 
516 } // namespace clangd
517 } // namespace clang
518