xref: /llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp (revision 8b76b44e46ac5b4efbf8cb3702f09d353af2a112)
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();
113   SourcePosition["character"] = Loc.getColumn();
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, "introducedVersion",
181                       serializeSemanticVersion(AvailInfo.Introduced));
182       serializeObject(Availability, "deprecatedVersion",
183                       serializeSemanticVersion(AvailInfo.Deprecated));
184       serializeObject(Availability, "obsoletedVersion",
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 
203   // Unsupported language currently
204   case Language::ObjCXX:
205   case Language::OpenCL:
206   case Language::OpenCLCXX:
207   case Language::CUDA:
208   case Language::RenderScript:
209   case Language::HIP:
210   case Language::HLSL:
211 
212   // Languages that the frontend cannot parse and compile
213   case Language::Unknown:
214   case Language::Asm:
215   case Language::LLVM_IR:
216     llvm_unreachable("Unsupported language kind");
217   }
218 
219   llvm_unreachable("Unhandled language kind");
220 }
221 
222 /// Serialize the identifier object as specified by the Symbol Graph format.
223 ///
224 /// The identifier property of a symbol contains the USR for precise and unique
225 /// references, and the interface language name.
226 Object serializeIdentifier(const APIRecord &Record, Language Lang) {
227   Object Identifier;
228   Identifier["precise"] = Record.USR;
229   Identifier["interfaceLanguage"] = getLanguageName(Lang);
230 
231   return Identifier;
232 }
233 
234 /// Serialize the documentation comments attached to a symbol, as specified by
235 /// the Symbol Graph format.
236 ///
237 /// The Symbol Graph \c docComment object contains an array of lines. Each line
238 /// represents one line of striped documentation comment, with source range
239 /// information.
240 /// e.g.
241 /// \code
242 ///   /// This is a documentation comment
243 ///       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'  First line.
244 ///   ///     with multiple lines.
245 ///       ^~~~~~~~~~~~~~~~~~~~~~~'         Second line.
246 /// \endcode
247 ///
248 /// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
249 /// the formatted lines.
250 std::optional<Object> serializeDocComment(const DocComment &Comment) {
251   if (Comment.empty())
252     return std::nullopt;
253 
254   Object DocComment;
255   Array LinesArray;
256   for (const auto &CommentLine : Comment) {
257     Object Line;
258     Line["text"] = CommentLine.Text;
259     serializeObject(Line, "range",
260                     serializeSourceRange(CommentLine.Begin, CommentLine.End));
261     LinesArray.emplace_back(std::move(Line));
262   }
263   serializeArray(DocComment, "lines", LinesArray);
264 
265   return DocComment;
266 }
267 
268 /// Serialize the declaration fragments of a symbol.
269 ///
270 /// The Symbol Graph declaration fragments is an array of tagged important
271 /// parts of a symbol's declaration. The fragments sequence can be joined to
272 /// form spans of declaration text, with attached information useful for
273 /// purposes like syntax-highlighting etc. For example:
274 /// \code
275 ///   const int pi; -> "declarationFragments" : [
276 ///                      {
277 ///                        "kind" : "keyword",
278 ///                        "spelling" : "const"
279 ///                      },
280 ///                      {
281 ///                        "kind" : "text",
282 ///                        "spelling" : " "
283 ///                      },
284 ///                      {
285 ///                        "kind" : "typeIdentifier",
286 ///                        "preciseIdentifier" : "c:I",
287 ///                        "spelling" : "int"
288 ///                      },
289 ///                      {
290 ///                        "kind" : "text",
291 ///                        "spelling" : " "
292 ///                      },
293 ///                      {
294 ///                        "kind" : "identifier",
295 ///                        "spelling" : "pi"
296 ///                      }
297 ///                    ]
298 /// \endcode
299 ///
300 /// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
301 /// formatted declaration fragments array.
302 std::optional<Array>
303 serializeDeclarationFragments(const DeclarationFragments &DF) {
304   if (DF.getFragments().empty())
305     return std::nullopt;
306 
307   Array Fragments;
308   for (const auto &F : DF.getFragments()) {
309     Object Fragment;
310     Fragment["spelling"] = F.Spelling;
311     Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
312     if (!F.PreciseIdentifier.empty())
313       Fragment["preciseIdentifier"] = F.PreciseIdentifier;
314     Fragments.emplace_back(std::move(Fragment));
315   }
316 
317   return Fragments;
318 }
319 
320 /// Serialize the \c names field of a symbol as specified by the Symbol Graph
321 /// format.
322 ///
323 /// The Symbol Graph names field contains multiple representations of a symbol
324 /// that can be used for different applications:
325 ///   - \c title : The simple declared name of the symbol;
326 ///   - \c subHeading : An array of declaration fragments that provides tags,
327 ///     and potentially more tokens (for example the \c +/- symbol for
328 ///     Objective-C methods). Can be used as sub-headings for documentation.
329 Object serializeNames(const APIRecord &Record) {
330   Object Names;
331   Names["title"] = Record.Name;
332   serializeArray(Names, "subHeading",
333                  serializeDeclarationFragments(Record.SubHeading));
334   DeclarationFragments NavigatorFragments;
335   NavigatorFragments.append(Record.Name,
336                             DeclarationFragments::FragmentKind::Identifier,
337                             /*PreciseIdentifier*/ "");
338   serializeArray(Names, "navigator",
339                  serializeDeclarationFragments(NavigatorFragments));
340 
341   return Names;
342 }
343 
344 Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
345   auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
346     return (getLanguageName(Lang) + "." + S).str();
347   };
348 
349   Object Kind;
350   switch (RK) {
351   case APIRecord::RK_Unknown:
352     llvm_unreachable("Records should have an explicit kind");
353     break;
354   case APIRecord::RK_GlobalFunction:
355     Kind["identifier"] = AddLangPrefix("func");
356     Kind["displayName"] = "Function";
357     break;
358   case APIRecord::RK_GlobalVariable:
359     Kind["identifier"] = AddLangPrefix("var");
360     Kind["displayName"] = "Global Variable";
361     break;
362   case APIRecord::RK_EnumConstant:
363     Kind["identifier"] = AddLangPrefix("enum.case");
364     Kind["displayName"] = "Enumeration Case";
365     break;
366   case APIRecord::RK_Enum:
367     Kind["identifier"] = AddLangPrefix("enum");
368     Kind["displayName"] = "Enumeration";
369     break;
370   case APIRecord::RK_StructField:
371     Kind["identifier"] = AddLangPrefix("property");
372     Kind["displayName"] = "Instance Property";
373     break;
374   case APIRecord::RK_Struct:
375     Kind["identifier"] = AddLangPrefix("struct");
376     Kind["displayName"] = "Structure";
377     break;
378   case APIRecord::RK_CXXField:
379     Kind["identifier"] = AddLangPrefix("property");
380     Kind["displayName"] = "Instance Property";
381     break;
382   case APIRecord::RK_Union:
383     Kind["identifier"] = AddLangPrefix("union");
384     Kind["displayName"] = "Union";
385     break;
386   case APIRecord::RK_StaticField:
387     Kind["identifier"] = AddLangPrefix("type.property");
388     Kind["displayName"] = "Type Property";
389     break;
390   case APIRecord::RK_CXXClass:
391     Kind["identifier"] = AddLangPrefix("class");
392     Kind["displayName"] = "Class";
393     break;
394   case APIRecord::RK_CXXStaticMethod:
395     Kind["identifier"] = AddLangPrefix("type.method");
396     Kind["displayName"] = "Static Method";
397     break;
398   case APIRecord::RK_CXXInstanceMethod:
399     Kind["identifier"] = AddLangPrefix("method");
400     Kind["displayName"] = "Instance Method";
401     break;
402   case APIRecord::RK_CXXConstructorMethod:
403     Kind["identifier"] = AddLangPrefix("method");
404     Kind["displayName"] = "Constructor";
405     break;
406   case APIRecord::RK_CXXDestructorMethod:
407     Kind["identifier"] = AddLangPrefix("method");
408     Kind["displayName"] = "Destructor";
409     break;
410   case APIRecord::RK_ObjCIvar:
411     Kind["identifier"] = AddLangPrefix("ivar");
412     Kind["displayName"] = "Instance Variable";
413     break;
414   case APIRecord::RK_ObjCInstanceMethod:
415     Kind["identifier"] = AddLangPrefix("method");
416     Kind["displayName"] = "Instance Method";
417     break;
418   case APIRecord::RK_ObjCClassMethod:
419     Kind["identifier"] = AddLangPrefix("type.method");
420     Kind["displayName"] = "Type Method";
421     break;
422   case APIRecord::RK_ObjCInstanceProperty:
423     Kind["identifier"] = AddLangPrefix("property");
424     Kind["displayName"] = "Instance Property";
425     break;
426   case APIRecord::RK_ObjCClassProperty:
427     Kind["identifier"] = AddLangPrefix("type.property");
428     Kind["displayName"] = "Type Property";
429     break;
430   case APIRecord::RK_ObjCInterface:
431     Kind["identifier"] = AddLangPrefix("class");
432     Kind["displayName"] = "Class";
433     break;
434   case APIRecord::RK_ObjCCategory:
435     // We don't serialize out standalone Objective-C category symbols yet.
436     llvm_unreachable("Serializing standalone Objective-C category symbols is "
437                      "not supported.");
438     break;
439   case APIRecord::RK_ObjCProtocol:
440     Kind["identifier"] = AddLangPrefix("protocol");
441     Kind["displayName"] = "Protocol";
442     break;
443   case APIRecord::RK_MacroDefinition:
444     Kind["identifier"] = AddLangPrefix("macro");
445     Kind["displayName"] = "Macro";
446     break;
447   case APIRecord::RK_Typedef:
448     Kind["identifier"] = AddLangPrefix("typealias");
449     Kind["displayName"] = "Type Alias";
450     break;
451   }
452 
453   return Kind;
454 }
455 
456 /// Serialize the symbol kind information.
457 ///
458 /// The Symbol Graph symbol kind property contains a shorthand \c identifier
459 /// which is prefixed by the source language name, useful for tooling to parse
460 /// the kind, and a \c displayName for rendering human-readable names.
461 Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
462   return serializeSymbolKind(Record.getKind(), Lang);
463 }
464 
465 template <typename RecordTy>
466 std::optional<Object>
467 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
468   const auto &FS = Record.Signature;
469   if (FS.empty())
470     return std::nullopt;
471 
472   Object Signature;
473   serializeArray(Signature, "returns",
474                  serializeDeclarationFragments(FS.getReturnType()));
475 
476   Array Parameters;
477   for (const auto &P : FS.getParameters()) {
478     Object Parameter;
479     Parameter["name"] = P.Name;
480     serializeArray(Parameter, "declarationFragments",
481                    serializeDeclarationFragments(P.Fragments));
482     Parameters.emplace_back(std::move(Parameter));
483   }
484 
485   if (!Parameters.empty())
486     Signature["parameters"] = std::move(Parameters);
487 
488   return Signature;
489 }
490 
491 template <typename RecordTy>
492 std::optional<Object>
493 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
494   return std::nullopt;
495 }
496 
497 /// Serialize the function signature field, as specified by the
498 /// Symbol Graph format.
499 ///
500 /// The Symbol Graph function signature property contains two arrays.
501 ///   - The \c returns array is the declaration fragments of the return type;
502 ///   - The \c parameters array contains names and declaration fragments of the
503 ///     parameters.
504 ///
505 /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
506 /// formatted function signature.
507 template <typename RecordTy>
508 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
509   serializeObject(Paren, "functionSignature",
510                   serializeFunctionSignatureMixinImpl(
511                       Record, has_function_signature<RecordTy>()));
512 }
513 
514 template <typename RecordTy>
515 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
516                                                     std::true_type) {
517   const auto &AccessControl = Record.Access;
518   std::string Access;
519   if (AccessControl.empty())
520     return std::nullopt;
521   Access = AccessControl.getAccess();
522   return Access;
523 }
524 
525 template <typename RecordTy>
526 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
527                                                     std::false_type) {
528   return std::nullopt;
529 }
530 
531 template <typename RecordTy>
532 void serializeAccessMixin(Object &Paren, const RecordTy &Record) {
533   auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>());
534   if (!accessLevel.has_value())
535     accessLevel = "public";
536   serializeString(Paren, "accessLevel", accessLevel);
537 }
538 
539 struct PathComponent {
540   StringRef USR;
541   StringRef Name;
542   APIRecord::RecordKind Kind;
543 
544   PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
545       : USR(USR), Name(Name), Kind(Kind) {}
546 };
547 
548 template <typename RecordTy>
549 bool generatePathComponents(
550     const RecordTy &Record, const APISet &API,
551     function_ref<void(const PathComponent &)> ComponentTransformer) {
552   SmallVector<PathComponent, 4> ReverseComponenents;
553   ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
554   const auto *CurrentParent = &Record.ParentInformation;
555   bool FailedToFindParent = false;
556   while (CurrentParent && !CurrentParent->empty()) {
557     PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
558                                          CurrentParent->ParentName,
559                                          CurrentParent->ParentKind);
560 
561     auto *ParentRecord = CurrentParent->ParentRecord;
562     // Slow path if we don't have a direct reference to the ParentRecord
563     if (!ParentRecord)
564       ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
565 
566     // If the parent is a category then we need to pretend this belongs to the
567     // associated interface.
568     if (auto *CategoryRecord =
569             dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
570       ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
571       CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
572                                              CategoryRecord->Interface.Name,
573                                              APIRecord::RK_ObjCInterface);
574     }
575 
576     // The parent record doesn't exist which means the symbol shouldn't be
577     // treated as part of the current product.
578     if (!ParentRecord) {
579       FailedToFindParent = true;
580       break;
581     }
582 
583     ReverseComponenents.push_back(std::move(CurrentParentComponent));
584     CurrentParent = &ParentRecord->ParentInformation;
585   }
586 
587   for (const auto &PC : reverse(ReverseComponenents))
588     ComponentTransformer(PC);
589 
590   return FailedToFindParent;
591 }
592 
593 Object serializeParentContext(const PathComponent &PC, Language Lang) {
594   Object ParentContextElem;
595   ParentContextElem["usr"] = PC.USR;
596   ParentContextElem["name"] = PC.Name;
597   ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
598   return ParentContextElem;
599 }
600 
601 template <typename RecordTy>
602 Array generateParentContexts(const RecordTy &Record, const APISet &API,
603                              Language Lang) {
604   Array ParentContexts;
605   generatePathComponents(
606       Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
607         ParentContexts.push_back(serializeParentContext(PC, Lang));
608       });
609 
610   return ParentContexts;
611 }
612 } // namespace
613 
614 /// Defines the format version emitted by SymbolGraphSerializer.
615 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
616 
617 Object SymbolGraphSerializer::serializeMetadata() const {
618   Object Metadata;
619   serializeObject(Metadata, "formatVersion",
620                   serializeSemanticVersion(FormatVersion));
621   Metadata["generator"] = clang::getClangFullVersion();
622   return Metadata;
623 }
624 
625 Object SymbolGraphSerializer::serializeModule() const {
626   Object Module;
627   // The user is expected to always pass `--product-name=` on the command line
628   // to populate this field.
629   Module["name"] = API.ProductName;
630   serializeObject(Module, "platform", serializePlatform(API.getTarget()));
631   return Module;
632 }
633 
634 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
635   // Skip explicitly ignored symbols.
636   if (IgnoresList.shouldIgnore(Record.Name))
637     return true;
638 
639   // Skip unconditionally unavailable symbols
640   if (Record.Availabilities.isUnconditionallyUnavailable())
641     return true;
642 
643   // Filter out symbols prefixed with an underscored as they are understood to
644   // be symbols clients should not use.
645   if (Record.Name.startswith("_"))
646     return true;
647 
648   return false;
649 }
650 
651 template <typename RecordTy>
652 std::optional<Object>
653 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
654   if (shouldSkip(Record))
655     return std::nullopt;
656 
657   Object Obj;
658   serializeObject(Obj, "identifier",
659                   serializeIdentifier(Record, API.getLanguage()));
660   serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
661   serializeObject(Obj, "names", serializeNames(Record));
662   serializeObject(
663       Obj, "location",
664       serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
665   serializeArray(Obj, "availability",
666                  serializeAvailability(Record.Availabilities));
667   serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
668   serializeArray(Obj, "declarationFragments",
669                  serializeDeclarationFragments(Record.Declaration));
670   SmallVector<StringRef, 4> PathComponentsNames;
671   // If this returns true it indicates that we couldn't find a symbol in the
672   // hierarchy.
673   if (generatePathComponents(Record, API,
674                              [&PathComponentsNames](const PathComponent &PC) {
675                                PathComponentsNames.push_back(PC.Name);
676                              }))
677     return {};
678 
679   serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
680 
681   serializeFunctionSignatureMixin(Obj, Record);
682   serializeAccessMixin(Obj, Record);
683 
684   return Obj;
685 }
686 
687 template <typename MemberTy>
688 void SymbolGraphSerializer::serializeMembers(
689     const APIRecord &Record,
690     const SmallVector<std::unique_ptr<MemberTy>> &Members) {
691   // Members should not be serialized if we aren't recursing.
692   if (!ShouldRecurse)
693     return;
694   for (const auto &Member : Members) {
695     auto MemberRecord = serializeAPIRecord(*Member);
696     if (!MemberRecord)
697       continue;
698 
699     Symbols.emplace_back(std::move(*MemberRecord));
700     serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
701   }
702 }
703 
704 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
705   switch (Kind) {
706   case RelationshipKind::MemberOf:
707     return "memberOf";
708   case RelationshipKind::InheritsFrom:
709     return "inheritsFrom";
710   case RelationshipKind::ConformsTo:
711     return "conformsTo";
712   }
713   llvm_unreachable("Unhandled relationship kind");
714 }
715 
716 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
717                                                   SymbolReference Source,
718                                                   SymbolReference Target) {
719   Object Relationship;
720   Relationship["source"] = Source.USR;
721   Relationship["target"] = Target.USR;
722   Relationship["targetFallback"] = Target.Name;
723   Relationship["kind"] = getRelationshipString(Kind);
724 
725   Relationships.emplace_back(std::move(Relationship));
726 }
727 
728 void SymbolGraphSerializer::visitGlobalFunctionRecord(
729     const GlobalFunctionRecord &Record) {
730   auto Obj = serializeAPIRecord(Record);
731   if (!Obj)
732     return;
733 
734   Symbols.emplace_back(std::move(*Obj));
735 }
736 
737 void SymbolGraphSerializer::visitGlobalVariableRecord(
738     const GlobalVariableRecord &Record) {
739   auto Obj = serializeAPIRecord(Record);
740   if (!Obj)
741     return;
742 
743   Symbols.emplace_back(std::move(*Obj));
744 }
745 
746 void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) {
747   auto Enum = serializeAPIRecord(Record);
748   if (!Enum)
749     return;
750 
751   Symbols.emplace_back(std::move(*Enum));
752   serializeMembers(Record, Record.Constants);
753 }
754 
755 void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) {
756   auto Struct = serializeAPIRecord(Record);
757   if (!Struct)
758     return;
759 
760   Symbols.emplace_back(std::move(*Struct));
761   serializeMembers(Record, Record.Fields);
762 }
763 
764 void SymbolGraphSerializer::visitStaticFieldRecord(
765     const StaticFieldRecord &Record) {
766   auto StaticField = serializeAPIRecord(Record);
767   if (!StaticField)
768     return;
769   Symbols.emplace_back(std::move(*StaticField));
770   serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context);
771 }
772 
773 void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
774   auto Class = serializeAPIRecord(Record);
775   if (!Class)
776     return;
777 
778   Symbols.emplace_back(std::move(*Class));
779   serializeMembers(Record, Record.Fields);
780   serializeMembers(Record, Record.Methods);
781 
782   for (const auto Base : Record.Bases)
783     serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
784 }
785 
786 void SymbolGraphSerializer::visitObjCContainerRecord(
787     const ObjCContainerRecord &Record) {
788   auto ObjCContainer = serializeAPIRecord(Record);
789   if (!ObjCContainer)
790     return;
791 
792   Symbols.emplace_back(std::move(*ObjCContainer));
793 
794   serializeMembers(Record, Record.Ivars);
795   serializeMembers(Record, Record.Methods);
796   serializeMembers(Record, Record.Properties);
797 
798   for (const auto &Protocol : Record.Protocols)
799     // Record that Record conforms to Protocol.
800     serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
801 
802   if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
803     if (!ObjCInterface->SuperClass.empty())
804       // If Record is an Objective-C interface record and it has a super class,
805       // record that Record is inherited from SuperClass.
806       serializeRelationship(RelationshipKind::InheritsFrom, Record,
807                             ObjCInterface->SuperClass);
808 
809     // Members of categories extending an interface are serialized as members of
810     // the interface.
811     for (const auto *Category : ObjCInterface->Categories) {
812       serializeMembers(Record, Category->Ivars);
813       serializeMembers(Record, Category->Methods);
814       serializeMembers(Record, Category->Properties);
815 
816       // Surface the protocols of the category to the interface.
817       for (const auto &Protocol : Category->Protocols)
818         serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
819     }
820   }
821 }
822 
823 void SymbolGraphSerializer::visitMacroDefinitionRecord(
824     const MacroDefinitionRecord &Record) {
825   auto Macro = serializeAPIRecord(Record);
826 
827   if (!Macro)
828     return;
829 
830   Symbols.emplace_back(std::move(*Macro));
831 }
832 
833 void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
834   switch (Record->getKind()) {
835   case APIRecord::RK_Unknown:
836     llvm_unreachable("Records should have a known kind!");
837   case APIRecord::RK_GlobalFunction:
838     visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
839     break;
840   case APIRecord::RK_GlobalVariable:
841     visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
842     break;
843   case APIRecord::RK_Enum:
844     visitEnumRecord(*cast<EnumRecord>(Record));
845     break;
846   case APIRecord::RK_Struct:
847     visitStructRecord(*cast<StructRecord>(Record));
848     break;
849   case APIRecord::RK_StaticField:
850     visitStaticFieldRecord(*cast<StaticFieldRecord>(Record));
851     break;
852   case APIRecord::RK_CXXClass:
853     visitCXXClassRecord(*cast<CXXClassRecord>(Record));
854     break;
855   case APIRecord::RK_ObjCInterface:
856     visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
857     break;
858   case APIRecord::RK_ObjCProtocol:
859     visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
860     break;
861   case APIRecord::RK_MacroDefinition:
862     visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
863     break;
864   case APIRecord::RK_Typedef:
865     visitTypedefRecord(*cast<TypedefRecord>(Record));
866     break;
867   default:
868     if (auto Obj = serializeAPIRecord(*Record)) {
869       Symbols.emplace_back(std::move(*Obj));
870       auto &ParentInformation = Record->ParentInformation;
871       if (!ParentInformation.empty())
872         serializeRelationship(RelationshipKind::MemberOf, *Record,
873                               *ParentInformation.ParentRecord);
874     }
875     break;
876   }
877 }
878 
879 void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) {
880   // Typedefs of anonymous types have their entries unified with the underlying
881   // type.
882   bool ShouldDrop = Record.UnderlyingType.Name.empty();
883   // enums declared with `NS_OPTION` have a named enum and a named typedef, with
884   // the same name
885   ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
886   if (ShouldDrop)
887     return;
888 
889   auto Typedef = serializeAPIRecord(Record);
890   if (!Typedef)
891     return;
892 
893   (*Typedef)["type"] = Record.UnderlyingType.USR;
894 
895   Symbols.emplace_back(std::move(*Typedef));
896 }
897 
898 Object SymbolGraphSerializer::serialize() {
899   traverseAPISet();
900   return serializeCurrentGraph();
901 }
902 
903 Object SymbolGraphSerializer::serializeCurrentGraph() {
904   Object Root;
905   serializeObject(Root, "metadata", serializeMetadata());
906   serializeObject(Root, "module", serializeModule());
907 
908   Root["symbols"] = std::move(Symbols);
909   Root["relationships"] = std::move(Relationships);
910 
911   return Root;
912 }
913 
914 void SymbolGraphSerializer::serialize(raw_ostream &os) {
915   Object root = serialize();
916   if (Options.Compact)
917     os << formatv("{0}", Value(std::move(root))) << "\n";
918   else
919     os << formatv("{0:2}", Value(std::move(root))) << "\n";
920 }
921 
922 std::optional<Object>
923 SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
924                                                 const APISet &API) {
925   APIRecord *Record = API.findRecordForUSR(USR);
926   if (!Record)
927     return {};
928 
929   if (isa<ObjCCategoryRecord>(Record))
930     return {};
931 
932   Object Root;
933   APIIgnoresList EmptyIgnores;
934   SymbolGraphSerializer Serializer(API, EmptyIgnores,
935                                    /*Options.Compact*/ {true},
936                                    /*ShouldRecurse*/ false);
937   Serializer.serializeSingleRecord(Record);
938   serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
939 
940   Language Lang = API.getLanguage();
941   serializeArray(Root, "parentContexts",
942                  generateParentContexts(*Record, API, Lang));
943 
944   Array RelatedSymbols;
945 
946   for (const auto &Fragment : Record->Declaration.getFragments()) {
947     // If we don't have a USR there isn't much we can do.
948     if (Fragment.PreciseIdentifier.empty())
949       continue;
950 
951     APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
952 
953     // If we can't find the record let's skip.
954     if (!RelatedRecord)
955       continue;
956 
957     Object RelatedSymbol;
958     RelatedSymbol["usr"] = RelatedRecord->USR;
959     RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
960     // TODO: once we record this properly let's serialize it right.
961     RelatedSymbol["accessLevel"] = "public";
962     RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
963     RelatedSymbol["moduleName"] = API.ProductName;
964     RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
965 
966     serializeArray(RelatedSymbol, "parentContexts",
967                    generateParentContexts(*RelatedRecord, API, Lang));
968     RelatedSymbols.push_back(std::move(RelatedSymbol));
969   }
970 
971   serializeArray(Root, "relatedSymbols", RelatedSymbols);
972   return Root;
973 }
974