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