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