xref: /freebsd-src/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
181ad6265SDimitry Andric //===- ExtractAPI/Serialization/SymbolGraphSerializer.cpp -------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric ///
981ad6265SDimitry Andric /// \file
1081ad6265SDimitry Andric /// This file implements the SymbolGraphSerializer.
1181ad6265SDimitry Andric ///
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric 
1481ad6265SDimitry Andric #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
15bdd1243dSDimitry Andric #include "clang/Basic/SourceLocation.h"
1681ad6265SDimitry Andric #include "clang/Basic/Version.h"
1781ad6265SDimitry Andric #include "clang/ExtractAPI/DeclarationFragments.h"
18bdd1243dSDimitry Andric #include "llvm/ADT/STLExtras.h"
19bdd1243dSDimitry Andric #include "llvm/ADT/STLFunctionalExtras.h"
20bdd1243dSDimitry Andric #include "llvm/Support/Casting.h"
21bdd1243dSDimitry Andric #include "llvm/Support/Compiler.h"
2281ad6265SDimitry Andric #include "llvm/Support/Path.h"
2381ad6265SDimitry Andric #include "llvm/Support/VersionTuple.h"
24bdd1243dSDimitry Andric #include <optional>
2581ad6265SDimitry Andric #include <type_traits>
2681ad6265SDimitry Andric 
2781ad6265SDimitry Andric using namespace clang;
2881ad6265SDimitry Andric using namespace clang::extractapi;
2981ad6265SDimitry Andric using namespace llvm;
3081ad6265SDimitry Andric using namespace llvm::json;
3181ad6265SDimitry Andric 
3281ad6265SDimitry Andric namespace {
3381ad6265SDimitry Andric 
3481ad6265SDimitry Andric /// Helper function to inject a JSON object \p Obj into another object \p Paren
3581ad6265SDimitry Andric /// at position \p Key.
36bdd1243dSDimitry Andric void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) {
3781ad6265SDimitry Andric   if (Obj)
38bdd1243dSDimitry Andric     Paren[Key] = std::move(*Obj);
3981ad6265SDimitry Andric }
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric /// Helper function to inject a JSON array \p Array into object \p Paren at
4281ad6265SDimitry Andric /// position \p Key.
43bdd1243dSDimitry Andric void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
4481ad6265SDimitry Andric   if (Array)
45bdd1243dSDimitry Andric     Paren[Key] = std::move(*Array);
4681ad6265SDimitry Andric }
4781ad6265SDimitry Andric 
4881ad6265SDimitry Andric /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
4981ad6265SDimitry Andric /// format.
5081ad6265SDimitry Andric ///
5181ad6265SDimitry Andric /// A semantic version object contains three numeric fields, representing the
5281ad6265SDimitry Andric /// \c major, \c minor, and \c patch parts of the version tuple.
5381ad6265SDimitry Andric /// For example version tuple 1.0.3 is serialized as:
5481ad6265SDimitry Andric /// \code
5581ad6265SDimitry Andric ///   {
5681ad6265SDimitry Andric ///     "major" : 1,
5781ad6265SDimitry Andric ///     "minor" : 0,
5881ad6265SDimitry Andric ///     "patch" : 3
5981ad6265SDimitry Andric ///   }
6081ad6265SDimitry Andric /// \endcode
6181ad6265SDimitry Andric ///
62bdd1243dSDimitry Andric /// \returns \c std::nullopt if the version \p V is empty, or an \c Object
63bdd1243dSDimitry Andric /// containing the semantic version representation of \p V.
64bdd1243dSDimitry Andric std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
6581ad6265SDimitry Andric   if (V.empty())
66bdd1243dSDimitry Andric     return std::nullopt;
6781ad6265SDimitry Andric 
6881ad6265SDimitry Andric   Object Version;
6981ad6265SDimitry Andric   Version["major"] = V.getMajor();
7081ad6265SDimitry Andric   Version["minor"] = V.getMinor().value_or(0);
7181ad6265SDimitry Andric   Version["patch"] = V.getSubminor().value_or(0);
7281ad6265SDimitry Andric   return Version;
7381ad6265SDimitry Andric }
7481ad6265SDimitry Andric 
7581ad6265SDimitry Andric /// Serialize the OS information in the Symbol Graph platform property.
7681ad6265SDimitry Andric ///
7781ad6265SDimitry Andric /// The OS information in Symbol Graph contains the \c name of the OS, and an
7881ad6265SDimitry Andric /// optional \c minimumVersion semantic version field.
7981ad6265SDimitry Andric Object serializeOperatingSystem(const Triple &T) {
8081ad6265SDimitry Andric   Object OS;
8181ad6265SDimitry Andric   OS["name"] = T.getOSTypeName(T.getOS());
8281ad6265SDimitry Andric   serializeObject(OS, "minimumVersion",
8381ad6265SDimitry Andric                   serializeSemanticVersion(T.getMinimumSupportedOSVersion()));
8481ad6265SDimitry Andric   return OS;
8581ad6265SDimitry Andric }
8681ad6265SDimitry Andric 
8781ad6265SDimitry Andric /// Serialize the platform information in the Symbol Graph module section.
8881ad6265SDimitry Andric ///
8981ad6265SDimitry Andric /// The platform object describes a target platform triple in corresponding
9081ad6265SDimitry Andric /// three fields: \c architecture, \c vendor, and \c operatingSystem.
9181ad6265SDimitry Andric Object serializePlatform(const Triple &T) {
9281ad6265SDimitry Andric   Object Platform;
9381ad6265SDimitry Andric   Platform["architecture"] = T.getArchName();
9481ad6265SDimitry Andric   Platform["vendor"] = T.getVendorName();
9581ad6265SDimitry Andric   Platform["operatingSystem"] = serializeOperatingSystem(T);
9681ad6265SDimitry Andric   return Platform;
9781ad6265SDimitry Andric }
9881ad6265SDimitry Andric 
9981ad6265SDimitry Andric /// Serialize a source position.
10081ad6265SDimitry Andric Object serializeSourcePosition(const PresumedLoc &Loc) {
10181ad6265SDimitry Andric   assert(Loc.isValid() && "invalid source position");
10281ad6265SDimitry Andric 
10381ad6265SDimitry Andric   Object SourcePosition;
10481ad6265SDimitry Andric   SourcePosition["line"] = Loc.getLine();
10581ad6265SDimitry Andric   SourcePosition["character"] = Loc.getColumn();
10681ad6265SDimitry Andric 
10781ad6265SDimitry Andric   return SourcePosition;
10881ad6265SDimitry Andric }
10981ad6265SDimitry Andric 
11081ad6265SDimitry Andric /// Serialize a source location in file.
11181ad6265SDimitry Andric ///
11281ad6265SDimitry Andric /// \param Loc The presumed location to serialize.
11381ad6265SDimitry Andric /// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
11481ad6265SDimitry Andric /// Defaults to false.
11581ad6265SDimitry Andric Object serializeSourceLocation(const PresumedLoc &Loc,
11681ad6265SDimitry Andric                                bool IncludeFileURI = false) {
11781ad6265SDimitry Andric   Object SourceLocation;
11881ad6265SDimitry Andric   serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));
11981ad6265SDimitry Andric 
12081ad6265SDimitry Andric   if (IncludeFileURI) {
12181ad6265SDimitry Andric     std::string FileURI = "file://";
12281ad6265SDimitry Andric     // Normalize file path to use forward slashes for the URI.
12381ad6265SDimitry Andric     FileURI += sys::path::convert_to_slash(Loc.getFilename());
12481ad6265SDimitry Andric     SourceLocation["uri"] = FileURI;
12581ad6265SDimitry Andric   }
12681ad6265SDimitry Andric 
12781ad6265SDimitry Andric   return SourceLocation;
12881ad6265SDimitry Andric }
12981ad6265SDimitry Andric 
13081ad6265SDimitry Andric /// Serialize a source range with begin and end locations.
13181ad6265SDimitry Andric Object serializeSourceRange(const PresumedLoc &BeginLoc,
13281ad6265SDimitry Andric                             const PresumedLoc &EndLoc) {
13381ad6265SDimitry Andric   Object SourceRange;
13481ad6265SDimitry Andric   serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
13581ad6265SDimitry Andric   serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
13681ad6265SDimitry Andric   return SourceRange;
13781ad6265SDimitry Andric }
13881ad6265SDimitry Andric 
13981ad6265SDimitry Andric /// Serialize the availability attributes of a symbol.
14081ad6265SDimitry Andric ///
14181ad6265SDimitry Andric /// Availability information contains the introduced, deprecated, and obsoleted
142bdd1243dSDimitry Andric /// versions of the symbol for a given domain (roughly corresponds to a
143bdd1243dSDimitry Andric /// platform) as semantic versions, if not default.  Availability information
144bdd1243dSDimitry Andric /// also contains flags to indicate if the symbol is unconditionally unavailable
145bdd1243dSDimitry Andric /// or deprecated, i.e. \c __attribute__((unavailable)) and \c
146bdd1243dSDimitry Andric /// __attribute__((deprecated)).
14781ad6265SDimitry Andric ///
148bdd1243dSDimitry Andric /// \returns \c std::nullopt if the symbol has default availability attributes,
149bdd1243dSDimitry Andric /// or an \c Array containing the formatted availability information.
150bdd1243dSDimitry Andric std::optional<Array>
151bdd1243dSDimitry Andric serializeAvailability(const AvailabilitySet &Availabilities) {
152bdd1243dSDimitry Andric   if (Availabilities.isDefault())
153bdd1243dSDimitry Andric     return std::nullopt;
15481ad6265SDimitry Andric 
155bdd1243dSDimitry Andric   Array AvailabilityArray;
15681ad6265SDimitry Andric 
157bdd1243dSDimitry Andric   if (Availabilities.isUnconditionallyDeprecated()) {
158bdd1243dSDimitry Andric     Object UnconditionallyDeprecated;
159bdd1243dSDimitry Andric     UnconditionallyDeprecated["domain"] = "*";
160bdd1243dSDimitry Andric     UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
161bdd1243dSDimitry Andric     AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
162bdd1243dSDimitry Andric   }
163bdd1243dSDimitry Andric 
164bdd1243dSDimitry Andric   // Note unconditionally unavailable records are skipped.
165bdd1243dSDimitry Andric 
166bdd1243dSDimitry Andric   for (const auto &AvailInfo : Availabilities) {
167bdd1243dSDimitry Andric     Object Availability;
168bdd1243dSDimitry Andric     Availability["domain"] = AvailInfo.Domain;
169*06c3fb27SDimitry Andric     if (AvailInfo.Unavailable)
170*06c3fb27SDimitry Andric       Availability["isUnconditionallyUnavailable"] = true;
171*06c3fb27SDimitry Andric     else {
172bdd1243dSDimitry Andric       serializeObject(Availability, "introducedVersion",
173bdd1243dSDimitry Andric                       serializeSemanticVersion(AvailInfo.Introduced));
174bdd1243dSDimitry Andric       serializeObject(Availability, "deprecatedVersion",
175bdd1243dSDimitry Andric                       serializeSemanticVersion(AvailInfo.Deprecated));
176bdd1243dSDimitry Andric       serializeObject(Availability, "obsoletedVersion",
177bdd1243dSDimitry Andric                       serializeSemanticVersion(AvailInfo.Obsoleted));
178*06c3fb27SDimitry Andric     }
179bdd1243dSDimitry Andric     AvailabilityArray.emplace_back(std::move(Availability));
180bdd1243dSDimitry Andric   }
181bdd1243dSDimitry Andric 
182bdd1243dSDimitry Andric   return AvailabilityArray;
18381ad6265SDimitry Andric }
18481ad6265SDimitry Andric 
18581ad6265SDimitry Andric /// Get the language name string for interface language references.
18681ad6265SDimitry Andric StringRef getLanguageName(Language Lang) {
18781ad6265SDimitry Andric   switch (Lang) {
18881ad6265SDimitry Andric   case Language::C:
18981ad6265SDimitry Andric     return "c";
19081ad6265SDimitry Andric   case Language::ObjC:
19181ad6265SDimitry Andric     return "objective-c";
19281ad6265SDimitry Andric 
19381ad6265SDimitry Andric   // Unsupported language currently
19481ad6265SDimitry Andric   case Language::CXX:
19581ad6265SDimitry Andric   case Language::ObjCXX:
19681ad6265SDimitry Andric   case Language::OpenCL:
19781ad6265SDimitry Andric   case Language::OpenCLCXX:
19881ad6265SDimitry Andric   case Language::CUDA:
19981ad6265SDimitry Andric   case Language::RenderScript:
20081ad6265SDimitry Andric   case Language::HIP:
20181ad6265SDimitry Andric   case Language::HLSL:
20281ad6265SDimitry Andric 
20381ad6265SDimitry Andric   // Languages that the frontend cannot parse and compile
20481ad6265SDimitry Andric   case Language::Unknown:
20581ad6265SDimitry Andric   case Language::Asm:
20681ad6265SDimitry Andric   case Language::LLVM_IR:
20781ad6265SDimitry Andric     llvm_unreachable("Unsupported language kind");
20881ad6265SDimitry Andric   }
20981ad6265SDimitry Andric 
21081ad6265SDimitry Andric   llvm_unreachable("Unhandled language kind");
21181ad6265SDimitry Andric }
21281ad6265SDimitry Andric 
21381ad6265SDimitry Andric /// Serialize the identifier object as specified by the Symbol Graph format.
21481ad6265SDimitry Andric ///
21581ad6265SDimitry Andric /// The identifier property of a symbol contains the USR for precise and unique
21681ad6265SDimitry Andric /// references, and the interface language name.
21781ad6265SDimitry Andric Object serializeIdentifier(const APIRecord &Record, Language Lang) {
21881ad6265SDimitry Andric   Object Identifier;
21981ad6265SDimitry Andric   Identifier["precise"] = Record.USR;
22081ad6265SDimitry Andric   Identifier["interfaceLanguage"] = getLanguageName(Lang);
22181ad6265SDimitry Andric 
22281ad6265SDimitry Andric   return Identifier;
22381ad6265SDimitry Andric }
22481ad6265SDimitry Andric 
22581ad6265SDimitry Andric /// Serialize the documentation comments attached to a symbol, as specified by
22681ad6265SDimitry Andric /// the Symbol Graph format.
22781ad6265SDimitry Andric ///
22881ad6265SDimitry Andric /// The Symbol Graph \c docComment object contains an array of lines. Each line
22981ad6265SDimitry Andric /// represents one line of striped documentation comment, with source range
23081ad6265SDimitry Andric /// information.
23181ad6265SDimitry Andric /// e.g.
23281ad6265SDimitry Andric /// \code
23381ad6265SDimitry Andric ///   /// This is a documentation comment
23481ad6265SDimitry Andric ///       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'  First line.
23581ad6265SDimitry Andric ///   ///     with multiple lines.
23681ad6265SDimitry Andric ///       ^~~~~~~~~~~~~~~~~~~~~~~'         Second line.
23781ad6265SDimitry Andric /// \endcode
23881ad6265SDimitry Andric ///
239bdd1243dSDimitry Andric /// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
240bdd1243dSDimitry Andric /// the formatted lines.
241bdd1243dSDimitry Andric std::optional<Object> serializeDocComment(const DocComment &Comment) {
24281ad6265SDimitry Andric   if (Comment.empty())
243bdd1243dSDimitry Andric     return std::nullopt;
24481ad6265SDimitry Andric 
24581ad6265SDimitry Andric   Object DocComment;
24681ad6265SDimitry Andric   Array LinesArray;
24781ad6265SDimitry Andric   for (const auto &CommentLine : Comment) {
24881ad6265SDimitry Andric     Object Line;
24981ad6265SDimitry Andric     Line["text"] = CommentLine.Text;
25081ad6265SDimitry Andric     serializeObject(Line, "range",
25181ad6265SDimitry Andric                     serializeSourceRange(CommentLine.Begin, CommentLine.End));
25281ad6265SDimitry Andric     LinesArray.emplace_back(std::move(Line));
25381ad6265SDimitry Andric   }
25481ad6265SDimitry Andric   serializeArray(DocComment, "lines", LinesArray);
25581ad6265SDimitry Andric 
25681ad6265SDimitry Andric   return DocComment;
25781ad6265SDimitry Andric }
25881ad6265SDimitry Andric 
25981ad6265SDimitry Andric /// Serialize the declaration fragments of a symbol.
26081ad6265SDimitry Andric ///
26181ad6265SDimitry Andric /// The Symbol Graph declaration fragments is an array of tagged important
26281ad6265SDimitry Andric /// parts of a symbol's declaration. The fragments sequence can be joined to
26381ad6265SDimitry Andric /// form spans of declaration text, with attached information useful for
26481ad6265SDimitry Andric /// purposes like syntax-highlighting etc. For example:
26581ad6265SDimitry Andric /// \code
26681ad6265SDimitry Andric ///   const int pi; -> "declarationFragments" : [
26781ad6265SDimitry Andric ///                      {
26881ad6265SDimitry Andric ///                        "kind" : "keyword",
26981ad6265SDimitry Andric ///                        "spelling" : "const"
27081ad6265SDimitry Andric ///                      },
27181ad6265SDimitry Andric ///                      {
27281ad6265SDimitry Andric ///                        "kind" : "text",
27381ad6265SDimitry Andric ///                        "spelling" : " "
27481ad6265SDimitry Andric ///                      },
27581ad6265SDimitry Andric ///                      {
27681ad6265SDimitry Andric ///                        "kind" : "typeIdentifier",
27781ad6265SDimitry Andric ///                        "preciseIdentifier" : "c:I",
27881ad6265SDimitry Andric ///                        "spelling" : "int"
27981ad6265SDimitry Andric ///                      },
28081ad6265SDimitry Andric ///                      {
28181ad6265SDimitry Andric ///                        "kind" : "text",
28281ad6265SDimitry Andric ///                        "spelling" : " "
28381ad6265SDimitry Andric ///                      },
28481ad6265SDimitry Andric ///                      {
28581ad6265SDimitry Andric ///                        "kind" : "identifier",
28681ad6265SDimitry Andric ///                        "spelling" : "pi"
28781ad6265SDimitry Andric ///                      }
28881ad6265SDimitry Andric ///                    ]
28981ad6265SDimitry Andric /// \endcode
29081ad6265SDimitry Andric ///
291bdd1243dSDimitry Andric /// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
292bdd1243dSDimitry Andric /// formatted declaration fragments array.
293bdd1243dSDimitry Andric std::optional<Array>
294bdd1243dSDimitry Andric serializeDeclarationFragments(const DeclarationFragments &DF) {
29581ad6265SDimitry Andric   if (DF.getFragments().empty())
296bdd1243dSDimitry Andric     return std::nullopt;
29781ad6265SDimitry Andric 
29881ad6265SDimitry Andric   Array Fragments;
29981ad6265SDimitry Andric   for (const auto &F : DF.getFragments()) {
30081ad6265SDimitry Andric     Object Fragment;
30181ad6265SDimitry Andric     Fragment["spelling"] = F.Spelling;
30281ad6265SDimitry Andric     Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
30381ad6265SDimitry Andric     if (!F.PreciseIdentifier.empty())
30481ad6265SDimitry Andric       Fragment["preciseIdentifier"] = F.PreciseIdentifier;
30581ad6265SDimitry Andric     Fragments.emplace_back(std::move(Fragment));
30681ad6265SDimitry Andric   }
30781ad6265SDimitry Andric 
30881ad6265SDimitry Andric   return Fragments;
30981ad6265SDimitry Andric }
31081ad6265SDimitry Andric 
31181ad6265SDimitry Andric /// Serialize the \c names field of a symbol as specified by the Symbol Graph
31281ad6265SDimitry Andric /// format.
31381ad6265SDimitry Andric ///
31481ad6265SDimitry Andric /// The Symbol Graph names field contains multiple representations of a symbol
31581ad6265SDimitry Andric /// that can be used for different applications:
31681ad6265SDimitry Andric ///   - \c title : The simple declared name of the symbol;
31781ad6265SDimitry Andric ///   - \c subHeading : An array of declaration fragments that provides tags,
31881ad6265SDimitry Andric ///     and potentially more tokens (for example the \c +/- symbol for
31981ad6265SDimitry Andric ///     Objective-C methods). Can be used as sub-headings for documentation.
32081ad6265SDimitry Andric Object serializeNames(const APIRecord &Record) {
32181ad6265SDimitry Andric   Object Names;
32281ad6265SDimitry Andric   Names["title"] = Record.Name;
32381ad6265SDimitry Andric   serializeArray(Names, "subHeading",
32481ad6265SDimitry Andric                  serializeDeclarationFragments(Record.SubHeading));
32581ad6265SDimitry Andric   DeclarationFragments NavigatorFragments;
32681ad6265SDimitry Andric   NavigatorFragments.append(Record.Name,
32781ad6265SDimitry Andric                             DeclarationFragments::FragmentKind::Identifier,
32881ad6265SDimitry Andric                             /*PreciseIdentifier*/ "");
32981ad6265SDimitry Andric   serializeArray(Names, "navigator",
33081ad6265SDimitry Andric                  serializeDeclarationFragments(NavigatorFragments));
33181ad6265SDimitry Andric 
33281ad6265SDimitry Andric   return Names;
33381ad6265SDimitry Andric }
33481ad6265SDimitry Andric 
335bdd1243dSDimitry Andric Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
33681ad6265SDimitry Andric   auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
33781ad6265SDimitry Andric     return (getLanguageName(Lang) + "." + S).str();
33881ad6265SDimitry Andric   };
33981ad6265SDimitry Andric 
34081ad6265SDimitry Andric   Object Kind;
341bdd1243dSDimitry Andric   switch (RK) {
342bdd1243dSDimitry Andric   case APIRecord::RK_Unknown:
343bdd1243dSDimitry Andric     llvm_unreachable("Records should have an explicit kind");
344bdd1243dSDimitry Andric     break;
34581ad6265SDimitry Andric   case APIRecord::RK_GlobalFunction:
34681ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("func");
34781ad6265SDimitry Andric     Kind["displayName"] = "Function";
34881ad6265SDimitry Andric     break;
34981ad6265SDimitry Andric   case APIRecord::RK_GlobalVariable:
35081ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("var");
35181ad6265SDimitry Andric     Kind["displayName"] = "Global Variable";
35281ad6265SDimitry Andric     break;
35381ad6265SDimitry Andric   case APIRecord::RK_EnumConstant:
35481ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("enum.case");
35581ad6265SDimitry Andric     Kind["displayName"] = "Enumeration Case";
35681ad6265SDimitry Andric     break;
35781ad6265SDimitry Andric   case APIRecord::RK_Enum:
35881ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("enum");
35981ad6265SDimitry Andric     Kind["displayName"] = "Enumeration";
36081ad6265SDimitry Andric     break;
36181ad6265SDimitry Andric   case APIRecord::RK_StructField:
36281ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("property");
36381ad6265SDimitry Andric     Kind["displayName"] = "Instance Property";
36481ad6265SDimitry Andric     break;
36581ad6265SDimitry Andric   case APIRecord::RK_Struct:
36681ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("struct");
36781ad6265SDimitry Andric     Kind["displayName"] = "Structure";
36881ad6265SDimitry Andric     break;
36981ad6265SDimitry Andric   case APIRecord::RK_ObjCIvar:
37081ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("ivar");
37181ad6265SDimitry Andric     Kind["displayName"] = "Instance Variable";
37281ad6265SDimitry Andric     break;
373bdd1243dSDimitry Andric   case APIRecord::RK_ObjCInstanceMethod:
37481ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("method");
37581ad6265SDimitry Andric     Kind["displayName"] = "Instance Method";
376bdd1243dSDimitry Andric     break;
377bdd1243dSDimitry Andric   case APIRecord::RK_ObjCClassMethod:
37881ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("type.method");
37981ad6265SDimitry Andric     Kind["displayName"] = "Type Method";
38081ad6265SDimitry Andric     break;
381bdd1243dSDimitry Andric   case APIRecord::RK_ObjCInstanceProperty:
38281ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("property");
38381ad6265SDimitry Andric     Kind["displayName"] = "Instance Property";
38481ad6265SDimitry Andric     break;
385bdd1243dSDimitry Andric   case APIRecord::RK_ObjCClassProperty:
386bdd1243dSDimitry Andric     Kind["identifier"] = AddLangPrefix("type.property");
387bdd1243dSDimitry Andric     Kind["displayName"] = "Type Property";
388bdd1243dSDimitry Andric     break;
38981ad6265SDimitry Andric   case APIRecord::RK_ObjCInterface:
39081ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("class");
39181ad6265SDimitry Andric     Kind["displayName"] = "Class";
39281ad6265SDimitry Andric     break;
39381ad6265SDimitry Andric   case APIRecord::RK_ObjCCategory:
39481ad6265SDimitry Andric     // We don't serialize out standalone Objective-C category symbols yet.
39581ad6265SDimitry Andric     llvm_unreachable("Serializing standalone Objective-C category symbols is "
39681ad6265SDimitry Andric                      "not supported.");
39781ad6265SDimitry Andric     break;
39881ad6265SDimitry Andric   case APIRecord::RK_ObjCProtocol:
39981ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("protocol");
40081ad6265SDimitry Andric     Kind["displayName"] = "Protocol";
40181ad6265SDimitry Andric     break;
40281ad6265SDimitry Andric   case APIRecord::RK_MacroDefinition:
40381ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("macro");
40481ad6265SDimitry Andric     Kind["displayName"] = "Macro";
40581ad6265SDimitry Andric     break;
40681ad6265SDimitry Andric   case APIRecord::RK_Typedef:
40781ad6265SDimitry Andric     Kind["identifier"] = AddLangPrefix("typealias");
40881ad6265SDimitry Andric     Kind["displayName"] = "Type Alias";
40981ad6265SDimitry Andric     break;
41081ad6265SDimitry Andric   }
41181ad6265SDimitry Andric 
41281ad6265SDimitry Andric   return Kind;
41381ad6265SDimitry Andric }
41481ad6265SDimitry Andric 
415bdd1243dSDimitry Andric /// Serialize the symbol kind information.
416bdd1243dSDimitry Andric ///
417bdd1243dSDimitry Andric /// The Symbol Graph symbol kind property contains a shorthand \c identifier
418bdd1243dSDimitry Andric /// which is prefixed by the source language name, useful for tooling to parse
419bdd1243dSDimitry Andric /// the kind, and a \c displayName for rendering human-readable names.
420bdd1243dSDimitry Andric Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
421bdd1243dSDimitry Andric   return serializeSymbolKind(Record.getKind(), Lang);
422bdd1243dSDimitry Andric }
423bdd1243dSDimitry Andric 
42481ad6265SDimitry Andric template <typename RecordTy>
425bdd1243dSDimitry Andric std::optional<Object>
426bdd1243dSDimitry Andric serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
42781ad6265SDimitry Andric   const auto &FS = Record.Signature;
42881ad6265SDimitry Andric   if (FS.empty())
429bdd1243dSDimitry Andric     return std::nullopt;
43081ad6265SDimitry Andric 
43181ad6265SDimitry Andric   Object Signature;
43281ad6265SDimitry Andric   serializeArray(Signature, "returns",
43381ad6265SDimitry Andric                  serializeDeclarationFragments(FS.getReturnType()));
43481ad6265SDimitry Andric 
43581ad6265SDimitry Andric   Array Parameters;
43681ad6265SDimitry Andric   for (const auto &P : FS.getParameters()) {
43781ad6265SDimitry Andric     Object Parameter;
43881ad6265SDimitry Andric     Parameter["name"] = P.Name;
43981ad6265SDimitry Andric     serializeArray(Parameter, "declarationFragments",
44081ad6265SDimitry Andric                    serializeDeclarationFragments(P.Fragments));
44181ad6265SDimitry Andric     Parameters.emplace_back(std::move(Parameter));
44281ad6265SDimitry Andric   }
44381ad6265SDimitry Andric 
44481ad6265SDimitry Andric   if (!Parameters.empty())
44581ad6265SDimitry Andric     Signature["parameters"] = std::move(Parameters);
44681ad6265SDimitry Andric 
44781ad6265SDimitry Andric   return Signature;
44881ad6265SDimitry Andric }
44981ad6265SDimitry Andric 
45081ad6265SDimitry Andric template <typename RecordTy>
451bdd1243dSDimitry Andric std::optional<Object>
452bdd1243dSDimitry Andric serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
453bdd1243dSDimitry Andric   return std::nullopt;
45481ad6265SDimitry Andric }
45581ad6265SDimitry Andric 
45681ad6265SDimitry Andric /// Serialize the function signature field, as specified by the
45781ad6265SDimitry Andric /// Symbol Graph format.
45881ad6265SDimitry Andric ///
45981ad6265SDimitry Andric /// The Symbol Graph function signature property contains two arrays.
46081ad6265SDimitry Andric ///   - The \c returns array is the declaration fragments of the return type;
46181ad6265SDimitry Andric ///   - The \c parameters array contains names and declaration fragments of the
46281ad6265SDimitry Andric ///     parameters.
46381ad6265SDimitry Andric ///
464bdd1243dSDimitry Andric /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
46581ad6265SDimitry Andric /// formatted function signature.
46681ad6265SDimitry Andric template <typename RecordTy>
46781ad6265SDimitry Andric void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
46881ad6265SDimitry Andric   serializeObject(Paren, "functionSignature",
46981ad6265SDimitry Andric                   serializeFunctionSignatureMixinImpl(
47081ad6265SDimitry Andric                       Record, has_function_signature<RecordTy>()));
47181ad6265SDimitry Andric }
47281ad6265SDimitry Andric 
473bdd1243dSDimitry Andric struct PathComponent {
474bdd1243dSDimitry Andric   StringRef USR;
475bdd1243dSDimitry Andric   StringRef Name;
476bdd1243dSDimitry Andric   APIRecord::RecordKind Kind;
477bdd1243dSDimitry Andric 
478bdd1243dSDimitry Andric   PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
479bdd1243dSDimitry Andric       : USR(USR), Name(Name), Kind(Kind) {}
480bdd1243dSDimitry Andric };
481bdd1243dSDimitry Andric 
482bdd1243dSDimitry Andric template <typename RecordTy>
483bdd1243dSDimitry Andric bool generatePathComponents(
484bdd1243dSDimitry Andric     const RecordTy &Record, const APISet &API,
485bdd1243dSDimitry Andric     function_ref<void(const PathComponent &)> ComponentTransformer) {
486bdd1243dSDimitry Andric   SmallVector<PathComponent, 4> ReverseComponenents;
487bdd1243dSDimitry Andric   ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
488bdd1243dSDimitry Andric   const auto *CurrentParent = &Record.ParentInformation;
489*06c3fb27SDimitry Andric   bool FailedToFindParent = false;
490bdd1243dSDimitry Andric   while (CurrentParent && !CurrentParent->empty()) {
491bdd1243dSDimitry Andric     PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
492bdd1243dSDimitry Andric                                          CurrentParent->ParentName,
493bdd1243dSDimitry Andric                                          CurrentParent->ParentKind);
494bdd1243dSDimitry Andric 
495bdd1243dSDimitry Andric     auto *ParentRecord = CurrentParent->ParentRecord;
496bdd1243dSDimitry Andric     // Slow path if we don't have a direct reference to the ParentRecord
497bdd1243dSDimitry Andric     if (!ParentRecord)
498bdd1243dSDimitry Andric       ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
499bdd1243dSDimitry Andric 
500bdd1243dSDimitry Andric     // If the parent is a category then we need to pretend this belongs to the
501bdd1243dSDimitry Andric     // associated interface.
502bdd1243dSDimitry Andric     if (auto *CategoryRecord =
503bdd1243dSDimitry Andric             dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
504bdd1243dSDimitry Andric       ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
505bdd1243dSDimitry Andric       CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
506bdd1243dSDimitry Andric                                              CategoryRecord->Interface.Name,
507bdd1243dSDimitry Andric                                              APIRecord::RK_ObjCInterface);
508bdd1243dSDimitry Andric     }
509bdd1243dSDimitry Andric 
510bdd1243dSDimitry Andric     // The parent record doesn't exist which means the symbol shouldn't be
511bdd1243dSDimitry Andric     // treated as part of the current product.
512*06c3fb27SDimitry Andric     if (!ParentRecord) {
513*06c3fb27SDimitry Andric       FailedToFindParent = true;
514*06c3fb27SDimitry Andric       break;
515*06c3fb27SDimitry Andric     }
516bdd1243dSDimitry Andric 
517bdd1243dSDimitry Andric     ReverseComponenents.push_back(std::move(CurrentParentComponent));
518bdd1243dSDimitry Andric     CurrentParent = &ParentRecord->ParentInformation;
519bdd1243dSDimitry Andric   }
520bdd1243dSDimitry Andric 
521bdd1243dSDimitry Andric   for (const auto &PC : reverse(ReverseComponenents))
522bdd1243dSDimitry Andric     ComponentTransformer(PC);
523bdd1243dSDimitry Andric 
524*06c3fb27SDimitry Andric   return FailedToFindParent;
525bdd1243dSDimitry Andric }
526*06c3fb27SDimitry Andric 
527bdd1243dSDimitry Andric Object serializeParentContext(const PathComponent &PC, Language Lang) {
528bdd1243dSDimitry Andric   Object ParentContextElem;
529bdd1243dSDimitry Andric   ParentContextElem["usr"] = PC.USR;
530bdd1243dSDimitry Andric   ParentContextElem["name"] = PC.Name;
531bdd1243dSDimitry Andric   ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
532bdd1243dSDimitry Andric   return ParentContextElem;
533bdd1243dSDimitry Andric }
534bdd1243dSDimitry Andric 
535bdd1243dSDimitry Andric template <typename RecordTy>
536bdd1243dSDimitry Andric Array generateParentContexts(const RecordTy &Record, const APISet &API,
537bdd1243dSDimitry Andric                              Language Lang) {
538bdd1243dSDimitry Andric   Array ParentContexts;
539*06c3fb27SDimitry Andric   generatePathComponents(
540bdd1243dSDimitry Andric       Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
541bdd1243dSDimitry Andric         ParentContexts.push_back(serializeParentContext(PC, Lang));
542*06c3fb27SDimitry Andric       });
543bdd1243dSDimitry Andric 
544bdd1243dSDimitry Andric   return ParentContexts;
545bdd1243dSDimitry Andric }
546bdd1243dSDimitry Andric 
54781ad6265SDimitry Andric } // namespace
54881ad6265SDimitry Andric 
54981ad6265SDimitry Andric /// Defines the format version emitted by SymbolGraphSerializer.
55081ad6265SDimitry Andric const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
55181ad6265SDimitry Andric 
55281ad6265SDimitry Andric Object SymbolGraphSerializer::serializeMetadata() const {
55381ad6265SDimitry Andric   Object Metadata;
55481ad6265SDimitry Andric   serializeObject(Metadata, "formatVersion",
55581ad6265SDimitry Andric                   serializeSemanticVersion(FormatVersion));
55681ad6265SDimitry Andric   Metadata["generator"] = clang::getClangFullVersion();
55781ad6265SDimitry Andric   return Metadata;
55881ad6265SDimitry Andric }
55981ad6265SDimitry Andric 
56081ad6265SDimitry Andric Object SymbolGraphSerializer::serializeModule() const {
56181ad6265SDimitry Andric   Object Module;
56281ad6265SDimitry Andric   // The user is expected to always pass `--product-name=` on the command line
56381ad6265SDimitry Andric   // to populate this field.
564bdd1243dSDimitry Andric   Module["name"] = API.ProductName;
56581ad6265SDimitry Andric   serializeObject(Module, "platform", serializePlatform(API.getTarget()));
56681ad6265SDimitry Andric   return Module;
56781ad6265SDimitry Andric }
56881ad6265SDimitry Andric 
56981ad6265SDimitry Andric bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
570bdd1243dSDimitry Andric   // Skip explicitly ignored symbols.
571bdd1243dSDimitry Andric   if (IgnoresList.shouldIgnore(Record.Name))
572bdd1243dSDimitry Andric     return true;
573bdd1243dSDimitry Andric 
57481ad6265SDimitry Andric   // Skip unconditionally unavailable symbols
575bdd1243dSDimitry Andric   if (Record.Availabilities.isUnconditionallyUnavailable())
57681ad6265SDimitry Andric     return true;
57781ad6265SDimitry Andric 
57881ad6265SDimitry Andric   // Filter out symbols prefixed with an underscored as they are understood to
57981ad6265SDimitry Andric   // be symbols clients should not use.
58081ad6265SDimitry Andric   if (Record.Name.startswith("_"))
58181ad6265SDimitry Andric     return true;
58281ad6265SDimitry Andric 
58381ad6265SDimitry Andric   return false;
58481ad6265SDimitry Andric }
58581ad6265SDimitry Andric 
58681ad6265SDimitry Andric template <typename RecordTy>
587bdd1243dSDimitry Andric std::optional<Object>
58881ad6265SDimitry Andric SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
58981ad6265SDimitry Andric   if (shouldSkip(Record))
590bdd1243dSDimitry Andric     return std::nullopt;
59181ad6265SDimitry Andric 
59281ad6265SDimitry Andric   Object Obj;
59381ad6265SDimitry Andric   serializeObject(Obj, "identifier",
59481ad6265SDimitry Andric                   serializeIdentifier(Record, API.getLanguage()));
59581ad6265SDimitry Andric   serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
59681ad6265SDimitry Andric   serializeObject(Obj, "names", serializeNames(Record));
59781ad6265SDimitry Andric   serializeObject(
59881ad6265SDimitry Andric       Obj, "location",
59981ad6265SDimitry Andric       serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
600bdd1243dSDimitry Andric   serializeArray(Obj, "availability",
601bdd1243dSDimitry Andric                  serializeAvailability(Record.Availabilities));
60281ad6265SDimitry Andric   serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
60381ad6265SDimitry Andric   serializeArray(Obj, "declarationFragments",
60481ad6265SDimitry Andric                  serializeDeclarationFragments(Record.Declaration));
60581ad6265SDimitry Andric   // TODO: Once we keep track of symbol access information serialize it
60681ad6265SDimitry Andric   // correctly here.
60781ad6265SDimitry Andric   Obj["accessLevel"] = "public";
608bdd1243dSDimitry Andric   SmallVector<StringRef, 4> PathComponentsNames;
609bdd1243dSDimitry Andric   // If this returns true it indicates that we couldn't find a symbol in the
610bdd1243dSDimitry Andric   // hierarchy.
611bdd1243dSDimitry Andric   if (generatePathComponents(Record, API,
612bdd1243dSDimitry Andric                              [&PathComponentsNames](const PathComponent &PC) {
613bdd1243dSDimitry Andric                                PathComponentsNames.push_back(PC.Name);
614bdd1243dSDimitry Andric                              }))
615bdd1243dSDimitry Andric     return {};
616bdd1243dSDimitry Andric 
617bdd1243dSDimitry Andric   serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
61881ad6265SDimitry Andric 
61981ad6265SDimitry Andric   serializeFunctionSignatureMixin(Obj, Record);
62081ad6265SDimitry Andric 
62181ad6265SDimitry Andric   return Obj;
62281ad6265SDimitry Andric }
62381ad6265SDimitry Andric 
62481ad6265SDimitry Andric template <typename MemberTy>
62581ad6265SDimitry Andric void SymbolGraphSerializer::serializeMembers(
62681ad6265SDimitry Andric     const APIRecord &Record,
62781ad6265SDimitry Andric     const SmallVector<std::unique_ptr<MemberTy>> &Members) {
628bdd1243dSDimitry Andric   // Members should not be serialized if we aren't recursing.
629bdd1243dSDimitry Andric   if (!ShouldRecurse)
630bdd1243dSDimitry Andric     return;
63181ad6265SDimitry Andric   for (const auto &Member : Members) {
63281ad6265SDimitry Andric     auto MemberRecord = serializeAPIRecord(*Member);
63381ad6265SDimitry Andric     if (!MemberRecord)
63481ad6265SDimitry Andric       continue;
63581ad6265SDimitry Andric 
63681ad6265SDimitry Andric     Symbols.emplace_back(std::move(*MemberRecord));
63781ad6265SDimitry Andric     serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
63881ad6265SDimitry Andric   }
63981ad6265SDimitry Andric }
64081ad6265SDimitry Andric 
64181ad6265SDimitry Andric StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
64281ad6265SDimitry Andric   switch (Kind) {
64381ad6265SDimitry Andric   case RelationshipKind::MemberOf:
64481ad6265SDimitry Andric     return "memberOf";
64581ad6265SDimitry Andric   case RelationshipKind::InheritsFrom:
64681ad6265SDimitry Andric     return "inheritsFrom";
64781ad6265SDimitry Andric   case RelationshipKind::ConformsTo:
64881ad6265SDimitry Andric     return "conformsTo";
64981ad6265SDimitry Andric   }
65081ad6265SDimitry Andric   llvm_unreachable("Unhandled relationship kind");
65181ad6265SDimitry Andric }
65281ad6265SDimitry Andric 
65381ad6265SDimitry Andric void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
65481ad6265SDimitry Andric                                                   SymbolReference Source,
65581ad6265SDimitry Andric                                                   SymbolReference Target) {
65681ad6265SDimitry Andric   Object Relationship;
65781ad6265SDimitry Andric   Relationship["source"] = Source.USR;
65881ad6265SDimitry Andric   Relationship["target"] = Target.USR;
659bdd1243dSDimitry Andric   Relationship["targetFallback"] = Target.Name;
66081ad6265SDimitry Andric   Relationship["kind"] = getRelationshipString(Kind);
66181ad6265SDimitry Andric 
66281ad6265SDimitry Andric   Relationships.emplace_back(std::move(Relationship));
66381ad6265SDimitry Andric }
66481ad6265SDimitry Andric 
665*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitGlobalFunctionRecord(
66681ad6265SDimitry Andric     const GlobalFunctionRecord &Record) {
66781ad6265SDimitry Andric   auto Obj = serializeAPIRecord(Record);
66881ad6265SDimitry Andric   if (!Obj)
66981ad6265SDimitry Andric     return;
67081ad6265SDimitry Andric 
67181ad6265SDimitry Andric   Symbols.emplace_back(std::move(*Obj));
67281ad6265SDimitry Andric }
67381ad6265SDimitry Andric 
674*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitGlobalVariableRecord(
67581ad6265SDimitry Andric     const GlobalVariableRecord &Record) {
67681ad6265SDimitry Andric   auto Obj = serializeAPIRecord(Record);
67781ad6265SDimitry Andric   if (!Obj)
67881ad6265SDimitry Andric     return;
67981ad6265SDimitry Andric 
68081ad6265SDimitry Andric   Symbols.emplace_back(std::move(*Obj));
68181ad6265SDimitry Andric }
68281ad6265SDimitry Andric 
683*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) {
68481ad6265SDimitry Andric   auto Enum = serializeAPIRecord(Record);
68581ad6265SDimitry Andric   if (!Enum)
68681ad6265SDimitry Andric     return;
68781ad6265SDimitry Andric 
68881ad6265SDimitry Andric   Symbols.emplace_back(std::move(*Enum));
68981ad6265SDimitry Andric   serializeMembers(Record, Record.Constants);
69081ad6265SDimitry Andric }
69181ad6265SDimitry Andric 
692*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) {
69381ad6265SDimitry Andric   auto Struct = serializeAPIRecord(Record);
69481ad6265SDimitry Andric   if (!Struct)
69581ad6265SDimitry Andric     return;
69681ad6265SDimitry Andric 
69781ad6265SDimitry Andric   Symbols.emplace_back(std::move(*Struct));
69881ad6265SDimitry Andric   serializeMembers(Record, Record.Fields);
69981ad6265SDimitry Andric }
70081ad6265SDimitry Andric 
701*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitObjCContainerRecord(
70281ad6265SDimitry Andric     const ObjCContainerRecord &Record) {
70381ad6265SDimitry Andric   auto ObjCContainer = serializeAPIRecord(Record);
70481ad6265SDimitry Andric   if (!ObjCContainer)
70581ad6265SDimitry Andric     return;
70681ad6265SDimitry Andric 
70781ad6265SDimitry Andric   Symbols.emplace_back(std::move(*ObjCContainer));
70881ad6265SDimitry Andric 
70981ad6265SDimitry Andric   serializeMembers(Record, Record.Ivars);
71081ad6265SDimitry Andric   serializeMembers(Record, Record.Methods);
71181ad6265SDimitry Andric   serializeMembers(Record, Record.Properties);
71281ad6265SDimitry Andric 
71381ad6265SDimitry Andric   for (const auto &Protocol : Record.Protocols)
71481ad6265SDimitry Andric     // Record that Record conforms to Protocol.
71581ad6265SDimitry Andric     serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
71681ad6265SDimitry Andric 
71781ad6265SDimitry Andric   if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
71881ad6265SDimitry Andric     if (!ObjCInterface->SuperClass.empty())
71981ad6265SDimitry Andric       // If Record is an Objective-C interface record and it has a super class,
72081ad6265SDimitry Andric       // record that Record is inherited from SuperClass.
72181ad6265SDimitry Andric       serializeRelationship(RelationshipKind::InheritsFrom, Record,
72281ad6265SDimitry Andric                             ObjCInterface->SuperClass);
72381ad6265SDimitry Andric 
72481ad6265SDimitry Andric     // Members of categories extending an interface are serialized as members of
72581ad6265SDimitry Andric     // the interface.
72681ad6265SDimitry Andric     for (const auto *Category : ObjCInterface->Categories) {
72781ad6265SDimitry Andric       serializeMembers(Record, Category->Ivars);
72881ad6265SDimitry Andric       serializeMembers(Record, Category->Methods);
72981ad6265SDimitry Andric       serializeMembers(Record, Category->Properties);
73081ad6265SDimitry Andric 
731bdd1243dSDimitry Andric       // Surface the protocols of the category to the interface.
73281ad6265SDimitry Andric       for (const auto &Protocol : Category->Protocols)
73381ad6265SDimitry Andric         serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
73481ad6265SDimitry Andric     }
73581ad6265SDimitry Andric   }
73681ad6265SDimitry Andric }
73781ad6265SDimitry Andric 
738*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitMacroDefinitionRecord(
73981ad6265SDimitry Andric     const MacroDefinitionRecord &Record) {
74081ad6265SDimitry Andric   auto Macro = serializeAPIRecord(Record);
74181ad6265SDimitry Andric 
74281ad6265SDimitry Andric   if (!Macro)
74381ad6265SDimitry Andric     return;
74481ad6265SDimitry Andric 
74581ad6265SDimitry Andric   Symbols.emplace_back(std::move(*Macro));
74681ad6265SDimitry Andric }
74781ad6265SDimitry Andric 
748bdd1243dSDimitry Andric void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
749bdd1243dSDimitry Andric   switch (Record->getKind()) {
750bdd1243dSDimitry Andric   case APIRecord::RK_Unknown:
751bdd1243dSDimitry Andric     llvm_unreachable("Records should have a known kind!");
752bdd1243dSDimitry Andric   case APIRecord::RK_GlobalFunction:
753*06c3fb27SDimitry Andric     visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
754bdd1243dSDimitry Andric     break;
755bdd1243dSDimitry Andric   case APIRecord::RK_GlobalVariable:
756*06c3fb27SDimitry Andric     visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
757bdd1243dSDimitry Andric     break;
758bdd1243dSDimitry Andric   case APIRecord::RK_Enum:
759*06c3fb27SDimitry Andric     visitEnumRecord(*cast<EnumRecord>(Record));
760bdd1243dSDimitry Andric     break;
761bdd1243dSDimitry Andric   case APIRecord::RK_Struct:
762*06c3fb27SDimitry Andric     visitStructRecord(*cast<StructRecord>(Record));
763bdd1243dSDimitry Andric     break;
764bdd1243dSDimitry Andric   case APIRecord::RK_ObjCInterface:
765*06c3fb27SDimitry Andric     visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
766bdd1243dSDimitry Andric     break;
767bdd1243dSDimitry Andric   case APIRecord::RK_ObjCProtocol:
768*06c3fb27SDimitry Andric     visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
769bdd1243dSDimitry Andric     break;
770bdd1243dSDimitry Andric   case APIRecord::RK_MacroDefinition:
771*06c3fb27SDimitry Andric     visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
772bdd1243dSDimitry Andric     break;
773bdd1243dSDimitry Andric   case APIRecord::RK_Typedef:
774*06c3fb27SDimitry Andric     visitTypedefRecord(*cast<TypedefRecord>(Record));
775bdd1243dSDimitry Andric     break;
776bdd1243dSDimitry Andric   default:
777bdd1243dSDimitry Andric     if (auto Obj = serializeAPIRecord(*Record)) {
778bdd1243dSDimitry Andric       Symbols.emplace_back(std::move(*Obj));
779bdd1243dSDimitry Andric       auto &ParentInformation = Record->ParentInformation;
780bdd1243dSDimitry Andric       if (!ParentInformation.empty())
781bdd1243dSDimitry Andric         serializeRelationship(RelationshipKind::MemberOf, *Record,
782bdd1243dSDimitry Andric                               *ParentInformation.ParentRecord);
783bdd1243dSDimitry Andric     }
784bdd1243dSDimitry Andric     break;
785bdd1243dSDimitry Andric   }
786bdd1243dSDimitry Andric }
787bdd1243dSDimitry Andric 
788*06c3fb27SDimitry Andric void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) {
78981ad6265SDimitry Andric   // Typedefs of anonymous types have their entries unified with the underlying
79081ad6265SDimitry Andric   // type.
79181ad6265SDimitry Andric   bool ShouldDrop = Record.UnderlyingType.Name.empty();
79281ad6265SDimitry Andric   // enums declared with `NS_OPTION` have a named enum and a named typedef, with
79381ad6265SDimitry Andric   // the same name
79481ad6265SDimitry Andric   ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
79581ad6265SDimitry Andric   if (ShouldDrop)
79681ad6265SDimitry Andric     return;
79781ad6265SDimitry Andric 
79881ad6265SDimitry Andric   auto Typedef = serializeAPIRecord(Record);
79981ad6265SDimitry Andric   if (!Typedef)
80081ad6265SDimitry Andric     return;
80181ad6265SDimitry Andric 
80281ad6265SDimitry Andric   (*Typedef)["type"] = Record.UnderlyingType.USR;
80381ad6265SDimitry Andric 
80481ad6265SDimitry Andric   Symbols.emplace_back(std::move(*Typedef));
80581ad6265SDimitry Andric }
80681ad6265SDimitry Andric 
80781ad6265SDimitry Andric Object SymbolGraphSerializer::serialize() {
808*06c3fb27SDimitry Andric   traverseAPISet();
809bdd1243dSDimitry Andric   return serializeCurrentGraph();
810bdd1243dSDimitry Andric }
811bdd1243dSDimitry Andric 
812bdd1243dSDimitry Andric Object SymbolGraphSerializer::serializeCurrentGraph() {
813bdd1243dSDimitry Andric   Object Root;
814bdd1243dSDimitry Andric   serializeObject(Root, "metadata", serializeMetadata());
815bdd1243dSDimitry Andric   serializeObject(Root, "module", serializeModule());
816bdd1243dSDimitry Andric 
81781ad6265SDimitry Andric   Root["symbols"] = std::move(Symbols);
81881ad6265SDimitry Andric   Root["relationships"] = std::move(Relationships);
81981ad6265SDimitry Andric 
82081ad6265SDimitry Andric   return Root;
82181ad6265SDimitry Andric }
82281ad6265SDimitry Andric 
82381ad6265SDimitry Andric void SymbolGraphSerializer::serialize(raw_ostream &os) {
82481ad6265SDimitry Andric   Object root = serialize();
82581ad6265SDimitry Andric   if (Options.Compact)
82681ad6265SDimitry Andric     os << formatv("{0}", Value(std::move(root))) << "\n";
82781ad6265SDimitry Andric   else
82881ad6265SDimitry Andric     os << formatv("{0:2}", Value(std::move(root))) << "\n";
82981ad6265SDimitry Andric }
830bdd1243dSDimitry Andric 
831bdd1243dSDimitry Andric std::optional<Object>
832bdd1243dSDimitry Andric SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
833bdd1243dSDimitry Andric                                                 const APISet &API) {
834bdd1243dSDimitry Andric   APIRecord *Record = API.findRecordForUSR(USR);
835bdd1243dSDimitry Andric   if (!Record)
836bdd1243dSDimitry Andric     return {};
837bdd1243dSDimitry Andric 
838*06c3fb27SDimitry Andric   if (isa<ObjCCategoryRecord>(Record))
839*06c3fb27SDimitry Andric     return {};
840*06c3fb27SDimitry Andric 
841bdd1243dSDimitry Andric   Object Root;
842bdd1243dSDimitry Andric   APIIgnoresList EmptyIgnores;
843bdd1243dSDimitry Andric   SymbolGraphSerializer Serializer(API, EmptyIgnores,
844bdd1243dSDimitry Andric                                    /*Options.Compact*/ {true},
845bdd1243dSDimitry Andric                                    /*ShouldRecurse*/ false);
846bdd1243dSDimitry Andric   Serializer.serializeSingleRecord(Record);
847bdd1243dSDimitry Andric   serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
848bdd1243dSDimitry Andric 
849bdd1243dSDimitry Andric   Language Lang = API.getLanguage();
850bdd1243dSDimitry Andric   serializeArray(Root, "parentContexts",
851bdd1243dSDimitry Andric                  generateParentContexts(*Record, API, Lang));
852bdd1243dSDimitry Andric 
853bdd1243dSDimitry Andric   Array RelatedSymbols;
854bdd1243dSDimitry Andric 
855bdd1243dSDimitry Andric   for (const auto &Fragment : Record->Declaration.getFragments()) {
856bdd1243dSDimitry Andric     // If we don't have a USR there isn't much we can do.
857bdd1243dSDimitry Andric     if (Fragment.PreciseIdentifier.empty())
858bdd1243dSDimitry Andric       continue;
859bdd1243dSDimitry Andric 
860bdd1243dSDimitry Andric     APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
861bdd1243dSDimitry Andric 
862bdd1243dSDimitry Andric     // If we can't find the record let's skip.
863bdd1243dSDimitry Andric     if (!RelatedRecord)
864bdd1243dSDimitry Andric       continue;
865bdd1243dSDimitry Andric 
866bdd1243dSDimitry Andric     Object RelatedSymbol;
867bdd1243dSDimitry Andric     RelatedSymbol["usr"] = RelatedRecord->USR;
868bdd1243dSDimitry Andric     RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
869bdd1243dSDimitry Andric     // TODO: once we record this properly let's serialize it right.
870bdd1243dSDimitry Andric     RelatedSymbol["accessLevel"] = "public";
871bdd1243dSDimitry Andric     RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
872bdd1243dSDimitry Andric     RelatedSymbol["moduleName"] = API.ProductName;
873bdd1243dSDimitry Andric     RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
874bdd1243dSDimitry Andric 
875bdd1243dSDimitry Andric     serializeArray(RelatedSymbol, "parentContexts",
876bdd1243dSDimitry Andric                    generateParentContexts(*RelatedRecord, API, Lang));
877bdd1243dSDimitry Andric     RelatedSymbols.push_back(std::move(RelatedSymbol));
878bdd1243dSDimitry Andric   }
879bdd1243dSDimitry Andric 
880bdd1243dSDimitry Andric   serializeArray(Root, "relatedSymbols", RelatedSymbols);
881bdd1243dSDimitry Andric   return Root;
882bdd1243dSDimitry Andric }
883