1 //===-- YAMLSerialization.cpp ------------------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
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
13 //===----------------------------------------------------------------------===//
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"
31 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences
)
32 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref
)
36 std::pair
<clang::clangd::SymbolID
, std::vector
<clang::clangd::Ref
>>;
37 // This is a pale imitation of std::variant<Symbol, RefBundle, Relation>
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
;
45 // A class helps YAML to serialize the 32-bit encoded position (Line&Column),
46 // as YAMLIO can't directly map bitfields.
52 // avoid ODR violation of specialization for non-owned CompileCommand
53 struct CompileCommandYAML
: clang::tooling::CompileCommand
{};
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
;
74 // Helper to (de)serialize the SymbolID. We serialize it as a hex string.
75 struct NormalizedSymbolID
{
76 NormalizedSymbolID(IO
&) {}
77 NormalizedSymbolID(IO
&, const SymbolID
&ID
) {
78 llvm::raw_string_ostream
OS(HexString
);
82 SymbolID
denormalize(IO
&I
) {
83 auto ID
= SymbolID::fromStr(HexString
);
85 I
.setError(llvm::toString(ID
.takeError()));
91 std::string HexString
;
94 struct NormalizedSymbolFlag
{
95 NormalizedSymbolFlag(IO
&) {}
96 NormalizedSymbolFlag(IO
&, Symbol::SymbolFlag F
) {
97 Flag
= static_cast<uint8_t>(F
);
100 Symbol::SymbolFlag
denormalize(IO
&) {
101 return static_cast<Symbol::SymbolFlag
>(Flag
);
107 template <> struct MappingTraits
<YPosition
> {
108 static void mapping(IO
&IO
, YPosition
&Value
) {
109 IO
.mapRequired("Line", Value
.Line
);
110 IO
.mapRequired("Column", Value
.Column
);
114 struct NormalizedPosition
{
115 using Position
= clang::clangd::SymbolLocation::Position
;
116 NormalizedPosition(IO
&) {}
117 NormalizedPosition(IO
&, const Position
&Pos
) {
119 P
.Column
= Pos
.column();
122 Position
denormalize(IO
&) {
125 Pos
.setColumn(P
.Column
);
131 struct NormalizedFileURI
{
132 NormalizedFileURI(IO
&) {}
133 NormalizedFileURI(IO
&, const char *FileURI
) { URI
= FileURI
; }
135 const char *denormalize(IO
&IO
) {
136 assert(IO
.getContext() &&
137 "Expecting an UniqueStringSaver to allocate data");
138 return static_cast<llvm::UniqueStringSaver
*>(IO
.getContext())
146 template <> struct MappingTraits
<SymbolLocation
> {
147 static void mapping(IO
&IO
, SymbolLocation
&Value
) {
148 MappingNormalization
<NormalizedFileURI
, const char *> NFile(IO
,
150 IO
.mapRequired("FileURI", NFile
->URI
);
151 MappingNormalization
<NormalizedPosition
, SymbolLocation::Position
> NStart(
153 IO
.mapRequired("Start", NStart
->P
);
154 MappingNormalization
<NormalizedPosition
, SymbolLocation::Position
> NEnd(
156 IO
.mapRequired("End", NEnd
->P
);
160 template <> struct MappingTraits
<SymbolInfo
> {
161 static void mapping(IO
&IO
, SymbolInfo
&SymInfo
) {
162 // FIXME: expose other fields?
163 IO
.mapRequired("Kind", SymInfo
.Kind
);
164 IO
.mapRequired("Lang", SymInfo
.Lang
);
169 struct MappingTraits
<clang::clangd::Symbol::IncludeHeaderWithReferences
> {
170 static void mapping(IO
&IO
,
171 clang::clangd::Symbol::IncludeHeaderWithReferences
&Inc
) {
172 IO
.mapRequired("Header", Inc
.IncludeHeader
);
173 IO
.mapRequired("References", Inc
.References
);
177 template <> struct MappingTraits
<Symbol
> {
178 static void mapping(IO
&IO
, Symbol
&Sym
) {
179 MappingNormalization
<NormalizedSymbolID
, SymbolID
> NSymbolID(IO
, Sym
.ID
);
180 MappingNormalization
<NormalizedSymbolFlag
, Symbol::SymbolFlag
> NSymbolFlag(
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
,
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
);
202 template <> struct ScalarEnumerationTraits
<SymbolLanguage
> {
203 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
);
211 template <> struct ScalarEnumerationTraits
<SymbolKind
> {
212 static void enumeration(IO
&IO
, SymbolKind
&Value
) {
213 #define DEFINE_ENUM(name) IO.enumCase(Value, #name, SymbolKind::name)
215 DEFINE_ENUM(Unknown
);
216 DEFINE_ENUM(Function
);
218 DEFINE_ENUM(Namespace
);
219 DEFINE_ENUM(NamespaceAlias
);
224 DEFINE_ENUM(Protocol
);
225 DEFINE_ENUM(Extension
);
227 DEFINE_ENUM(TypeAlias
);
228 DEFINE_ENUM(Function
);
229 DEFINE_ENUM(Variable
);
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
);
248 template <> struct MappingTraits
<RefBundle
> {
249 static void mapping(IO
&IO
, RefBundle
&Refs
) {
250 MappingNormalization
<NormalizedSymbolID
, SymbolID
> NSymbolID(IO
,
252 IO
.mapRequired("ID", NSymbolID
->HexString
);
253 IO
.mapRequired("References", Refs
.second
);
257 struct NormalizedRefKind
{
258 NormalizedRefKind(IO
&) {}
259 NormalizedRefKind(IO
&, RefKind O
) { Kind
= static_cast<uint8_t>(O
); }
261 RefKind
denormalize(IO
&) { return static_cast<RefKind
>(Kind
); }
266 template <> struct MappingTraits
<Ref
> {
267 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
);
274 struct NormalizedSymbolRole
{
275 NormalizedSymbolRole(IO
&) {}
276 NormalizedSymbolRole(IO
&IO
, RelationKind R
) {
277 Kind
= static_cast<uint8_t>(R
);
280 RelationKind
denormalize(IO
&IO
) { return static_cast<RelationKind
>(Kind
); }
285 template <> struct MappingTraits
<SymbolID
> {
286 static void mapping(IO
&IO
, SymbolID
&ID
) {
287 MappingNormalization
<NormalizedSymbolID
, SymbolID
> NSymbolID(IO
, ID
);
288 IO
.mapRequired("ID", NSymbolID
->HexString
);
292 template <> struct MappingTraits
<Relation
> {
293 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
);
302 struct NormalizedSourceFlag
{
303 NormalizedSourceFlag(IO
&) {}
304 NormalizedSourceFlag(IO
&, IncludeGraphNode::SourceFlag O
) {
305 Flag
= static_cast<uint8_t>(O
);
308 IncludeGraphNode::SourceFlag
denormalize(IO
&) {
309 return static_cast<IncludeGraphNode::SourceFlag
>(Flag
);
315 struct NormalizedFileDigest
{
316 NormalizedFileDigest(IO
&) {}
317 NormalizedFileDigest(IO
&, const FileDigest
&Digest
) {
318 HexString
= llvm::toHex(Digest
);
321 FileDigest
denormalize(IO
&I
) {
323 if (HexString
.size() == Digest
.size() * 2 &&
324 llvm::all_of(HexString
, llvm::isHexDigit
)) {
325 memcpy(Digest
.data(), llvm::fromHex(HexString
).data(), Digest
.size());
327 I
.setError(std::string("Bad hex file digest: ") + HexString
);
332 std::string HexString
;
335 template <> struct MappingTraits
<IncludeGraphNode
> {
336 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
,
343 IO
.mapRequired("Digest", NDigest
->HexString
);
344 IO
.mapRequired("DirectIncludes", Node
.DirectIncludes
);
348 template <> struct MappingTraits
<CompileCommandYAML
> {
349 static void mapping(IO
&IO
, CompileCommandYAML
&Cmd
) {
350 IO
.mapRequired("Directory", Cmd
.Directory
);
351 IO
.mapRequired("CommandLine", Cmd
.CommandLine
);
355 template <> struct MappingTraits
<VariantEntry
> {
356 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
));
388 void writeYAML(const IndexFileOut
&O
, llvm::raw_ostream
&OS
) {
389 llvm::yaml::Output
Yout(OS
);
390 for (const auto &Sym
: *O
.Symbols
) {
396 for (auto &Sym
: *O
.Refs
) {
402 for (auto &R
: *O
.Relations
) {
408 for (const auto &Source
: *O
.Sources
) {
410 Entry
.Source
= Source
.getValue();
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
);
437 return llvm::errorCodeToError(Yin
.error());
439 if (Variant
.Symbol
) {
440 Variant
.Symbol
->Origin
= Origin
;
441 Symbols
.insert(*Variant
.Symbol
);
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();
463 Result
.Symbols
.emplace(std::move(Symbols
).build());
464 Result
.Refs
.emplace(std::move(Refs
).build());
465 Result
.Relations
.emplace(std::move(Relations
).build());
467 Result
.Sources
= std::move(Sources
);
468 Result
.Cmd
= std::move(Cmd
);
469 return std::move(Result
);
472 std::string
toYAML(const Symbol
&S
) {
475 llvm::raw_string_ostream
OS(Buf
);
476 llvm::yaml::Output
Yout(OS
);
477 Symbol Sym
= S
; // copy: Yout<< requires mutability.
483 std::string
toYAML(const std::pair
<SymbolID
, llvm::ArrayRef
<Ref
>> &Data
) {
484 RefBundle Refs
= {Data
.first
, Data
.second
};
487 llvm::raw_string_ostream
OS(Buf
);
488 llvm::yaml::Output
Yout(OS
);
494 std::string
toYAML(const Relation
&R
) {
497 llvm::raw_string_ostream
OS(Buf
);
498 llvm::yaml::Output
Yout(OS
);
499 Relation Rel
= R
; // copy: Yout<< requires mutability.
505 std::string
toYAML(const Ref
&R
) {
508 llvm::raw_string_ostream
OS(Buf
);
509 llvm::yaml::Output
Yout(OS
);
510 Ref Reference
= R
; // copy: Yout<< requires mutability.
516 } // namespace clangd