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