diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2024-07-27 23:34:35 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2024-10-23 18:26:01 +0000 | 
| commit | 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583 (patch) | |
| tree | 6cf5ab1f05330c6773b1f3f64799d56a9c7a1faa /contrib/llvm-project/clang/lib/ExtractAPI/Serialization | |
| parent | 6b9f7133aba44189d9625c352bc2c2a59baf18ef (diff) | |
| parent | ac9a064cb179f3425b310fa2847f8764ac970a4d (diff) | |
Diffstat (limited to 'contrib/llvm-project/clang/lib/ExtractAPI/Serialization')
| -rw-r--r-- | contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp | 988 | 
1 files changed, 410 insertions, 578 deletions
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index 349b93e2a232..6e56ee5b573f 100644 --- a/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -14,45 +14,49 @@  #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"  #include "clang/Basic/SourceLocation.h"  #include "clang/Basic/Version.h" +#include "clang/ExtractAPI/API.h"  #include "clang/ExtractAPI/DeclarationFragments.h"  #include "llvm/ADT/STLExtras.h"  #include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/SmallVector.h"  #include "llvm/Support/Casting.h"  #include "llvm/Support/Compiler.h"  #include "llvm/Support/Path.h"  #include "llvm/Support/VersionTuple.h" +#include "llvm/Support/raw_ostream.h" +#include <iterator>  #include <optional>  #include <type_traits>  using namespace clang;  using namespace clang::extractapi;  using namespace llvm; -using namespace llvm::json;  namespace {  /// Helper function to inject a JSON object \p Obj into another object \p Paren  /// at position \p Key. -void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) { +void serializeObject(Object &Paren, StringRef Key, +                     std::optional<Object> &&Obj) {    if (Obj)      Paren[Key] = std::move(*Obj);  } -/// Helper function to inject a StringRef \p String into an object \p Paren at -/// position \p Key -void serializeString(Object &Paren, StringRef Key, -                     std::optional<std::string> String) { -  if (String) -    Paren[Key] = std::move(*String); -} -  /// Helper function to inject a JSON array \p Array into object \p Paren at  /// position \p Key. -void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) { +void serializeArray(Object &Paren, StringRef Key, +                    std::optional<Array> &&Array) {    if (Array)      Paren[Key] = std::move(*Array);  } +/// Helper function to inject a JSON array composed of the values in \p C into +/// object \p Paren at position \p Key. +template <typename ContainerTy> +void serializeArray(Object &Paren, StringRef Key, ContainerTy &&C) { +  Paren[Key] = Array(C); +} +  /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version  /// format.  /// @@ -159,27 +163,29 @@ std::optional<Array> serializeAvailability(const AvailabilityInfo &Avail) {    if (Avail.isDefault())      return std::nullopt; -  Object Availability;    Array AvailabilityArray; -  Availability["domain"] = Avail.Domain; -  serializeObject(Availability, "introduced", -                  serializeSemanticVersion(Avail.Introduced)); -  serializeObject(Availability, "deprecated", -                  serializeSemanticVersion(Avail.Deprecated)); -  serializeObject(Availability, "obsoleted", -                  serializeSemanticVersion(Avail.Obsoleted)); +    if (Avail.isUnconditionallyDeprecated()) {      Object UnconditionallyDeprecated;      UnconditionallyDeprecated["domain"] = "*";      UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;      AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));    } -  if (Avail.isUnconditionallyUnavailable()) { -    Object UnconditionallyUnavailable; -    UnconditionallyUnavailable["domain"] = "*"; -    UnconditionallyUnavailable["isUnconditionallyUnavailable"] = true; -    AvailabilityArray.emplace_back(std::move(UnconditionallyUnavailable)); +  Object Availability; + +  Availability["domain"] = Avail.Domain; + +  if (Avail.isUnavailable()) { +    Availability["isUnconditionallyUnavailable"] = true; +  } else { +    serializeObject(Availability, "introduced", +                    serializeSemanticVersion(Avail.Introduced)); +    serializeObject(Availability, "deprecated", +                    serializeSemanticVersion(Avail.Deprecated)); +    serializeObject(Availability, "obsoleted", +                    serializeSemanticVersion(Avail.Obsoleted));    } +    AvailabilityArray.emplace_back(std::move(Availability));    return AvailabilityArray;  } @@ -208,6 +214,7 @@ StringRef getLanguageName(Language Lang) {    case Language::Unknown:    case Language::Asm:    case Language::LLVM_IR: +  case Language::CIR:      llvm_unreachable("Unsupported language kind");    } @@ -247,6 +254,7 @@ std::optional<Object> serializeDocComment(const DocComment &Comment) {      return std::nullopt;    Object DocComment; +    Array LinesArray;    for (const auto &CommentLine : Comment) {      Object Line; @@ -255,7 +263,8 @@ std::optional<Object> serializeDocComment(const DocComment &Comment) {                      serializeSourceRange(CommentLine.Begin, CommentLine.End));      LinesArray.emplace_back(std::move(Line));    } -  serializeArray(DocComment, "lines", LinesArray); + +  serializeArray(DocComment, "lines", std::move(LinesArray));    return DocComment;  } @@ -321,19 +330,14 @@ serializeDeclarationFragments(const DeclarationFragments &DF) {  ///   - \c subHeading : An array of declaration fragments that provides tags,  ///     and potentially more tokens (for example the \c +/- symbol for  ///     Objective-C methods). Can be used as sub-headings for documentation. -Object serializeNames(const APIRecord &Record) { +Object serializeNames(const APIRecord *Record) {    Object Names; -  if (auto *CategoryRecord = -          dyn_cast_or_null<const ObjCCategoryRecord>(&Record)) -    Names["title"] = -        (CategoryRecord->Interface.Name + " (" + Record.Name + ")").str(); -  else -    Names["title"] = Record.Name; +  Names["title"] = Record->Name;    serializeArray(Names, "subHeading", -                 serializeDeclarationFragments(Record.SubHeading)); +                 serializeDeclarationFragments(Record->SubHeading));    DeclarationFragments NavigatorFragments; -  NavigatorFragments.append(Record.Name, +  NavigatorFragments.append(Record->Name,                              DeclarationFragments::FragmentKind::Identifier,                              /*PreciseIdentifier*/ "");    serializeArray(Names, "navigator", @@ -350,7 +354,8 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {    Object Kind;    switch (RK) {    case APIRecord::RK_Unknown: -    llvm_unreachable("Records should have an explicit kind"); +    Kind["identifier"] = AddLangPrefix("unknown"); +    Kind["displayName"] = "Unknown";      break;    case APIRecord::RK_Namespace:      Kind["identifier"] = AddLangPrefix("namespace"); @@ -483,10 +488,6 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {      Kind["identifier"] = AddLangPrefix("class.extension");      Kind["displayName"] = "Class Extension";      break; -  case APIRecord::RK_ObjCCategoryModule: -    Kind["identifier"] = AddLangPrefix("module.extension"); -    Kind["displayName"] = "Module Extension"; -    break;    case APIRecord::RK_ObjCProtocol:      Kind["identifier"] = AddLangPrefix("protocol");      Kind["displayName"] = "Protocol"; @@ -499,6 +500,8 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {      Kind["identifier"] = AddLangPrefix("typealias");      Kind["displayName"] = "Type Alias";      break; +  default: +    llvm_unreachable("API Record with uninstantiable kind");    }    return Kind; @@ -510,15 +513,21 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {  /// which is prefixed by the source language name, useful for tooling to parse  /// the kind, and a \c displayName for rendering human-readable names.  Object serializeSymbolKind(const APIRecord &Record, Language Lang) { -  return serializeSymbolKind(Record.getKind(), Lang); +  return serializeSymbolKind(Record.KindForDisplay, Lang);  } +/// Serialize the function signature field, as specified by the +/// Symbol Graph format. +/// +/// The Symbol Graph function signature property contains two arrays. +///   - The \c returns array is the declaration fragments of the return type; +///   - The \c parameters array contains names and declaration fragments of the +///     parameters.  template <typename RecordTy> -std::optional<Object> -serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) { +void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {    const auto &FS = Record.Signature;    if (FS.empty()) -    return std::nullopt; +    return;    Object Signature;    serializeArray(Signature, "returns", @@ -536,63 +545,14 @@ serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {    if (!Parameters.empty())      Signature["parameters"] = std::move(Parameters); -  return Signature; +  serializeObject(Paren, "functionSignature", std::move(Signature));  }  template <typename RecordTy> -std::optional<Object> -serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) { -  return std::nullopt; -} - -/// Serialize the function signature field, as specified by the -/// Symbol Graph format. -/// -/// The Symbol Graph function signature property contains two arrays. -///   - The \c returns array is the declaration fragments of the return type; -///   - The \c parameters array contains names and declaration fragments of the -///     parameters. -/// -/// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the -/// formatted function signature. -template <typename RecordTy> -void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { -  serializeObject(Paren, "functionSignature", -                  serializeFunctionSignatureMixinImpl( -                      Record, has_function_signature<RecordTy>())); -} - -template <typename RecordTy> -std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record, -                                                    std::true_type) { -  const auto &AccessControl = Record.Access; -  std::string Access; -  if (AccessControl.empty()) -    return std::nullopt; -  Access = AccessControl.getAccess(); -  return Access; -} - -template <typename RecordTy> -std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record, -                                                    std::false_type) { -  return std::nullopt; -} - -template <typename RecordTy> -void serializeAccessMixin(Object &Paren, const RecordTy &Record) { -  auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>()); -  if (!accessLevel.has_value()) -    accessLevel = "public"; -  serializeString(Paren, "accessLevel", accessLevel); -} - -template <typename RecordTy> -std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, -                                                 std::true_type) { +void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {    const auto &Template = Record.Templ;    if (Template.empty()) -    return std::nullopt; +    return;    Object Generics;    Array GenericParameters; @@ -618,97 +578,66 @@ std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record,    if (!GenericConstraints.empty())      Generics["constraints"] = std::move(GenericConstraints); -  return Generics; +  serializeObject(Paren, "swiftGenerics", Generics);  } -template <typename RecordTy> -std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, -                                                 std::false_type) { -  return std::nullopt; -} - -template <typename RecordTy> -void serializeTemplateMixin(Object &Paren, const RecordTy &Record) { -  serializeObject(Paren, "swiftGenerics", -                  serializeTemplateMixinImpl(Record, has_template<RecordTy>())); -} +Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents, +                             Language Lang) { +  Array ParentContexts; -struct PathComponent { -  StringRef USR; -  StringRef Name; -  APIRecord::RecordKind Kind; +  for (const auto &Parent : Parents) { +    Object Elem; +    Elem["usr"] = Parent.USR; +    Elem["name"] = Parent.Name; +    if (Parent.Record) +      Elem["kind"] = serializeSymbolKind(Parent.Record->KindForDisplay, +                                         Lang)["identifier"]; +    else +      Elem["kind"] = +          serializeSymbolKind(APIRecord::RK_Unknown, Lang)["identifier"]; +    ParentContexts.emplace_back(std::move(Elem)); +  } -  PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind) -      : USR(USR), Name(Name), Kind(Kind) {} -}; +  return ParentContexts; +} -template <typename RecordTy> -bool generatePathComponents( -    const RecordTy &Record, const APISet &API, -    function_ref<void(const PathComponent &)> ComponentTransformer) { -  SmallVector<PathComponent, 4> ReverseComponenents; -  ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind()); -  const auto *CurrentParent = &Record.ParentInformation; -  bool FailedToFindParent = false; -  while (CurrentParent && !CurrentParent->empty()) { -    PathComponent CurrentParentComponent(CurrentParent->ParentUSR, -                                         CurrentParent->ParentName, -                                         CurrentParent->ParentKind); - -    auto *ParentRecord = CurrentParent->ParentRecord; -    // Slow path if we don't have a direct reference to the ParentRecord -    if (!ParentRecord) -      ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR); - -    // If the parent is a category extended from internal module then we need to -    // pretend this belongs to the associated interface. -    if (auto *CategoryRecord = -            dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) { -      if (!CategoryRecord->IsFromExternalModule) { -        ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR); -        CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR, -                                               CategoryRecord->Interface.Name, -                                               APIRecord::RK_ObjCInterface); -      } -    } - -    // The parent record doesn't exist which means the symbol shouldn't be -    // treated as part of the current product. -    if (!ParentRecord) { -      FailedToFindParent = true; -      break; -    } - -    ReverseComponenents.push_back(std::move(CurrentParentComponent)); -    CurrentParent = &ParentRecord->ParentInformation; +/// Walk the records parent information in reverse to generate a hierarchy +/// suitable for serialization. +SmallVector<SymbolReference, 8> +generateHierarchyFromRecord(const APIRecord *Record) { +  SmallVector<SymbolReference, 8> ReverseHierarchy; +  for (const auto *Current = Record; Current != nullptr; +       Current = Current->Parent.Record) +    ReverseHierarchy.emplace_back(Current); + +  return SmallVector<SymbolReference, 8>( +      std::make_move_iterator(ReverseHierarchy.rbegin()), +      std::make_move_iterator(ReverseHierarchy.rend())); +} + +SymbolReference getHierarchyReference(const APIRecord *Record, +                                      const APISet &API) { +  // If the parent is a category extended from internal module then we need to +  // pretend this belongs to the associated interface. +  if (auto *CategoryRecord = dyn_cast_or_null<ObjCCategoryRecord>(Record)) { +    return CategoryRecord->Interface; +    // FIXME: TODO generate path components correctly for categories extending +    // an external module.    } -  for (const auto &PC : reverse(ReverseComponenents)) -    ComponentTransformer(PC); - -  return FailedToFindParent; +  return SymbolReference(Record);  } -Object serializeParentContext(const PathComponent &PC, Language Lang) { -  Object ParentContextElem; -  ParentContextElem["usr"] = PC.USR; -  ParentContextElem["name"] = PC.Name; -  ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"]; -  return ParentContextElem; -} +} // namespace -template <typename RecordTy> -Array generateParentContexts(const RecordTy &Record, const APISet &API, -                             Language Lang) { -  Array ParentContexts; -  generatePathComponents( -      Record, API, [Lang, &ParentContexts](const PathComponent &PC) { -        ParentContexts.push_back(serializeParentContext(PC, Lang)); -      }); +Object *ExtendedModule::addSymbol(Object &&Symbol) { +  Symbols.emplace_back(std::move(Symbol)); +  return Symbols.back().getAsObject(); +} -  return ParentContexts; +void ExtendedModule::addRelationship(Object &&Relationship) { +  Relationships.emplace_back(std::move(Relationship));  } -} // namespace  /// Defines the format version emitted by SymbolGraphSerializer.  const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3}; @@ -721,84 +650,52 @@ Object SymbolGraphSerializer::serializeMetadata() const {    return Metadata;  } -Object SymbolGraphSerializer::serializeModule() const { +Object +SymbolGraphSerializer::serializeModuleObject(StringRef ModuleName) const {    Object Module; -  // The user is expected to always pass `--product-name=` on the command line -  // to populate this field. -  Module["name"] = API.ProductName; +  Module["name"] = ModuleName;    serializeObject(Module, "platform", serializePlatform(API.getTarget()));    return Module;  } -bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const { -  // Skip explicitly ignored symbols. -  if (IgnoresList.shouldIgnore(Record.Name)) +bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const { +  if (!Record)      return true;    // Skip unconditionally unavailable symbols -  if (Record.Availability.isUnconditionallyUnavailable()) +  if (Record->Availability.isUnconditionallyUnavailable())      return true; +  // Filter out symbols without a name as we can generate correct symbol graphs +  // for them. In practice these are anonymous record types that aren't attached +  // to a declaration. +  if (auto *Tag = dyn_cast<TagRecord>(Record)) { +    if (Tag->IsEmbeddedInVarDeclarator) +      return true; +  } +    // Filter out symbols prefixed with an underscored as they are understood to    // be symbols clients should not use. -  if (Record.Name.starts_with("_")) +  if (Record->Name.starts_with("_")) +    return true; + +  // Skip explicitly ignored symbols. +  if (IgnoresList.shouldIgnore(Record->Name))      return true;    return false;  } -template <typename RecordTy> -std::optional<Object> -SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { -  if (shouldSkip(Record)) -    return std::nullopt; +ExtendedModule &SymbolGraphSerializer::getModuleForCurrentSymbol() { +  if (!ForceEmitToMainModule && ModuleForCurrentSymbol) +    return *ModuleForCurrentSymbol; -  Object Obj; -  serializeObject(Obj, "identifier", -                  serializeIdentifier(Record, API.getLanguage())); -  serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage())); -  serializeObject(Obj, "names", serializeNames(Record)); -  serializeObject( -      Obj, "location", -      serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true)); -  serializeArray(Obj, "availability", -                 serializeAvailability(Record.Availability)); -  serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); -  serializeArray(Obj, "declarationFragments", -                 serializeDeclarationFragments(Record.Declaration)); -  SmallVector<StringRef, 4> PathComponentsNames; -  // If this returns true it indicates that we couldn't find a symbol in the -  // hierarchy. -  if (generatePathComponents(Record, API, -                             [&PathComponentsNames](const PathComponent &PC) { -                               PathComponentsNames.push_back(PC.Name); -                             })) -    return {}; - -  serializeArray(Obj, "pathComponents", Array(PathComponentsNames)); - -  serializeFunctionSignatureMixin(Obj, Record); -  serializeAccessMixin(Obj, Record); -  serializeTemplateMixin(Obj, Record); - -  return Obj; +  return MainModule;  } -template <typename MemberTy> -void SymbolGraphSerializer::serializeMembers( -    const APIRecord &Record, -    const SmallVector<std::unique_ptr<MemberTy>> &Members) { -  // Members should not be serialized if we aren't recursing. -  if (!ShouldRecurse) -    return; -  for (const auto &Member : Members) { -    auto MemberRecord = serializeAPIRecord(*Member); -    if (!MemberRecord) -      continue; - -    Symbols.emplace_back(std::move(*MemberRecord)); -    serializeRelationship(RelationshipKind::MemberOf, *Member, Record); -  } +Array SymbolGraphSerializer::serializePathComponents( +    const APIRecord *Record) const { +  return Array(map_range(Hierarchy, [](auto Elt) { return Elt.Name; }));  }  StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) { @@ -815,6 +712,33 @@ StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {    llvm_unreachable("Unhandled relationship kind");  } +void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, +                                                  const SymbolReference &Source, +                                                  const SymbolReference &Target, +                                                  ExtendedModule &Into) { +  Object Relationship; +  SmallString<64> TestRelLabel; +  if (EmitSymbolLabelsForTesting) { +    llvm::raw_svector_ostream OS(TestRelLabel); +    OS << SymbolGraphSerializer::getRelationshipString(Kind) << " $ " +       << Source.USR << " $ "; +    if (Target.USR.empty()) +      OS << Target.Name; +    else +      OS << Target.USR; +    Relationship["!testRelLabel"] = TestRelLabel; +  } +  Relationship["source"] = Source.USR; +  Relationship["target"] = Target.USR; +  Relationship["targetFallback"] = Target.Name; +  Relationship["kind"] = SymbolGraphSerializer::getRelationshipString(Kind); + +  if (ForceEmitToMainModule) +    MainModule.addRelationship(std::move(Relationship)); +  else +    Into.addRelationship(std::move(Relationship)); +} +  StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {    switch (Kind) {    case ConstraintKind::Conformance: @@ -825,430 +749,331 @@ StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {    llvm_unreachable("Unhandled constraint kind");  } -void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, -                                                  SymbolReference Source, -                                                  SymbolReference Target) { -  Object Relationship; -  Relationship["source"] = Source.USR; -  Relationship["target"] = Target.USR; -  Relationship["targetFallback"] = Target.Name; -  Relationship["kind"] = getRelationshipString(Kind); - -  Relationships.emplace_back(std::move(Relationship)); -} +void SymbolGraphSerializer::serializeAPIRecord(const APIRecord *Record) { +  Object Obj; -void SymbolGraphSerializer::visitNamespaceRecord( -    const NamespaceRecord &Record) { -  auto Namespace = serializeAPIRecord(Record); -  if (!Namespace) -    return; -  Symbols.emplace_back(std::move(*Namespace)); -  if (!Record.ParentInformation.empty()) -    serializeRelationship(RelationshipKind::MemberOf, Record, -                          Record.ParentInformation.ParentRecord); -} +  // If we need symbol labels for testing emit the USR as the value and the key +  // starts with '!'' to ensure it ends up at the top of the object. +  if (EmitSymbolLabelsForTesting) +    Obj["!testLabel"] = Record->USR; -void SymbolGraphSerializer::visitGlobalFunctionRecord( -    const GlobalFunctionRecord &Record) { -  auto Obj = serializeAPIRecord(Record); -  if (!Obj) -    return; +  serializeObject(Obj, "identifier", +                  serializeIdentifier(*Record, API.getLanguage())); +  serializeObject(Obj, "kind", serializeSymbolKind(*Record, API.getLanguage())); +  serializeObject(Obj, "names", serializeNames(Record)); +  serializeObject( +      Obj, "location", +      serializeSourceLocation(Record->Location, /*IncludeFileURI=*/true)); +  serializeArray(Obj, "availability", +                 serializeAvailability(Record->Availability)); +  serializeObject(Obj, "docComment", serializeDocComment(Record->Comment)); +  serializeArray(Obj, "declarationFragments", +                 serializeDeclarationFragments(Record->Declaration)); -  Symbols.emplace_back(std::move(*Obj)); -} +  Obj["pathComponents"] = serializePathComponents(Record); +  Obj["accessLevel"] = Record->Access.getAccess(); -void SymbolGraphSerializer::visitGlobalVariableRecord( -    const GlobalVariableRecord &Record) { -  auto Obj = serializeAPIRecord(Record); -  if (!Obj) -    return; +  ExtendedModule &Module = getModuleForCurrentSymbol(); +  // If the hierarchy has at least one parent and child. +  if (Hierarchy.size() >= 2) +    serializeRelationship(MemberOf, Hierarchy.back(), +                          Hierarchy[Hierarchy.size() - 2], Module); -  Symbols.emplace_back(std::move(*Obj)); +  CurrentSymbol = Module.addSymbol(std::move(Obj));  } -void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) { -  auto Enum = serializeAPIRecord(Record); -  if (!Enum) -    return; - -  Symbols.emplace_back(std::move(*Enum)); -  serializeMembers(Record, Record.Constants); +bool SymbolGraphSerializer::traverseAPIRecord(const APIRecord *Record) { +  if (!Record) +    return true; +  if (shouldSkip(Record)) +    return true; +  Hierarchy.push_back(getHierarchyReference(Record, API)); +  // Defer traversal mechanics to APISetVisitor base implementation +  auto RetVal = Base::traverseAPIRecord(Record); +  Hierarchy.pop_back(); +  return RetVal;  } -void SymbolGraphSerializer::visitRecordRecord(const RecordRecord &Record) { -  auto SerializedRecord = serializeAPIRecord(Record); -  if (!SerializedRecord) -    return; - -  Symbols.emplace_back(std::move(*SerializedRecord)); -  serializeMembers(Record, Record.Fields); +bool SymbolGraphSerializer::visitAPIRecord(const APIRecord *Record) { +  serializeAPIRecord(Record); +  return true;  } -void SymbolGraphSerializer::visitStaticFieldRecord( -    const StaticFieldRecord &Record) { -  auto StaticField = serializeAPIRecord(Record); -  if (!StaticField) -    return; -  Symbols.emplace_back(std::move(*StaticField)); -  serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context); +bool SymbolGraphSerializer::visitGlobalFunctionRecord( +    const GlobalFunctionRecord *Record) { +  if (!CurrentSymbol) +    return true; + +  serializeFunctionSignatureMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) { -  auto Class = serializeAPIRecord(Record); -  if (!Class) -    return; +bool SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord *Record) { +  if (!CurrentSymbol) +    return true; -  Symbols.emplace_back(std::move(*Class)); -  for (const auto &Base : Record.Bases) -    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); -  if (!Record.ParentInformation.empty()) -    serializeRelationship(RelationshipKind::MemberOf, Record, -                          Record.ParentInformation.ParentRecord); +  for (const auto &Base : Record->Bases) +    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base, +                          getModuleForCurrentSymbol()); +  return true;  } -void SymbolGraphSerializer::visitClassTemplateRecord( -    const ClassTemplateRecord &Record) { -  auto Class = serializeAPIRecord(Record); -  if (!Class) -    return; +bool SymbolGraphSerializer::visitClassTemplateRecord( +    const ClassTemplateRecord *Record) { +  if (!CurrentSymbol) +    return true; -  Symbols.emplace_back(std::move(*Class)); -  for (const auto &Base : Record.Bases) -    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); -  if (!Record.ParentInformation.empty()) -    serializeRelationship(RelationshipKind::MemberOf, Record, -                          Record.ParentInformation.ParentRecord); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitClassTemplateSpecializationRecord( -    const ClassTemplateSpecializationRecord &Record) { -  auto Class = serializeAPIRecord(Record); -  if (!Class) -    return; - -  Symbols.emplace_back(std::move(*Class)); +bool SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord( +    const ClassTemplatePartialSpecializationRecord *Record) { +  if (!CurrentSymbol) +    return true; -  for (const auto &Base : Record.Bases) -    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); -  if (!Record.ParentInformation.empty()) -    serializeRelationship(RelationshipKind::MemberOf, Record, -                          Record.ParentInformation.ParentRecord); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord( -    const ClassTemplatePartialSpecializationRecord &Record) { -  auto Class = serializeAPIRecord(Record); -  if (!Class) -    return; - -  Symbols.emplace_back(std::move(*Class)); +bool SymbolGraphSerializer::visitCXXMethodRecord( +    const CXXMethodRecord *Record) { +  if (!CurrentSymbol) +    return true; -  for (const auto &Base : Record.Bases) -    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); -  if (!Record.ParentInformation.empty()) -    serializeRelationship(RelationshipKind::MemberOf, Record, -                          Record.ParentInformation.ParentRecord); +  serializeFunctionSignatureMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitCXXInstanceMethodRecord( -    const CXXInstanceMethodRecord &Record) { -  auto InstanceMethod = serializeAPIRecord(Record); -  if (!InstanceMethod) -    return; +bool SymbolGraphSerializer::visitCXXMethodTemplateRecord( +    const CXXMethodTemplateRecord *Record) { +  if (!CurrentSymbol) +    return true; -  Symbols.emplace_back(std::move(*InstanceMethod)); -  serializeRelationship(RelationshipKind::MemberOf, Record, -                        Record.ParentInformation.ParentRecord); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitCXXStaticMethodRecord( -    const CXXStaticMethodRecord &Record) { -  auto StaticMethod = serializeAPIRecord(Record); -  if (!StaticMethod) -    return; +bool SymbolGraphSerializer::visitCXXFieldTemplateRecord( +    const CXXFieldTemplateRecord *Record) { +  if (!CurrentSymbol) +    return true; -  Symbols.emplace_back(std::move(*StaticMethod)); -  serializeRelationship(RelationshipKind::MemberOf, Record, -                        Record.ParentInformation.ParentRecord); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitMethodTemplateRecord( -    const CXXMethodTemplateRecord &Record) { -  if (!ShouldRecurse) -    // Ignore child symbols -    return; -  auto MethodTemplate = serializeAPIRecord(Record); -  if (!MethodTemplate) -    return; -  Symbols.emplace_back(std::move(*MethodTemplate)); -  serializeRelationship(RelationshipKind::MemberOf, Record, -                        Record.ParentInformation.ParentRecord); -} +bool SymbolGraphSerializer::visitConceptRecord(const ConceptRecord *Record) { +  if (!CurrentSymbol) +    return true; -void SymbolGraphSerializer::visitMethodTemplateSpecializationRecord( -    const CXXMethodTemplateSpecializationRecord &Record) { -  if (!ShouldRecurse) -    // Ignore child symbols -    return; -  auto MethodTemplateSpecialization = serializeAPIRecord(Record); -  if (!MethodTemplateSpecialization) -    return; -  Symbols.emplace_back(std::move(*MethodTemplateSpecialization)); -  serializeRelationship(RelationshipKind::MemberOf, Record, -                        Record.ParentInformation.ParentRecord); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitCXXFieldRecord(const CXXFieldRecord &Record) { -  if (!ShouldRecurse) -    return; -  auto CXXField = serializeAPIRecord(Record); -  if (!CXXField) -    return; -  Symbols.emplace_back(std::move(*CXXField)); -  serializeRelationship(RelationshipKind::MemberOf, Record, -                        Record.ParentInformation.ParentRecord); -} +bool SymbolGraphSerializer::visitGlobalVariableTemplateRecord( +    const GlobalVariableTemplateRecord *Record) { +  if (!CurrentSymbol) +    return true; -void SymbolGraphSerializer::visitCXXFieldTemplateRecord( -    const CXXFieldTemplateRecord &Record) { -  if (!ShouldRecurse) -    // Ignore child symbols -    return; -  auto CXXFieldTemplate = serializeAPIRecord(Record); -  if (!CXXFieldTemplate) -    return; -  Symbols.emplace_back(std::move(*CXXFieldTemplate)); -  serializeRelationship(RelationshipKind::MemberOf, Record, -                        Record.ParentInformation.ParentRecord); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) { -  auto Concept = serializeAPIRecord(Record); -  if (!Concept) -    return; +bool SymbolGraphSerializer:: +    visitGlobalVariableTemplatePartialSpecializationRecord( +        const GlobalVariableTemplatePartialSpecializationRecord *Record) { +  if (!CurrentSymbol) +    return true; -  Symbols.emplace_back(std::move(*Concept)); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer::visitGlobalVariableTemplateRecord( -    const GlobalVariableTemplateRecord &Record) { -  auto GlobalVariableTemplate = serializeAPIRecord(Record); -  if (!GlobalVariableTemplate) -    return; -  Symbols.emplace_back(std::move(*GlobalVariableTemplate)); -} +bool SymbolGraphSerializer::visitGlobalFunctionTemplateRecord( +    const GlobalFunctionTemplateRecord *Record) { +  if (!CurrentSymbol) +    return true; -void SymbolGraphSerializer::visitGlobalVariableTemplateSpecializationRecord( -    const GlobalVariableTemplateSpecializationRecord &Record) { -  auto GlobalVariableTemplateSpecialization = serializeAPIRecord(Record); -  if (!GlobalVariableTemplateSpecialization) -    return; -  Symbols.emplace_back(std::move(*GlobalVariableTemplateSpecialization)); +  serializeTemplateMixin(*CurrentSymbol, *Record); +  return true;  } -void SymbolGraphSerializer:: -    visitGlobalVariableTemplatePartialSpecializationRecord( -        const GlobalVariableTemplatePartialSpecializationRecord &Record) { -  auto GlobalVariableTemplatePartialSpecialization = serializeAPIRecord(Record); -  if (!GlobalVariableTemplatePartialSpecialization) -    return; -  Symbols.emplace_back(std::move(*GlobalVariableTemplatePartialSpecialization)); -} +bool SymbolGraphSerializer::visitObjCContainerRecord( +    const ObjCContainerRecord *Record) { +  if (!CurrentSymbol) +    return true; -void SymbolGraphSerializer::visitGlobalFunctionTemplateRecord( -    const GlobalFunctionTemplateRecord &Record) { -  auto GlobalFunctionTemplate = serializeAPIRecord(Record); -  if (!GlobalFunctionTemplate) -    return; -  Symbols.emplace_back(std::move(*GlobalFunctionTemplate)); -} +  for (const auto &Protocol : Record->Protocols) +    serializeRelationship(ConformsTo, Record, Protocol, +                          getModuleForCurrentSymbol()); -void SymbolGraphSerializer::visitGlobalFunctionTemplateSpecializationRecord( -    const GlobalFunctionTemplateSpecializationRecord &Record) { -  auto GlobalFunctionTemplateSpecialization = serializeAPIRecord(Record); -  if (!GlobalFunctionTemplateSpecialization) -    return; -  Symbols.emplace_back(std::move(*GlobalFunctionTemplateSpecialization)); +  return true;  } -void SymbolGraphSerializer::visitObjCContainerRecord( -    const ObjCContainerRecord &Record) { -  auto ObjCContainer = serializeAPIRecord(Record); -  if (!ObjCContainer) -    return; +bool SymbolGraphSerializer::visitObjCInterfaceRecord( +    const ObjCInterfaceRecord *Record) { +  if (!CurrentSymbol) +    return true; -  Symbols.emplace_back(std::move(*ObjCContainer)); - -  serializeMembers(Record, Record.Ivars); -  serializeMembers(Record, Record.Methods); -  serializeMembers(Record, Record.Properties); - -  for (const auto &Protocol : Record.Protocols) -    // Record that Record conforms to Protocol. -    serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); - -  if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) { -    if (!ObjCInterface->SuperClass.empty()) -      // If Record is an Objective-C interface record and it has a super class, -      // record that Record is inherited from SuperClass. -      serializeRelationship(RelationshipKind::InheritsFrom, Record, -                            ObjCInterface->SuperClass); - -    // Members of categories extending an interface are serialized as members of -    // the interface. -    for (const auto *Category : ObjCInterface->Categories) { -      serializeMembers(Record, Category->Ivars); -      serializeMembers(Record, Category->Methods); -      serializeMembers(Record, Category->Properties); - -      // Surface the protocols of the category to the interface. -      for (const auto &Protocol : Category->Protocols) -        serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); -    } -  } +  if (!Record->SuperClass.empty()) +    serializeRelationship(InheritsFrom, Record, Record->SuperClass, +                          getModuleForCurrentSymbol()); +  return true;  } -void SymbolGraphSerializer::visitObjCCategoryRecord( -    const ObjCCategoryRecord &Record) { -  if (!Record.IsFromExternalModule) -    return; +bool SymbolGraphSerializer::traverseObjCCategoryRecord( +    const ObjCCategoryRecord *Record) { +  if (SkipSymbolsInCategoriesToExternalTypes && +      !API.findRecordForUSR(Record->Interface.USR)) +    return true; -  // Check if the current Category' parent has been visited before, if so skip. -  if (!visitedCategories.contains(Record.Interface.Name)) { -    visitedCategories.insert(Record.Interface.Name); -    Object Obj; -    serializeObject(Obj, "identifier", -                    serializeIdentifier(Record, API.getLanguage())); -    serializeObject(Obj, "kind", -                    serializeSymbolKind(APIRecord::RK_ObjCCategoryModule, -                                        API.getLanguage())); -    Obj["accessLevel"] = "public"; -    Symbols.emplace_back(std::move(Obj)); -  } +  auto *CurrentModule = ModuleForCurrentSymbol; +  if (Record->isExtendingExternalModule()) +    ModuleForCurrentSymbol = &ExtendedModules[Record->Interface.Source]; -  Object Relationship; -  Relationship["source"] = Record.USR; -  Relationship["target"] = Record.Interface.USR; -  Relationship["targetFallback"] = Record.Interface.Name; -  Relationship["kind"] = getRelationshipString(RelationshipKind::ExtensionTo); -  Relationships.emplace_back(std::move(Relationship)); +  if (!walkUpFromObjCCategoryRecord(Record)) +    return false; -  auto ObjCCategory = serializeAPIRecord(Record); +  bool RetVal = traverseRecordContext(Record); +  ModuleForCurrentSymbol = CurrentModule; +  return RetVal; +} -  if (!ObjCCategory) -    return; +bool SymbolGraphSerializer::walkUpFromObjCCategoryRecord( +    const ObjCCategoryRecord *Record) { +  return visitObjCCategoryRecord(Record); +} -  Symbols.emplace_back(std::move(*ObjCCategory)); -  serializeMembers(Record, Record.Methods); -  serializeMembers(Record, Record.Properties); +bool SymbolGraphSerializer::visitObjCCategoryRecord( +    const ObjCCategoryRecord *Record) { +  // If we need to create a record for the category in the future do so here, +  // otherwise everything is set up to pretend that the category is in fact the +  // interface it extends. +  for (const auto &Protocol : Record->Protocols) +    serializeRelationship(ConformsTo, Record->Interface, Protocol, +                          getModuleForCurrentSymbol()); -  // Surface the protocols of the category to the interface. -  for (const auto &Protocol : Record.Protocols) -    serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); +  return true;  } -void SymbolGraphSerializer::visitMacroDefinitionRecord( -    const MacroDefinitionRecord &Record) { -  auto Macro = serializeAPIRecord(Record); +bool SymbolGraphSerializer::visitObjCMethodRecord( +    const ObjCMethodRecord *Record) { +  if (!CurrentSymbol) +    return true; -  if (!Macro) -    return; +  serializeFunctionSignatureMixin(*CurrentSymbol, *Record); +  return true; +} -  Symbols.emplace_back(std::move(*Macro)); +bool SymbolGraphSerializer::visitObjCInstanceVariableRecord( +    const ObjCInstanceVariableRecord *Record) { +  // FIXME: serialize ivar access control here. +  return true;  } -void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) { -  switch (Record->getKind()) { -  case APIRecord::RK_Unknown: -    llvm_unreachable("Records should have a known kind!"); -  case APIRecord::RK_GlobalFunction: -    visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record)); -    break; -  case APIRecord::RK_GlobalVariable: -    visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record)); -    break; -  case APIRecord::RK_Enum: -    visitEnumRecord(*cast<EnumRecord>(Record)); -    break; -  case APIRecord::RK_Struct: -    LLVM_FALLTHROUGH; -  case APIRecord::RK_Union: -    visitRecordRecord(*cast<RecordRecord>(Record)); -    break; -  case APIRecord::RK_StaticField: -    visitStaticFieldRecord(*cast<StaticFieldRecord>(Record)); -    break; -  case APIRecord::RK_CXXClass: -    visitCXXClassRecord(*cast<CXXClassRecord>(Record)); -    break; -  case APIRecord::RK_ObjCInterface: -    visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record)); -    break; -  case APIRecord::RK_ObjCProtocol: -    visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record)); -    break; -  case APIRecord::RK_ObjCCategory: -    visitObjCCategoryRecord(*cast<ObjCCategoryRecord>(Record)); -    break; -  case APIRecord::RK_MacroDefinition: -    visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record)); -    break; -  case APIRecord::RK_Typedef: -    visitTypedefRecord(*cast<TypedefRecord>(Record)); -    break; -  default: -    if (auto Obj = serializeAPIRecord(*Record)) { -      Symbols.emplace_back(std::move(*Obj)); -      auto &ParentInformation = Record->ParentInformation; -      if (!ParentInformation.empty()) -        serializeRelationship(RelationshipKind::MemberOf, *Record, -                              *ParentInformation.ParentRecord); -    } -    break; -  } +bool SymbolGraphSerializer::walkUpFromTypedefRecord( +    const TypedefRecord *Record) { +  // Short-circuit walking up the class hierarchy and handle creating typedef +  // symbol objects manually as there are additional symbol dropping rules to +  // respect. +  return visitTypedefRecord(Record);  } -void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) { +bool SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord *Record) {    // Typedefs of anonymous types have their entries unified with the underlying    // type. -  bool ShouldDrop = Record.UnderlyingType.Name.empty(); +  bool ShouldDrop = Record->UnderlyingType.Name.empty();    // enums declared with `NS_OPTION` have a named enum and a named typedef, with    // the same name -  ShouldDrop |= (Record.UnderlyingType.Name == Record.Name); +  ShouldDrop |= (Record->UnderlyingType.Name == Record->Name);    if (ShouldDrop) -    return; +    return true; -  auto Typedef = serializeAPIRecord(Record); -  if (!Typedef) -    return; +  // Create the symbol record if the other symbol droppping rules permit it. +  serializeAPIRecord(Record); +  if (!CurrentSymbol) +    return true; -  (*Typedef)["type"] = Record.UnderlyingType.USR; +  (*CurrentSymbol)["type"] = Record->UnderlyingType.USR; -  Symbols.emplace_back(std::move(*Typedef)); +  return true;  } -Object SymbolGraphSerializer::serialize() { -  traverseAPISet(); -  return serializeCurrentGraph(); +void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) { +  switch (Record->getKind()) { +    // dispatch to the relevant walkUpFromMethod +#define CONCRETE_RECORD(CLASS, BASE, KIND)                                     \ +  case APIRecord::KIND: {                                                      \ +    walkUpFrom##CLASS(static_cast<const CLASS *>(Record));                     \ +    break;                                                                     \ +  } +#include "clang/ExtractAPI/APIRecords.inc" +  // otherwise fallback on the only behavior we can implement safely. +  case APIRecord::RK_Unknown: +    visitAPIRecord(Record); +    break; +  default: +    llvm_unreachable("API Record with uninstantiable kind"); +  }  } -Object SymbolGraphSerializer::serializeCurrentGraph() { +Object SymbolGraphSerializer::serializeGraph(StringRef ModuleName, +                                             ExtendedModule &&EM) {    Object Root;    serializeObject(Root, "metadata", serializeMetadata()); -  serializeObject(Root, "module", serializeModule()); +  serializeObject(Root, "module", serializeModuleObject(ModuleName)); -  Root["symbols"] = std::move(Symbols); -  Root["relationships"] = std::move(Relationships); +  Root["symbols"] = std::move(EM.Symbols); +  Root["relationships"] = std::move(EM.Relationships);    return Root;  } -void SymbolGraphSerializer::serialize(raw_ostream &os) { -  Object root = serialize(); +void SymbolGraphSerializer::serializeGraphToStream( +    raw_ostream &OS, SymbolGraphSerializerOption Options, StringRef ModuleName, +    ExtendedModule &&EM) { +  Object Root = serializeGraph(ModuleName, std::move(EM));    if (Options.Compact) -    os << formatv("{0}", Value(std::move(root))) << "\n"; +    OS << formatv("{0}", json::Value(std::move(Root))) << "\n";    else -    os << formatv("{0:2}", Value(std::move(root))) << "\n"; +    OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n"; +} + +void SymbolGraphSerializer::serializeMainSymbolGraph( +    raw_ostream &OS, const APISet &API, const APIIgnoresList &IgnoresList, +    SymbolGraphSerializerOption Options) { +  SymbolGraphSerializer Serializer( +      API, IgnoresList, Options.EmitSymbolLabelsForTesting, +      /*ForceEmitToMainModule=*/true, +      /*SkipSymbolsInCategoriesToExternalTypes=*/true); + +  Serializer.traverseAPISet(); +  Serializer.serializeGraphToStream(OS, Options, API.ProductName, +                                    std::move(Serializer.MainModule)); +  // FIXME: TODO handle extended modules here +} + +void SymbolGraphSerializer::serializeWithExtensionGraphs( +    raw_ostream &MainOutput, const APISet &API, +    const APIIgnoresList &IgnoresList, +    llvm::function_ref<std::unique_ptr<llvm::raw_pwrite_stream>(Twine BaseName)> +        CreateOutputStream, +    SymbolGraphSerializerOption Options) { +  SymbolGraphSerializer Serializer(API, IgnoresList, +                                   Options.EmitSymbolLabelsForTesting); +  Serializer.traverseAPISet(); + +  Serializer.serializeGraphToStream(MainOutput, Options, API.ProductName, +                                    std::move(Serializer.MainModule)); + +  for (auto &ExtensionSGF : Serializer.ExtendedModules) { +    if (auto ExtensionOS = +            CreateOutputStream(ExtensionSGF.getKey() + "@" + API.ProductName)) +      Serializer.serializeGraphToStream(*ExtensionOS, Options, +                                        ExtensionSGF.getKey(), +                                        std::move(ExtensionSGF.getValue())); +  }  }  std::optional<Object> @@ -1261,14 +1086,20 @@ SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,    Object Root;    APIIgnoresList EmptyIgnores;    SymbolGraphSerializer Serializer(API, EmptyIgnores, -                                   /*Options.Compact*/ {true}, -                                   /*ShouldRecurse*/ false); +                                   /*EmitSymbolLabelsForTesting*/ false, +                                   /*ForceEmitToMainModule*/ true); + +  // Set up serializer parent chain +  Serializer.Hierarchy = generateHierarchyFromRecord(Record); +    Serializer.serializeSingleRecord(Record); -  serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph()); +  serializeObject(Root, "symbolGraph", +                  Serializer.serializeGraph(API.ProductName, +                                            std::move(Serializer.MainModule)));    Language Lang = API.getLanguage();    serializeArray(Root, "parentContexts", -                 generateParentContexts(*Record, API, Lang)); +                 generateParentContexts(Serializer.Hierarchy, Lang));    Array RelatedSymbols; @@ -1286,14 +1117,15 @@ SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,      Object RelatedSymbol;      RelatedSymbol["usr"] = RelatedRecord->USR;      RelatedSymbol["declarationLanguage"] = getLanguageName(Lang); -    // TODO: once we record this properly let's serialize it right. -    RelatedSymbol["accessLevel"] = "public"; +    RelatedSymbol["accessLevel"] = RelatedRecord->Access.getAccess();      RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();      RelatedSymbol["moduleName"] = API.ProductName;      RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;      serializeArray(RelatedSymbol, "parentContexts", -                   generateParentContexts(*RelatedRecord, API, Lang)); +                   generateParentContexts( +                       generateHierarchyFromRecord(RelatedRecord), Lang)); +      RelatedSymbols.push_back(std::move(RelatedSymbol));    }  | 
