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 StringRef \p String into an object \p Paren at
42 /// position \p Key
43 void serializeString(Object &Paren, StringRef Key,
44                      std::optional<std::string> String) {
45   if (String)
46     Paren[Key] = std::move(*String);
47 }
48 
49 /// Helper function to inject a JSON array \p Array into object \p Paren at
50 /// position \p Key.
51 void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
52   if (Array)
53     Paren[Key] = std::move(*Array);
54 }
55 
56 /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
57 /// format.
58 ///
59 /// A semantic version object contains three numeric fields, representing the
60 /// \c major, \c minor, and \c patch parts of the version tuple.
61 /// For example version tuple 1.0.3 is serialized as:
62 /// \code
63 ///   {
64 ///     "major" : 1,
65 ///     "minor" : 0,
66 ///     "patch" : 3
67 ///   }
68 /// \endcode
69 ///
70 /// \returns \c std::nullopt if the version \p V is empty, or an \c Object
71 /// containing the semantic version representation of \p V.
72 std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
73   if (V.empty())
74     return std::nullopt;
75 
76   Object Version;
77   Version["major"] = V.getMajor();
78   Version["minor"] = V.getMinor().value_or(0);
79   Version["patch"] = V.getSubminor().value_or(0);
80   return Version;
81 }
82 
83 /// Serialize the OS information in the Symbol Graph platform property.
84 ///
85 /// The OS information in Symbol Graph contains the \c name of the OS, and an
86 /// optional \c minimumVersion semantic version field.
87 Object serializeOperatingSystem(const Triple &T) {
88   Object OS;
89   OS["name"] = T.getOSTypeName(T.getOS());
90   serializeObject(OS, "minimumVersion",
91                   serializeSemanticVersion(T.getMinimumSupportedOSVersion()));
92   return OS;
93 }
94 
95 /// Serialize the platform information in the Symbol Graph module section.
96 ///
97 /// The platform object describes a target platform triple in corresponding
98 /// three fields: \c architecture, \c vendor, and \c operatingSystem.
99 Object serializePlatform(const Triple &T) {
100   Object Platform;
101   Platform["architecture"] = T.getArchName();
102   Platform["vendor"] = T.getVendorName();
103   Platform["operatingSystem"] = serializeOperatingSystem(T);
104   return Platform;
105 }
106 
107 /// Serialize a source position.
108 Object serializeSourcePosition(const PresumedLoc &Loc) {
109   assert(Loc.isValid() && "invalid source position");
110 
111   Object SourcePosition;
112   SourcePosition["line"] = Loc.getLine() - 1;
113   SourcePosition["character"] = Loc.getColumn() - 1;
114 
115   return SourcePosition;
116 }
117 
118 /// Serialize a source location in file.
119 ///
120 /// \param Loc The presumed location to serialize.
121 /// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
122 /// Defaults to false.
123 Object serializeSourceLocation(const PresumedLoc &Loc,
124                                bool IncludeFileURI = false) {
125   Object SourceLocation;
126   serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));
127 
128   if (IncludeFileURI) {
129     std::string FileURI = "file://";
130     // Normalize file path to use forward slashes for the URI.
131     FileURI += sys::path::convert_to_slash(Loc.getFilename());
132     SourceLocation["uri"] = FileURI;
133   }
134 
135   return SourceLocation;
136 }
137 
138 /// Serialize a source range with begin and end locations.
139 Object serializeSourceRange(const PresumedLoc &BeginLoc,
140                             const PresumedLoc &EndLoc) {
141   Object SourceRange;
142   serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
143   serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
144   return SourceRange;
145 }
146 
147 /// Serialize the availability attributes of a symbol.
148 ///
149 /// Availability information contains the introduced, deprecated, and obsoleted
150 /// versions of the symbol for a given domain (roughly corresponds to a
151 /// platform) as semantic versions, if not default.  Availability information
152 /// also contains flags to indicate if the symbol is unconditionally unavailable
153 /// or deprecated, i.e. \c __attribute__((unavailable)) and \c
154 /// __attribute__((deprecated)).
155 ///
156 /// \returns \c std::nullopt if the symbol has default availability attributes,
157 /// or an \c Array containing the formatted availability information.
158 std::optional<Array>
159 serializeAvailability(const AvailabilitySet &Availabilities) {
160   if (Availabilities.isDefault())
161     return std::nullopt;
162 
163   Array AvailabilityArray;
164 
165   if (Availabilities.isUnconditionallyDeprecated()) {
166     Object UnconditionallyDeprecated;
167     UnconditionallyDeprecated["domain"] = "*";
168     UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
169     AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
170   }
171 
172   // Note unconditionally unavailable records are skipped.
173 
174   for (const auto &AvailInfo : Availabilities) {
175     Object Availability;
176     Availability["domain"] = AvailInfo.Domain;
177     if (AvailInfo.Unavailable)
178       Availability["isUnconditionallyUnavailable"] = true;
179     else {
180       serializeObject(Availability, "introduced",
181                       serializeSemanticVersion(AvailInfo.Introduced));
182       serializeObject(Availability, "deprecated",
183                       serializeSemanticVersion(AvailInfo.Deprecated));
184       serializeObject(Availability, "obsoleted",
185                       serializeSemanticVersion(AvailInfo.Obsoleted));
186     }
187     AvailabilityArray.emplace_back(std::move(Availability));
188   }
189 
190   return AvailabilityArray;
191 }
192 
193 /// Get the language name string for interface language references.
194 StringRef getLanguageName(Language Lang) {
195   switch (Lang) {
196   case Language::C:
197     return "c";
198   case Language::ObjC:
199     return "objective-c";
200   case Language::CXX:
201     return "c++";
202   case Language::ObjCXX:
203     return "objective-c++";
204 
205   // Unsupported language currently
206   case Language::OpenCL:
207   case Language::OpenCLCXX:
208   case Language::CUDA:
209   case Language::RenderScript:
210   case Language::HIP:
211   case Language::HLSL:
212 
213   // Languages that the frontend cannot parse and compile
214   case Language::Unknown:
215   case Language::Asm:
216   case Language::LLVM_IR:
217     llvm_unreachable("Unsupported language kind");
218   }
219 
220   llvm_unreachable("Unhandled language kind");
221 }
222 
223 /// Serialize the identifier object as specified by the Symbol Graph format.
224 ///
225 /// The identifier property of a symbol contains the USR for precise and unique
226 /// references, and the interface language name.
227 Object serializeIdentifier(const APIRecord &Record, Language Lang) {
228   Object Identifier;
229   Identifier["precise"] = Record.USR;
230   Identifier["interfaceLanguage"] = getLanguageName(Lang);
231 
232   return Identifier;
233 }
234 
235 /// Serialize the documentation comments attached to a symbol, as specified by
236 /// the Symbol Graph format.
237 ///
238 /// The Symbol Graph \c docComment object contains an array of lines. Each line
239 /// represents one line of striped documentation comment, with source range
240 /// information.
241 /// e.g.
242 /// \code
243 ///   /// This is a documentation comment
244 ///       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'  First line.
245 ///   ///     with multiple lines.
246 ///       ^~~~~~~~~~~~~~~~~~~~~~~'         Second line.
247 /// \endcode
248 ///
249 /// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
250 /// the formatted lines.
251 std::optional<Object> serializeDocComment(const DocComment &Comment) {
252   if (Comment.empty())
253     return std::nullopt;
254 
255   Object DocComment;
256   Array LinesArray;
257   for (const auto &CommentLine : Comment) {
258     Object Line;
259     Line["text"] = CommentLine.Text;
260     serializeObject(Line, "range",
261                     serializeSourceRange(CommentLine.Begin, CommentLine.End));
262     LinesArray.emplace_back(std::move(Line));
263   }
264   serializeArray(DocComment, "lines", LinesArray);
265 
266   return DocComment;
267 }
268 
269 /// Serialize the declaration fragments of a symbol.
270 ///
271 /// The Symbol Graph declaration fragments is an array of tagged important
272 /// parts of a symbol's declaration. The fragments sequence can be joined to
273 /// form spans of declaration text, with attached information useful for
274 /// purposes like syntax-highlighting etc. For example:
275 /// \code
276 ///   const int pi; -> "declarationFragments" : [
277 ///                      {
278 ///                        "kind" : "keyword",
279 ///                        "spelling" : "const"
280 ///                      },
281 ///                      {
282 ///                        "kind" : "text",
283 ///                        "spelling" : " "
284 ///                      },
285 ///                      {
286 ///                        "kind" : "typeIdentifier",
287 ///                        "preciseIdentifier" : "c:I",
288 ///                        "spelling" : "int"
289 ///                      },
290 ///                      {
291 ///                        "kind" : "text",
292 ///                        "spelling" : " "
293 ///                      },
294 ///                      {
295 ///                        "kind" : "identifier",
296 ///                        "spelling" : "pi"
297 ///                      }
298 ///                    ]
299 /// \endcode
300 ///
301 /// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
302 /// formatted declaration fragments array.
303 std::optional<Array>
304 serializeDeclarationFragments(const DeclarationFragments &DF) {
305   if (DF.getFragments().empty())
306     return std::nullopt;
307 
308   Array Fragments;
309   for (const auto &F : DF.getFragments()) {
310     Object Fragment;
311     Fragment["spelling"] = F.Spelling;
312     Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
313     if (!F.PreciseIdentifier.empty())
314       Fragment["preciseIdentifier"] = F.PreciseIdentifier;
315     Fragments.emplace_back(std::move(Fragment));
316   }
317 
318   return Fragments;
319 }
320 
321 /// Serialize the \c names field of a symbol as specified by the Symbol Graph
322 /// format.
323 ///
324 /// The Symbol Graph names field contains multiple representations of a symbol
325 /// that can be used for different applications:
326 ///   - \c title : The simple declared name of the symbol;
327 ///   - \c subHeading : An array of declaration fragments that provides tags,
328 ///     and potentially more tokens (for example the \c +/- symbol for
329 ///     Objective-C methods). Can be used as sub-headings for documentation.
330 Object serializeNames(const APIRecord &Record) {
331   Object Names;
332   if (auto *CategoryRecord =
333           dyn_cast_or_null<const ObjCCategoryRecord>(&Record))
334     Names["title"] =
335         (CategoryRecord->Interface.Name + " (" + Record.Name + ")").str();
336   else
337     Names["title"] = Record.Name;
338 
339   serializeArray(Names, "subHeading",
340                  serializeDeclarationFragments(Record.SubHeading));
341   DeclarationFragments NavigatorFragments;
342   NavigatorFragments.append(Record.Name,
343                             DeclarationFragments::FragmentKind::Identifier,
344                             /*PreciseIdentifier*/ "");
345   serializeArray(Names, "navigator",
346                  serializeDeclarationFragments(NavigatorFragments));
347 
348   return Names;
349 }
350 
351 Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
352   auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
353     return (getLanguageName(Lang) + "." + S).str();
354   };
355 
356   Object Kind;
357   switch (RK) {
358   case APIRecord::RK_Unknown:
359     llvm_unreachable("Records should have an explicit kind");
360     break;
361   case APIRecord::RK_Namespace:
362     Kind["identifier"] = AddLangPrefix("namespace");
363     Kind["displayName"] = "Namespace";
364     break;
365   case APIRecord::RK_GlobalFunction:
366     Kind["identifier"] = AddLangPrefix("func");
367     Kind["displayName"] = "Function";
368     break;
369   case APIRecord::RK_GlobalFunctionTemplate:
370     Kind["identifier"] = AddLangPrefix("func");
371     Kind["displayName"] = "Function Template";
372     break;
373   case APIRecord::RK_GlobalFunctionTemplateSpecialization:
374     Kind["identifier"] = AddLangPrefix("func");
375     Kind["displayName"] = "Function Template Specialization";
376     break;
377   case APIRecord::RK_GlobalVariableTemplate:
378     Kind["identifier"] = AddLangPrefix("var");
379     Kind["displayName"] = "Global Variable Template";
380     break;
381   case APIRecord::RK_GlobalVariableTemplateSpecialization:
382     Kind["identifier"] = AddLangPrefix("var");
383     Kind["displayName"] = "Global Variable Template Specialization";
384     break;
385   case APIRecord::RK_GlobalVariableTemplatePartialSpecialization:
386     Kind["identifier"] = AddLangPrefix("var");
387     Kind["displayName"] = "Global Variable Template Partial Specialization";
388     break;
389   case APIRecord::RK_GlobalVariable:
390     Kind["identifier"] = AddLangPrefix("var");
391     Kind["displayName"] = "Global Variable";
392     break;
393   case APIRecord::RK_EnumConstant:
394     Kind["identifier"] = AddLangPrefix("enum.case");
395     Kind["displayName"] = "Enumeration Case";
396     break;
397   case APIRecord::RK_Enum:
398     Kind["identifier"] = AddLangPrefix("enum");
399     Kind["displayName"] = "Enumeration";
400     break;
401   case APIRecord::RK_StructField:
402     Kind["identifier"] = AddLangPrefix("property");
403     Kind["displayName"] = "Instance Property";
404     break;
405   case APIRecord::RK_Struct:
406     Kind["identifier"] = AddLangPrefix("struct");
407     Kind["displayName"] = "Structure";
408     break;
409   case APIRecord::RK_CXXField:
410     Kind["identifier"] = AddLangPrefix("property");
411     Kind["displayName"] = "Instance Property";
412     break;
413   case APIRecord::RK_Union:
414     Kind["identifier"] = AddLangPrefix("union");
415     Kind["displayName"] = "Union";
416     break;
417   case APIRecord::RK_StaticField:
418     Kind["identifier"] = AddLangPrefix("type.property");
419     Kind["displayName"] = "Type Property";
420     break;
421   case APIRecord::RK_ClassTemplate:
422   case APIRecord::RK_ClassTemplateSpecialization:
423   case APIRecord::RK_ClassTemplatePartialSpecialization:
424   case APIRecord::RK_CXXClass:
425     Kind["identifier"] = AddLangPrefix("class");
426     Kind["displayName"] = "Class";
427     break;
428   case APIRecord::RK_CXXMethodTemplate:
429     Kind["identifier"] = AddLangPrefix("method");
430     Kind["displayName"] = "Method Template";
431     break;
432   case APIRecord::RK_CXXMethodTemplateSpecialization:
433     Kind["identifier"] = AddLangPrefix("method");
434     Kind["displayName"] = "Method Template Specialization";
435     break;
436   case APIRecord::RK_CXXFieldTemplate:
437     Kind["identifier"] = AddLangPrefix("property");
438     Kind["displayName"] = "Template Property";
439     break;
440   case APIRecord::RK_Concept:
441     Kind["identifier"] = AddLangPrefix("concept");
442     Kind["displayName"] = "Concept";
443     break;
444   case APIRecord::RK_CXXStaticMethod:
445     Kind["identifier"] = AddLangPrefix("type.method");
446     Kind["displayName"] = "Static Method";
447     break;
448   case APIRecord::RK_CXXInstanceMethod:
449     Kind["identifier"] = AddLangPrefix("method");
450     Kind["displayName"] = "Instance Method";
451     break;
452   case APIRecord::RK_CXXConstructorMethod:
453     Kind["identifier"] = AddLangPrefix("method");
454     Kind["displayName"] = "Constructor";
455     break;
456   case APIRecord::RK_CXXDestructorMethod:
457     Kind["identifier"] = AddLangPrefix("method");
458     Kind["displayName"] = "Destructor";
459     break;
460   case APIRecord::RK_ObjCIvar:
461     Kind["identifier"] = AddLangPrefix("ivar");
462     Kind["displayName"] = "Instance Variable";
463     break;
464   case APIRecord::RK_ObjCInstanceMethod:
465     Kind["identifier"] = AddLangPrefix("method");
466     Kind["displayName"] = "Instance Method";
467     break;
468   case APIRecord::RK_ObjCClassMethod:
469     Kind["identifier"] = AddLangPrefix("type.method");
470     Kind["displayName"] = "Type Method";
471     break;
472   case APIRecord::RK_ObjCInstanceProperty:
473     Kind["identifier"] = AddLangPrefix("property");
474     Kind["displayName"] = "Instance Property";
475     break;
476   case APIRecord::RK_ObjCClassProperty:
477     Kind["identifier"] = AddLangPrefix("type.property");
478     Kind["displayName"] = "Type Property";
479     break;
480   case APIRecord::RK_ObjCInterface:
481     Kind["identifier"] = AddLangPrefix("class");
482     Kind["displayName"] = "Class";
483     break;
484   case APIRecord::RK_ObjCCategory:
485     Kind["identifier"] = AddLangPrefix("class.extension");
486     Kind["displayName"] = "Class Extension";
487     break;
488   case APIRecord::RK_ObjCCategoryModule:
489     Kind["identifier"] = AddLangPrefix("module.extension");
490     Kind["displayName"] = "Module Extension";
491     break;
492   case APIRecord::RK_ObjCProtocol:
493     Kind["identifier"] = AddLangPrefix("protocol");
494     Kind["displayName"] = "Protocol";
495     break;
496   case APIRecord::RK_MacroDefinition:
497     Kind["identifier"] = AddLangPrefix("macro");
498     Kind["displayName"] = "Macro";
499     break;
500   case APIRecord::RK_Typedef:
501     Kind["identifier"] = AddLangPrefix("typealias");
502     Kind["displayName"] = "Type Alias";
503     break;
504   }
505 
506   return Kind;
507 }
508 
509 /// Serialize the symbol kind information.
510 ///
511 /// The Symbol Graph symbol kind property contains a shorthand \c identifier
512 /// which is prefixed by the source language name, useful for tooling to parse
513 /// the kind, and a \c displayName for rendering human-readable names.
514 Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
515   return serializeSymbolKind(Record.getKind(), Lang);
516 }
517 
518 template <typename RecordTy>
519 std::optional<Object>
520 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
521   const auto &FS = Record.Signature;
522   if (FS.empty())
523     return std::nullopt;
524 
525   Object Signature;
526   serializeArray(Signature, "returns",
527                  serializeDeclarationFragments(FS.getReturnType()));
528 
529   Array Parameters;
530   for (const auto &P : FS.getParameters()) {
531     Object Parameter;
532     Parameter["name"] = P.Name;
533     serializeArray(Parameter, "declarationFragments",
534                    serializeDeclarationFragments(P.Fragments));
535     Parameters.emplace_back(std::move(Parameter));
536   }
537 
538   if (!Parameters.empty())
539     Signature["parameters"] = std::move(Parameters);
540 
541   return Signature;
542 }
543 
544 template <typename RecordTy>
545 std::optional<Object>
546 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
547   return std::nullopt;
548 }
549 
550 /// Serialize the function signature field, as specified by the
551 /// Symbol Graph format.
552 ///
553 /// The Symbol Graph function signature property contains two arrays.
554 ///   - The \c returns array is the declaration fragments of the return type;
555 ///   - The \c parameters array contains names and declaration fragments of the
556 ///     parameters.
557 ///
558 /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
559 /// formatted function signature.
560 template <typename RecordTy>
561 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
562   serializeObject(Paren, "functionSignature",
563                   serializeFunctionSignatureMixinImpl(
564                       Record, has_function_signature<RecordTy>()));
565 }
566 
567 template <typename RecordTy>
568 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
569                                                     std::true_type) {
570   const auto &AccessControl = Record.Access;
571   std::string Access;
572   if (AccessControl.empty())
573     return std::nullopt;
574   Access = AccessControl.getAccess();
575   return Access;
576 }
577 
578 template <typename RecordTy>
579 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
580                                                     std::false_type) {
581   return std::nullopt;
582 }
583 
584 template <typename RecordTy>
585 void serializeAccessMixin(Object &Paren, const RecordTy &Record) {
586   auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>());
587   if (!accessLevel.has_value())
588     accessLevel = "public";
589   serializeString(Paren, "accessLevel", accessLevel);
590 }
591 
592 template <typename RecordTy>
593 std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record,
594                                                  std::true_type) {
595   const auto &Template = Record.Templ;
596   if (Template.empty())
597     return std::nullopt;
598 
599   Object Generics;
600   Array GenericParameters;
601   for (const auto &Param : Template.getParameters()) {
602     Object Parameter;
603     Parameter["name"] = Param.Name;
604     Parameter["index"] = Param.Index;
605     Parameter["depth"] = Param.Depth;
606     GenericParameters.emplace_back(std::move(Parameter));
607   }
608   if (!GenericParameters.empty())
609     Generics["parameters"] = std::move(GenericParameters);
610 
611   Array GenericConstraints;
612   for (const auto &Constr : Template.getConstraints()) {
613     Object Constraint;
614     Constraint["kind"] = Constr.Kind;
615     Constraint["lhs"] = Constr.LHS;
616     Constraint["rhs"] = Constr.RHS;
617     GenericConstraints.emplace_back(std::move(Constraint));
618   }
619 
620   if (!GenericConstraints.empty())
621     Generics["constraints"] = std::move(GenericConstraints);
622 
623   return Generics;
624 }
625 
626 template <typename RecordTy>
627 std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record,
628                                                  std::false_type) {
629   return std::nullopt;
630 }
631 
632 template <typename RecordTy>
633 void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
634   serializeObject(Paren, "swiftGenerics",
635                   serializeTemplateMixinImpl(Record, has_template<RecordTy>()));
636 }
637 
638 struct PathComponent {
639   StringRef USR;
640   StringRef Name;
641   APIRecord::RecordKind Kind;
642 
643   PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
644       : USR(USR), Name(Name), Kind(Kind) {}
645 };
646 
647 template <typename RecordTy>
648 bool generatePathComponents(
649     const RecordTy &Record, const APISet &API,
650     function_ref<void(const PathComponent &)> ComponentTransformer) {
651   SmallVector<PathComponent, 4> ReverseComponenents;
652   ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
653   const auto *CurrentParent = &Record.ParentInformation;
654   bool FailedToFindParent = false;
655   while (CurrentParent && !CurrentParent->empty()) {
656     PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
657                                          CurrentParent->ParentName,
658                                          CurrentParent->ParentKind);
659 
660     auto *ParentRecord = CurrentParent->ParentRecord;
661     // Slow path if we don't have a direct reference to the ParentRecord
662     if (!ParentRecord)
663       ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
664 
665     // If the parent is a category extended from internal module then we need to
666     // pretend this belongs to the associated interface.
667     if (auto *CategoryRecord =
668             dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
669       if (!CategoryRecord->IsFromExternalModule) {
670         ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
671         CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
672                                                CategoryRecord->Interface.Name,
673                                                APIRecord::RK_ObjCInterface);
674       }
675     }
676 
677     // The parent record doesn't exist which means the symbol shouldn't be
678     // treated as part of the current product.
679     if (!ParentRecord) {
680       FailedToFindParent = true;
681       break;
682     }
683 
684     ReverseComponenents.push_back(std::move(CurrentParentComponent));
685     CurrentParent = &ParentRecord->ParentInformation;
686   }
687 
688   for (const auto &PC : reverse(ReverseComponenents))
689     ComponentTransformer(PC);
690 
691   return FailedToFindParent;
692 }
693 
694 Object serializeParentContext(const PathComponent &PC, Language Lang) {
695   Object ParentContextElem;
696   ParentContextElem["usr"] = PC.USR;
697   ParentContextElem["name"] = PC.Name;
698   ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
699   return ParentContextElem;
700 }
701 
702 template <typename RecordTy>
703 Array generateParentContexts(const RecordTy &Record, const APISet &API,
704                              Language Lang) {
705   Array ParentContexts;
706   generatePathComponents(
707       Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
708         ParentContexts.push_back(serializeParentContext(PC, Lang));
709       });
710 
711   return ParentContexts;
712 }
713 } // namespace
714 
715 /// Defines the format version emitted by SymbolGraphSerializer.
716 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
717 
718 Object SymbolGraphSerializer::serializeMetadata() const {
719   Object Metadata;
720   serializeObject(Metadata, "formatVersion",
721                   serializeSemanticVersion(FormatVersion));
722   Metadata["generator"] = clang::getClangFullVersion();
723   return Metadata;
724 }
725 
726 Object SymbolGraphSerializer::serializeModule() const {
727   Object Module;
728   // The user is expected to always pass `--product-name=` on the command line
729   // to populate this field.
730   Module["name"] = API.ProductName;
731   serializeObject(Module, "platform", serializePlatform(API.getTarget()));
732   return Module;
733 }
734 
735 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
736   // Skip explicitly ignored symbols.
737   if (IgnoresList.shouldIgnore(Record.Name))
738     return true;
739 
740   // Skip unconditionally unavailable symbols
741   if (Record.Availabilities.isUnconditionallyUnavailable())
742     return true;
743 
744   // Filter out symbols prefixed with an underscored as they are understood to
745   // be symbols clients should not use.
746   if (Record.Name.starts_with("_"))
747     return true;
748 
749   return false;
750 }
751 
752 template <typename RecordTy>
753 std::optional<Object>
754 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
755   if (shouldSkip(Record))
756     return std::nullopt;
757 
758   Object Obj;
759   serializeObject(Obj, "identifier",
760                   serializeIdentifier(Record, API.getLanguage()));
761   serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
762   serializeObject(Obj, "names", serializeNames(Record));
763   serializeObject(
764       Obj, "location",
765       serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
766   serializeArray(Obj, "availability",
767                  serializeAvailability(Record.Availabilities));
768   serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
769   serializeArray(Obj, "declarationFragments",
770                  serializeDeclarationFragments(Record.Declaration));
771   SmallVector<StringRef, 4> PathComponentsNames;
772   // If this returns true it indicates that we couldn't find a symbol in the
773   // hierarchy.
774   if (generatePathComponents(Record, API,
775                              [&PathComponentsNames](const PathComponent &PC) {
776                                PathComponentsNames.push_back(PC.Name);
777                              }))
778     return {};
779 
780   serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
781 
782   serializeFunctionSignatureMixin(Obj, Record);
783   serializeAccessMixin(Obj, Record);
784   serializeTemplateMixin(Obj, Record);
785 
786   return Obj;
787 }
788 
789 template <typename MemberTy>
790 void SymbolGraphSerializer::serializeMembers(
791     const APIRecord &Record,
792     const SmallVector<std::unique_ptr<MemberTy>> &Members) {
793   // Members should not be serialized if we aren't recursing.
794   if (!ShouldRecurse)
795     return;
796   for (const auto &Member : Members) {
797     auto MemberRecord = serializeAPIRecord(*Member);
798     if (!MemberRecord)
799       continue;
800 
801     Symbols.emplace_back(std::move(*MemberRecord));
802     serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
803   }
804 }
805 
806 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
807   switch (Kind) {
808   case RelationshipKind::MemberOf:
809     return "memberOf";
810   case RelationshipKind::InheritsFrom:
811     return "inheritsFrom";
812   case RelationshipKind::ConformsTo:
813     return "conformsTo";
814   case RelationshipKind::ExtensionTo:
815     return "extensionTo";
816   }
817   llvm_unreachable("Unhandled relationship kind");
818 }
819 
820 StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {
821   switch (Kind) {
822   case ConstraintKind::Conformance:
823     return "conformance";
824   case ConstraintKind::ConditionalConformance:
825     return "conditionalConformance";
826   }
827   llvm_unreachable("Unhandled constraint kind");
828 }
829 
830 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
831                                                   SymbolReference Source,
832                                                   SymbolReference Target) {
833   Object Relationship;
834   Relationship["source"] = Source.USR;
835   Relationship["target"] = Target.USR;
836   Relationship["targetFallback"] = Target.Name;
837   Relationship["kind"] = getRelationshipString(Kind);
838 
839   Relationships.emplace_back(std::move(Relationship));
840 }
841 
842 void SymbolGraphSerializer::visitNamespaceRecord(
843     const NamespaceRecord &Record) {
844   auto Namespace = serializeAPIRecord(Record);
845   if (!Namespace)
846     return;
847   Symbols.emplace_back(std::move(*Namespace));
848   if (!Record.ParentInformation.empty())
849     serializeRelationship(RelationshipKind::MemberOf, Record,
850                           Record.ParentInformation.ParentRecord);
851 }
852 
853 void SymbolGraphSerializer::visitGlobalFunctionRecord(
854     const GlobalFunctionRecord &Record) {
855   auto Obj = serializeAPIRecord(Record);
856   if (!Obj)
857     return;
858 
859   Symbols.emplace_back(std::move(*Obj));
860 }
861 
862 void SymbolGraphSerializer::visitGlobalVariableRecord(
863     const GlobalVariableRecord &Record) {
864   auto Obj = serializeAPIRecord(Record);
865   if (!Obj)
866     return;
867 
868   Symbols.emplace_back(std::move(*Obj));
869 }
870 
871 void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) {
872   auto Enum = serializeAPIRecord(Record);
873   if (!Enum)
874     return;
875 
876   Symbols.emplace_back(std::move(*Enum));
877   serializeMembers(Record, Record.Constants);
878 }
879 
880 void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) {
881   auto Struct = serializeAPIRecord(Record);
882   if (!Struct)
883     return;
884 
885   Symbols.emplace_back(std::move(*Struct));
886   serializeMembers(Record, Record.Fields);
887 }
888 
889 void SymbolGraphSerializer::visitStaticFieldRecord(
890     const StaticFieldRecord &Record) {
891   auto StaticField = serializeAPIRecord(Record);
892   if (!StaticField)
893     return;
894   Symbols.emplace_back(std::move(*StaticField));
895   serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context);
896 }
897 
898 void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
899   auto Class = serializeAPIRecord(Record);
900   if (!Class)
901     return;
902 
903   Symbols.emplace_back(std::move(*Class));
904   for (const auto &Base : Record.Bases)
905     serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
906   if (!Record.ParentInformation.empty())
907     serializeRelationship(RelationshipKind::MemberOf, Record,
908                           Record.ParentInformation.ParentRecord);
909 }
910 
911 void SymbolGraphSerializer::visitClassTemplateRecord(
912     const ClassTemplateRecord &Record) {
913   auto Class = serializeAPIRecord(Record);
914   if (!Class)
915     return;
916 
917   Symbols.emplace_back(std::move(*Class));
918   for (const auto &Base : Record.Bases)
919     serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
920   if (!Record.ParentInformation.empty())
921     serializeRelationship(RelationshipKind::MemberOf, Record,
922                           Record.ParentInformation.ParentRecord);
923 }
924 
925 void SymbolGraphSerializer::visitClassTemplateSpecializationRecord(
926     const ClassTemplateSpecializationRecord &Record) {
927   auto Class = serializeAPIRecord(Record);
928   if (!Class)
929     return;
930 
931   Symbols.emplace_back(std::move(*Class));
932 
933   for (const auto &Base : Record.Bases)
934     serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
935   if (!Record.ParentInformation.empty())
936     serializeRelationship(RelationshipKind::MemberOf, Record,
937                           Record.ParentInformation.ParentRecord);
938 }
939 
940 void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
941     const ClassTemplatePartialSpecializationRecord &Record) {
942   auto Class = serializeAPIRecord(Record);
943   if (!Class)
944     return;
945 
946   Symbols.emplace_back(std::move(*Class));
947 
948   for (const auto &Base : Record.Bases)
949     serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
950   if (!Record.ParentInformation.empty())
951     serializeRelationship(RelationshipKind::MemberOf, Record,
952                           Record.ParentInformation.ParentRecord);
953 }
954 
955 void SymbolGraphSerializer::visitCXXInstanceMethodRecord(
956     const CXXInstanceMethodRecord &Record) {
957   auto InstanceMethod = serializeAPIRecord(Record);
958   if (!InstanceMethod)
959     return;
960 
961   Symbols.emplace_back(std::move(*InstanceMethod));
962   serializeRelationship(RelationshipKind::MemberOf, Record,
963                         Record.ParentInformation.ParentRecord);
964 }
965 
966 void SymbolGraphSerializer::visitCXXStaticMethodRecord(
967     const CXXStaticMethodRecord &Record) {
968   auto StaticMethod = serializeAPIRecord(Record);
969   if (!StaticMethod)
970     return;
971 
972   Symbols.emplace_back(std::move(*StaticMethod));
973   serializeRelationship(RelationshipKind::MemberOf, Record,
974                         Record.ParentInformation.ParentRecord);
975 }
976 
977 void SymbolGraphSerializer::visitMethodTemplateRecord(
978     const CXXMethodTemplateRecord &Record) {
979   if (!ShouldRecurse)
980     // Ignore child symbols
981     return;
982   auto MethodTemplate = serializeAPIRecord(Record);
983   if (!MethodTemplate)
984     return;
985   Symbols.emplace_back(std::move(*MethodTemplate));
986   serializeRelationship(RelationshipKind::MemberOf, Record,
987                         Record.ParentInformation.ParentRecord);
988 }
989 
990 void SymbolGraphSerializer::visitMethodTemplateSpecializationRecord(
991     const CXXMethodTemplateSpecializationRecord &Record) {
992   if (!ShouldRecurse)
993     // Ignore child symbols
994     return;
995   auto MethodTemplateSpecialization = serializeAPIRecord(Record);
996   if (!MethodTemplateSpecialization)
997     return;
998   Symbols.emplace_back(std::move(*MethodTemplateSpecialization));
999   serializeRelationship(RelationshipKind::MemberOf, Record,
1000                         Record.ParentInformation.ParentRecord);
1001 }
1002 
1003 void SymbolGraphSerializer::visitCXXFieldRecord(const CXXFieldRecord &Record) {
1004   if (!ShouldRecurse)
1005     return;
1006   auto CXXField = serializeAPIRecord(Record);
1007   if (!CXXField)
1008     return;
1009   Symbols.emplace_back(std::move(*CXXField));
1010   serializeRelationship(RelationshipKind::MemberOf, Record,
1011                         Record.ParentInformation.ParentRecord);
1012 }
1013 
1014 void SymbolGraphSerializer::visitCXXFieldTemplateRecord(
1015     const CXXFieldTemplateRecord &Record) {
1016   if (!ShouldRecurse)
1017     // Ignore child symbols
1018     return;
1019   auto CXXFieldTemplate = serializeAPIRecord(Record);
1020   if (!CXXFieldTemplate)
1021     return;
1022   Symbols.emplace_back(std::move(*CXXFieldTemplate));
1023   serializeRelationship(RelationshipKind::MemberOf, Record,
1024                         Record.ParentInformation.ParentRecord);
1025 }
1026 
1027 void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) {
1028   auto Concept = serializeAPIRecord(Record);
1029   if (!Concept)
1030     return;
1031 
1032   Symbols.emplace_back(std::move(*Concept));
1033 }
1034 
1035 void SymbolGraphSerializer::visitGlobalVariableTemplateRecord(
1036     const GlobalVariableTemplateRecord &Record) {
1037   auto GlobalVariableTemplate = serializeAPIRecord(Record);
1038   if (!GlobalVariableTemplate)
1039     return;
1040   Symbols.emplace_back(std::move(*GlobalVariableTemplate));
1041 }
1042 
1043 void SymbolGraphSerializer::visitGlobalVariableTemplateSpecializationRecord(
1044     const GlobalVariableTemplateSpecializationRecord &Record) {
1045   auto GlobalVariableTemplateSpecialization = serializeAPIRecord(Record);
1046   if (!GlobalVariableTemplateSpecialization)
1047     return;
1048   Symbols.emplace_back(std::move(*GlobalVariableTemplateSpecialization));
1049 }
1050 
1051 void SymbolGraphSerializer::
1052     visitGlobalVariableTemplatePartialSpecializationRecord(
1053         const GlobalVariableTemplatePartialSpecializationRecord &Record) {
1054   auto GlobalVariableTemplatePartialSpecialization = serializeAPIRecord(Record);
1055   if (!GlobalVariableTemplatePartialSpecialization)
1056     return;
1057   Symbols.emplace_back(std::move(*GlobalVariableTemplatePartialSpecialization));
1058 }
1059 
1060 void SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(
1061     const GlobalFunctionTemplateRecord &Record) {
1062   auto GlobalFunctionTemplate = serializeAPIRecord(Record);
1063   if (!GlobalFunctionTemplate)
1064     return;
1065   Symbols.emplace_back(std::move(*GlobalFunctionTemplate));
1066 }
1067 
1068 void SymbolGraphSerializer::visitGlobalFunctionTemplateSpecializationRecord(
1069     const GlobalFunctionTemplateSpecializationRecord &Record) {
1070   auto GlobalFunctionTemplateSpecialization = serializeAPIRecord(Record);
1071   if (!GlobalFunctionTemplateSpecialization)
1072     return;
1073   Symbols.emplace_back(std::move(*GlobalFunctionTemplateSpecialization));
1074 }
1075 
1076 void SymbolGraphSerializer::visitObjCContainerRecord(
1077     const ObjCContainerRecord &Record) {
1078   auto ObjCContainer = serializeAPIRecord(Record);
1079   if (!ObjCContainer)
1080     return;
1081 
1082   Symbols.emplace_back(std::move(*ObjCContainer));
1083 
1084   serializeMembers(Record, Record.Ivars);
1085   serializeMembers(Record, Record.Methods);
1086   serializeMembers(Record, Record.Properties);
1087 
1088   for (const auto &Protocol : Record.Protocols)
1089     // Record that Record conforms to Protocol.
1090     serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
1091 
1092   if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
1093     if (!ObjCInterface->SuperClass.empty())
1094       // If Record is an Objective-C interface record and it has a super class,
1095       // record that Record is inherited from SuperClass.
1096       serializeRelationship(RelationshipKind::InheritsFrom, Record,
1097                             ObjCInterface->SuperClass);
1098 
1099     // Members of categories extending an interface are serialized as members of
1100     // the interface.
1101     for (const auto *Category : ObjCInterface->Categories) {
1102       serializeMembers(Record, Category->Ivars);
1103       serializeMembers(Record, Category->Methods);
1104       serializeMembers(Record, Category->Properties);
1105 
1106       // Surface the protocols of the category to the interface.
1107       for (const auto &Protocol : Category->Protocols)
1108         serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
1109     }
1110   }
1111 }
1112 
1113 void SymbolGraphSerializer::visitObjCCategoryRecord(
1114     const ObjCCategoryRecord &Record) {
1115   if (!Record.IsFromExternalModule)
1116     return;
1117 
1118   // Check if the current Category' parent has been visited before, if so skip.
1119   if (!visitedCategories.contains(Record.Interface.Name)) {
1120     visitedCategories.insert(Record.Interface.Name);
1121     Object Obj;
1122     serializeObject(Obj, "identifier",
1123                     serializeIdentifier(Record, API.getLanguage()));
1124     serializeObject(Obj, "kind",
1125                     serializeSymbolKind(APIRecord::RK_ObjCCategoryModule,
1126                                         API.getLanguage()));
1127     Obj["accessLevel"] = "public";
1128     Symbols.emplace_back(std::move(Obj));
1129   }
1130 
1131   Object Relationship;
1132   Relationship["source"] = Record.USR;
1133   Relationship["target"] = Record.Interface.USR;
1134   Relationship["targetFallback"] = Record.Interface.Name;
1135   Relationship["kind"] = getRelationshipString(RelationshipKind::ExtensionTo);
1136   Relationships.emplace_back(std::move(Relationship));
1137 
1138   auto ObjCCategory = serializeAPIRecord(Record);
1139 
1140   if (!ObjCCategory)
1141     return;
1142 
1143   Symbols.emplace_back(std::move(*ObjCCategory));
1144   serializeMembers(Record, Record.Methods);
1145   serializeMembers(Record, Record.Properties);
1146 
1147   // Surface the protocols of the category to the interface.
1148   for (const auto &Protocol : Record.Protocols)
1149     serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
1150 }
1151 
1152 void SymbolGraphSerializer::visitMacroDefinitionRecord(
1153     const MacroDefinitionRecord &Record) {
1154   auto Macro = serializeAPIRecord(Record);
1155 
1156   if (!Macro)
1157     return;
1158 
1159   Symbols.emplace_back(std::move(*Macro));
1160 }
1161 
1162 void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
1163   switch (Record->getKind()) {
1164   case APIRecord::RK_Unknown:
1165     llvm_unreachable("Records should have a known kind!");
1166   case APIRecord::RK_GlobalFunction:
1167     visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
1168     break;
1169   case APIRecord::RK_GlobalVariable:
1170     visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
1171     break;
1172   case APIRecord::RK_Enum:
1173     visitEnumRecord(*cast<EnumRecord>(Record));
1174     break;
1175   case APIRecord::RK_Struct:
1176     visitStructRecord(*cast<StructRecord>(Record));
1177     break;
1178   case APIRecord::RK_StaticField:
1179     visitStaticFieldRecord(*cast<StaticFieldRecord>(Record));
1180     break;
1181   case APIRecord::RK_CXXClass:
1182     visitCXXClassRecord(*cast<CXXClassRecord>(Record));
1183     break;
1184   case APIRecord::RK_ObjCInterface:
1185     visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
1186     break;
1187   case APIRecord::RK_ObjCProtocol:
1188     visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
1189     break;
1190   case APIRecord::RK_ObjCCategory:
1191     visitObjCCategoryRecord(*cast<ObjCCategoryRecord>(Record));
1192     break;
1193   case APIRecord::RK_MacroDefinition:
1194     visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
1195     break;
1196   case APIRecord::RK_Typedef:
1197     visitTypedefRecord(*cast<TypedefRecord>(Record));
1198     break;
1199   default:
1200     if (auto Obj = serializeAPIRecord(*Record)) {
1201       Symbols.emplace_back(std::move(*Obj));
1202       auto &ParentInformation = Record->ParentInformation;
1203       if (!ParentInformation.empty())
1204         serializeRelationship(RelationshipKind::MemberOf, *Record,
1205                               *ParentInformation.ParentRecord);
1206     }
1207     break;
1208   }
1209 }
1210 
1211 void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) {
1212   // Typedefs of anonymous types have their entries unified with the underlying
1213   // type.
1214   bool ShouldDrop = Record.UnderlyingType.Name.empty();
1215   // enums declared with `NS_OPTION` have a named enum and a named typedef, with
1216   // the same name
1217   ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
1218   if (ShouldDrop)
1219     return;
1220 
1221   auto Typedef = serializeAPIRecord(Record);
1222   if (!Typedef)
1223     return;
1224 
1225   (*Typedef)["type"] = Record.UnderlyingType.USR;
1226 
1227   Symbols.emplace_back(std::move(*Typedef));
1228 }
1229 
1230 Object SymbolGraphSerializer::serialize() {
1231   traverseAPISet();
1232   return serializeCurrentGraph();
1233 }
1234 
1235 Object SymbolGraphSerializer::serializeCurrentGraph() {
1236   Object Root;
1237   serializeObject(Root, "metadata", serializeMetadata());
1238   serializeObject(Root, "module", serializeModule());
1239 
1240   Root["symbols"] = std::move(Symbols);
1241   Root["relationships"] = std::move(Relationships);
1242 
1243   return Root;
1244 }
1245 
1246 void SymbolGraphSerializer::serialize(raw_ostream &os) {
1247   Object root = serialize();
1248   if (Options.Compact)
1249     os << formatv("{0}", Value(std::move(root))) << "\n";
1250   else
1251     os << formatv("{0:2}", Value(std::move(root))) << "\n";
1252 }
1253 
1254 std::optional<Object>
1255 SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
1256                                                 const APISet &API) {
1257   APIRecord *Record = API.findRecordForUSR(USR);
1258   if (!Record)
1259     return {};
1260 
1261   Object Root;
1262   APIIgnoresList EmptyIgnores;
1263   SymbolGraphSerializer Serializer(API, EmptyIgnores,
1264                                    /*Options.Compact*/ {true},
1265                                    /*ShouldRecurse*/ false);
1266   Serializer.serializeSingleRecord(Record);
1267   serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
1268 
1269   Language Lang = API.getLanguage();
1270   serializeArray(Root, "parentContexts",
1271                  generateParentContexts(*Record, API, Lang));
1272 
1273   Array RelatedSymbols;
1274 
1275   for (const auto &Fragment : Record->Declaration.getFragments()) {
1276     // If we don't have a USR there isn't much we can do.
1277     if (Fragment.PreciseIdentifier.empty())
1278       continue;
1279 
1280     APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
1281 
1282     // If we can't find the record let's skip.
1283     if (!RelatedRecord)
1284       continue;
1285 
1286     Object RelatedSymbol;
1287     RelatedSymbol["usr"] = RelatedRecord->USR;
1288     RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
1289     // TODO: once we record this properly let's serialize it right.
1290     RelatedSymbol["accessLevel"] = "public";
1291     RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
1292     RelatedSymbol["moduleName"] = API.ProductName;
1293     RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
1294 
1295     serializeArray(RelatedSymbol, "parentContexts",
1296                    generateParentContexts(*RelatedRecord, API, Lang));
1297     RelatedSymbols.push_back(std::move(RelatedSymbol));
1298   }
1299 
1300   serializeArray(Root, "relatedSymbols", RelatedSymbols);
1301   return Root;
1302 }
1303