Path: blob/main/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
35271 views
//===- ExtractAPI/Serialization/SymbolGraphSerializer.cpp -------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file implements the SymbolGraphSerializer.10///11//===----------------------------------------------------------------------===//1213#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"14#include "clang/Basic/SourceLocation.h"15#include "clang/Basic/Version.h"16#include "clang/ExtractAPI/API.h"17#include "clang/ExtractAPI/DeclarationFragments.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/ADT/STLFunctionalExtras.h"20#include "llvm/ADT/SmallVector.h"21#include "llvm/Support/Casting.h"22#include "llvm/Support/Compiler.h"23#include "llvm/Support/Path.h"24#include "llvm/Support/VersionTuple.h"25#include "llvm/Support/raw_ostream.h"26#include <iterator>27#include <optional>28#include <type_traits>2930using namespace clang;31using namespace clang::extractapi;32using namespace llvm;3334namespace {3536/// Helper function to inject a JSON object \p Obj into another object \p Paren37/// at position \p Key.38void serializeObject(Object &Paren, StringRef Key,39std::optional<Object> &&Obj) {40if (Obj)41Paren[Key] = std::move(*Obj);42}4344/// Helper function to inject a JSON array \p Array into object \p Paren at45/// position \p Key.46void serializeArray(Object &Paren, StringRef Key,47std::optional<Array> &&Array) {48if (Array)49Paren[Key] = std::move(*Array);50}5152/// Helper function to inject a JSON array composed of the values in \p C into53/// object \p Paren at position \p Key.54template <typename ContainerTy>55void serializeArray(Object &Paren, StringRef Key, ContainerTy &&C) {56Paren[Key] = Array(C);57}5859/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version60/// format.61///62/// A semantic version object contains three numeric fields, representing the63/// \c major, \c minor, and \c patch parts of the version tuple.64/// For example version tuple 1.0.3 is serialized as:65/// \code66/// {67/// "major" : 1,68/// "minor" : 0,69/// "patch" : 370/// }71/// \endcode72///73/// \returns \c std::nullopt if the version \p V is empty, or an \c Object74/// containing the semantic version representation of \p V.75std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {76if (V.empty())77return std::nullopt;7879Object Version;80Version["major"] = V.getMajor();81Version["minor"] = V.getMinor().value_or(0);82Version["patch"] = V.getSubminor().value_or(0);83return Version;84}8586/// Serialize the OS information in the Symbol Graph platform property.87///88/// The OS information in Symbol Graph contains the \c name of the OS, and an89/// optional \c minimumVersion semantic version field.90Object serializeOperatingSystem(const Triple &T) {91Object OS;92OS["name"] = T.getOSTypeName(T.getOS());93serializeObject(OS, "minimumVersion",94serializeSemanticVersion(T.getMinimumSupportedOSVersion()));95return OS;96}9798/// Serialize the platform information in the Symbol Graph module section.99///100/// The platform object describes a target platform triple in corresponding101/// three fields: \c architecture, \c vendor, and \c operatingSystem.102Object serializePlatform(const Triple &T) {103Object Platform;104Platform["architecture"] = T.getArchName();105Platform["vendor"] = T.getVendorName();106Platform["operatingSystem"] = serializeOperatingSystem(T);107return Platform;108}109110/// Serialize a source position.111Object serializeSourcePosition(const PresumedLoc &Loc) {112assert(Loc.isValid() && "invalid source position");113114Object SourcePosition;115SourcePosition["line"] = Loc.getLine() - 1;116SourcePosition["character"] = Loc.getColumn() - 1;117118return SourcePosition;119}120121/// Serialize a source location in file.122///123/// \param Loc The presumed location to serialize.124/// \param IncludeFileURI If true, include the file path of \p Loc as a URI.125/// Defaults to false.126Object serializeSourceLocation(const PresumedLoc &Loc,127bool IncludeFileURI = false) {128Object SourceLocation;129serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));130131if (IncludeFileURI) {132std::string FileURI = "file://";133// Normalize file path to use forward slashes for the URI.134FileURI += sys::path::convert_to_slash(Loc.getFilename());135SourceLocation["uri"] = FileURI;136}137138return SourceLocation;139}140141/// Serialize a source range with begin and end locations.142Object serializeSourceRange(const PresumedLoc &BeginLoc,143const PresumedLoc &EndLoc) {144Object SourceRange;145serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));146serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));147return SourceRange;148}149150/// Serialize the availability attributes of a symbol.151///152/// Availability information contains the introduced, deprecated, and obsoleted153/// versions of the symbol as semantic versions, if not default.154/// Availability information also contains flags to indicate if the symbol is155/// unconditionally unavailable or deprecated,156/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)).157///158/// \returns \c std::nullopt if the symbol has default availability attributes,159/// or an \c Array containing an object with the formatted availability160/// information.161std::optional<Array> serializeAvailability(const AvailabilityInfo &Avail) {162if (Avail.isDefault())163return std::nullopt;164165Array AvailabilityArray;166167if (Avail.isUnconditionallyDeprecated()) {168Object UnconditionallyDeprecated;169UnconditionallyDeprecated["domain"] = "*";170UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;171AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));172}173Object Availability;174175Availability["domain"] = Avail.Domain;176177if (Avail.isUnavailable()) {178Availability["isUnconditionallyUnavailable"] = true;179} else {180serializeObject(Availability, "introduced",181serializeSemanticVersion(Avail.Introduced));182serializeObject(Availability, "deprecated",183serializeSemanticVersion(Avail.Deprecated));184serializeObject(Availability, "obsoleted",185serializeSemanticVersion(Avail.Obsoleted));186}187188AvailabilityArray.emplace_back(std::move(Availability));189return AvailabilityArray;190}191192/// Get the language name string for interface language references.193StringRef getLanguageName(Language Lang) {194switch (Lang) {195case Language::C:196return "c";197case Language::ObjC:198return "objective-c";199case Language::CXX:200return "c++";201case Language::ObjCXX:202return "objective-c++";203204// Unsupported language currently205case Language::OpenCL:206case Language::OpenCLCXX:207case Language::CUDA:208case Language::RenderScript:209case Language::HIP:210case Language::HLSL:211212// Languages that the frontend cannot parse and compile213case Language::Unknown:214case Language::Asm:215case Language::LLVM_IR:216case Language::CIR:217llvm_unreachable("Unsupported language kind");218}219220llvm_unreachable("Unhandled language kind");221}222223/// Serialize the identifier object as specified by the Symbol Graph format.224///225/// The identifier property of a symbol contains the USR for precise and unique226/// references, and the interface language name.227Object serializeIdentifier(const APIRecord &Record, Language Lang) {228Object Identifier;229Identifier["precise"] = Record.USR;230Identifier["interfaceLanguage"] = getLanguageName(Lang);231232return Identifier;233}234235/// Serialize the documentation comments attached to a symbol, as specified by236/// the Symbol Graph format.237///238/// The Symbol Graph \c docComment object contains an array of lines. Each line239/// represents one line of striped documentation comment, with source range240/// information.241/// e.g.242/// \code243/// /// This is a documentation comment244/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.245/// /// with multiple lines.246/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.247/// \endcode248///249/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing250/// the formatted lines.251std::optional<Object> serializeDocComment(const DocComment &Comment) {252if (Comment.empty())253return std::nullopt;254255Object DocComment;256257Array LinesArray;258for (const auto &CommentLine : Comment) {259Object Line;260Line["text"] = CommentLine.Text;261serializeObject(Line, "range",262serializeSourceRange(CommentLine.Begin, CommentLine.End));263LinesArray.emplace_back(std::move(Line));264}265266serializeArray(DocComment, "lines", std::move(LinesArray));267268return DocComment;269}270271/// Serialize the declaration fragments of a symbol.272///273/// The Symbol Graph declaration fragments is an array of tagged important274/// parts of a symbol's declaration. The fragments sequence can be joined to275/// form spans of declaration text, with attached information useful for276/// purposes like syntax-highlighting etc. For example:277/// \code278/// const int pi; -> "declarationFragments" : [279/// {280/// "kind" : "keyword",281/// "spelling" : "const"282/// },283/// {284/// "kind" : "text",285/// "spelling" : " "286/// },287/// {288/// "kind" : "typeIdentifier",289/// "preciseIdentifier" : "c:I",290/// "spelling" : "int"291/// },292/// {293/// "kind" : "text",294/// "spelling" : " "295/// },296/// {297/// "kind" : "identifier",298/// "spelling" : "pi"299/// }300/// ]301/// \endcode302///303/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the304/// formatted declaration fragments array.305std::optional<Array>306serializeDeclarationFragments(const DeclarationFragments &DF) {307if (DF.getFragments().empty())308return std::nullopt;309310Array Fragments;311for (const auto &F : DF.getFragments()) {312Object Fragment;313Fragment["spelling"] = F.Spelling;314Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);315if (!F.PreciseIdentifier.empty())316Fragment["preciseIdentifier"] = F.PreciseIdentifier;317Fragments.emplace_back(std::move(Fragment));318}319320return Fragments;321}322323/// Serialize the \c names field of a symbol as specified by the Symbol Graph324/// format.325///326/// The Symbol Graph names field contains multiple representations of a symbol327/// that can be used for different applications:328/// - \c title : The simple declared name of the symbol;329/// - \c subHeading : An array of declaration fragments that provides tags,330/// and potentially more tokens (for example the \c +/- symbol for331/// Objective-C methods). Can be used as sub-headings for documentation.332Object serializeNames(const APIRecord *Record) {333Object Names;334Names["title"] = Record->Name;335336serializeArray(Names, "subHeading",337serializeDeclarationFragments(Record->SubHeading));338DeclarationFragments NavigatorFragments;339NavigatorFragments.append(Record->Name,340DeclarationFragments::FragmentKind::Identifier,341/*PreciseIdentifier*/ "");342serializeArray(Names, "navigator",343serializeDeclarationFragments(NavigatorFragments));344345return Names;346}347348Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {349auto AddLangPrefix = [&Lang](StringRef S) -> std::string {350return (getLanguageName(Lang) + "." + S).str();351};352353Object Kind;354switch (RK) {355case APIRecord::RK_Unknown:356Kind["identifier"] = AddLangPrefix("unknown");357Kind["displayName"] = "Unknown";358break;359case APIRecord::RK_Namespace:360Kind["identifier"] = AddLangPrefix("namespace");361Kind["displayName"] = "Namespace";362break;363case APIRecord::RK_GlobalFunction:364Kind["identifier"] = AddLangPrefix("func");365Kind["displayName"] = "Function";366break;367case APIRecord::RK_GlobalFunctionTemplate:368Kind["identifier"] = AddLangPrefix("func");369Kind["displayName"] = "Function Template";370break;371case APIRecord::RK_GlobalFunctionTemplateSpecialization:372Kind["identifier"] = AddLangPrefix("func");373Kind["displayName"] = "Function Template Specialization";374break;375case APIRecord::RK_GlobalVariableTemplate:376Kind["identifier"] = AddLangPrefix("var");377Kind["displayName"] = "Global Variable Template";378break;379case APIRecord::RK_GlobalVariableTemplateSpecialization:380Kind["identifier"] = AddLangPrefix("var");381Kind["displayName"] = "Global Variable Template Specialization";382break;383case APIRecord::RK_GlobalVariableTemplatePartialSpecialization:384Kind["identifier"] = AddLangPrefix("var");385Kind["displayName"] = "Global Variable Template Partial Specialization";386break;387case APIRecord::RK_GlobalVariable:388Kind["identifier"] = AddLangPrefix("var");389Kind["displayName"] = "Global Variable";390break;391case APIRecord::RK_EnumConstant:392Kind["identifier"] = AddLangPrefix("enum.case");393Kind["displayName"] = "Enumeration Case";394break;395case APIRecord::RK_Enum:396Kind["identifier"] = AddLangPrefix("enum");397Kind["displayName"] = "Enumeration";398break;399case APIRecord::RK_StructField:400Kind["identifier"] = AddLangPrefix("property");401Kind["displayName"] = "Instance Property";402break;403case APIRecord::RK_Struct:404Kind["identifier"] = AddLangPrefix("struct");405Kind["displayName"] = "Structure";406break;407case APIRecord::RK_UnionField:408Kind["identifier"] = AddLangPrefix("property");409Kind["displayName"] = "Instance Property";410break;411case APIRecord::RK_Union:412Kind["identifier"] = AddLangPrefix("union");413Kind["displayName"] = "Union";414break;415case APIRecord::RK_CXXField:416Kind["identifier"] = AddLangPrefix("property");417Kind["displayName"] = "Instance Property";418break;419case APIRecord::RK_StaticField:420Kind["identifier"] = AddLangPrefix("type.property");421Kind["displayName"] = "Type Property";422break;423case APIRecord::RK_ClassTemplate:424case APIRecord::RK_ClassTemplateSpecialization:425case APIRecord::RK_ClassTemplatePartialSpecialization:426case APIRecord::RK_CXXClass:427Kind["identifier"] = AddLangPrefix("class");428Kind["displayName"] = "Class";429break;430case APIRecord::RK_CXXMethodTemplate:431Kind["identifier"] = AddLangPrefix("method");432Kind["displayName"] = "Method Template";433break;434case APIRecord::RK_CXXMethodTemplateSpecialization:435Kind["identifier"] = AddLangPrefix("method");436Kind["displayName"] = "Method Template Specialization";437break;438case APIRecord::RK_CXXFieldTemplate:439Kind["identifier"] = AddLangPrefix("property");440Kind["displayName"] = "Template Property";441break;442case APIRecord::RK_Concept:443Kind["identifier"] = AddLangPrefix("concept");444Kind["displayName"] = "Concept";445break;446case APIRecord::RK_CXXStaticMethod:447Kind["identifier"] = AddLangPrefix("type.method");448Kind["displayName"] = "Static Method";449break;450case APIRecord::RK_CXXInstanceMethod:451Kind["identifier"] = AddLangPrefix("method");452Kind["displayName"] = "Instance Method";453break;454case APIRecord::RK_CXXConstructorMethod:455Kind["identifier"] = AddLangPrefix("method");456Kind["displayName"] = "Constructor";457break;458case APIRecord::RK_CXXDestructorMethod:459Kind["identifier"] = AddLangPrefix("method");460Kind["displayName"] = "Destructor";461break;462case APIRecord::RK_ObjCIvar:463Kind["identifier"] = AddLangPrefix("ivar");464Kind["displayName"] = "Instance Variable";465break;466case APIRecord::RK_ObjCInstanceMethod:467Kind["identifier"] = AddLangPrefix("method");468Kind["displayName"] = "Instance Method";469break;470case APIRecord::RK_ObjCClassMethod:471Kind["identifier"] = AddLangPrefix("type.method");472Kind["displayName"] = "Type Method";473break;474case APIRecord::RK_ObjCInstanceProperty:475Kind["identifier"] = AddLangPrefix("property");476Kind["displayName"] = "Instance Property";477break;478case APIRecord::RK_ObjCClassProperty:479Kind["identifier"] = AddLangPrefix("type.property");480Kind["displayName"] = "Type Property";481break;482case APIRecord::RK_ObjCInterface:483Kind["identifier"] = AddLangPrefix("class");484Kind["displayName"] = "Class";485break;486case APIRecord::RK_ObjCCategory:487Kind["identifier"] = AddLangPrefix("class.extension");488Kind["displayName"] = "Class Extension";489break;490case APIRecord::RK_ObjCProtocol:491Kind["identifier"] = AddLangPrefix("protocol");492Kind["displayName"] = "Protocol";493break;494case APIRecord::RK_MacroDefinition:495Kind["identifier"] = AddLangPrefix("macro");496Kind["displayName"] = "Macro";497break;498case APIRecord::RK_Typedef:499Kind["identifier"] = AddLangPrefix("typealias");500Kind["displayName"] = "Type Alias";501break;502default:503llvm_unreachable("API Record with uninstantiable kind");504}505506return Kind;507}508509/// Serialize the symbol kind information.510///511/// The Symbol Graph symbol kind property contains a shorthand \c identifier512/// which is prefixed by the source language name, useful for tooling to parse513/// the kind, and a \c displayName for rendering human-readable names.514Object serializeSymbolKind(const APIRecord &Record, Language Lang) {515return serializeSymbolKind(Record.KindForDisplay, Lang);516}517518/// Serialize the function signature field, as specified by the519/// Symbol Graph format.520///521/// The Symbol Graph function signature property contains two arrays.522/// - The \c returns array is the declaration fragments of the return type;523/// - The \c parameters array contains names and declaration fragments of the524/// parameters.525template <typename RecordTy>526void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {527const auto &FS = Record.Signature;528if (FS.empty())529return;530531Object Signature;532serializeArray(Signature, "returns",533serializeDeclarationFragments(FS.getReturnType()));534535Array Parameters;536for (const auto &P : FS.getParameters()) {537Object Parameter;538Parameter["name"] = P.Name;539serializeArray(Parameter, "declarationFragments",540serializeDeclarationFragments(P.Fragments));541Parameters.emplace_back(std::move(Parameter));542}543544if (!Parameters.empty())545Signature["parameters"] = std::move(Parameters);546547serializeObject(Paren, "functionSignature", std::move(Signature));548}549550template <typename RecordTy>551void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {552const auto &Template = Record.Templ;553if (Template.empty())554return;555556Object Generics;557Array GenericParameters;558for (const auto &Param : Template.getParameters()) {559Object Parameter;560Parameter["name"] = Param.Name;561Parameter["index"] = Param.Index;562Parameter["depth"] = Param.Depth;563GenericParameters.emplace_back(std::move(Parameter));564}565if (!GenericParameters.empty())566Generics["parameters"] = std::move(GenericParameters);567568Array GenericConstraints;569for (const auto &Constr : Template.getConstraints()) {570Object Constraint;571Constraint["kind"] = Constr.Kind;572Constraint["lhs"] = Constr.LHS;573Constraint["rhs"] = Constr.RHS;574GenericConstraints.emplace_back(std::move(Constraint));575}576577if (!GenericConstraints.empty())578Generics["constraints"] = std::move(GenericConstraints);579580serializeObject(Paren, "swiftGenerics", Generics);581}582583Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents,584Language Lang) {585Array ParentContexts;586587for (const auto &Parent : Parents) {588Object Elem;589Elem["usr"] = Parent.USR;590Elem["name"] = Parent.Name;591if (Parent.Record)592Elem["kind"] = serializeSymbolKind(Parent.Record->KindForDisplay,593Lang)["identifier"];594else595Elem["kind"] =596serializeSymbolKind(APIRecord::RK_Unknown, Lang)["identifier"];597ParentContexts.emplace_back(std::move(Elem));598}599600return ParentContexts;601}602603/// Walk the records parent information in reverse to generate a hierarchy604/// suitable for serialization.605SmallVector<SymbolReference, 8>606generateHierarchyFromRecord(const APIRecord *Record) {607SmallVector<SymbolReference, 8> ReverseHierarchy;608for (const auto *Current = Record; Current != nullptr;609Current = Current->Parent.Record)610ReverseHierarchy.emplace_back(Current);611612return SmallVector<SymbolReference, 8>(613std::make_move_iterator(ReverseHierarchy.rbegin()),614std::make_move_iterator(ReverseHierarchy.rend()));615}616617SymbolReference getHierarchyReference(const APIRecord *Record,618const APISet &API) {619// If the parent is a category extended from internal module then we need to620// pretend this belongs to the associated interface.621if (auto *CategoryRecord = dyn_cast_or_null<ObjCCategoryRecord>(Record)) {622return CategoryRecord->Interface;623// FIXME: TODO generate path components correctly for categories extending624// an external module.625}626627return SymbolReference(Record);628}629630} // namespace631632Object *ExtendedModule::addSymbol(Object &&Symbol) {633Symbols.emplace_back(std::move(Symbol));634return Symbols.back().getAsObject();635}636637void ExtendedModule::addRelationship(Object &&Relationship) {638Relationships.emplace_back(std::move(Relationship));639}640641/// Defines the format version emitted by SymbolGraphSerializer.642const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};643644Object SymbolGraphSerializer::serializeMetadata() const {645Object Metadata;646serializeObject(Metadata, "formatVersion",647serializeSemanticVersion(FormatVersion));648Metadata["generator"] = clang::getClangFullVersion();649return Metadata;650}651652Object653SymbolGraphSerializer::serializeModuleObject(StringRef ModuleName) const {654Object Module;655Module["name"] = ModuleName;656serializeObject(Module, "platform", serializePlatform(API.getTarget()));657return Module;658}659660bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {661if (!Record)662return true;663664// Skip unconditionally unavailable symbols665if (Record->Availability.isUnconditionallyUnavailable())666return true;667668// Filter out symbols without a name as we can generate correct symbol graphs669// for them. In practice these are anonymous record types that aren't attached670// to a declaration.671if (auto *Tag = dyn_cast<TagRecord>(Record)) {672if (Tag->IsEmbeddedInVarDeclarator)673return true;674}675676// Filter out symbols prefixed with an underscored as they are understood to677// be symbols clients should not use.678if (Record->Name.starts_with("_"))679return true;680681// Skip explicitly ignored symbols.682if (IgnoresList.shouldIgnore(Record->Name))683return true;684685return false;686}687688ExtendedModule &SymbolGraphSerializer::getModuleForCurrentSymbol() {689if (!ForceEmitToMainModule && ModuleForCurrentSymbol)690return *ModuleForCurrentSymbol;691692return MainModule;693}694695Array SymbolGraphSerializer::serializePathComponents(696const APIRecord *Record) const {697return Array(map_range(Hierarchy, [](auto Elt) { return Elt.Name; }));698}699700StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {701switch (Kind) {702case RelationshipKind::MemberOf:703return "memberOf";704case RelationshipKind::InheritsFrom:705return "inheritsFrom";706case RelationshipKind::ConformsTo:707return "conformsTo";708case RelationshipKind::ExtensionTo:709return "extensionTo";710}711llvm_unreachable("Unhandled relationship kind");712}713714void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,715const SymbolReference &Source,716const SymbolReference &Target,717ExtendedModule &Into) {718Object Relationship;719SmallString<64> TestRelLabel;720if (EmitSymbolLabelsForTesting) {721llvm::raw_svector_ostream OS(TestRelLabel);722OS << SymbolGraphSerializer::getRelationshipString(Kind) << " $ "723<< Source.USR << " $ ";724if (Target.USR.empty())725OS << Target.Name;726else727OS << Target.USR;728Relationship["!testRelLabel"] = TestRelLabel;729}730Relationship["source"] = Source.USR;731Relationship["target"] = Target.USR;732Relationship["targetFallback"] = Target.Name;733Relationship["kind"] = SymbolGraphSerializer::getRelationshipString(Kind);734735if (ForceEmitToMainModule)736MainModule.addRelationship(std::move(Relationship));737else738Into.addRelationship(std::move(Relationship));739}740741StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {742switch (Kind) {743case ConstraintKind::Conformance:744return "conformance";745case ConstraintKind::ConditionalConformance:746return "conditionalConformance";747}748llvm_unreachable("Unhandled constraint kind");749}750751void SymbolGraphSerializer::serializeAPIRecord(const APIRecord *Record) {752Object Obj;753754// If we need symbol labels for testing emit the USR as the value and the key755// starts with '!'' to ensure it ends up at the top of the object.756if (EmitSymbolLabelsForTesting)757Obj["!testLabel"] = Record->USR;758759serializeObject(Obj, "identifier",760serializeIdentifier(*Record, API.getLanguage()));761serializeObject(Obj, "kind", serializeSymbolKind(*Record, API.getLanguage()));762serializeObject(Obj, "names", serializeNames(Record));763serializeObject(764Obj, "location",765serializeSourceLocation(Record->Location, /*IncludeFileURI=*/true));766serializeArray(Obj, "availability",767serializeAvailability(Record->Availability));768serializeObject(Obj, "docComment", serializeDocComment(Record->Comment));769serializeArray(Obj, "declarationFragments",770serializeDeclarationFragments(Record->Declaration));771772Obj["pathComponents"] = serializePathComponents(Record);773Obj["accessLevel"] = Record->Access.getAccess();774775ExtendedModule &Module = getModuleForCurrentSymbol();776// If the hierarchy has at least one parent and child.777if (Hierarchy.size() >= 2)778serializeRelationship(MemberOf, Hierarchy.back(),779Hierarchy[Hierarchy.size() - 2], Module);780781CurrentSymbol = Module.addSymbol(std::move(Obj));782}783784bool SymbolGraphSerializer::traverseAPIRecord(const APIRecord *Record) {785if (!Record)786return true;787if (shouldSkip(Record))788return true;789Hierarchy.push_back(getHierarchyReference(Record, API));790// Defer traversal mechanics to APISetVisitor base implementation791auto RetVal = Base::traverseAPIRecord(Record);792Hierarchy.pop_back();793return RetVal;794}795796bool SymbolGraphSerializer::visitAPIRecord(const APIRecord *Record) {797serializeAPIRecord(Record);798return true;799}800801bool SymbolGraphSerializer::visitGlobalFunctionRecord(802const GlobalFunctionRecord *Record) {803if (!CurrentSymbol)804return true;805806serializeFunctionSignatureMixin(*CurrentSymbol, *Record);807return true;808}809810bool SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord *Record) {811if (!CurrentSymbol)812return true;813814for (const auto &Base : Record->Bases)815serializeRelationship(RelationshipKind::InheritsFrom, Record, Base,816getModuleForCurrentSymbol());817return true;818}819820bool SymbolGraphSerializer::visitClassTemplateRecord(821const ClassTemplateRecord *Record) {822if (!CurrentSymbol)823return true;824825serializeTemplateMixin(*CurrentSymbol, *Record);826return true;827}828829bool SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(830const ClassTemplatePartialSpecializationRecord *Record) {831if (!CurrentSymbol)832return true;833834serializeTemplateMixin(*CurrentSymbol, *Record);835return true;836}837838bool SymbolGraphSerializer::visitCXXMethodRecord(839const CXXMethodRecord *Record) {840if (!CurrentSymbol)841return true;842843serializeFunctionSignatureMixin(*CurrentSymbol, *Record);844return true;845}846847bool SymbolGraphSerializer::visitCXXMethodTemplateRecord(848const CXXMethodTemplateRecord *Record) {849if (!CurrentSymbol)850return true;851852serializeTemplateMixin(*CurrentSymbol, *Record);853return true;854}855856bool SymbolGraphSerializer::visitCXXFieldTemplateRecord(857const CXXFieldTemplateRecord *Record) {858if (!CurrentSymbol)859return true;860861serializeTemplateMixin(*CurrentSymbol, *Record);862return true;863}864865bool SymbolGraphSerializer::visitConceptRecord(const ConceptRecord *Record) {866if (!CurrentSymbol)867return true;868869serializeTemplateMixin(*CurrentSymbol, *Record);870return true;871}872873bool SymbolGraphSerializer::visitGlobalVariableTemplateRecord(874const GlobalVariableTemplateRecord *Record) {875if (!CurrentSymbol)876return true;877878serializeTemplateMixin(*CurrentSymbol, *Record);879return true;880}881882bool SymbolGraphSerializer::883visitGlobalVariableTemplatePartialSpecializationRecord(884const GlobalVariableTemplatePartialSpecializationRecord *Record) {885if (!CurrentSymbol)886return true;887888serializeTemplateMixin(*CurrentSymbol, *Record);889return true;890}891892bool SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(893const GlobalFunctionTemplateRecord *Record) {894if (!CurrentSymbol)895return true;896897serializeTemplateMixin(*CurrentSymbol, *Record);898return true;899}900901bool SymbolGraphSerializer::visitObjCContainerRecord(902const ObjCContainerRecord *Record) {903if (!CurrentSymbol)904return true;905906for (const auto &Protocol : Record->Protocols)907serializeRelationship(ConformsTo, Record, Protocol,908getModuleForCurrentSymbol());909910return true;911}912913bool SymbolGraphSerializer::visitObjCInterfaceRecord(914const ObjCInterfaceRecord *Record) {915if (!CurrentSymbol)916return true;917918if (!Record->SuperClass.empty())919serializeRelationship(InheritsFrom, Record, Record->SuperClass,920getModuleForCurrentSymbol());921return true;922}923924bool SymbolGraphSerializer::traverseObjCCategoryRecord(925const ObjCCategoryRecord *Record) {926if (SkipSymbolsInCategoriesToExternalTypes &&927!API.findRecordForUSR(Record->Interface.USR))928return true;929930auto *CurrentModule = ModuleForCurrentSymbol;931if (Record->isExtendingExternalModule())932ModuleForCurrentSymbol = &ExtendedModules[Record->Interface.Source];933934if (!walkUpFromObjCCategoryRecord(Record))935return false;936937bool RetVal = traverseRecordContext(Record);938ModuleForCurrentSymbol = CurrentModule;939return RetVal;940}941942bool SymbolGraphSerializer::walkUpFromObjCCategoryRecord(943const ObjCCategoryRecord *Record) {944return visitObjCCategoryRecord(Record);945}946947bool SymbolGraphSerializer::visitObjCCategoryRecord(948const ObjCCategoryRecord *Record) {949// If we need to create a record for the category in the future do so here,950// otherwise everything is set up to pretend that the category is in fact the951// interface it extends.952for (const auto &Protocol : Record->Protocols)953serializeRelationship(ConformsTo, Record->Interface, Protocol,954getModuleForCurrentSymbol());955956return true;957}958959bool SymbolGraphSerializer::visitObjCMethodRecord(960const ObjCMethodRecord *Record) {961if (!CurrentSymbol)962return true;963964serializeFunctionSignatureMixin(*CurrentSymbol, *Record);965return true;966}967968bool SymbolGraphSerializer::visitObjCInstanceVariableRecord(969const ObjCInstanceVariableRecord *Record) {970// FIXME: serialize ivar access control here.971return true;972}973974bool SymbolGraphSerializer::walkUpFromTypedefRecord(975const TypedefRecord *Record) {976// Short-circuit walking up the class hierarchy and handle creating typedef977// symbol objects manually as there are additional symbol dropping rules to978// respect.979return visitTypedefRecord(Record);980}981982bool SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord *Record) {983// Typedefs of anonymous types have their entries unified with the underlying984// type.985bool ShouldDrop = Record->UnderlyingType.Name.empty();986// enums declared with `NS_OPTION` have a named enum and a named typedef, with987// the same name988ShouldDrop |= (Record->UnderlyingType.Name == Record->Name);989if (ShouldDrop)990return true;991992// Create the symbol record if the other symbol droppping rules permit it.993serializeAPIRecord(Record);994if (!CurrentSymbol)995return true;996997(*CurrentSymbol)["type"] = Record->UnderlyingType.USR;998999return true;1000}10011002void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {1003switch (Record->getKind()) {1004// dispatch to the relevant walkUpFromMethod1005#define CONCRETE_RECORD(CLASS, BASE, KIND) \1006case APIRecord::KIND: { \1007walkUpFrom##CLASS(static_cast<const CLASS *>(Record)); \1008break; \1009}1010#include "clang/ExtractAPI/APIRecords.inc"1011// otherwise fallback on the only behavior we can implement safely.1012case APIRecord::RK_Unknown:1013visitAPIRecord(Record);1014break;1015default:1016llvm_unreachable("API Record with uninstantiable kind");1017}1018}10191020Object SymbolGraphSerializer::serializeGraph(StringRef ModuleName,1021ExtendedModule &&EM) {1022Object Root;1023serializeObject(Root, "metadata", serializeMetadata());1024serializeObject(Root, "module", serializeModuleObject(ModuleName));10251026Root["symbols"] = std::move(EM.Symbols);1027Root["relationships"] = std::move(EM.Relationships);10281029return Root;1030}10311032void SymbolGraphSerializer::serializeGraphToStream(1033raw_ostream &OS, SymbolGraphSerializerOption Options, StringRef ModuleName,1034ExtendedModule &&EM) {1035Object Root = serializeGraph(ModuleName, std::move(EM));1036if (Options.Compact)1037OS << formatv("{0}", json::Value(std::move(Root))) << "\n";1038else1039OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n";1040}10411042void SymbolGraphSerializer::serializeMainSymbolGraph(1043raw_ostream &OS, const APISet &API, const APIIgnoresList &IgnoresList,1044SymbolGraphSerializerOption Options) {1045SymbolGraphSerializer Serializer(1046API, IgnoresList, Options.EmitSymbolLabelsForTesting,1047/*ForceEmitToMainModule=*/true,1048/*SkipSymbolsInCategoriesToExternalTypes=*/true);10491050Serializer.traverseAPISet();1051Serializer.serializeGraphToStream(OS, Options, API.ProductName,1052std::move(Serializer.MainModule));1053// FIXME: TODO handle extended modules here1054}10551056void SymbolGraphSerializer::serializeWithExtensionGraphs(1057raw_ostream &MainOutput, const APISet &API,1058const APIIgnoresList &IgnoresList,1059llvm::function_ref<std::unique_ptr<llvm::raw_pwrite_stream>(Twine BaseName)>1060CreateOutputStream,1061SymbolGraphSerializerOption Options) {1062SymbolGraphSerializer Serializer(API, IgnoresList,1063Options.EmitSymbolLabelsForTesting);1064Serializer.traverseAPISet();10651066Serializer.serializeGraphToStream(MainOutput, Options, API.ProductName,1067std::move(Serializer.MainModule));10681069for (auto &ExtensionSGF : Serializer.ExtendedModules) {1070if (auto ExtensionOS =1071CreateOutputStream(ExtensionSGF.getKey() + "@" + API.ProductName))1072Serializer.serializeGraphToStream(*ExtensionOS, Options,1073ExtensionSGF.getKey(),1074std::move(ExtensionSGF.getValue()));1075}1076}10771078std::optional<Object>1079SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,1080const APISet &API) {1081APIRecord *Record = API.findRecordForUSR(USR);1082if (!Record)1083return {};10841085Object Root;1086APIIgnoresList EmptyIgnores;1087SymbolGraphSerializer Serializer(API, EmptyIgnores,1088/*EmitSymbolLabelsForTesting*/ false,1089/*ForceEmitToMainModule*/ true);10901091// Set up serializer parent chain1092Serializer.Hierarchy = generateHierarchyFromRecord(Record);10931094Serializer.serializeSingleRecord(Record);1095serializeObject(Root, "symbolGraph",1096Serializer.serializeGraph(API.ProductName,1097std::move(Serializer.MainModule)));10981099Language Lang = API.getLanguage();1100serializeArray(Root, "parentContexts",1101generateParentContexts(Serializer.Hierarchy, Lang));11021103Array RelatedSymbols;11041105for (const auto &Fragment : Record->Declaration.getFragments()) {1106// If we don't have a USR there isn't much we can do.1107if (Fragment.PreciseIdentifier.empty())1108continue;11091110APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);11111112// If we can't find the record let's skip.1113if (!RelatedRecord)1114continue;11151116Object RelatedSymbol;1117RelatedSymbol["usr"] = RelatedRecord->USR;1118RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);1119RelatedSymbol["accessLevel"] = RelatedRecord->Access.getAccess();1120RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();1121RelatedSymbol["moduleName"] = API.ProductName;1122RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;11231124serializeArray(RelatedSymbol, "parentContexts",1125generateParentContexts(1126generateHierarchyFromRecord(RelatedRecord), Lang));11271128RelatedSymbols.push_back(std::move(RelatedSymbol));1129}11301131serializeArray(Root, "relatedSymbols", RelatedSymbols);1132return Root;1133}113411351136