xref: /freebsd/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 //===- ExtractAPI/Serialization/SymbolGraphSerializer.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 /// \file
10 /// This file implements the SymbolGraphSerializer.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
15 #include "clang/Basic/SourceLocation.h"
16 #include "clang/Basic/Version.h"
17 #include "clang/ExtractAPI/DeclarationFragments.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/STLFunctionalExtras.h"
20 #include "llvm/Support/Casting.h"
21 #include "llvm/Support/Compiler.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/VersionTuple.h"
24 #include <optional>
25 #include <type_traits>
26 
27 using namespace clang;
28 using namespace clang::extractapi;
29 using namespace llvm;
30 using namespace llvm::json;
31 
32 namespace {
33 
34 /// Helper function to inject a JSON object \p Obj into another object \p Paren
35 /// at position \p Key.
36 void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) {
37   if (Obj)
38     Paren[Key] = std::move(*Obj);
39 }
40 
41 /// Helper function to inject a JSON array \p Array into object \p Paren at
42 /// position \p Key.
43 void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
44   if (Array)
45     Paren[Key] = std::move(*Array);
46 }
47 
48 /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
49 /// format.
50 ///
51 /// A semantic version object contains three numeric fields, representing the
52 /// \c major, \c minor, and \c patch parts of the version tuple.
53 /// For example version tuple 1.0.3 is serialized as:
54 /// \code
55 ///   {
56 ///     "major" : 1,
57 ///     "minor" : 0,
58 ///     "patch" : 3
59 ///   }
60 /// \endcode
61 ///
62 /// \returns \c std::nullopt if the version \p V is empty, or an \c Object
63 /// containing the semantic version representation of \p V.
64 std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
65   if (V.empty())
66     return std::nullopt;
67 
68   Object Version;
69   Version["major"] = V.getMajor();
70   Version["minor"] = V.getMinor().value_or(0);
71   Version["patch"] = V.getSubminor().value_or(0);
72   return Version;
73 }
74 
75 /// Serialize the OS information in the Symbol Graph platform property.
76 ///
77 /// The OS information in Symbol Graph contains the \c name of the OS, and an
78 /// optional \c minimumVersion semantic version field.
79 Object serializeOperatingSystem(const Triple &T) {
80   Object OS;
81   OS["name"] = T.getOSTypeName(T.getOS());
82   serializeObject(OS, "minimumVersion",
83                   serializeSemanticVersion(T.getMinimumSupportedOSVersion()));
84   return OS;
85 }
86 
87 /// Serialize the platform information in the Symbol Graph module section.
88 ///
89 /// The platform object describes a target platform triple in corresponding
90 /// three fields: \c architecture, \c vendor, and \c operatingSystem.
91 Object serializePlatform(const Triple &T) {
92   Object Platform;
93   Platform["architecture"] = T.getArchName();
94   Platform["vendor"] = T.getVendorName();
95   Platform["operatingSystem"] = serializeOperatingSystem(T);
96   return Platform;
97 }
98 
99 /// Serialize a source position.
100 Object serializeSourcePosition(const PresumedLoc &Loc) {
101   assert(Loc.isValid() && "invalid source position");
102 
103   Object SourcePosition;
104   SourcePosition["line"] = Loc.getLine();
105   SourcePosition["character"] = Loc.getColumn();
106 
107   return SourcePosition;
108 }
109 
110 /// Serialize a source location in file.
111 ///
112 /// \param Loc The presumed location to serialize.
113 /// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
114 /// Defaults to false.
115 Object serializeSourceLocation(const PresumedLoc &Loc,
116                                bool IncludeFileURI = false) {
117   Object SourceLocation;
118   serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));
119 
120   if (IncludeFileURI) {
121     std::string FileURI = "file://";
122     // Normalize file path to use forward slashes for the URI.
123     FileURI += sys::path::convert_to_slash(Loc.getFilename());
124     SourceLocation["uri"] = FileURI;
125   }
126 
127   return SourceLocation;
128 }
129 
130 /// Serialize a source range with begin and end locations.
131 Object serializeSourceRange(const PresumedLoc &BeginLoc,
132                             const PresumedLoc &EndLoc) {
133   Object SourceRange;
134   serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
135   serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
136   return SourceRange;
137 }
138 
139 /// Serialize the availability attributes of a symbol.
140 ///
141 /// Availability information contains the introduced, deprecated, and obsoleted
142 /// versions of the symbol for a given domain (roughly corresponds to a
143 /// platform) as semantic versions, if not default.  Availability information
144 /// also contains flags to indicate if the symbol is unconditionally unavailable
145 /// or deprecated, i.e. \c __attribute__((unavailable)) and \c
146 /// __attribute__((deprecated)).
147 ///
148 /// \returns \c std::nullopt if the symbol has default availability attributes,
149 /// or an \c Array containing the formatted availability information.
150 std::optional<Array>
151 serializeAvailability(const AvailabilitySet &Availabilities) {
152   if (Availabilities.isDefault())
153     return std::nullopt;
154 
155   Array AvailabilityArray;
156 
157   if (Availabilities.isUnconditionallyDeprecated()) {
158     Object UnconditionallyDeprecated;
159     UnconditionallyDeprecated["domain"] = "*";
160     UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
161     AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
162   }
163 
164   // Note unconditionally unavailable records are skipped.
165 
166   for (const auto &AvailInfo : Availabilities) {
167     Object Availability;
168     Availability["domain"] = AvailInfo.Domain;
169     if (AvailInfo.Unavailable)
170       Availability["isUnconditionallyUnavailable"] = true;
171     else {
172       serializeObject(Availability, "introducedVersion",
173                       serializeSemanticVersion(AvailInfo.Introduced));
174       serializeObject(Availability, "deprecatedVersion",
175                       serializeSemanticVersion(AvailInfo.Deprecated));
176       serializeObject(Availability, "obsoletedVersion",
177                       serializeSemanticVersion(AvailInfo.Obsoleted));
178     }
179     AvailabilityArray.emplace_back(std::move(Availability));
180   }
181 
182   return AvailabilityArray;
183 }
184 
185 /// Get the language name string for interface language references.
186 StringRef getLanguageName(Language Lang) {
187   switch (Lang) {
188   case Language::C:
189     return "c";
190   case Language::ObjC:
191     return "objective-c";
192 
193   // Unsupported language currently
194   case Language::CXX:
195   case Language::ObjCXX:
196   case Language::OpenCL:
197   case Language::OpenCLCXX:
198   case Language::CUDA:
199   case Language::RenderScript:
200   case Language::HIP:
201   case Language::HLSL:
202 
203   // Languages that the frontend cannot parse and compile
204   case Language::Unknown:
205   case Language::Asm:
206   case Language::LLVM_IR:
207     llvm_unreachable("Unsupported language kind");
208   }
209 
210   llvm_unreachable("Unhandled language kind");
211 }
212 
213 /// Serialize the identifier object as specified by the Symbol Graph format.
214 ///
215 /// The identifier property of a symbol contains the USR for precise and unique
216 /// references, and the interface language name.
217 Object serializeIdentifier(const APIRecord &Record, Language Lang) {
218   Object Identifier;
219   Identifier["precise"] = Record.USR;
220   Identifier["interfaceLanguage"] = getLanguageName(Lang);
221 
222   return Identifier;
223 }
224 
225 /// Serialize the documentation comments attached to a symbol, as specified by
226 /// the Symbol Graph format.
227 ///
228 /// The Symbol Graph \c docComment object contains an array of lines. Each line
229 /// represents one line of striped documentation comment, with source range
230 /// information.
231 /// e.g.
232 /// \code
233 ///   /// This is a documentation comment
234 ///       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'  First line.
235 ///   ///     with multiple lines.
236 ///       ^~~~~~~~~~~~~~~~~~~~~~~'         Second line.
237 /// \endcode
238 ///
239 /// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
240 /// the formatted lines.
241 std::optional<Object> serializeDocComment(const DocComment &Comment) {
242   if (Comment.empty())
243     return std::nullopt;
244 
245   Object DocComment;
246   Array LinesArray;
247   for (const auto &CommentLine : Comment) {
248     Object Line;
249     Line["text"] = CommentLine.Text;
250     serializeObject(Line, "range",
251                     serializeSourceRange(CommentLine.Begin, CommentLine.End));
252     LinesArray.emplace_back(std::move(Line));
253   }
254   serializeArray(DocComment, "lines", LinesArray);
255 
256   return DocComment;
257 }
258 
259 /// Serialize the declaration fragments of a symbol.
260 ///
261 /// The Symbol Graph declaration fragments is an array of tagged important
262 /// parts of a symbol's declaration. The fragments sequence can be joined to
263 /// form spans of declaration text, with attached information useful for
264 /// purposes like syntax-highlighting etc. For example:
265 /// \code
266 ///   const int pi; -> "declarationFragments" : [
267 ///                      {
268 ///                        "kind" : "keyword",
269 ///                        "spelling" : "const"
270 ///                      },
271 ///                      {
272 ///                        "kind" : "text",
273 ///                        "spelling" : " "
274 ///                      },
275 ///                      {
276 ///                        "kind" : "typeIdentifier",
277 ///                        "preciseIdentifier" : "c:I",
278 ///                        "spelling" : "int"
279 ///                      },
280 ///                      {
281 ///                        "kind" : "text",
282 ///                        "spelling" : " "
283 ///                      },
284 ///                      {
285 ///                        "kind" : "identifier",
286 ///                        "spelling" : "pi"
287 ///                      }
288 ///                    ]
289 /// \endcode
290 ///
291 /// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
292 /// formatted declaration fragments array.
293 std::optional<Array>
294 serializeDeclarationFragments(const DeclarationFragments &DF) {
295   if (DF.getFragments().empty())
296     return std::nullopt;
297 
298   Array Fragments;
299   for (const auto &F : DF.getFragments()) {
300     Object Fragment;
301     Fragment["spelling"] = F.Spelling;
302     Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
303     if (!F.PreciseIdentifier.empty())
304       Fragment["preciseIdentifier"] = F.PreciseIdentifier;
305     Fragments.emplace_back(std::move(Fragment));
306   }
307 
308   return Fragments;
309 }
310 
311 /// Serialize the \c names field of a symbol as specified by the Symbol Graph
312 /// format.
313 ///
314 /// The Symbol Graph names field contains multiple representations of a symbol
315 /// that can be used for different applications:
316 ///   - \c title : The simple declared name of the symbol;
317 ///   - \c subHeading : An array of declaration fragments that provides tags,
318 ///     and potentially more tokens (for example the \c +/- symbol for
319 ///     Objective-C methods). Can be used as sub-headings for documentation.
320 Object serializeNames(const APIRecord &Record) {
321   Object Names;
322   Names["title"] = Record.Name;
323   serializeArray(Names, "subHeading",
324                  serializeDeclarationFragments(Record.SubHeading));
325   DeclarationFragments NavigatorFragments;
326   NavigatorFragments.append(Record.Name,
327                             DeclarationFragments::FragmentKind::Identifier,
328                             /*PreciseIdentifier*/ "");
329   serializeArray(Names, "navigator",
330                  serializeDeclarationFragments(NavigatorFragments));
331 
332   return Names;
333 }
334 
335 Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
336   auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
337     return (getLanguageName(Lang) + "." + S).str();
338   };
339 
340   Object Kind;
341   switch (RK) {
342   case APIRecord::RK_Unknown:
343     llvm_unreachable("Records should have an explicit kind");
344     break;
345   case APIRecord::RK_GlobalFunction:
346     Kind["identifier"] = AddLangPrefix("func");
347     Kind["displayName"] = "Function";
348     break;
349   case APIRecord::RK_GlobalVariable:
350     Kind["identifier"] = AddLangPrefix("var");
351     Kind["displayName"] = "Global Variable";
352     break;
353   case APIRecord::RK_EnumConstant:
354     Kind["identifier"] = AddLangPrefix("enum.case");
355     Kind["displayName"] = "Enumeration Case";
356     break;
357   case APIRecord::RK_Enum:
358     Kind["identifier"] = AddLangPrefix("enum");
359     Kind["displayName"] = "Enumeration";
360     break;
361   case APIRecord::RK_StructField:
362     Kind["identifier"] = AddLangPrefix("property");
363     Kind["displayName"] = "Instance Property";
364     break;
365   case APIRecord::RK_Struct:
366     Kind["identifier"] = AddLangPrefix("struct");
367     Kind["displayName"] = "Structure";
368     break;
369   case APIRecord::RK_ObjCIvar:
370     Kind["identifier"] = AddLangPrefix("ivar");
371     Kind["displayName"] = "Instance Variable";
372     break;
373   case APIRecord::RK_ObjCInstanceMethod:
374     Kind["identifier"] = AddLangPrefix("method");
375     Kind["displayName"] = "Instance Method";
376     break;
377   case APIRecord::RK_ObjCClassMethod:
378     Kind["identifier"] = AddLangPrefix("type.method");
379     Kind["displayName"] = "Type Method";
380     break;
381   case APIRecord::RK_ObjCInstanceProperty:
382     Kind["identifier"] = AddLangPrefix("property");
383     Kind["displayName"] = "Instance Property";
384     break;
385   case APIRecord::RK_ObjCClassProperty:
386     Kind["identifier"] = AddLangPrefix("type.property");
387     Kind["displayName"] = "Type Property";
388     break;
389   case APIRecord::RK_ObjCInterface:
390     Kind["identifier"] = AddLangPrefix("class");
391     Kind["displayName"] = "Class";
392     break;
393   case APIRecord::RK_ObjCCategory:
394     // We don't serialize out standalone Objective-C category symbols yet.
395     llvm_unreachable("Serializing standalone Objective-C category symbols is "
396                      "not supported.");
397     break;
398   case APIRecord::RK_ObjCProtocol:
399     Kind["identifier"] = AddLangPrefix("protocol");
400     Kind["displayName"] = "Protocol";
401     break;
402   case APIRecord::RK_MacroDefinition:
403     Kind["identifier"] = AddLangPrefix("macro");
404     Kind["displayName"] = "Macro";
405     break;
406   case APIRecord::RK_Typedef:
407     Kind["identifier"] = AddLangPrefix("typealias");
408     Kind["displayName"] = "Type Alias";
409     break;
410   }
411 
412   return Kind;
413 }
414 
415 /// Serialize the symbol kind information.
416 ///
417 /// The Symbol Graph symbol kind property contains a shorthand \c identifier
418 /// which is prefixed by the source language name, useful for tooling to parse
419 /// the kind, and a \c displayName for rendering human-readable names.
420 Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
421   return serializeSymbolKind(Record.getKind(), Lang);
422 }
423 
424 template <typename RecordTy>
425 std::optional<Object>
426 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
427   const auto &FS = Record.Signature;
428   if (FS.empty())
429     return std::nullopt;
430 
431   Object Signature;
432   serializeArray(Signature, "returns",
433                  serializeDeclarationFragments(FS.getReturnType()));
434 
435   Array Parameters;
436   for (const auto &P : FS.getParameters()) {
437     Object Parameter;
438     Parameter["name"] = P.Name;
439     serializeArray(Parameter, "declarationFragments",
440                    serializeDeclarationFragments(P.Fragments));
441     Parameters.emplace_back(std::move(Parameter));
442   }
443 
444   if (!Parameters.empty())
445     Signature["parameters"] = std::move(Parameters);
446 
447   return Signature;
448 }
449 
450 template <typename RecordTy>
451 std::optional<Object>
452 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
453   return std::nullopt;
454 }
455 
456 /// Serialize the function signature field, as specified by the
457 /// Symbol Graph format.
458 ///
459 /// The Symbol Graph function signature property contains two arrays.
460 ///   - The \c returns array is the declaration fragments of the return type;
461 ///   - The \c parameters array contains names and declaration fragments of the
462 ///     parameters.
463 ///
464 /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
465 /// formatted function signature.
466 template <typename RecordTy>
467 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
468   serializeObject(Paren, "functionSignature",
469                   serializeFunctionSignatureMixinImpl(
470                       Record, has_function_signature<RecordTy>()));
471 }
472 
473 struct PathComponent {
474   StringRef USR;
475   StringRef Name;
476   APIRecord::RecordKind Kind;
477 
478   PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
479       : USR(USR), Name(Name), Kind(Kind) {}
480 };
481 
482 template <typename RecordTy>
483 bool generatePathComponents(
484     const RecordTy &Record, const APISet &API,
485     function_ref<void(const PathComponent &)> ComponentTransformer) {
486   SmallVector<PathComponent, 4> ReverseComponenents;
487   ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
488   const auto *CurrentParent = &Record.ParentInformation;
489   bool FailedToFindParent = false;
490   while (CurrentParent && !CurrentParent->empty()) {
491     PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
492                                          CurrentParent->ParentName,
493                                          CurrentParent->ParentKind);
494 
495     auto *ParentRecord = CurrentParent->ParentRecord;
496     // Slow path if we don't have a direct reference to the ParentRecord
497     if (!ParentRecord)
498       ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
499 
500     // If the parent is a category then we need to pretend this belongs to the
501     // associated interface.
502     if (auto *CategoryRecord =
503             dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
504       ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
505       CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
506                                              CategoryRecord->Interface.Name,
507                                              APIRecord::RK_ObjCInterface);
508     }
509 
510     // The parent record doesn't exist which means the symbol shouldn't be
511     // treated as part of the current product.
512     if (!ParentRecord) {
513       FailedToFindParent = true;
514       break;
515     }
516 
517     ReverseComponenents.push_back(std::move(CurrentParentComponent));
518     CurrentParent = &ParentRecord->ParentInformation;
519   }
520 
521   for (const auto &PC : reverse(ReverseComponenents))
522     ComponentTransformer(PC);
523 
524   return FailedToFindParent;
525 }
526 
527 Object serializeParentContext(const PathComponent &PC, Language Lang) {
528   Object ParentContextElem;
529   ParentContextElem["usr"] = PC.USR;
530   ParentContextElem["name"] = PC.Name;
531   ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
532   return ParentContextElem;
533 }
534 
535 template <typename RecordTy>
536 Array generateParentContexts(const RecordTy &Record, const APISet &API,
537                              Language Lang) {
538   Array ParentContexts;
539   generatePathComponents(
540       Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
541         ParentContexts.push_back(serializeParentContext(PC, Lang));
542       });
543 
544   return ParentContexts;
545 }
546 
547 } // namespace
548 
549 /// Defines the format version emitted by SymbolGraphSerializer.
550 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
551 
552 Object SymbolGraphSerializer::serializeMetadata() const {
553   Object Metadata;
554   serializeObject(Metadata, "formatVersion",
555                   serializeSemanticVersion(FormatVersion));
556   Metadata["generator"] = clang::getClangFullVersion();
557   return Metadata;
558 }
559 
560 Object SymbolGraphSerializer::serializeModule() const {
561   Object Module;
562   // The user is expected to always pass `--product-name=` on the command line
563   // to populate this field.
564   Module["name"] = API.ProductName;
565   serializeObject(Module, "platform", serializePlatform(API.getTarget()));
566   return Module;
567 }
568 
569 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
570   // Skip explicitly ignored symbols.
571   if (IgnoresList.shouldIgnore(Record.Name))
572     return true;
573 
574   // Skip unconditionally unavailable symbols
575   if (Record.Availabilities.isUnconditionallyUnavailable())
576     return true;
577 
578   // Filter out symbols prefixed with an underscored as they are understood to
579   // be symbols clients should not use.
580   if (Record.Name.startswith("_"))
581     return true;
582 
583   return false;
584 }
585 
586 template <typename RecordTy>
587 std::optional<Object>
588 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
589   if (shouldSkip(Record))
590     return std::nullopt;
591 
592   Object Obj;
593   serializeObject(Obj, "identifier",
594                   serializeIdentifier(Record, API.getLanguage()));
595   serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
596   serializeObject(Obj, "names", serializeNames(Record));
597   serializeObject(
598       Obj, "location",
599       serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
600   serializeArray(Obj, "availability",
601                  serializeAvailability(Record.Availabilities));
602   serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
603   serializeArray(Obj, "declarationFragments",
604                  serializeDeclarationFragments(Record.Declaration));
605   // TODO: Once we keep track of symbol access information serialize it
606   // correctly here.
607   Obj["accessLevel"] = "public";
608   SmallVector<StringRef, 4> PathComponentsNames;
609   // If this returns true it indicates that we couldn't find a symbol in the
610   // hierarchy.
611   if (generatePathComponents(Record, API,
612                              [&PathComponentsNames](const PathComponent &PC) {
613                                PathComponentsNames.push_back(PC.Name);
614                              }))
615     return {};
616 
617   serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
618 
619   serializeFunctionSignatureMixin(Obj, Record);
620 
621   return Obj;
622 }
623 
624 template <typename MemberTy>
625 void SymbolGraphSerializer::serializeMembers(
626     const APIRecord &Record,
627     const SmallVector<std::unique_ptr<MemberTy>> &Members) {
628   // Members should not be serialized if we aren't recursing.
629   if (!ShouldRecurse)
630     return;
631   for (const auto &Member : Members) {
632     auto MemberRecord = serializeAPIRecord(*Member);
633     if (!MemberRecord)
634       continue;
635 
636     Symbols.emplace_back(std::move(*MemberRecord));
637     serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
638   }
639 }
640 
641 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
642   switch (Kind) {
643   case RelationshipKind::MemberOf:
644     return "memberOf";
645   case RelationshipKind::InheritsFrom:
646     return "inheritsFrom";
647   case RelationshipKind::ConformsTo:
648     return "conformsTo";
649   }
650   llvm_unreachable("Unhandled relationship kind");
651 }
652 
653 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
654                                                   SymbolReference Source,
655                                                   SymbolReference Target) {
656   Object Relationship;
657   Relationship["source"] = Source.USR;
658   Relationship["target"] = Target.USR;
659   Relationship["targetFallback"] = Target.Name;
660   Relationship["kind"] = getRelationshipString(Kind);
661 
662   Relationships.emplace_back(std::move(Relationship));
663 }
664 
665 void SymbolGraphSerializer::visitGlobalFunctionRecord(
666     const GlobalFunctionRecord &Record) {
667   auto Obj = serializeAPIRecord(Record);
668   if (!Obj)
669     return;
670 
671   Symbols.emplace_back(std::move(*Obj));
672 }
673 
674 void SymbolGraphSerializer::visitGlobalVariableRecord(
675     const GlobalVariableRecord &Record) {
676   auto Obj = serializeAPIRecord(Record);
677   if (!Obj)
678     return;
679 
680   Symbols.emplace_back(std::move(*Obj));
681 }
682 
683 void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) {
684   auto Enum = serializeAPIRecord(Record);
685   if (!Enum)
686     return;
687 
688   Symbols.emplace_back(std::move(*Enum));
689   serializeMembers(Record, Record.Constants);
690 }
691 
692 void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) {
693   auto Struct = serializeAPIRecord(Record);
694   if (!Struct)
695     return;
696 
697   Symbols.emplace_back(std::move(*Struct));
698   serializeMembers(Record, Record.Fields);
699 }
700 
701 void SymbolGraphSerializer::visitObjCContainerRecord(
702     const ObjCContainerRecord &Record) {
703   auto ObjCContainer = serializeAPIRecord(Record);
704   if (!ObjCContainer)
705     return;
706 
707   Symbols.emplace_back(std::move(*ObjCContainer));
708 
709   serializeMembers(Record, Record.Ivars);
710   serializeMembers(Record, Record.Methods);
711   serializeMembers(Record, Record.Properties);
712 
713   for (const auto &Protocol : Record.Protocols)
714     // Record that Record conforms to Protocol.
715     serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
716 
717   if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
718     if (!ObjCInterface->SuperClass.empty())
719       // If Record is an Objective-C interface record and it has a super class,
720       // record that Record is inherited from SuperClass.
721       serializeRelationship(RelationshipKind::InheritsFrom, Record,
722                             ObjCInterface->SuperClass);
723 
724     // Members of categories extending an interface are serialized as members of
725     // the interface.
726     for (const auto *Category : ObjCInterface->Categories) {
727       serializeMembers(Record, Category->Ivars);
728       serializeMembers(Record, Category->Methods);
729       serializeMembers(Record, Category->Properties);
730 
731       // Surface the protocols of the category to the interface.
732       for (const auto &Protocol : Category->Protocols)
733         serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
734     }
735   }
736 }
737 
738 void SymbolGraphSerializer::visitMacroDefinitionRecord(
739     const MacroDefinitionRecord &Record) {
740   auto Macro = serializeAPIRecord(Record);
741 
742   if (!Macro)
743     return;
744 
745   Symbols.emplace_back(std::move(*Macro));
746 }
747 
748 void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
749   switch (Record->getKind()) {
750   case APIRecord::RK_Unknown:
751     llvm_unreachable("Records should have a known kind!");
752   case APIRecord::RK_GlobalFunction:
753     visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
754     break;
755   case APIRecord::RK_GlobalVariable:
756     visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
757     break;
758   case APIRecord::RK_Enum:
759     visitEnumRecord(*cast<EnumRecord>(Record));
760     break;
761   case APIRecord::RK_Struct:
762     visitStructRecord(*cast<StructRecord>(Record));
763     break;
764   case APIRecord::RK_ObjCInterface:
765     visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
766     break;
767   case APIRecord::RK_ObjCProtocol:
768     visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
769     break;
770   case APIRecord::RK_MacroDefinition:
771     visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
772     break;
773   case APIRecord::RK_Typedef:
774     visitTypedefRecord(*cast<TypedefRecord>(Record));
775     break;
776   default:
777     if (auto Obj = serializeAPIRecord(*Record)) {
778       Symbols.emplace_back(std::move(*Obj));
779       auto &ParentInformation = Record->ParentInformation;
780       if (!ParentInformation.empty())
781         serializeRelationship(RelationshipKind::MemberOf, *Record,
782                               *ParentInformation.ParentRecord);
783     }
784     break;
785   }
786 }
787 
788 void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) {
789   // Typedefs of anonymous types have their entries unified with the underlying
790   // type.
791   bool ShouldDrop = Record.UnderlyingType.Name.empty();
792   // enums declared with `NS_OPTION` have a named enum and a named typedef, with
793   // the same name
794   ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
795   if (ShouldDrop)
796     return;
797 
798   auto Typedef = serializeAPIRecord(Record);
799   if (!Typedef)
800     return;
801 
802   (*Typedef)["type"] = Record.UnderlyingType.USR;
803 
804   Symbols.emplace_back(std::move(*Typedef));
805 }
806 
807 Object SymbolGraphSerializer::serialize() {
808   traverseAPISet();
809   return serializeCurrentGraph();
810 }
811 
812 Object SymbolGraphSerializer::serializeCurrentGraph() {
813   Object Root;
814   serializeObject(Root, "metadata", serializeMetadata());
815   serializeObject(Root, "module", serializeModule());
816 
817   Root["symbols"] = std::move(Symbols);
818   Root["relationships"] = std::move(Relationships);
819 
820   return Root;
821 }
822 
823 void SymbolGraphSerializer::serialize(raw_ostream &os) {
824   Object root = serialize();
825   if (Options.Compact)
826     os << formatv("{0}", Value(std::move(root))) << "\n";
827   else
828     os << formatv("{0:2}", Value(std::move(root))) << "\n";
829 }
830 
831 std::optional<Object>
832 SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
833                                                 const APISet &API) {
834   APIRecord *Record = API.findRecordForUSR(USR);
835   if (!Record)
836     return {};
837 
838   if (isa<ObjCCategoryRecord>(Record))
839     return {};
840 
841   Object Root;
842   APIIgnoresList EmptyIgnores;
843   SymbolGraphSerializer Serializer(API, EmptyIgnores,
844                                    /*Options.Compact*/ {true},
845                                    /*ShouldRecurse*/ false);
846   Serializer.serializeSingleRecord(Record);
847   serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
848 
849   Language Lang = API.getLanguage();
850   serializeArray(Root, "parentContexts",
851                  generateParentContexts(*Record, API, Lang));
852 
853   Array RelatedSymbols;
854 
855   for (const auto &Fragment : Record->Declaration.getFragments()) {
856     // If we don't have a USR there isn't much we can do.
857     if (Fragment.PreciseIdentifier.empty())
858       continue;
859 
860     APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
861 
862     // If we can't find the record let's skip.
863     if (!RelatedRecord)
864       continue;
865 
866     Object RelatedSymbol;
867     RelatedSymbol["usr"] = RelatedRecord->USR;
868     RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
869     // TODO: once we record this properly let's serialize it right.
870     RelatedSymbol["accessLevel"] = "public";
871     RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
872     RelatedSymbol["moduleName"] = API.ProductName;
873     RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
874 
875     serializeArray(RelatedSymbol, "parentContexts",
876                    generateParentContexts(*RelatedRecord, API, Lang));
877     RelatedSymbols.push_back(std::move(RelatedSymbol));
878   }
879 
880   serializeArray(Root, "relatedSymbols", RelatedSymbols);
881   return Root;
882 }
883