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 "Index.h"
16 #include "Serialization.h"
17 #include "SymbolLocation.h"
18 #include "SymbolOrigin.h"
19 #include "Trace.h"
20 #include "dex/Dex.h"
21 #include "llvm/ADT/Optional.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/Support/Allocator.h"
25 #include "llvm/Support/Errc.h"
26 #include "llvm/Support/MemoryBuffer.h"
27 #include "llvm/Support/StringSaver.h"
28 #include "llvm/Support/YAMLTraits.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include <cstdint>
31 
32 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences)
33 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref)
34 
35 namespace {
36 using RefBundle =
37     std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>;
38 // This is a pale imitation of std::variant<Symbol, RefBundle>
39 struct VariantEntry {
40   llvm::Optional<clang::clangd::Symbol> Symbol;
41   llvm::Optional<RefBundle> Refs;
42 };
43 // A class helps YAML to serialize the 32-bit encoded position (Line&Column),
44 // as YAMLIO can't directly map bitfields.
45 struct YPosition {
46   uint32_t Line;
47   uint32_t Column;
48 };
49 
50 } // namespace
51 namespace llvm {
52 namespace yaml {
53 
54 using clang::clangd::Ref;
55 using clang::clangd::RefKind;
56 using clang::clangd::Symbol;
57 using clang::clangd::SymbolID;
58 using clang::clangd::SymbolLocation;
59 using clang::clangd::SymbolOrigin;
60 using clang::index::SymbolInfo;
61 using clang::index::SymbolKind;
62 using clang::index::SymbolLanguage;
63 
64 // Helper to (de)serialize the SymbolID. We serialize it as a hex string.
65 struct NormalizedSymbolID {
66   NormalizedSymbolID(IO &) {}
67   NormalizedSymbolID(IO &, const SymbolID &ID) {
68     llvm::raw_string_ostream OS(HexString);
69     OS << ID;
70   }
71 
72   SymbolID denormalize(IO &I) {
73     auto ID = SymbolID::fromStr(HexString);
74     if (!ID) {
75       I.setError(llvm::toString(ID.takeError()));
76       return SymbolID();
77     }
78     return *ID;
79   }
80 
81   std::string HexString;
82 };
83 
84 struct NormalizedSymbolFlag {
85   NormalizedSymbolFlag(IO &) {}
86   NormalizedSymbolFlag(IO &, Symbol::SymbolFlag F) {
87     Flag = static_cast<uint8_t>(F);
88   }
89 
90   Symbol::SymbolFlag denormalize(IO &) {
91     return static_cast<Symbol::SymbolFlag>(Flag);
92   }
93 
94   uint8_t Flag = 0;
95 };
96 
97 struct NormalizedSymbolOrigin {
98   NormalizedSymbolOrigin(IO &) {}
99   NormalizedSymbolOrigin(IO &, SymbolOrigin O) {
100     Origin = static_cast<uint8_t>(O);
101   }
102 
103   SymbolOrigin denormalize(IO &) { return static_cast<SymbolOrigin>(Origin); }
104 
105   uint8_t Origin = 0;
106 };
107 
108 template <> struct MappingTraits<YPosition> {
109   static void mapping(IO &IO, YPosition &Value) {
110     IO.mapRequired("Line", Value.Line);
111     IO.mapRequired("Column", Value.Column);
112   }
113 };
114 
115 struct NormalizedPosition {
116   using Position = clang::clangd::SymbolLocation::Position;
117   NormalizedPosition(IO &) {}
118   NormalizedPosition(IO &, const Position &Pos) {
119     P.Line = Pos.line();
120     P.Column = Pos.column();
121   }
122 
123   Position denormalize(IO &) {
124     Position Pos;
125     Pos.setLine(P.Line);
126     Pos.setColumn(P.Column);
127     return Pos;
128   }
129   YPosition P;
130 };
131 
132 struct NormalizedFileURI {
133   NormalizedFileURI(IO &) {}
134   NormalizedFileURI(IO &, const char *FileURI) { URI = FileURI; }
135 
136   const char *denormalize(IO &IO) {
137     assert(IO.getContext() &&
138            "Expecting an UniqueStringSaver to allocate data");
139     return static_cast<llvm::UniqueStringSaver *>(IO.getContext())
140         ->save(URI)
141         .data();
142   }
143 
144   std::string URI;
145 };
146 
147 template <> struct MappingTraits<SymbolLocation> {
148   static void mapping(IO &IO, SymbolLocation &Value) {
149     MappingNormalization<NormalizedFileURI, const char *> NFile(IO,
150                                                                 Value.FileURI);
151     IO.mapRequired("FileURI", NFile->URI);
152     MappingNormalization<NormalizedPosition, SymbolLocation::Position> NStart(
153         IO, Value.Start);
154     IO.mapRequired("Start", NStart->P);
155     MappingNormalization<NormalizedPosition, SymbolLocation::Position> NEnd(
156         IO, Value.End);
157     IO.mapRequired("End", NEnd->P);
158   }
159 };
160 
161 template <> struct MappingTraits<SymbolInfo> {
162   static void mapping(IO &io, SymbolInfo &SymInfo) {
163     // FIXME: expose other fields?
164     io.mapRequired("Kind", SymInfo.Kind);
165     io.mapRequired("Lang", SymInfo.Lang);
166   }
167 };
168 
169 template <>
170 struct MappingTraits<clang::clangd::Symbol::IncludeHeaderWithReferences> {
171   static void mapping(IO &io,
172                       clang::clangd::Symbol::IncludeHeaderWithReferences &Inc) {
173     io.mapRequired("Header", Inc.IncludeHeader);
174     io.mapRequired("References", Inc.References);
175   }
176 };
177 
178 template <> struct MappingTraits<Symbol> {
179   static void mapping(IO &IO, Symbol &Sym) {
180     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, Sym.ID);
181     MappingNormalization<NormalizedSymbolFlag, Symbol::SymbolFlag> NSymbolFlag(
182         IO, Sym.Flags);
183     MappingNormalization<NormalizedSymbolOrigin, SymbolOrigin> NSymbolOrigin(
184         IO, Sym.Origin);
185     IO.mapRequired("ID", NSymbolID->HexString);
186     IO.mapRequired("Name", Sym.Name);
187     IO.mapRequired("Scope", Sym.Scope);
188     IO.mapRequired("SymInfo", Sym.SymInfo);
189     IO.mapOptional("CanonicalDeclaration", Sym.CanonicalDeclaration,
190                    SymbolLocation());
191     IO.mapOptional("Definition", Sym.Definition, SymbolLocation());
192     IO.mapOptional("References", Sym.References, 0u);
193     IO.mapOptional("Origin", NSymbolOrigin->Origin);
194     IO.mapOptional("Flags", NSymbolFlag->Flag);
195     IO.mapOptional("Signature", Sym.Signature);
196     IO.mapOptional("TemplateSpecializationArgs",
197                    Sym.TemplateSpecializationArgs);
198     IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix);
199     IO.mapOptional("Documentation", Sym.Documentation);
200     IO.mapOptional("ReturnType", Sym.ReturnType);
201     IO.mapOptional("Type", Sym.Type);
202     IO.mapOptional("IncludeHeaders", Sym.IncludeHeaders);
203   }
204 };
205 
206 template <> struct ScalarEnumerationTraits<SymbolLanguage> {
207   static void enumeration(IO &IO, SymbolLanguage &Value) {
208     IO.enumCase(Value, "C", SymbolLanguage::C);
209     IO.enumCase(Value, "Cpp", SymbolLanguage::CXX);
210     IO.enumCase(Value, "ObjC", SymbolLanguage::ObjC);
211     IO.enumCase(Value, "Swift", SymbolLanguage::Swift);
212   }
213 };
214 
215 template <> struct ScalarEnumerationTraits<SymbolKind> {
216   static void enumeration(IO &IO, SymbolKind &Value) {
217 #define DEFINE_ENUM(name) IO.enumCase(Value, #name, SymbolKind::name)
218 
219     DEFINE_ENUM(Unknown);
220     DEFINE_ENUM(Function);
221     DEFINE_ENUM(Module);
222     DEFINE_ENUM(Namespace);
223     DEFINE_ENUM(NamespaceAlias);
224     DEFINE_ENUM(Macro);
225     DEFINE_ENUM(Enum);
226     DEFINE_ENUM(Struct);
227     DEFINE_ENUM(Class);
228     DEFINE_ENUM(Protocol);
229     DEFINE_ENUM(Extension);
230     DEFINE_ENUM(Union);
231     DEFINE_ENUM(TypeAlias);
232     DEFINE_ENUM(Function);
233     DEFINE_ENUM(Variable);
234     DEFINE_ENUM(Field);
235     DEFINE_ENUM(EnumConstant);
236     DEFINE_ENUM(InstanceMethod);
237     DEFINE_ENUM(ClassMethod);
238     DEFINE_ENUM(StaticMethod);
239     DEFINE_ENUM(InstanceProperty);
240     DEFINE_ENUM(ClassProperty);
241     DEFINE_ENUM(StaticProperty);
242     DEFINE_ENUM(Constructor);
243     DEFINE_ENUM(Destructor);
244     DEFINE_ENUM(ConversionFunction);
245     DEFINE_ENUM(Parameter);
246     DEFINE_ENUM(Using);
247 
248 #undef DEFINE_ENUM
249   }
250 };
251 
252 template <> struct MappingTraits<RefBundle> {
253   static void mapping(IO &IO, RefBundle &Refs) {
254     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO,
255                                                                  Refs.first);
256     IO.mapRequired("ID", NSymbolID->HexString);
257     IO.mapRequired("References", Refs.second);
258   }
259 };
260 
261 struct NormalizedRefKind {
262   NormalizedRefKind(IO &) {}
263   NormalizedRefKind(IO &, RefKind O) { Kind = static_cast<uint8_t>(O); }
264 
265   RefKind denormalize(IO &) { return static_cast<RefKind>(Kind); }
266 
267   uint8_t Kind = 0;
268 };
269 
270 template <> struct MappingTraits<Ref> {
271   static void mapping(IO &IO, Ref &R) {
272     MappingNormalization<NormalizedRefKind, RefKind> NKind(IO, R.Kind);
273     IO.mapRequired("Kind", NKind->Kind);
274     IO.mapRequired("Location", R.Location);
275   }
276 };
277 
278 template <> struct MappingTraits<VariantEntry> {
279   static void mapping(IO &IO, VariantEntry &Variant) {
280     if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) {
281       if (!IO.outputting())
282         Variant.Symbol.emplace();
283       MappingTraits<Symbol>::mapping(IO, *Variant.Symbol);
284     } else if (IO.mapTag("!Refs", Variant.Refs.hasValue())) {
285       if (!IO.outputting())
286         Variant.Refs.emplace();
287       MappingTraits<RefBundle>::mapping(IO, *Variant.Refs);
288     }
289   }
290 };
291 
292 } // namespace yaml
293 } // namespace llvm
294 
295 namespace clang {
296 namespace clangd {
297 
298 void writeYAML(const IndexFileOut &O, llvm::raw_ostream &OS) {
299   llvm::yaml::Output Yout(OS);
300   for (const auto &Sym : *O.Symbols) {
301     VariantEntry Entry;
302     Entry.Symbol = Sym;
303     Yout << Entry;
304   }
305   if (O.Refs)
306     for (auto &Sym : *O.Refs) {
307       VariantEntry Entry;
308       Entry.Refs = Sym;
309       Yout << Entry;
310     }
311 }
312 
313 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data) {
314   SymbolSlab::Builder Symbols;
315   RefSlab::Builder Refs;
316   llvm::BumpPtrAllocator
317       Arena; // store the underlying data of Position::FileURI.
318   llvm::UniqueStringSaver Strings(Arena);
319   llvm::yaml::Input Yin(Data, &Strings);
320   while (Yin.setCurrentDocument()) {
321     llvm::yaml::EmptyContext Ctx;
322     VariantEntry Variant;
323     yamlize(Yin, Variant, true, Ctx);
324     if (Yin.error())
325       return llvm::errorCodeToError(Yin.error());
326 
327     if (Variant.Symbol)
328       Symbols.insert(*Variant.Symbol);
329     if (Variant.Refs)
330       for (const auto &Ref : Variant.Refs->second)
331         Refs.insert(Variant.Refs->first, Ref);
332     Yin.nextDocument();
333   }
334 
335   IndexFileIn Result;
336   Result.Symbols.emplace(std::move(Symbols).build());
337   Result.Refs.emplace(std::move(Refs).build());
338   return std::move(Result);
339 }
340 
341 std::string toYAML(const Symbol &S) {
342   std::string Buf;
343   {
344     llvm::raw_string_ostream OS(Buf);
345     llvm::yaml::Output Yout(OS);
346     Symbol Sym = S; // copy: Yout<< requires mutability.
347     Yout << Sym;
348   }
349   return Buf;
350 }
351 
352 std::string toYAML(const std::pair<SymbolID, llvm::ArrayRef<Ref>> &Data) {
353   RefBundle Refs = {Data.first, Data.second};
354   std::string Buf;
355   {
356     llvm::raw_string_ostream OS(Buf);
357     llvm::yaml::Output Yout(OS);
358     Yout << Refs;
359   }
360   return Buf;
361 }
362 
363 } // namespace clangd
364 } // namespace clang
365