[clang] 209a1e8 - Revert "[clang][ExtractAPI] Add ability to create multiple symbol graphs (#86676)"

Daniel Grumberg via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 2 07:35:24 PDT 2024


Author: Daniel Grumberg
Date: 2024-04-02T15:34:52+01:00
New Revision: 209a1e8dfdf1c104dd53b50eb196d6bc0dd01659

URL: https://github.com/llvm/llvm-project/commit/209a1e8dfdf1c104dd53b50eb196d6bc0dd01659
DIFF: https://github.com/llvm/llvm-project/commit/209a1e8dfdf1c104dd53b50eb196d6bc0dd01659.diff

LOG: Revert "[clang][ExtractAPI] Add ability to create multiple symbol graphs (#86676)"

This failed the test suite due to missing DiagGroup for a new warning.

This reverts commit b31414bf4f9898f7817a9fcf8a91f62ec26f3eaf.

Added: 
    clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
    clang/test/ExtractAPI/objc_module_category.m
    clang/test/ExtractAPI/objc_various_categories.m

Modified: 
    clang/include/clang/Basic/DiagnosticDriverKinds.td
    clang/include/clang/Basic/DiagnosticFrontendKinds.td
    clang/include/clang/Driver/Options.td
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/DeclarationFragments.h
    clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
    clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
    clang/include/clang/ExtractAPI/FrontendActions.h
    clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
    clang/include/clang/Frontend/FrontendOptions.h
    clang/lib/Driver/Driver.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
    clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
    clang/test/ExtractAPI/anonymous_record_no_typedef.c
    clang/test/ExtractAPI/availability.c
    clang/test/ExtractAPI/bool.c
    clang/test/ExtractAPI/bool.cpp
    clang/test/ExtractAPI/class.cpp
    clang/test/ExtractAPI/class_template.cpp
    clang/test/ExtractAPI/class_template_param_inheritance.cpp
    clang/test/ExtractAPI/class_template_partial_spec.cpp
    clang/test/ExtractAPI/class_template_spec.cpp
    clang/test/ExtractAPI/concept.cpp
    clang/test/ExtractAPI/constructor_destructor.cpp
    clang/test/ExtractAPI/conversions.cpp
    clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
    clang/test/ExtractAPI/emit-symbol-graph/single_file.c
    clang/test/ExtractAPI/enum.c
    clang/test/ExtractAPI/field_template.cpp
    clang/test/ExtractAPI/function_noexcepts.cpp
    clang/test/ExtractAPI/global_func_template.cpp
    clang/test/ExtractAPI/global_func_template_spec.cpp
    clang/test/ExtractAPI/global_record.c
    clang/test/ExtractAPI/global_record_multifile.c
    clang/test/ExtractAPI/global_var_template.cpp
    clang/test/ExtractAPI/global_var_template_partial_spec.cpp
    clang/test/ExtractAPI/global_var_template_spec.cpp
    clang/test/ExtractAPI/known_files_only.c
    clang/test/ExtractAPI/language.c
    clang/test/ExtractAPI/macro_undefined.c
    clang/test/ExtractAPI/macros.c
    clang/test/ExtractAPI/method_template.cpp
    clang/test/ExtractAPI/method_template_spec.cpp
    clang/test/ExtractAPI/methods.cpp
    clang/test/ExtractAPI/multiple_inheritance.cpp
    clang/test/ExtractAPI/namespace.cpp
    clang/test/ExtractAPI/nested_namespaces.cpp
    clang/test/ExtractAPI/objc_block.m
    clang/test/ExtractAPI/objc_category.m
    clang/test/ExtractAPI/objc_id_protocol.m
    clang/test/ExtractAPI/objc_instancetype.m
    clang/test/ExtractAPI/objc_interface.m
    clang/test/ExtractAPI/objc_property.m
    clang/test/ExtractAPI/objc_protocol.m
    clang/test/ExtractAPI/operator_overload.cpp
    clang/test/ExtractAPI/relative_include.m
    clang/test/ExtractAPI/simple_inheritance.cpp
    clang/test/ExtractAPI/struct.c
    clang/test/ExtractAPI/typedef.c
    clang/test/ExtractAPI/typedef_anonymous_record.c
    clang/test/ExtractAPI/typedef_chain.c
    clang/test/ExtractAPI/typedef_struct_enum.c
    clang/test/ExtractAPI/underscored.c
    clang/test/ExtractAPI/union.c
    clang/test/ExtractAPI/vfs_redirected_include.m
    clang/test/Index/extract-api-cursor.m
    clang/tools/libclang/CXExtractAPI.cpp

Removed: 
    clang/include/clang/ExtractAPI/APIRecords.inc
    clang/include/clang/ExtractAPI/Serialization/APISetVisitor.h
    clang/test/ExtractAPI/metadata_and_module.c
    clang/test/ExtractAPI/objc_external_category.m


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 3d86f7510bde20..592ed3bda51506 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -548,12 +548,6 @@ def err_drv_extract_api_wrong_kind : Error<
   "header file '%0' input '%1' does not match the type of prior input "
   "in api extraction; use '-x %2' to override">;
 
-def err_drv_missing_symbol_graph_dir: Error<
-  "Must provide a symbol graph output directory using --symbol-graph-dir=<directory>">;
-
-def err_drv_unexpected_symbol_graph_output : Error<
-  "Unexpected output symbol graph '%1'; please provide --symbol-graph-dir=<directory> instead">;
-
 def warn_slash_u_filename : Warning<"'/U%0' treated as the '/U' option">,
   InGroup<DiagGroup<"slash-u-filename">>;
 def note_use_dashdash : Note<

diff  --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 241544b2b8c6fa..ba23cf84c5e343 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -366,6 +366,4 @@ def warn_profile_data_misexpect : Warning<
 def err_extract_api_ignores_file_not_found :
   Error<"file '%0' specified by '--extract-api-ignores=' not found">, DefaultFatal;
 
-def warn_missing_symbol_graph_dir : Warning<"Missing symbol graph output directory, defaulting to working directory">;
-
 }

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c3e90a70925b78..f5289fb00c895e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1507,29 +1507,14 @@ def extract_api : Flag<["-"], "extract-api">,
 def product_name_EQ: Joined<["--"], "product-name=">,
   Visibility<[ClangOption, CC1Option]>,
   MarshallingInfoString<FrontendOpts<"ProductName">>;
-def emit_symbol_graph: Flag<["-"], "emit-symbol-graph">,
+def emit_symbol_graph_EQ: JoinedOrSeparate<["--"], "emit-symbol-graph=">,
   Visibility<[ClangOption, CC1Option]>,
-  HelpText<"Generate Extract API information as a side effect of compilation.">,
-  MarshallingInfoFlag<FrontendOpts<"EmitSymbolGraph">>;
-def emit_extension_symbol_graphs: Flag<["--"], "emit-extension-symbol-graphs">,
-  Visibility<[ClangOption, CC1Option]>,
-  HelpText<"Generate additional symbol graphs for extended modules.">,
-  MarshallingInfoFlag<FrontendOpts<"EmitExtensionSymbolGraphs">>;
+    HelpText<"Generate Extract API information as a side effect of compilation.">,
+    MarshallingInfoString<FrontendOpts<"SymbolGraphOutputDir">>;
 def extract_api_ignores_EQ: CommaJoined<["--"], "extract-api-ignores=">,
   Visibility<[ClangOption, CC1Option]>,
     HelpText<"Comma separated list of files containing a new line separated list of API symbols to ignore when extracting API information.">,
     MarshallingInfoStringVector<FrontendOpts<"ExtractAPIIgnoresFileList">>;
-def symbol_graph_dir_EQ: Joined<["--"], "symbol-graph-dir=">,
-   Visibility<[ClangOption, CC1Option]>,
-   HelpText<"Directory in which to emit symbol graphs.">,
-   MarshallingInfoString<FrontendOpts<"SymbolGraphOutputDir">>;
-def emit_pretty_sgf: Flag<["--"], "pretty-sgf">,
-    Visibility<[ClangOption, CC1Option]>,
-    HelpText<"Emit pretty printed symbol graphs">,
-    MarshallingInfoFlag<FrontendOpts<"EmitPrettySymbolGraphs">>;
-def emit_sgf_symbol_labels_for_testing: Flag<["--"], "emit-sgf-symbol-labels-for-testing">,
-   Visibility<[CC1Option]>,
-   MarshallingInfoFlag<FrontendOpts<"EmitSymbolGraphSymbolLabelsForTesting">>;
 def e : Separate<["-"], "e">, Flags<[LinkerInput]>, Group<Link_Group>;
 def fmax_tokens_EQ : Joined<["-"], "fmax-tokens=">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option]>,

diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 92cacf65c7d64e..b220db294101d8 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -20,25 +20,17 @@
 
 #include "clang/AST/Availability.h"
 #include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/RawCommentList.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/ExtractAPI/DeclarationFragments.h"
-#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Casting.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Triple.h"
-#include <cstddef>
-#include <iterator>
 #include <memory>
-#include <optional>
 #include <type_traits>
 
 namespace clang {
@@ -157,58 +149,15 @@ class Template {
 /// \endcode
 using DocComment = std::vector<RawComment::CommentLine>;
 
-struct APIRecord;
-
-// This represents a reference to another symbol that might come from external
-/// sources.
-struct SymbolReference {
-  StringRef Name;
-  StringRef USR;
-
-  /// The source project/module/product of the referred symbol.
-  StringRef Source;
-
-  // A Pointer to the APIRecord for this reference if known
-  const APIRecord *Record = nullptr;
-
-  SymbolReference() = default;
-  SymbolReference(StringRef Name, StringRef USR, StringRef Source = "")
-      : Name(Name), USR(USR), Source(Source) {}
-  SymbolReference(const APIRecord *R);
-
-  /// Determine if this SymbolReference is empty.
-  ///
-  /// \returns true if and only if all \c Name, \c USR, and \c Source is empty.
-  bool empty() const { return Name.empty() && USR.empty() && Source.empty(); }
-};
-
-class RecordContext;
-
-// Concrete classes deriving from APIRecord need to have a construct with first
-// arguments USR, and Name, in that order. This is so that they
-// are compatible with `APISet::createRecord`.
-// When adding a new kind of record don't forget to update APIRecords.inc!
+// Classes deriving from APIRecord need to have USR be the first constructor
+// argument. This is so that they are compatible with `addTopLevelRecord`
+// defined in API.cpp
 /// The base representation of an API record. Holds common symbol information.
 struct APIRecord {
   /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
   enum RecordKind {
     RK_Unknown,
-    // If adding a record context record kind here make sure to update
-    // RecordContext::classof if needed and add a RECORD_CONTEXT entry to
-    // APIRecords.inc
-    RK_FirstRecordContext,
     RK_Namespace,
-    RK_Enum,
-    RK_Struct,
-    RK_Union,
-    RK_ObjCInterface,
-    RK_ObjCCategory,
-    RK_ObjCProtocol,
-    RK_CXXClass,
-    RK_ClassTemplate,
-    RK_ClassTemplateSpecialization,
-    RK_ClassTemplatePartialSpecialization,
-    RK_LastRecordContext,
     RK_GlobalFunction,
     RK_GlobalFunctionTemplate,
     RK_GlobalFunctionTemplateSpecialization,
@@ -217,11 +166,18 @@ struct APIRecord {
     RK_GlobalVariableTemplateSpecialization,
     RK_GlobalVariableTemplatePartialSpecialization,
     RK_EnumConstant,
+    RK_Enum,
     RK_StructField,
+    RK_Struct,
     RK_UnionField,
+    RK_Union,
     RK_StaticField,
     RK_CXXField,
     RK_CXXFieldTemplate,
+    RK_CXXClass,
+    RK_ClassTemplate,
+    RK_ClassTemplateSpecialization,
+    RK_ClassTemplatePartialSpecialization,
     RK_Concept,
     RK_CXXStaticMethod,
     RK_CXXInstanceMethod,
@@ -234,15 +190,40 @@ struct APIRecord {
     RK_ObjCIvar,
     RK_ObjCClassMethod,
     RK_ObjCInstanceMethod,
+    RK_ObjCInterface,
+    RK_ObjCCategory,
+    RK_ObjCCategoryModule,
+    RK_ObjCProtocol,
     RK_MacroDefinition,
     RK_Typedef,
   };
 
+  /// Stores information about the context of the declaration of this API.
+  /// This is roughly analogous to the DeclContext hierarchy for an AST Node.
+  struct HierarchyInformation {
+    /// The USR of the parent API.
+    StringRef ParentUSR;
+    /// The name of the parent API.
+    StringRef ParentName;
+    /// The record kind of the parent API.
+    RecordKind ParentKind = RK_Unknown;
+    /// A pointer to the parent APIRecord if known.
+    APIRecord *ParentRecord = nullptr;
+
+    HierarchyInformation() = default;
+    HierarchyInformation(StringRef ParentUSR, StringRef ParentName,
+                         RecordKind Kind, APIRecord *ParentRecord = nullptr)
+        : ParentUSR(ParentUSR), ParentName(ParentName), ParentKind(Kind),
+          ParentRecord(ParentRecord) {}
+
+    bool empty() const {
+      return ParentUSR.empty() && ParentName.empty() &&
+             ParentKind == RK_Unknown && ParentRecord == nullptr;
+    }
+  };
+
   StringRef USR;
   StringRef Name;
-
-  SymbolReference Parent;
-
   PresumedLoc Location;
   AvailabilityInfo Availability;
   LinkageInfo Linkage;
@@ -261,169 +242,79 @@ struct APIRecord {
   /// Objective-C class/instance methods).
   DeclarationFragments SubHeading;
 
+  /// Information about the parent record of this record.
+  HierarchyInformation ParentInformation;
+
   /// Whether the symbol was defined in a system header.
   bool IsFromSystemHeader;
 
-  AccessControl Access;
-
 private:
   const RecordKind Kind;
-  friend class RecordContext;
-  // Used to store the next child record in RecordContext. This works because
-  // APIRecords semantically only have one parent.
-  mutable APIRecord *NextInContext = nullptr;
 
 public:
-  APIRecord *getNextInContext() const { return NextInContext; }
-
   RecordKind getKind() const { return Kind; }
 
-  static APIRecord *castFromRecordContext(const RecordContext *Ctx);
-  static RecordContext *castToRecordContext(const APIRecord *Record);
-
   APIRecord() = delete;
 
   APIRecord(RecordKind Kind, StringRef USR, StringRef Name,
-            SymbolReference Parent, PresumedLoc Location,
-            AvailabilityInfo Availability, LinkageInfo Linkage,
-            const DocComment &Comment, DeclarationFragments Declaration,
-            DeclarationFragments SubHeading, bool IsFromSystemHeader,
-            AccessControl Access = AccessControl())
-      : USR(USR), Name(Name), Parent(std::move(Parent)), Location(Location),
+            PresumedLoc Location, AvailabilityInfo Availability,
+            LinkageInfo Linkage, const DocComment &Comment,
+            DeclarationFragments Declaration, DeclarationFragments SubHeading,
+            bool IsFromSystemHeader)
+      : USR(USR), Name(Name), Location(Location),
         Availability(std::move(Availability)), Linkage(Linkage),
         Comment(Comment), Declaration(Declaration), SubHeading(SubHeading),
-        IsFromSystemHeader(IsFromSystemHeader), Access(std::move(Access)),
-        Kind(Kind) {}
+        IsFromSystemHeader(IsFromSystemHeader), Kind(Kind) {}
 
   APIRecord(RecordKind Kind, StringRef USR, StringRef Name)
       : USR(USR), Name(Name), Kind(Kind) {}
 
   // Pure virtual destructor to make APIRecord abstract
   virtual ~APIRecord() = 0;
-  static bool classof(const APIRecord *Record) { return true; }
-  static bool classofKind(RecordKind K) { return true; }
-  static bool classof(const RecordContext *Ctx) { return true; }
-};
-
-/// Base class used for specific record types that have children records this is
-/// analogous to the DeclContext for the AST
-class RecordContext {
-public:
-  static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(APIRecord::RecordKind K) {
-    return K > APIRecord::RK_FirstRecordContext &&
-           K < APIRecord::RK_LastRecordContext;
-  }
-
-  static bool classof(const RecordContext *Context) { return true; }
-
-  RecordContext(APIRecord::RecordKind Kind) : Kind(Kind) {}
-
-  APIRecord::RecordKind getKind() const { return Kind; }
-
-  struct record_iterator {
-  private:
-    APIRecord *Current = nullptr;
-
-  public:
-    using value_type = APIRecord *;
-    using reference = const value_type &;
-    using pointer = const value_type *;
-    using iterator_category = std::forward_iterator_tag;
-    using 
diff erence_type = std::ptr
diff _t;
-
-    record_iterator() = default;
-    explicit record_iterator(value_type R) : Current(R) {}
-    reference operator*() const { return Current; }
-    // This doesn't strictly meet the iterator requirements, but it's the
-    // behavior we want here.
-    value_type operator->() const { return Current; }
-    record_iterator &operator++() {
-      Current = Current->getNextInContext();
-      return *this;
-    }
-    record_iterator operator++(int) {
-      record_iterator tmp(*this);
-      ++(*this);
-      return tmp;
-    }
-
-    friend bool operator==(record_iterator x, record_iterator y) {
-      return x.Current == y.Current;
-    }
-    friend bool operator!=(record_iterator x, record_iterator y) {
-      return x.Current != y.Current;
-    }
-  };
-
-  using record_range = llvm::iterator_range<record_iterator>;
-  record_range records() const {
-    return record_range(records_begin(), records_end());
-  }
-  record_iterator records_begin() const { return record_iterator(First); };
-  record_iterator records_end() const { return record_iterator(); }
-  bool records_empty() const { return First == nullptr; };
-
-private:
-  APIRecord::RecordKind Kind;
-  mutable APIRecord *First = nullptr;
-  mutable APIRecord *Last = nullptr;
-
-protected:
-  friend class APISet;
-  void addToRecordChain(APIRecord *) const;
 };
 
-struct NamespaceRecord : APIRecord, RecordContext {
-  NamespaceRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                  PresumedLoc Loc, AvailabilityInfo Availability,
-                  LinkageInfo Linkage, const DocComment &Comment,
-                  DeclarationFragments Declaration,
+struct NamespaceRecord : APIRecord {
+  NamespaceRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                  AvailabilityInfo Availability, LinkageInfo Linkage,
+                  const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_Namespace, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(RK_Namespace, USR, Name, Loc, std::move(Availability),
                   Linkage, Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
-        RecordContext(RK_Namespace) {}
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_Namespace;
   }
-  static bool classofKind(RecordKind K) { return K == RK_Namespace; }
 };
 
 /// This holds information associated with global functions.
 struct GlobalFunctionRecord : APIRecord {
   FunctionSignature Signature;
 
-  GlobalFunctionRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                       PresumedLoc Loc, AvailabilityInfo Availability,
-                       LinkageInfo Linkage, const DocComment &Comment,
+  GlobalFunctionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                       AvailabilityInfo Availability, LinkageInfo Linkage,
+                       const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
                        FunctionSignature Signature, bool IsFromSystemHeader)
-      : APIRecord(RK_GlobalFunction, USR, Name, Parent, Loc,
-                  std::move(Availability), Linkage, Comment, Declaration,
-                  SubHeading, IsFromSystemHeader),
+      : APIRecord(RK_GlobalFunction, USR, Name, Loc, std::move(Availability),
+                  Linkage, Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
         Signature(Signature) {}
 
   GlobalFunctionRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                       SymbolReference Parent, PresumedLoc Loc,
-                       AvailabilityInfo Availability, LinkageInfo Linkage,
-                       const DocComment &Comment,
+                       PresumedLoc Loc, AvailabilityInfo Availability,
+                       LinkageInfo Linkage, const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
                        FunctionSignature Signature, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
-                  Linkage, Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability), Linkage,
+                  Comment, Declaration, SubHeading, IsFromSystemHeader),
         Signature(Signature) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_GlobalFunction;
   }
-  static bool classofKind(RecordKind K) { return K == RK_GlobalFunction; }
 
 private:
   virtual void anchor();
@@ -432,74 +323,63 @@ struct GlobalFunctionRecord : APIRecord {
 struct GlobalFunctionTemplateRecord : GlobalFunctionRecord {
   Template Templ;
 
-  GlobalFunctionTemplateRecord(StringRef USR, StringRef Name,
-                               SymbolReference Parent, PresumedLoc Loc,
+  GlobalFunctionTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                                AvailabilityInfo Availability,
                                LinkageInfo Linkage, const DocComment &Comment,
                                DeclarationFragments Declaration,
                                DeclarationFragments SubHeading,
                                FunctionSignature Signature, Template Template,
                                bool IsFromSystemHeader)
-      : GlobalFunctionRecord(RK_GlobalFunctionTemplate, USR, Name, Parent, Loc,
+      : GlobalFunctionRecord(RK_GlobalFunctionTemplate, USR, Name, Loc,
                              std::move(Availability), Linkage, Comment,
                              Declaration, SubHeading, Signature,
                              IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_GlobalFunctionTemplate;
+    return Record->getKind() == RK_GlobalFunctionTemplate;
   }
 };
 
 struct GlobalFunctionTemplateSpecializationRecord : GlobalFunctionRecord {
   GlobalFunctionTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
+      StringRef USR, StringRef Name, PresumedLoc Loc,
       AvailabilityInfo Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, FunctionSignature Signature,
       bool IsFromSystemHeader)
       : GlobalFunctionRecord(RK_GlobalFunctionTemplateSpecialization, USR, Name,
-                             Parent, Loc, std::move(Availability), Linkage,
-                             Comment, Declaration, SubHeading, Signature,
+                             Loc, std::move(Availability), Linkage, Comment,
+                             Declaration, SubHeading, Signature,
                              IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_GlobalFunctionTemplateSpecialization;
+    return Record->getKind() == RK_GlobalFunctionTemplateSpecialization;
   }
 };
 
 /// This holds information associated with global functions.
 struct GlobalVariableRecord : APIRecord {
-  GlobalVariableRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                       PresumedLoc Loc, AvailabilityInfo Availability,
-                       LinkageInfo Linkage, const DocComment &Comment,
+  GlobalVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                       AvailabilityInfo Availability, LinkageInfo Linkage,
+                       const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_GlobalVariable, USR, Name, Parent, Loc,
-                  std::move(Availability), Linkage, Comment, Declaration,
-                  SubHeading, IsFromSystemHeader) {}
+      : APIRecord(RK_GlobalVariable, USR, Name, Loc, std::move(Availability),
+                  Linkage, Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   GlobalVariableRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                       SymbolReference Parent,
-
                        PresumedLoc Loc, AvailabilityInfo Availability,
                        LinkageInfo Linkage, const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
-                  Linkage, Comment, Declaration, SubHeading,
-                  IsFromSystemHeader) {}
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability), Linkage,
+                  Comment, Declaration, SubHeading, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_GlobalVariable;
   }
-  static bool classofKind(RecordKind K) { return K == RK_GlobalVariable; }
 
 private:
   virtual void anchor();
@@ -508,42 +388,34 @@ struct GlobalVariableRecord : APIRecord {
 struct GlobalVariableTemplateRecord : GlobalVariableRecord {
   Template Templ;
 
-  GlobalVariableTemplateRecord(StringRef USR, StringRef Name,
-                               SymbolReference Parent, PresumedLoc Loc,
+  GlobalVariableTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                                AvailabilityInfo Availability,
                                LinkageInfo Linkage, const DocComment &Comment,
                                DeclarationFragments Declaration,
                                DeclarationFragments SubHeading,
                                class Template Template, bool IsFromSystemHeader)
-      : GlobalVariableRecord(RK_GlobalVariableTemplate, USR, Name, Parent, Loc,
+      : GlobalVariableRecord(RK_GlobalVariableTemplate, USR, Name, Loc,
                              std::move(Availability), Linkage, Comment,
                              Declaration, SubHeading, IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_GlobalVariableTemplate;
+    return Record->getKind() == RK_GlobalVariableTemplate;
   }
 };
 
 struct GlobalVariableTemplateSpecializationRecord : GlobalVariableRecord {
   GlobalVariableTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
+      StringRef USR, StringRef Name, PresumedLoc Loc,
       AvailabilityInfo Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : GlobalVariableRecord(RK_GlobalVariableTemplateSpecialization, USR, Name,
-                             Parent, Loc, std::move(Availability), Linkage,
-                             Comment, Declaration, SubHeading,
-                             IsFromSystemHeader) {}
+                             Loc, std::move(Availability), Linkage, Comment,
+                             Declaration, SubHeading, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_GlobalVariableTemplateSpecialization;
+    return Record->getKind() == RK_GlobalVariableTemplateSpecialization;
   }
 };
 
@@ -552,203 +424,126 @@ struct GlobalVariableTemplatePartialSpecializationRecord
   Template Templ;
 
   GlobalVariableTemplatePartialSpecializationRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
+      StringRef USR, StringRef Name, PresumedLoc Loc,
       AvailabilityInfo Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, class Template Template,
       bool IsFromSystemHeader)
       : GlobalVariableRecord(RK_GlobalVariableTemplatePartialSpecialization,
-                             USR, Name, Parent, Loc, std::move(Availability),
-                             Linkage, Comment, Declaration, SubHeading,
+                             USR, Name, Loc, std::move(Availability), Linkage,
+                             Comment, Declaration, SubHeading,
                              IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_GlobalVariableTemplatePartialSpecialization;
+    return Record->getKind() == RK_GlobalVariableTemplatePartialSpecialization;
   }
 };
 
 /// This holds information associated with enum constants.
 struct EnumConstantRecord : APIRecord {
-  EnumConstantRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                     PresumedLoc Loc, AvailabilityInfo Availability,
-                     const DocComment &Comment,
+  EnumConstantRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                     AvailabilityInfo Availability, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_EnumConstant, USR, Name, Parent, Loc,
-                  std::move(Availability), LinkageInfo::none(), Comment,
-                  Declaration, SubHeading, IsFromSystemHeader) {}
+      : APIRecord(RK_EnumConstant, USR, Name, Loc, std::move(Availability),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_EnumConstant;
   }
-  static bool classofKind(RecordKind K) { return K == RK_EnumConstant; }
 
 private:
   virtual void anchor();
 };
 
 /// This holds information associated with enums.
-struct EnumRecord : APIRecord, RecordContext {
-  EnumRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-             PresumedLoc Loc, AvailabilityInfo Availability,
-             const DocComment &Comment, DeclarationFragments Declaration,
-             DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_Enum, USR, Name, Parent, Loc, std::move(Availability),
+struct EnumRecord : APIRecord {
+  SmallVector<std::unique_ptr<EnumConstantRecord>> Constants;
+
+  EnumRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+             AvailabilityInfo Availability, const DocComment &Comment,
+             DeclarationFragments Declaration, DeclarationFragments SubHeading,
+             bool IsFromSystemHeader)
+      : APIRecord(RK_Enum, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
-        RecordContext(RK_Enum) {}
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_Enum;
   }
-  static bool classofKind(RecordKind K) { return K == RK_Enum; }
 
 private:
   virtual void anchor();
 };
 
-/// This holds information associated with struct or union fields fields.
+/// This holds information associated with struct fields.
 struct RecordFieldRecord : APIRecord {
-  RecordFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                    SymbolReference Parent, PresumedLoc Loc,
+  RecordFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                     AvailabilityInfo Availability, const DocComment &Comment,
                     DeclarationFragments Declaration,
-                    DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+                    DeclarationFragments SubHeading, RecordKind Kind,
+                    bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_StructField || K == RK_UnionField;
+    return Record->getKind() == RK_StructField ||
+           Record->getKind() == RK_UnionField;
   }
 
-  virtual ~RecordFieldRecord() = 0;
-};
-
-/// This holds information associated with structs and unions.
-struct RecordRecord : APIRecord, RecordContext {
-  RecordRecord(RecordKind Kind, StringRef USR, StringRef Name,
-               SymbolReference Parent, PresumedLoc Loc,
-               AvailabilityInfo Availability, const DocComment &Comment,
-               DeclarationFragments Declaration,
-               DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
-        RecordContext(Kind) {}
-
-  static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_Struct || K == RK_Union;
-  }
-
-  virtual ~RecordRecord() = 0;
-};
-
-struct StructFieldRecord : RecordFieldRecord {
-  StructFieldRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                    PresumedLoc Loc, AvailabilityInfo Availability,
-                    const DocComment &Comment, DeclarationFragments Declaration,
-                    DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : RecordFieldRecord(RK_StructField, USR, Name, Parent, Loc,
-                          std::move(Availability), Comment, Declaration,
-                          SubHeading, IsFromSystemHeader) {}
-
-  static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) { return K == RK_StructField; }
-
 private:
   virtual void anchor();
 };
 
-struct StructRecord : RecordRecord {
-  StructRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-               PresumedLoc Loc, AvailabilityInfo Availability,
-               const DocComment &Comment, DeclarationFragments Declaration,
-               DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : RecordRecord(RK_Struct, USR, Name, Parent, Loc, std::move(Availability),
-                     Comment, Declaration, SubHeading, IsFromSystemHeader) {}
-
-  static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) { return K == RK_Struct; }
-
-private:
-  virtual void anchor();
-};
-
-struct UnionFieldRecord : RecordFieldRecord {
-  UnionFieldRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                   PresumedLoc Loc, AvailabilityInfo Availability,
-                   const DocComment &Comment, DeclarationFragments Declaration,
-                   DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : RecordFieldRecord(RK_UnionField, USR, Name, Parent, Loc,
-                          std::move(Availability), Comment, Declaration,
-                          SubHeading, IsFromSystemHeader) {}
-
-  static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) { return K == RK_UnionField; }
+/// This holds information associated with structs.
+struct RecordRecord : APIRecord {
+  SmallVector<std::unique_ptr<RecordFieldRecord>> Fields;
 
-private:
-  virtual void anchor();
-};
-
-struct UnionRecord : RecordRecord {
-  UnionRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-              PresumedLoc Loc, AvailabilityInfo Availability,
-              const DocComment &Comment, DeclarationFragments Declaration,
-              DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : RecordRecord(RK_Union, USR, Name, Parent, Loc, std::move(Availability),
-                     Comment, Declaration, SubHeading, IsFromSystemHeader) {}
+  RecordRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+               AvailabilityInfo Availability, const DocComment &Comment,
+               DeclarationFragments Declaration,
+               DeclarationFragments SubHeading, RecordKind Kind,
+               bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_Struct || Record->getKind() == RK_Union;
   }
-  static bool classofKind(RecordKind K) { return K == RK_Union; }
 
 private:
   virtual void anchor();
 };
 
 struct CXXFieldRecord : APIRecord {
-  CXXFieldRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                 PresumedLoc Loc, AvailabilityInfo Availability,
-                 const DocComment &Comment, DeclarationFragments Declaration,
+  AccessControl Access;
+
+  CXXFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                 AvailabilityInfo Availability, const DocComment &Comment,
+                 DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, AccessControl Access,
                  bool IsFromSystemHeader)
-      : APIRecord(RK_CXXField, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(RK_CXXField, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader, std::move(Access)) {}
+                  IsFromSystemHeader),
+        Access(Access) {}
 
   CXXFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                 SymbolReference Parent, PresumedLoc Loc,
-                 AvailabilityInfo Availability, const DocComment &Comment,
-                 DeclarationFragments Declaration,
+                 PresumedLoc Loc, AvailabilityInfo Availability,
+                 const DocComment &Comment, DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, AccessControl Access,
                  bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader, std::move(Access)) {}
+                  IsFromSystemHeader),
+        Access(Access) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_CXXField || K == RK_CXXFieldTemplate || K == RK_StaticField;
+    return Record->getKind() == RK_CXXField;
   }
 
 private:
@@ -758,122 +553,111 @@ struct CXXFieldRecord : APIRecord {
 struct CXXFieldTemplateRecord : CXXFieldRecord {
   Template Templ;
 
-  CXXFieldTemplateRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                         PresumedLoc Loc, AvailabilityInfo Availability,
+  CXXFieldTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                         AvailabilityInfo Availability,
                          const DocComment &Comment,
                          DeclarationFragments Declaration,
                          DeclarationFragments SubHeading, AccessControl Access,
                          Template Template, bool IsFromSystemHeader)
-      : CXXFieldRecord(RK_CXXFieldTemplate, USR, Name, Parent, Loc,
+      : CXXFieldRecord(RK_CXXFieldTemplate, USR, Name, Loc,
                        std::move(Availability), Comment, Declaration,
-                       SubHeading, std::move(Access), IsFromSystemHeader),
+                       SubHeading, Access, IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_CXXFieldTemplate;
   }
-  static bool classofKind(RecordKind K) { return K == RK_CXXFieldTemplate; }
 };
 
 struct CXXMethodRecord : APIRecord {
   FunctionSignature Signature;
+  AccessControl Access;
 
   CXXMethodRecord() = delete;
 
   CXXMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                  SymbolReference Parent, PresumedLoc Loc,
-                  AvailabilityInfo Availability, const DocComment &Comment,
-                  DeclarationFragments Declaration,
+                  PresumedLoc Loc, AvailabilityInfo Availability,
+                  const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, FunctionSignature Signature,
                   AccessControl Access, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader, std::move(Access)),
-        Signature(Signature) {}
+                  IsFromSystemHeader),
+        Signature(Signature), Access(Access) {}
 
   virtual ~CXXMethodRecord() = 0;
 };
 
 struct CXXConstructorRecord : CXXMethodRecord {
-  CXXConstructorRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                       PresumedLoc Loc, AvailabilityInfo Availability,
-                       const DocComment &Comment,
+  CXXConstructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                       AvailabilityInfo Availability, const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
                        FunctionSignature Signature, AccessControl Access,
                        bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, Parent, Loc,
+      : CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, Loc,
                         std::move(Availability), Comment, Declaration,
-                        SubHeading, Signature, std::move(Access),
-                        IsFromSystemHeader) {}
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_CXXConstructorMethod;
   }
-  static bool classofKind(RecordKind K) { return K == RK_CXXConstructorMethod; }
 
 private:
   virtual void anchor();
 };
 
 struct CXXDestructorRecord : CXXMethodRecord {
-  CXXDestructorRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                      PresumedLoc Loc, AvailabilityInfo Availability,
-                      const DocComment &Comment,
+  CXXDestructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                      AvailabilityInfo Availability, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading,
                       FunctionSignature Signature, AccessControl Access,
                       bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, Parent, Loc,
+      : CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, Loc,
                         std::move(Availability), Comment, Declaration,
-                        SubHeading, Signature, std::move(Access),
-                        IsFromSystemHeader) {}
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_CXXDestructorMethod;
   }
-  static bool classofKind(RecordKind K) { return K == RK_CXXDestructorMethod; }
 
 private:
   virtual void anchor();
 };
 
 struct CXXStaticMethodRecord : CXXMethodRecord {
-  CXXStaticMethodRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                        PresumedLoc Loc, AvailabilityInfo Availability,
+  CXXStaticMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                        AvailabilityInfo Availability,
                         const DocComment &Comment,
                         DeclarationFragments Declaration,
                         DeclarationFragments SubHeading,
                         FunctionSignature Signature, AccessControl Access,
                         bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXStaticMethod, USR, Name, Parent, Loc,
+      : CXXMethodRecord(RK_CXXStaticMethod, USR, Name, Loc,
                         std::move(Availability), Comment, Declaration,
-                        SubHeading, Signature, std::move(Access),
-                        IsFromSystemHeader) {}
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_CXXStaticMethod;
   }
-  static bool classofKind(RecordKind K) { return K == RK_CXXStaticMethod; }
 
 private:
   virtual void anchor();
 };
 
 struct CXXInstanceMethodRecord : CXXMethodRecord {
-  CXXInstanceMethodRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                          PresumedLoc Loc, AvailabilityInfo Availability,
+  CXXInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                          AvailabilityInfo Availability,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading,
                           FunctionSignature Signature, AccessControl Access,
                           bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, Parent, Loc,
+      : CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, Loc,
                         std::move(Availability), Comment, Declaration,
-                        SubHeading, Signature, std::move(Access),
-                        IsFromSystemHeader) {}
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_CXXInstanceMethod;
   }
-  static bool classofKind(RecordKind K) { return K == RK_CXXInstanceMethod; }
 
 private:
   virtual void anchor();
@@ -882,42 +666,36 @@ struct CXXInstanceMethodRecord : CXXMethodRecord {
 struct CXXMethodTemplateRecord : CXXMethodRecord {
   Template Templ;
 
-  CXXMethodTemplateRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                          PresumedLoc Loc, AvailabilityInfo Availability,
+  CXXMethodTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                          AvailabilityInfo Availability,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading,
                           FunctionSignature Signature, AccessControl Access,
                           Template Template, bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXMethodTemplate, USR, Name, Parent, Loc,
+      : CXXMethodRecord(RK_CXXMethodTemplate, USR, Name, Loc,
                         std::move(Availability), Comment, Declaration,
-                        SubHeading, Signature, std::move(Access),
-                        IsFromSystemHeader),
+                        SubHeading, Signature, Access, IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_CXXMethodTemplate;
   }
-  static bool classofKind(RecordKind K) { return K == RK_CXXMethodTemplate; }
 };
 
 struct CXXMethodTemplateSpecializationRecord : CXXMethodRecord {
   CXXMethodTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
+      StringRef USR, StringRef Name, PresumedLoc Loc,
       AvailabilityInfo Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access,
       bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXMethodTemplateSpecialization, USR, Name, Parent,
-                        Loc, std::move(Availability), Comment, Declaration,
-                        SubHeading, Signature, std::move(Access),
-                        IsFromSystemHeader) {}
+      : CXXMethodRecord(RK_CXXMethodTemplateSpecialization, USR, Name, Loc,
+                        std::move(Availability), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_CXXMethodTemplateSpecialization;
+    return Record->getKind() == RK_CXXMethodTemplateSpecialization;
   }
 };
 
@@ -936,13 +714,13 @@ struct ObjCPropertyRecord : APIRecord {
   bool IsOptional;
 
   ObjCPropertyRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                     SymbolReference Parent, PresumedLoc Loc,
-                     AvailabilityInfo Availability, const DocComment &Comment,
+                     PresumedLoc Loc, AvailabilityInfo Availability,
+                     const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, AttributeKind Attributes,
                      StringRef GetterName, StringRef SetterName,
                      bool IsOptional, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Attributes(Attributes), GetterName(GetterName), SetterName(SetterName),
@@ -955,44 +733,44 @@ struct ObjCPropertyRecord : APIRecord {
 };
 
 struct ObjCInstancePropertyRecord : ObjCPropertyRecord {
-  ObjCInstancePropertyRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
-      AvailabilityInfo Availability, const DocComment &Comment,
-      DeclarationFragments Declaration, DeclarationFragments SubHeading,
-      AttributeKind Attributes, StringRef GetterName, StringRef SetterName,
-      bool IsOptional, bool IsFromSystemHeader)
-      : ObjCPropertyRecord(RK_ObjCInstanceProperty, USR, Name, Parent, Loc,
+  ObjCInstancePropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                             AvailabilityInfo Availability,
+                             const DocComment &Comment,
+                             DeclarationFragments Declaration,
+                             DeclarationFragments SubHeading,
+                             AttributeKind Attributes, StringRef GetterName,
+                             StringRef SetterName, bool IsOptional,
+                             bool IsFromSystemHeader)
+      : ObjCPropertyRecord(RK_ObjCInstanceProperty, USR, Name, Loc,
                            std::move(Availability), Comment, Declaration,
                            SubHeading, Attributes, GetterName, SetterName,
                            IsOptional, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCInstanceProperty;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCInstanceProperty; }
 
 private:
   virtual void anchor();
 };
 
 struct ObjCClassPropertyRecord : ObjCPropertyRecord {
-  ObjCClassPropertyRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                          PresumedLoc Loc, AvailabilityInfo Availability,
+  ObjCClassPropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                          AvailabilityInfo Availability,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading,
                           AttributeKind Attributes, StringRef GetterName,
                           StringRef SetterName, bool IsOptional,
                           bool IsFromSystemHeader)
-      : ObjCPropertyRecord(RK_ObjCClassProperty, USR, Name, Parent, Loc,
+      : ObjCPropertyRecord(RK_ObjCClassProperty, USR, Name, Loc,
                            std::move(Availability), Comment, Declaration,
                            SubHeading, Attributes, GetterName, SetterName,
                            IsOptional, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCClassProperty;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCClassProperty; }
 
 private:
   virtual void anchor();
@@ -1000,21 +778,23 @@ struct ObjCClassPropertyRecord : ObjCPropertyRecord {
 
 /// This holds information associated with Objective-C instance variables.
 struct ObjCInstanceVariableRecord : APIRecord {
-  ObjCInstanceVariableRecord(StringRef USR, StringRef Name,
-                             SymbolReference Parent, PresumedLoc Loc,
+  using AccessControl = ObjCIvarDecl::AccessControl;
+  AccessControl Access;
+
+  ObjCInstanceVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                              AvailabilityInfo Availability,
                              const DocComment &Comment,
                              DeclarationFragments Declaration,
                              DeclarationFragments SubHeading,
-                             bool IsFromSystemHeader)
-      : APIRecord(RK_ObjCIvar, USR, Name, Parent, Loc, std::move(Availability),
+                             AccessControl Access, bool IsFromSystemHeader)
+      : APIRecord(RK_ObjCIvar, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader) {}
+                  IsFromSystemHeader),
+        Access(Access) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCIvar;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCIvar; }
 
 private:
   virtual void anchor();
@@ -1027,12 +807,11 @@ struct ObjCMethodRecord : APIRecord {
   ObjCMethodRecord() = delete;
 
   ObjCMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                   SymbolReference Parent, PresumedLoc Loc,
-                   AvailabilityInfo Availability, const DocComment &Comment,
-                   DeclarationFragments Declaration,
+                   PresumedLoc Loc, AvailabilityInfo Availability,
+                   const DocComment &Comment, DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, FunctionSignature Signature,
                    bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Signature(Signature) {}
@@ -1041,103 +820,122 @@ struct ObjCMethodRecord : APIRecord {
 };
 
 struct ObjCInstanceMethodRecord : ObjCMethodRecord {
-  ObjCInstanceMethodRecord(StringRef USR, StringRef Name,
-                           SymbolReference Parent, PresumedLoc Loc,
+  ObjCInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                            AvailabilityInfo Availability,
                            const DocComment &Comment,
                            DeclarationFragments Declaration,
                            DeclarationFragments SubHeading,
                            FunctionSignature Signature, bool IsFromSystemHeader)
-      : ObjCMethodRecord(RK_ObjCInstanceMethod, USR, Name, Parent, Loc,
+      : ObjCMethodRecord(RK_ObjCInstanceMethod, USR, Name, Loc,
                          std::move(Availability), Comment, Declaration,
                          SubHeading, Signature, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCInstanceMethod;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCInstanceMethod; }
 
 private:
   virtual void anchor();
 };
 
 struct ObjCClassMethodRecord : ObjCMethodRecord {
-  ObjCClassMethodRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                        PresumedLoc Loc, AvailabilityInfo Availability,
+  ObjCClassMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                        AvailabilityInfo Availability,
                         const DocComment &Comment,
                         DeclarationFragments Declaration,
                         DeclarationFragments SubHeading,
                         FunctionSignature Signature, bool IsFromSystemHeader)
-      : ObjCMethodRecord(RK_ObjCClassMethod, USR, Name, Parent, Loc,
+      : ObjCMethodRecord(RK_ObjCClassMethod, USR, Name, Loc,
                          std::move(Availability), Comment, Declaration,
                          SubHeading, Signature, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCClassMethod;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCClassMethod; }
 
 private:
   virtual void anchor();
 };
 
+/// This represents a reference to another symbol that might come from external
+/// sources.
+struct SymbolReference {
+  StringRef Name;
+  StringRef USR;
+
+  /// The source project/module/product of the referred symbol.
+  StringRef Source;
+
+  SymbolReference() = default;
+  SymbolReference(StringRef Name, StringRef USR = "", StringRef Source = "")
+      : Name(Name), USR(USR), Source(Source) {}
+  SymbolReference(const APIRecord &Record)
+      : Name(Record.Name), USR(Record.USR) {}
+  SymbolReference(const APIRecord *Record)
+      : Name(Record->Name), USR(Record->USR) {}
+
+  /// Determine if this SymbolReference is empty.
+  ///
+  /// \returns true if and only if all \c Name, \c USR, and \c Source is empty.
+  bool empty() const { return Name.empty() && USR.empty() && Source.empty(); }
+};
+
 struct StaticFieldRecord : CXXFieldRecord {
-  StaticFieldRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                    PresumedLoc Loc, AvailabilityInfo Availability,
-                    LinkageInfo Linkage, const DocComment &Comment,
-                    DeclarationFragments Declaration,
-                    DeclarationFragments SubHeading, AccessControl Access,
-                    bool IsFromSystemHeader)
-      : CXXFieldRecord(RK_StaticField, USR, Name, Parent, Loc,
-                       std::move(Availability), Comment, Declaration,
-                       SubHeading, std::move(Access), IsFromSystemHeader) {}
+  SymbolReference Context;
+
+  StaticFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                    AvailabilityInfo Availability, LinkageInfo Linkage,
+                    const DocComment &Comment, DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading, SymbolReference Context,
+                    AccessControl Access, bool IsFromSystemHeader)
+      : CXXFieldRecord(RK_StaticField, USR, Name, Loc, std::move(Availability),
+                       Comment, Declaration, SubHeading, Access,
+                       IsFromSystemHeader),
+        Context(Context) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_StaticField;
   }
-  static bool classofKind(RecordKind K) { return K == RK_StaticField; }
 };
 
 /// The base representation of an Objective-C container record. Holds common
 /// information associated with Objective-C containers.
-struct ObjCContainerRecord : APIRecord, RecordContext {
+struct ObjCContainerRecord : APIRecord {
+  SmallVector<std::unique_ptr<ObjCMethodRecord>> Methods;
+  SmallVector<std::unique_ptr<ObjCPropertyRecord>> Properties;
+  SmallVector<std::unique_ptr<ObjCInstanceVariableRecord>> Ivars;
   SmallVector<SymbolReference> Protocols;
 
   ObjCContainerRecord() = delete;
 
   ObjCContainerRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                      SymbolReference Parent, PresumedLoc Loc,
-                      AvailabilityInfo Availability, LinkageInfo Linkage,
-                      const DocComment &Comment,
+                      PresumedLoc Loc, AvailabilityInfo Availability,
+                      LinkageInfo Linkage, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
-                  Linkage, Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
-        RecordContext(Kind) {}
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability), Linkage,
+                  Comment, Declaration, SubHeading, IsFromSystemHeader) {}
 
   virtual ~ObjCContainerRecord() = 0;
 };
 
-struct CXXClassRecord : APIRecord, RecordContext {
+struct CXXClassRecord : APIRecord {
+  SmallVector<std::unique_ptr<CXXFieldRecord>> Fields;
+  SmallVector<std::unique_ptr<CXXMethodRecord>> Methods;
   SmallVector<SymbolReference> Bases;
+  AccessControl Access;
 
-  CXXClassRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                 PresumedLoc Loc, AvailabilityInfo Availability,
-                 const DocComment &Comment, DeclarationFragments Declaration,
+  CXXClassRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                 AvailabilityInfo Availability, const DocComment &Comment,
+                 DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, RecordKind Kind,
                  AccessControl Access, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader, std::move(Access)),
-        RecordContext(Kind) {}
+                  IsFromSystemHeader),
+        Access(Access) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_CXXClass || K == RK_ClassTemplate ||
-           K == RK_ClassTemplateSpecialization ||
-           K == RK_ClassTemplatePartialSpecialization;
+    return (Record->getKind() == RK_CXXClass);
   }
 
 private:
@@ -1147,108 +945,86 @@ struct CXXClassRecord : APIRecord, RecordContext {
 struct ClassTemplateRecord : CXXClassRecord {
   Template Templ;
 
-  ClassTemplateRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                      PresumedLoc Loc, AvailabilityInfo Availability,
-                      const DocComment &Comment,
+  ClassTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                      AvailabilityInfo Availability, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading, Template Template,
                       AccessControl Access, bool IsFromSystemHeader)
-      : CXXClassRecord(USR, Name, Parent, Loc, std::move(Availability), Comment,
-                       Declaration, SubHeading, RK_ClassTemplate,
-                       std::move(Access), IsFromSystemHeader),
+      : CXXClassRecord(USR, Name, Loc, std::move(Availability), Comment,
+                       Declaration, SubHeading, RK_ClassTemplate, Access,
+                       IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ClassTemplate;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ClassTemplate; }
 };
 
 struct ClassTemplateSpecializationRecord : CXXClassRecord {
   ClassTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
+      StringRef USR, StringRef Name, PresumedLoc Loc,
       AvailabilityInfo Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       AccessControl Access, bool IsFromSystemHeader)
-      : CXXClassRecord(USR, Name, Parent, Loc, std::move(Availability), Comment,
+      : CXXClassRecord(USR, Name, Loc, std::move(Availability), Comment,
                        Declaration, SubHeading, RK_ClassTemplateSpecialization,
                        Access, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_ClassTemplateSpecialization;
+    return Record->getKind() == RK_ClassTemplateSpecialization;
   }
 };
 
 struct ClassTemplatePartialSpecializationRecord : CXXClassRecord {
   Template Templ;
   ClassTemplatePartialSpecializationRecord(
-      StringRef USR, StringRef Name, SymbolReference Parent, PresumedLoc Loc,
+      StringRef USR, StringRef Name, PresumedLoc Loc,
       AvailabilityInfo Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       Template Template, AccessControl Access, bool IsFromSystemHeader)
-      : CXXClassRecord(USR, Name, Parent, Loc, std::move(Availability), Comment,
-                       Declaration, SubHeading,
-                       RK_ClassTemplatePartialSpecialization, Access,
-                       IsFromSystemHeader),
+      : CXXClassRecord(USR, Name, Loc, std::move(Availability), Comment,
+                       Declaration, SubHeading, RK_ClassTemplateSpecialization,
+                       Access, IsFromSystemHeader),
         Templ(Template) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) {
-    return K == RK_ClassTemplatePartialSpecialization;
+    return Record->getKind() == RK_ClassTemplatePartialSpecialization;
   }
 };
 
 struct ConceptRecord : APIRecord {
   Template Templ;
 
-  ConceptRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                PresumedLoc Loc, AvailabilityInfo Availability,
-                const DocComment &Comment, DeclarationFragments Declaration,
+  ConceptRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                AvailabilityInfo Availability, const DocComment &Comment,
+                DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, Template Template,
                 bool IsFromSystemHeader)
-      : APIRecord(RK_Concept, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(RK_Concept, USR, Name, Loc, std::move(Availability),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Templ(Template) {}
-
-  static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) { return K == RK_Concept; }
 };
 
 /// This holds information associated with Objective-C categories.
 struct ObjCCategoryRecord : ObjCContainerRecord {
   SymbolReference Interface;
+  /// Determine whether the Category is derived from external class interface.
+  bool IsFromExternalModule = false;
 
-  ObjCCategoryRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                     PresumedLoc Loc, AvailabilityInfo Availability,
-                     const DocComment &Comment,
+  ObjCCategoryRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                     AvailabilityInfo Availability, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, SymbolReference Interface,
                      bool IsFromSystemHeader)
-      : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Parent, Loc,
+      : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Loc,
                             std::move(Availability), LinkageInfo::none(),
                             Comment, Declaration, SubHeading,
                             IsFromSystemHeader),
         Interface(Interface) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
-  }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCCategory; }
-
-  bool isExtendingExternalModule() const { return !Interface.Source.empty(); }
-
-  std::optional<StringRef> getExtendedExternalModule() const {
-    if (!isExtendingExternalModule())
-      return {};
-    return Interface.Source;
+    return Record->getKind() == RK_ObjCCategory;
   }
 
 private:
@@ -1258,22 +1034,23 @@ struct ObjCCategoryRecord : ObjCContainerRecord {
 /// This holds information associated with Objective-C interfaces/classes.
 struct ObjCInterfaceRecord : ObjCContainerRecord {
   SymbolReference SuperClass;
+  // ObjCCategoryRecord%s are stored in and owned by APISet.
+  SmallVector<ObjCCategoryRecord *> Categories;
 
-  ObjCInterfaceRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                      PresumedLoc Loc, AvailabilityInfo Availability,
-                      LinkageInfo Linkage, const DocComment &Comment,
+  ObjCInterfaceRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                      AvailabilityInfo Availability, LinkageInfo Linkage,
+                      const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading,
                       SymbolReference SuperClass, bool IsFromSystemHeader)
-      : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Parent, Loc,
+      : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Loc,
                             std::move(Availability), Linkage, Comment,
                             Declaration, SubHeading, IsFromSystemHeader),
         SuperClass(SuperClass) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCInterface;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCInterface; }
 
 private:
   virtual void anchor();
@@ -1281,20 +1058,18 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
 
 /// This holds information associated with Objective-C protocols.
 struct ObjCProtocolRecord : ObjCContainerRecord {
-  ObjCProtocolRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                     PresumedLoc Loc, AvailabilityInfo Availability,
-                     const DocComment &Comment,
+  ObjCProtocolRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                     AvailabilityInfo Availability, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Parent, Loc,
+      : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Loc,
                             std::move(Availability), LinkageInfo::none(),
                             Comment, Declaration, SubHeading,
                             IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_ObjCProtocol;
   }
-  static bool classofKind(RecordKind K) { return K == RK_ObjCProtocol; }
 
 private:
   virtual void anchor();
@@ -1302,18 +1077,17 @@ struct ObjCProtocolRecord : ObjCContainerRecord {
 
 /// This holds information associated with macro definitions.
 struct MacroDefinitionRecord : APIRecord {
-  MacroDefinitionRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                        PresumedLoc Loc, DeclarationFragments Declaration,
+  MacroDefinitionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                        DeclarationFragments Declaration,
                         DeclarationFragments SubHeading,
                         bool IsFromSystemHeader)
-      : APIRecord(RK_MacroDefinition, USR, Name, Parent, Loc,
-                  AvailabilityInfo(), LinkageInfo(), {}, Declaration,
-                  SubHeading, IsFromSystemHeader) {}
+      : APIRecord(RK_MacroDefinition, USR, Name, Loc, AvailabilityInfo(),
+                  LinkageInfo(), {}, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_MacroDefinition;
   }
-  static bool classofKind(RecordKind K) { return K == RK_MacroDefinition; }
 
 private:
   virtual void anchor();
@@ -1327,228 +1101,575 @@ struct MacroDefinitionRecord : APIRecord {
 struct TypedefRecord : APIRecord {
   SymbolReference UnderlyingType;
 
-  TypedefRecord(StringRef USR, StringRef Name, SymbolReference Parent,
-                PresumedLoc Loc, AvailabilityInfo Availability,
-                const DocComment &Comment, DeclarationFragments Declaration,
+  TypedefRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                AvailabilityInfo Availability, const DocComment &Comment,
+                DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, SymbolReference UnderlyingType,
                 bool IsFromSystemHeader)
-      : APIRecord(RK_Typedef, USR, Name, Parent, Loc, std::move(Availability),
+      : APIRecord(RK_Typedef, USR, Name, Loc, std::move(Availability),
                   LinkageInfo(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         UnderlyingType(UnderlyingType) {}
 
   static bool classof(const APIRecord *Record) {
-    return classofKind(Record->getKind());
+    return Record->getKind() == RK_Typedef;
   }
-  static bool classofKind(RecordKind K) { return K == RK_Typedef; }
 
 private:
   virtual void anchor();
 };
 
+/// Check if a record type has a function signature mixin.
+///
+/// This is denoted by the record type having a ``Signature`` field of type
+/// FunctionSignature.
+template <typename RecordTy>
+struct has_function_signature : public std::false_type {};
+template <>
+struct has_function_signature<GlobalFunctionRecord> : public std::true_type {};
+template <>
+struct has_function_signature<ObjCMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<ObjCInstanceMethodRecord>
+    : public std::true_type {};
+template <>
+struct has_function_signature<ObjCClassMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<CXXInstanceMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<CXXStaticMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<CXXMethodTemplateRecord> : public std::true_type {
+};
+template <>
+struct has_function_signature<CXXMethodTemplateSpecializationRecord>
+    : public std::true_type {};
+
+template <typename RecordTy> struct has_access : public std::false_type {};
+template <> struct has_access<CXXInstanceMethodRecord> : public std::true_type {};
+template <> struct has_access<CXXStaticMethodRecord> : public std::true_type {};
+template <> struct has_access<CXXFieldRecord> : public std::true_type {};
+template <>
+struct has_access<CXXMethodTemplateRecord> : public std::true_type {};
+template <>
+struct has_access<CXXMethodTemplateSpecializationRecord>
+    : public std::true_type {};
+template <>
+struct has_access<CXXFieldTemplateRecord> : public std::true_type {};
+template <> struct has_access<CXXClassRecord> : public std::true_type {};
+template <> struct has_access<ClassTemplateRecord> : public std::true_type {};
+template <>
+struct has_access<ClassTemplateSpecializationRecord> : public std::true_type {};
+template <>
+struct has_access<ClassTemplatePartialSpecializationRecord>
+    : public std::true_type {};
+
+template <typename RecordTy> struct has_template : public std::false_type {};
+template <> struct has_template<ClassTemplateRecord> : public std::true_type {};
+template <>
+struct has_template<ClassTemplatePartialSpecializationRecord>
+    : public std::true_type {};
+template <> struct has_template<ConceptRecord> : public std::true_type {};
+template <>
+struct has_template<GlobalVariableTemplateRecord> : public std::true_type {};
+template <>
+struct has_template<GlobalVariableTemplatePartialSpecializationRecord>
+    : public std::true_type {};
+template <>
+struct has_template<CXXMethodTemplateRecord> : public std::true_type {};
+template <>
+struct has_template<CXXFieldTemplateRecord> : public std::true_type {};
+
+template <>
+struct has_template<GlobalFunctionTemplateRecord> : public std::true_type {};
+template <>
+struct has_function_signature<GlobalFunctionTemplateRecord>
+    : public std::true_type {};
+template <>
+struct has_function_signature<GlobalFunctionTemplateSpecializationRecord>
+    : public std::true_type {};
+
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
 public:
-  /// Get the target triple for the ExtractAPI invocation.
-  const llvm::Triple &getTarget() const { return Target; }
+  NamespaceRecord *addNamespace(APIRecord *Parent, StringRef Name,
+                                StringRef USR, PresumedLoc Loc,
+                                AvailabilityInfo Availability,
+                                LinkageInfo Linkage, const DocComment &Comment,
+                                DeclarationFragments Declaration,
+                                DeclarationFragments SubHeading,
+                                bool IsFromSystemHeaderg);
+  /// Create and add a global variable record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  GlobalVariableRecord *
+  addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
+               AvailabilityInfo Availability, LinkageInfo Linkage,
+               const DocComment &Comment, DeclarationFragments Declaration,
+               DeclarationFragments SubHeadin, bool IsFromSystemHeaderg);
 
-  /// Get the language used by the APIs.
-  Language getLanguage() const { return Lang; }
+  GlobalVariableTemplateRecord *
+  addGlobalVariableTemplate(StringRef Name, StringRef USR, PresumedLoc Loc,
+                            AvailabilityInfo Availability, LinkageInfo Linkage,
+                            const DocComment &Comment,
+                            DeclarationFragments Declaration,
+                            DeclarationFragments SubHeading, Template Template,
+                            bool IsFromSystemHeader);
 
-  /// Finds the APIRecord for a given USR.
+  /// Create and add a function record into the API set.
   ///
-  /// \returns a pointer to the APIRecord associated with that USR or nullptr.
-  APIRecord *findRecordForUSR(StringRef USR) const;
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  GlobalFunctionRecord *
+  addGlobalFunction(StringRef Name, StringRef USR, PresumedLoc Loc,
+                    AvailabilityInfo Availability, LinkageInfo Linkage,
+                    const DocComment &Comment, DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading,
+                    FunctionSignature Signature, bool IsFromSystemHeader);
 
-  /// Copy \p String into the Allocator in this APISet.
+  GlobalFunctionTemplateRecord *addGlobalFunctionTemplate(
+      StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, LinkageInfo Linkage,
+      const DocComment &Comment, DeclarationFragments Declaration,
+      DeclarationFragments SubHeading, FunctionSignature Signature,
+      Template Template, bool IsFromSystemHeader);
+
+  GlobalFunctionTemplateSpecializationRecord *
+  addGlobalFunctionTemplateSpecialization(
+      StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, LinkageInfo Linkage,
+      const DocComment &Comment, DeclarationFragments Declaration,
+      DeclarationFragments SubHeading, FunctionSignature Signature,
+      bool IsFromSystemHeader);
+
+  /// Create and add an enum constant record into the API set.
   ///
-  /// \returns a StringRef of the copied string in APISet::Allocator.
-  StringRef copyString(StringRef String);
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  EnumConstantRecord *
+  addEnumConstant(EnumRecord *Enum, StringRef Name, StringRef USR,
+                  PresumedLoc Loc, AvailabilityInfo Availability,
+                  const DocComment &Comment, DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
-  SymbolReference createSymbolReference(StringRef Name, StringRef USR,
-                                        StringRef Source = "");
+  /// Create and add an enum record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  EnumRecord *addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+                      AvailabilityInfo Availability, const DocComment &Comment,
+                      DeclarationFragments Declaration,
+                      DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
-  /// Create a subclass of \p APIRecord and store it in the APISet.
+  /// Create and add a record field record into the API set.
   ///
-  /// \returns A pointer to the created record or the already existing record
-  /// matching this USR.
-  template <typename RecordTy, typename... CtorArgsContTy>
-  typename std::enable_if_t<std::is_base_of_v<APIRecord, RecordTy>, RecordTy> *
-  createRecord(StringRef USR, StringRef Name, CtorArgsContTy &&...CtorArgs);
-
-  ArrayRef<const APIRecord *> getTopLevelRecords() const {
-    return TopLevelRecords;
-  }
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  RecordFieldRecord *
+  addRecordField(RecordRecord *Record, StringRef Name, StringRef USR,
+                 PresumedLoc Loc, AvailabilityInfo Availability,
+                 const DocComment &Comment, DeclarationFragments Declaration,
+                 DeclarationFragments SubHeading, APIRecord::RecordKind Kind,
+                 bool IsFromSystemHeader);
 
-  APISet(const llvm::Triple &Target, Language Lang,
-         const std::string &ProductName)
-      : Target(Target), Lang(Lang), ProductName(ProductName) {}
+  /// Create and add a record record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  RecordRecord *addRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                          AvailabilityInfo Availability,
+                          const DocComment &Comment,
+                          DeclarationFragments Declaration,
+                          DeclarationFragments SubHeading,
+                          APIRecord::RecordKind Kind, bool IsFromSystemHeader);
 
-  // Prevent moves and copies
-  APISet(const APISet &Other) = delete;
-  APISet &operator=(const APISet &Other) = delete;
-  APISet(APISet &&Other) = delete;
-  APISet &operator=(APISet &&Other) = delete;
+  StaticFieldRecord *
+  addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
+                 AvailabilityInfo Availability, LinkageInfo Linkage,
+                 const DocComment &Comment, DeclarationFragments Declaration,
+                 DeclarationFragments SubHeading, SymbolReference Context,
+                 AccessControl Access, bool IsFromSystemHeaderg);
+
+  CXXFieldRecord *addCXXField(APIRecord *CXXClass, StringRef Name,
+                              StringRef USR, PresumedLoc Loc,
+                              AvailabilityInfo Availability,
+                              const DocComment &Comment,
+                              DeclarationFragments Declaration,
+                              DeclarationFragments SubHeading,
+                              AccessControl Access, bool IsFromSystemHeader);
+
+  CXXFieldTemplateRecord *addCXXFieldTemplate(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      AccessControl Access, Template Template, bool IsFromSystemHeader);
+
+  CXXClassRecord *addCXXClass(APIRecord *Parent, StringRef Name, StringRef USR,
+                              PresumedLoc Loc, AvailabilityInfo Availability,
+                              const DocComment &Comment,
+                              DeclarationFragments Declaration,
+                              DeclarationFragments SubHeading,
+                              APIRecord::RecordKind Kind, AccessControl Access,
+                              bool IsFromSystemHeader);
+
+  ClassTemplateRecord *
+  addClassTemplate(APIRecord *Parent, StringRef Name, StringRef USR,
+                   PresumedLoc Loc, AvailabilityInfo Availability,
+                   const DocComment &Comment, DeclarationFragments Declaration,
+                   DeclarationFragments SubHeading, Template Template,
+                   AccessControl Access, bool IsFromSystemHeader);
 
-private:
-  /// BumpPtrAllocator that serves as the memory arena for the allocated objects
-  llvm::BumpPtrAllocator Allocator;
+  ClassTemplateSpecializationRecord *addClassTemplateSpecialization(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      AccessControl Access, bool IsFromSystemHeader);
 
-  const llvm::Triple Target;
-  const Language Lang;
+  ClassTemplatePartialSpecializationRecord *
+  addClassTemplatePartialSpecialization(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      Template Template, AccessControl Access, bool IsFromSystemHeader);
 
-  struct APIRecordDeleter {
-    void operator()(APIRecord *Record) { Record->~APIRecord(); }
-  };
+  GlobalVariableTemplateSpecializationRecord *
+  addGlobalVariableTemplateSpecialization(
+      StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, LinkageInfo Linkage,
+      const DocComment &Comment, DeclarationFragments Declaration,
+      DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
-  // Ensure that the destructor of each record is called when the LookupTable is
-  // destroyed without calling delete operator as the memory for the record
-  // lives in the BumpPtrAllocator.
-  using APIRecordStoredPtr = std::unique_ptr<APIRecord, APIRecordDeleter>;
-  llvm::DenseMap<StringRef, APIRecordStoredPtr> USRBasedLookupTable;
-  std::vector<const APIRecord *> TopLevelRecords;
+  GlobalVariableTemplatePartialSpecializationRecord *
+  addGlobalVariableTemplatePartialSpecialization(
+      StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, LinkageInfo Linkage,
+      const DocComment &Comment, DeclarationFragments Declaration,
+      DeclarationFragments SubHeading, Template Template,
+      bool IsFromSystemHeader);
 
-public:
-  const std::string ProductName;
-};
+  CXXMethodRecord *addCXXInstanceMethod(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      FunctionSignature Signature, AccessControl Access,
+      bool IsFromSystemHeader);
 
-template <typename RecordTy, typename... CtorArgsContTy>
-typename std::enable_if_t<std::is_base_of_v<APIRecord, RecordTy>, RecordTy> *
-APISet::createRecord(StringRef USR, StringRef Name,
-                     CtorArgsContTy &&...CtorArgs) {
-  // Ensure USR refers to a String stored in the allocator.
-  auto USRString = copyString(USR);
-  auto Result = USRBasedLookupTable.insert({USRString, nullptr});
-  RecordTy *Record;
-
-  // Create the record if it does not already exist
-  if (Result.second) {
-    Record = new (Allocator) RecordTy(
-        USRString, copyString(Name), std::forward<CtorArgsContTy>(CtorArgs)...);
-    // Store the record in the record lookup map
-    Result.first->second = APIRecordStoredPtr(Record);
-
-    if (auto *ParentContext =
-            dyn_cast_if_present<RecordContext>(Record->Parent.Record))
-      ParentContext->addToRecordChain(Record);
-    else
-      TopLevelRecords.push_back(Record);
-  } else {
-    Record = dyn_cast<RecordTy>(Result.first->second.get());
-  }
+  CXXMethodRecord *addCXXStaticMethod(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      FunctionSignature Signature, AccessControl Access,
+      bool IsFromSystemHeader);
 
-  return Record;
-}
+  CXXMethodRecord *addCXXSpecialMethod(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      FunctionSignature Signature, AccessControl Access,
+      bool IsFromSystemHeader);
 
-// Helper type for implementing casting to RecordContext pointers.
-// Selected when FromTy not a known subclass of RecordContext.
-template <typename FromTy,
-          bool IsKnownSubType = std::is_base_of_v<RecordContext, FromTy>>
-struct ToRecordContextCastInfoWrapper {
-  static_assert(std::is_base_of_v<APIRecord, FromTy>,
-                "Can only cast APIRecord and derived classes to RecordContext");
+  CXXMethodTemplateRecord *addCXXMethodTemplate(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      FunctionSignature Signature, AccessControl Access, Template Template,
+      bool IsFromSystemHeader);
 
-  static bool isPossible(FromTy *From) { return RecordContext::classof(From); }
+  CXXMethodTemplateSpecializationRecord *addCXXMethodTemplateSpec(
+      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      FunctionSignature Signature, AccessControl Access,
+      bool IsFromSystemHeader);
 
-  static RecordContext *doCast(FromTy *From) {
-    return APIRecord::castToRecordContext(From);
-  }
-};
+  ConceptRecord *addConcept(StringRef Name, StringRef USR, PresumedLoc Loc,
+                            AvailabilityInfo Availability,
+                            const DocComment &Comment,
+                            DeclarationFragments Declaration,
+                            DeclarationFragments SubHeading, Template Template,
+                            bool IsFromSystemHeader);
 
-// Selected when FromTy is a known subclass of RecordContext.
-template <typename FromTy> struct ToRecordContextCastInfoWrapper<FromTy, true> {
-  static_assert(std::is_base_of_v<APIRecord, FromTy>,
-                "Can only cast APIRecord and derived classes to RecordContext");
-  static bool isPossible(const FromTy *From) { return true; }
-  static RecordContext *doCast(FromTy *From) {
-    return static_cast<RecordContext *>(From);
-  }
-};
+  /// Create and add an Objective-C category record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCCategoryRecord *
+  addObjCCategory(StringRef Name, StringRef USR, PresumedLoc Loc,
+                  AvailabilityInfo Availability, const DocComment &Comment,
+                  DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, SymbolReference Interface,
+                  bool IsFromSystemHeader, bool IsFromExternalModule);
 
-// Helper type for implementing casting to RecordContext pointers.
-// Selected when ToTy isn't a known subclass of RecordContext
-template <typename ToTy,
-          bool IsKnownSubType = std::is_base_of_v<RecordContext, ToTy>>
-struct FromRecordContextCastInfoWrapper {
-  static_assert(
-      std::is_base_of_v<APIRecord, ToTy>,
-      "Can only class RecordContext to APIRecord and derived classes");
-
-  static bool isPossible(RecordContext *Ctx) {
-    return ToTy::classofKind(Ctx->getKind());
-  }
+  /// Create and add an Objective-C interface record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCInterfaceRecord *
+  addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+                   AvailabilityInfo Availability, LinkageInfo Linkage,
+                   const DocComment &Comment, DeclarationFragments Declaration,
+                   DeclarationFragments SubHeading, SymbolReference SuperClass,
+                   bool IsFromSystemHeader);
 
-  static ToTy *doCast(RecordContext *Ctx) {
-    return APIRecord::castFromRecordContext(Ctx);
-  }
-};
+  /// Create and add an Objective-C method record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCMethodRecord *
+  addObjCMethod(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+                PresumedLoc Loc, AvailabilityInfo Availability,
+                const DocComment &Comment, DeclarationFragments Declaration,
+                DeclarationFragments SubHeading, FunctionSignature Signature,
+                bool IsInstanceMethod, bool IsFromSystemHeader);
+
+  /// Create and add an Objective-C property record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCPropertyRecord *
+  addObjCProperty(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+                  PresumedLoc Loc, AvailabilityInfo Availability,
+                  const DocComment &Comment, DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading,
+                  ObjCPropertyRecord::AttributeKind Attributes,
+                  StringRef GetterName, StringRef SetterName, bool IsOptional,
+                  bool IsInstanceProperty, bool IsFromSystemHeader);
+
+  /// Create and add an Objective-C instance variable record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCInstanceVariableRecord *addObjCInstanceVariable(
+      ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+      PresumedLoc Loc, AvailabilityInfo Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      ObjCInstanceVariableRecord::AccessControl Access,
+      bool IsFromSystemHeader);
+
+  /// Create and add an Objective-C protocol record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCProtocolRecord *
+  addObjCProtocol(StringRef Name, StringRef USR, PresumedLoc Loc,
+                  AvailabilityInfo Availability, const DocComment &Comment,
+                  DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
-// Selected when ToTy is a known subclass of RecordContext.
-template <typename ToTy> struct FromRecordContextCastInfoWrapper<ToTy, true> {
-  static_assert(
-      std::is_base_of_v<APIRecord, ToTy>,
-      "Can only class RecordContext to APIRecord and derived classes");
-  static bool isPossible(RecordContext *Ctx) {
-    return ToTy::classof(Ctx->getKind());
+  /// Create a macro definition record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSRForMacro(StringRef Name,
+  /// SourceLocation SL, const SourceManager &SM) is a helper method to generate
+  /// the USR for the macro and keep it alive in APISet.
+  MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR,
+                                            PresumedLoc Loc,
+                                            DeclarationFragments Declaration,
+                                            DeclarationFragments SubHeading,
+                                            bool IsFromSystemHeader);
+
+  /// Create a typedef record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  TypedefRecord *
+  addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+             AvailabilityInfo Availability, const DocComment &Comment,
+             DeclarationFragments Declaration, DeclarationFragments SubHeading,
+             SymbolReference UnderlyingType, bool IsFromSystemHeader);
+
+  /// A mapping type to store a set of APIRecord%s with the USR as the key.
+  template <typename RecordTy,
+            typename =
+                std::enable_if_t<std::is_base_of<APIRecord, RecordTy>::value>>
+  using RecordMap = llvm::MapVector<StringRef, std::unique_ptr<RecordTy>>;
+
+  /// Get the target triple for the ExtractAPI invocation.
+  const llvm::Triple &getTarget() const { return Target; }
+
+  /// Get the language used by the APIs.
+  Language getLanguage() const { return Lang; }
+
+  const RecordMap<NamespaceRecord> &getNamespaces() const { return Namespaces; }
+  const RecordMap<GlobalFunctionRecord> &getGlobalFunctions() const {
+    return GlobalFunctions;
   }
-  static RecordContext *doCast(RecordContext *Ctx) {
-    return static_cast<ToTy *>(Ctx);
+  const RecordMap<GlobalFunctionTemplateRecord> &
+  getGlobalFunctionTemplates() const {
+    return GlobalFunctionTemplates;
   }
-};
+  const RecordMap<GlobalFunctionTemplateSpecializationRecord> &
+  getGlobalFunctionTemplateSpecializations() const {
+    return GlobalFunctionTemplateSpecializations;
+  }
+  const RecordMap<GlobalVariableRecord> &getGlobalVariables() const {
+    return GlobalVariables;
+  }
+  const RecordMap<GlobalVariableTemplateRecord> &
+  getGlobalVariableTemplates() const {
+    return GlobalVariableTemplates;
+  }
+  const RecordMap<StaticFieldRecord> &getStaticFields() const {
+    return StaticFields;
+  }
+  const RecordMap<GlobalVariableTemplateSpecializationRecord> &
+  getGlobalVariableTemplateSpecializations() const {
+    return GlobalVariableTemplateSpecializations;
+  }
+  const RecordMap<GlobalVariableTemplatePartialSpecializationRecord> &
+  getGlobalVariableTemplatePartialSpecializations() const {
+    return GlobalVariableTemplatePartialSpecializations;
+  }
+  const RecordMap<EnumRecord> &getEnums() const { return Enums; }
+  const RecordMap<RecordRecord> &getRecords() const { return Records; }
+  const RecordMap<CXXClassRecord> &getCXXClasses() const { return CXXClasses; }
+  const RecordMap<CXXMethodTemplateRecord> &getCXXMethodTemplates() const {
+    return CXXMethodTemplates;
+  }
+  const RecordMap<CXXInstanceMethodRecord> &getCXXInstanceMethods() const {
+    return CXXInstanceMethods;
+  }
+  const RecordMap<CXXStaticMethodRecord> &getCXXStaticMethods() const {
+    return CXXStaticMethods;
+  }
+  const RecordMap<CXXFieldRecord> &getCXXFields() const { return CXXFields; }
+  const RecordMap<CXXMethodTemplateSpecializationRecord> &
+  getCXXMethodTemplateSpecializations() const {
+    return CXXMethodTemplateSpecializations;
+  }
+  const RecordMap<CXXFieldTemplateRecord> &getCXXFieldTemplates() const {
+    return CXXFieldTemplates;
+  }
+  const RecordMap<ClassTemplateRecord> &getClassTemplates() const {
+    return ClassTemplates;
+  }
+  const RecordMap<ClassTemplateSpecializationRecord> &
+  getClassTemplateSpecializations() const {
+    return ClassTemplateSpecializations;
+  }
+  const RecordMap<ClassTemplatePartialSpecializationRecord> &
+  getClassTemplatePartialSpecializations() const {
+    return ClassTemplatePartialSpecializations;
+  }
+  const RecordMap<ConceptRecord> &getConcepts() const { return Concepts; }
+  const RecordMap<ObjCCategoryRecord> &getObjCCategories() const {
+    return ObjCCategories;
+  }
+  const RecordMap<ObjCInterfaceRecord> &getObjCInterfaces() const {
+    return ObjCInterfaces;
+  }
+  const RecordMap<ObjCProtocolRecord> &getObjCProtocols() const {
+    return ObjCProtocols;
+  }
+  const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }
+  const RecordMap<TypedefRecord> &getTypedefs() const { return Typedefs; }
 
-} // namespace extractapi
-} // namespace clang
+  /// Finds the APIRecord for a given USR.
+  ///
+  /// \returns a pointer to the APIRecord associated with that USR or nullptr.
+  APIRecord *findRecordForUSR(StringRef USR) const;
 
-// Implement APIRecord (and derived classes) to and from RecordContext
-// conversions
-namespace llvm {
-
-template <typename FromTy>
-struct CastInfo<::clang::extractapi::RecordContext, FromTy *>
-    : public NullableValueCastFailed<::clang::extractapi::RecordContext *>,
-      public DefaultDoCastIfPossible<
-          ::clang::extractapi::RecordContext *, FromTy *,
-          CastInfo<::clang::extractapi::RecordContext, FromTy *>> {
-  static inline bool isPossible(FromTy *From) {
-    return ::clang::extractapi::ToRecordContextCastInfoWrapper<
-        FromTy>::isPossible(From);
-  }
+  /// Generate and store the USR of declaration \p D.
+  ///
+  /// Note: The USR string is stored in and owned by Allocator.
+  ///
+  /// \returns a StringRef of the generated USR string.
+  StringRef recordUSR(const Decl *D);
 
-  static inline ::clang::extractapi::RecordContext *doCast(FromTy *From) {
-    return ::clang::extractapi::ToRecordContextCastInfoWrapper<FromTy>::doCast(
-        From);
-  }
-};
+  /// Generate and store the USR for a macro \p Name.
+  ///
+  /// Note: The USR string is stored in and owned by Allocator.
+  ///
+  /// \returns a StringRef to the generate USR string.
+  StringRef recordUSRForMacro(StringRef Name, SourceLocation SL,
+                              const SourceManager &SM);
 
-template <typename FromTy>
-struct CastInfo<::clang::extractapi::RecordContext, const FromTy *>
-    : public ConstStrippingForwardingCast<
-          ::clang::extractapi::RecordContext, const FromTy *,
-          CastInfo<::clang::extractapi::RecordContext, FromTy *>> {};
-
-template <typename ToTy>
-struct CastInfo<ToTy, ::clang::extractapi::RecordContext *>
-    : public NullableValueCastFailed<ToTy *>,
-      public DefaultDoCastIfPossible<
-          ToTy *, ::clang::extractapi::RecordContext *,
-          CastInfo<ToTy, ::clang::extractapi::RecordContext *>> {
-  static inline bool isPossible(::clang::extractapi::RecordContext *Ctx) {
-    return ::clang::extractapi::FromRecordContextCastInfoWrapper<
-        ToTy>::isPossible(Ctx);
-  }
+  /// Copy \p String into the Allocator in this APISet.
+  ///
+  /// \returns a StringRef of the copied string in APISet::Allocator.
+  StringRef copyString(StringRef String);
 
-  static inline ToTy *doCast(::clang::extractapi::RecordContext *Ctx) {
-    return ::clang::extractapi::FromRecordContextCastInfoWrapper<ToTy>::doCast(
-        Ctx);
-  }
-};
+  APISet(const llvm::Triple &Target, Language Lang,
+         const std::string &ProductName)
+      : Target(Target), Lang(Lang), ProductName(ProductName) {}
+
+private:
+  /// BumpPtrAllocator to store generated/copied strings.
+  ///
+  /// Note: The main use for this is being able to deduplicate strings.
+  llvm::BumpPtrAllocator StringAllocator;
+
+  const llvm::Triple Target;
+  const Language Lang;
 
-template <typename ToTy>
-struct CastInfo<ToTy, const ::clang::extractapi::RecordContext *>
-    : public ConstStrippingForwardingCast<
-          ToTy, const ::clang::extractapi::RecordContext *,
-          CastInfo<ToTy, ::clang::extractapi::RecordContext *>> {};
+  llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
+  RecordMap<NamespaceRecord> Namespaces;
+  RecordMap<GlobalFunctionRecord> GlobalFunctions;
+  RecordMap<GlobalFunctionTemplateRecord> GlobalFunctionTemplates;
+  RecordMap<GlobalFunctionTemplateSpecializationRecord>
+      GlobalFunctionTemplateSpecializations;
+  RecordMap<GlobalVariableRecord> GlobalVariables;
+  RecordMap<GlobalVariableTemplateRecord> GlobalVariableTemplates;
+  RecordMap<GlobalVariableTemplateSpecializationRecord>
+      GlobalVariableTemplateSpecializations;
+  RecordMap<GlobalVariableTemplatePartialSpecializationRecord>
+      GlobalVariableTemplatePartialSpecializations;
+  RecordMap<ConceptRecord> Concepts;
+  RecordMap<StaticFieldRecord> StaticFields;
+  RecordMap<EnumRecord> Enums;
+  RecordMap<RecordRecord> Records;
+  RecordMap<CXXClassRecord> CXXClasses;
+  RecordMap<CXXFieldRecord> CXXFields;
+  RecordMap<CXXMethodRecord> CXXMethods;
+  RecordMap<CXXInstanceMethodRecord> CXXInstanceMethods;
+  RecordMap<CXXStaticMethodRecord> CXXStaticMethods;
+  RecordMap<CXXMethodTemplateRecord> CXXMethodTemplates;
+  RecordMap<CXXMethodTemplateSpecializationRecord>
+      CXXMethodTemplateSpecializations;
+  RecordMap<CXXFieldTemplateRecord> CXXFieldTemplates;
+  RecordMap<ClassTemplateRecord> ClassTemplates;
+  RecordMap<ClassTemplateSpecializationRecord> ClassTemplateSpecializations;
+  RecordMap<ClassTemplatePartialSpecializationRecord>
+      ClassTemplatePartialSpecializations;
+  RecordMap<ObjCCategoryRecord> ObjCCategories;
+  RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
+  RecordMap<ObjCProtocolRecord> ObjCProtocols;
+  RecordMap<MacroDefinitionRecord> Macros;
+  RecordMap<TypedefRecord> Typedefs;
 
-} // namespace llvm
+public:
+  const std::string ProductName;
+};
+
+} // namespace extractapi
+} // namespace clang
 
 #endif // LLVM_CLANG_EXTRACTAPI_API_H

diff  --git a/clang/include/clang/ExtractAPI/APIRecords.inc b/clang/include/clang/ExtractAPI/APIRecords.inc
deleted file mode 100644
index 15fee809656d9a..00000000000000
--- a/clang/include/clang/ExtractAPI/APIRecords.inc
+++ /dev/null
@@ -1,103 +0,0 @@
-//===- ExtractAPI/APIRecords.inc --------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file defines the classes defined from ExtractAPI's APIRecord
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef ABSTRACT_RECORD
-#define ABSTRACT_RECORD(CLASS, BASE) RECORD(CLASS, BASE)
-#endif
-#ifndef CONCRETE_RECORD
-#define CONCRETE_RECORD(CLASS, BASE, KIND) RECORD(CLASS, BASE)
-#endif
-#ifndef RECORD
-#define RECORD(CLASS, BASE)
-#endif
-
-CONCRETE_RECORD(NamespaceRecord, APIRecord, RK_Namespace)
-CONCRETE_RECORD(GlobalFunctionRecord, APIRecord, RK_GlobalFunction)
-CONCRETE_RECORD(GlobalFunctionTemplateRecord, GlobalFunctionRecord,
-                RK_GlobalFunctionTemplate)
-CONCRETE_RECORD(GlobalFunctionTemplateSpecializationRecord,
-                GlobalFunctionRecord, RK_GlobalFunctionTemplateSpecialization)
-CONCRETE_RECORD(GlobalVariableRecord, APIRecord, RK_GlobalVariable)
-CONCRETE_RECORD(GlobalVariableTemplateRecord, GlobalVariableRecord,
-                RK_GlobalVariableTemplate)
-CONCRETE_RECORD(GlobalVariableTemplateSpecializationRecord,
-                GlobalVariableRecord, RK_GlobalVariableTemplateSpecialization)
-CONCRETE_RECORD(GlobalVariableTemplatePartialSpecializationRecord,
-                GlobalVariableRecord,
-                RK_GlobalVariableTemplatePartialSpecialization)
-CONCRETE_RECORD(EnumConstantRecord, APIRecord, RK_EnumConstant)
-CONCRETE_RECORD(EnumRecord, APIRecord, RK_Enum)
-ABSTRACT_RECORD(RecordFieldRecord, APIRecord)
-ABSTRACT_RECORD(RecordRecord, APIRecord)
-CONCRETE_RECORD(StructFieldRecord, RecordFieldRecord, RK_StructField)
-CONCRETE_RECORD(StructRecord, APIRecord, RK_Struct)
-CONCRETE_RECORD(UnionFieldRecord, RecordFieldRecord, RK_UnionField)
-CONCRETE_RECORD(UnionRecord, APIRecord, RK_Union)
-CONCRETE_RECORD(CXXFieldRecord, APIRecord, RK_CXXField)
-CONCRETE_RECORD(CXXFieldTemplateRecord, CXXFieldRecord, RK_CXXFieldTemplate)
-ABSTRACT_RECORD(CXXMethodRecord, APIRecord)
-CONCRETE_RECORD(CXXConstructorRecord, CXXMethodRecord, RK_CXXConstructorMethod)
-CONCRETE_RECORD(CXXDestructorRecord, CXXMethodRecord, RK_CXXDestructorMethod)
-CONCRETE_RECORD(CXXStaticMethodRecord, CXXMethodRecord, RK_CXXStaticMethod)
-CONCRETE_RECORD(CXXInstanceMethodRecord, CXXMethodRecord, RK_CXXInstanceMethod)
-CONCRETE_RECORD(CXXMethodTemplateRecord, CXXMethodRecord, RK_CXXMethodTemplate)
-CONCRETE_RECORD(CXXMethodTemplateSpecializationRecord, CXXMethodRecord,
-                RK_CXXMethodTemplateSpecialization)
-ABSTRACT_RECORD(ObjCPropertyRecord, APIRecord)
-CONCRETE_RECORD(ObjCInstancePropertyRecord, ObjCPropertyRecord,
-                RK_ObjCInstanceProperty)
-CONCRETE_RECORD(ObjCClassPropertyRecord, ObjCPropertyRecord,
-                RK_ObjCClassProperty)
-CONCRETE_RECORD(ObjCInstanceVariableRecord, APIRecord, RK_ObjCIvar)
-ABSTRACT_RECORD(ObjCMethodRecord, APIRecord)
-CONCRETE_RECORD(ObjCInstanceMethodRecord, ObjCMethodRecord,
-                RK_ObjCInstanceMethod)
-CONCRETE_RECORD(ObjCClassMethodRecord, ObjCMethodRecord, RK_ObjCClassMethod)
-CONCRETE_RECORD(StaticFieldRecord, CXXFieldRecord, RK_StaticField)
-ABSTRACT_RECORD(ObjCContainerRecord, APIRecord)
-CONCRETE_RECORD(CXXClassRecord, APIRecord, RK_CXXClass)
-CONCRETE_RECORD(ClassTemplateRecord, CXXClassRecord, RK_ClassTemplate)
-CONCRETE_RECORD(ClassTemplateSpecializationRecord, CXXClassRecord,
-                RK_ClassTemplateSpecialization)
-CONCRETE_RECORD(ClassTemplatePartialSpecializationRecord, CXXClassRecord,
-                RK_ClassTemplatePartialSpecialization)
-CONCRETE_RECORD(ConceptRecord, APIRecord, RK_Concept)
-CONCRETE_RECORD(ObjCCategoryRecord, ObjCContainerRecord, RK_ObjCCategory)
-CONCRETE_RECORD(ObjCInterfaceRecord, ObjCContainerRecord, RK_ObjCInterface)
-CONCRETE_RECORD(ObjCProtocolRecord, ObjCContainerRecord, RK_ObjCProtocol)
-CONCRETE_RECORD(MacroDefinitionRecord, APIRecord, RK_MacroDefinition)
-CONCRETE_RECORD(TypedefRecord, APIRecord, RK_Typedef)
-
-#undef CONCRETE_RECORD
-#undef ABSTRACT_RECORD
-#undef RECORD
-
-#ifndef RECORD_CONTEXT
-#define RECORD_CONTEXT(CLASS, KIND)
-#endif
-
-RECORD_CONTEXT(NamespaceRecord, RK_Namespace)
-RECORD_CONTEXT(EnumRecord, RK_Enum)
-RECORD_CONTEXT(StructRecord, RK_Struct)
-RECORD_CONTEXT(UnionRecord, RK_Union)
-RECORD_CONTEXT(ObjCCategoryRecord, RK_ObjCCategory)
-RECORD_CONTEXT(ObjCInterfaceRecord, RK_ObjCInterface)
-RECORD_CONTEXT(ObjCProtocolRecord, RK_ObjCProtocol)
-RECORD_CONTEXT(CXXClassRecord, RK_CXXClass)
-RECORD_CONTEXT(ClassTemplateRecord, RK_ClassTemplate)
-RECORD_CONTEXT(ClassTemplateSpecializationRecord,
-               RK_ClassTemplateSpecialization)
-RECORD_CONTEXT(ClassTemplatePartialSpecializationRecord,
-               RK_ClassTemplatePartialSpecialization)
-
-#undef RECORD_CONTEXT

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index ec97c2e3e254c2..b85a5d21d61217 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -182,18 +182,6 @@ class DeclarationFragments {
   /// appending to chain up consecutive appends.
   DeclarationFragments &appendSpace();
 
-  /// Append a text Fragment of a semicolon character.
-  ///
-  /// \returns a reference to the DeclarationFragments object itself after
-  /// appending to chain up consecutive appends.
-  DeclarationFragments &appendSemicolon();
-
-  /// Removes a trailing semicolon character if present.
-  ///
-  /// \returns a reference to the DeclarationFragments object itself after
-  /// removing to chain up consecutive operations.
-  DeclarationFragments &removeTrailingSemicolon();
-
   /// Get the string description of a FragmentKind \p Kind.
   static StringRef getFragmentKindString(FragmentKind Kind);
 
@@ -206,14 +194,12 @@ class DeclarationFragments {
   static DeclarationFragments getStructureTypeFragment(const RecordDecl *Decl);
 
 private:
-  DeclarationFragments &appendUnduplicatedTextCharacter(char Character);
   std::vector<Fragment> Fragments;
 };
 
 class AccessControl {
 public:
   AccessControl(std::string Access) : Access(Access) {}
-  AccessControl() : Access("public") {}
 
   const std::string &getAccess() const { return Access; }
 

diff  --git a/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h b/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
index 08210a7ee05954..ac4f391db5f14a 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
@@ -17,8 +17,6 @@
 
 #include "clang/ExtractAPI/API.h"
 #include "clang/ExtractAPI/APIIgnoresList.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "llvm/Support/raw_ostream.h"
 
 namespace clang {
 
@@ -31,8 +29,8 @@ class ExtractAPIActionBase {
   /// A representation of the APIs this action extracts.
   std::unique_ptr<extractapi::APISet> API;
 
-  /// A stream to the main output file of this action.
-  std::unique_ptr<llvm::raw_pwrite_stream> OS;
+  /// A stream to the output file of this action.
+  std::unique_ptr<raw_pwrite_stream> OS;
 
   /// The product this action is extracting API information for.
   std::string ProductName;
@@ -48,7 +46,7 @@ class ExtractAPIActionBase {
   ///
   /// Use the serializer to generate output symbol graph files from
   /// the information gathered during the execution of Action.
-  void ImplEndSourceFileAction(CompilerInstance &CI);
+  void ImplEndSourceFileAction();
 };
 
 } // namespace clang

diff  --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 4cb866892b5d00..e1c3e41c750d40 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -14,23 +14,23 @@
 #ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
 #define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
 
-#include "clang/AST/ASTContext.h"
+#include "clang/AST/Availability.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "llvm/ADT/FunctionExtras.h"
+
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/Basic/Module.h"
 #include "clang/Basic/SourceManager.h"
-#include "clang/Basic/Specifiers.h"
 #include "clang/ExtractAPI/API.h"
-#include "clang/ExtractAPI/DeclarationFragments.h"
 #include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
 #include "clang/Index/USRGeneration.h"
-#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Casting.h"
 #include <type_traits>
 
 namespace clang {
@@ -130,6 +130,12 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
   void recordEnumConstants(EnumRecord *EnumRecord,
                            const EnumDecl::enumerator_range Constants);
 
+  /// Collect API information for the record fields and associate with the
+  /// parent struct.
+  void recordRecordFields(RecordRecord *RecordRecord,
+                          APIRecord::RecordKind FieldKind,
+                          const RecordDecl::field_range Fields);
+
   /// Collect API information for the Objective-C methods and associate with the
   /// parent container.
   void recordObjCMethods(ObjCContainerRecord *Container,
@@ -166,7 +172,6 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
     return *static_cast<Derived *>(this);
   }
 
-protected:
   SmallVector<SymbolReference> getBases(const CXXRecordDecl *Decl) {
     // FIXME: store AccessSpecifier given by inheritance
     SmallVector<SymbolReference> Bases;
@@ -177,54 +182,49 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
       SymbolReference BaseClass;
       if (BaseSpecifier.getType().getTypePtr()->isTemplateTypeParmType()) {
         BaseClass.Name = API.copyString(BaseSpecifier.getType().getAsString());
-        if (auto *TTPTD = BaseSpecifier.getType()
-                              ->getAs<TemplateTypeParmType>()
-                              ->getDecl()) {
-          SmallString<128> USR;
-          index::generateUSRForDecl(TTPTD, USR);
-          BaseClass.USR = API.copyString(USR);
-          BaseClass.Source = API.copyString(getOwningModuleName(*TTPTD));
-        }
+        BaseClass.USR = API.recordUSR(
+            BaseSpecifier.getType()->getAs<TemplateTypeParmType>()->getDecl());
       } else {
-        BaseClass = createSymbolReferenceForDecl(
-            *BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl());
+        CXXRecordDecl *BaseClassDecl =
+            BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl();
+        BaseClass.Name = BaseClassDecl->getName();
+        BaseClass.USR = API.recordUSR(BaseClassDecl);
       }
       Bases.emplace_back(BaseClass);
     }
     return Bases;
   }
 
-  StringRef getOwningModuleName(const Decl &D) {
-    if (auto *OwningModule = D.getImportedOwningModule())
-      return OwningModule->Name;
+  APIRecord *determineParentRecord(const DeclContext *Context) {
+    SmallString<128> ParentUSR;
+    if (Context->getDeclKind() == Decl::TranslationUnit)
+      return nullptr;
 
-    return {};
-  }
+    index::generateUSRForDecl(dyn_cast<Decl>(Context), ParentUSR);
 
-  SymbolReference createHierarchyInformationForDecl(const Decl &D) {
-    const auto *Context = cast_if_present<Decl>(D.getDeclContext());
-
-    if (!Context || isa<TranslationUnitDecl>(Context))
-      return {};
-
-    return createSymbolReferenceForDecl(*Context);
+    APIRecord *Parent = API.findRecordForUSR(ParentUSR);
+    return Parent;
   }
+};
 
-  SymbolReference createSymbolReferenceForDecl(const Decl &D) {
-    SmallString<128> USR;
-    index::generateUSRForDecl(&D, USR);
-
-    APIRecord *Record = API.findRecordForUSR(USR);
-    if (Record)
-      return SymbolReference(Record);
-
-    StringRef Name;
-    if (auto *ND = dyn_cast<NamedDecl>(&D))
-      Name = ND->getName();
-
-    return API.createSymbolReference(Name, USR, getOwningModuleName(D));
+template <typename T>
+static void modifyRecords(const T &Records, const StringRef &Name) {
+  for (const auto &Record : Records) {
+    if (Name == Record.second.get()->Name) {
+      auto &DeclFragment = Record.second->Declaration;
+      DeclFragment.insert(DeclFragment.begin(), " ",
+                          DeclarationFragments::FragmentKind::Text);
+      DeclFragment.insert(DeclFragment.begin(), "typedef",
+                          DeclarationFragments::FragmentKind::Keyword, "",
+                          nullptr);
+      DeclFragment.insert(--DeclFragment.end(), " { ... } ",
+                          DeclarationFragments::FragmentKind::Text);
+      DeclFragment.insert(--DeclFragment.end(), Name,
+                          DeclarationFragments::FragmentKind::Identifier);
+      break;
+    }
   }
-};
+}
 
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
@@ -251,8 +251,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -268,17 +267,21 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
   if (Decl->isStaticDataMember()) {
+    SymbolReference Context;
+    // getDeclContext() should return a RecordDecl since we
+    // are currently handling a static data member.
+    auto *Record = cast<RecordDecl>(Decl->getDeclContext());
+    Context.Name = Record->getName();
+    Context.USR = API.recordUSR(Record);
     auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
-    API.createRecord<StaticFieldRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-        SubHeading, Access, isInSystemHeader(Decl));
+    API.addStaticField(Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl),
+                       Linkage, Comment, Declaration, SubHeading, Context,
+                       Access, isInSystemHeader(Decl));
   } else
     // Add the global variable record to the API set.
-    API.createRecord<GlobalVariableRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-        SubHeading, isInSystemHeader(Decl));
+    API.addGlobalVar(Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl),
+                     Linkage, Comment, Declaration, SubHeading,
+                     isInSystemHeader(Decl));
   return true;
 }
 
@@ -301,7 +304,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
       return true;
   }
 
-  // Skip templated functions that aren't processed here.
+  // Skip templated functions.
   switch (Decl->getTemplatedKind()) {
   case FunctionDecl::TK_NonTemplate:
   case FunctionDecl::TK_DependentNonTemplate:
@@ -318,8 +321,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -335,19 +337,18 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
   FunctionSignature Signature =
       DeclarationFragmentsBuilder::getFunctionSignature(Decl);
   if (Decl->getTemplateSpecializationInfo())
-    API.createRecord<GlobalFunctionTemplateSpecializationRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
+    API.addGlobalFunctionTemplateSpecialization(
+        Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage,
+        Comment,
         DeclarationFragmentsBuilder::
             getFragmentsForFunctionTemplateSpecialization(Decl),
         SubHeading, Signature, isInSystemHeader(Decl));
   else
     // Add the function record to the API set.
-    API.createRecord<GlobalFunctionRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
-        DeclarationFragmentsBuilder::getFragmentsForFunction(Decl), SubHeading,
-        Signature, isInSystemHeader(Decl));
+    API.addGlobalFunction(
+        Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage,
+        Comment, DeclarationFragmentsBuilder::getFragmentsForFunction(Decl),
+        SubHeading, Signature, isInSystemHeader(Decl));
   return true;
 }
 
@@ -367,8 +368,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
     Name = QualifiedNameBuffer.str();
   }
 
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -382,13 +382,13 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
       DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
-  auto *ER = API.createRecord<EnumRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      isInSystemHeader(Decl));
+  EnumRecord *EnumRecord = API.addEnum(
+      API.copyString(Name), USR, Loc, AvailabilityInfo::createFromDecl(Decl),
+      Comment, Declaration, SubHeading, isInSystemHeader(Decl));
 
   // Now collect information about the enumerators in this enum.
-  getDerivedExtractAPIVisitor().recordEnumConstants(ER, Decl->enumerators());
+  getDerivedExtractAPIVisitor().recordEnumConstants(EnumRecord,
+                                                    Decl->enumerators());
 
   return true;
 }
@@ -476,13 +476,13 @@ bool ExtractAPIVisitorBase<Derived>::WalkUpFromNamespaceDecl(
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitNamespaceDecl(
     const NamespaceDecl *Decl) {
+
   if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
     return true;
   if (Decl->isAnonymousNamespace())
     return true;
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
@@ -497,10 +497,10 @@ bool ExtractAPIVisitorBase<Derived>::VisitNamespaceDecl(
       DeclarationFragmentsBuilder::getFragmentsForNamespace(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
-  API.createRecord<NamespaceRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-      SubHeading, isInSystemHeader(Decl));
+  APIRecord *Parent = determineParentRecord(Decl->getDeclContext());
+  API.addNamespace(Parent, Name, USR, Loc,
+                   AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
+                   Declaration, SubHeading, isInSystemHeader(Decl));
 
   return true;
 }
@@ -509,20 +509,14 @@ template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
   if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
     return true;
-
-  SmallString<128> QualifiedNameBuffer;
   // Collect symbol information.
   StringRef Name = Decl->getName();
   if (Name.empty())
     Name = getTypedefName(Decl);
-  if (Name.empty()) {
-    llvm::raw_svector_ostream OS(QualifiedNameBuffer);
-    Decl->printQualifiedName(OS);
-    Name = QualifiedNameBuffer.str();
-  }
+  if (Name.empty())
+    return true;
 
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -537,16 +531,21 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
 
-  if (Decl->isUnion())
-    API.createRecord<UnionRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, isInSystemHeader(Decl));
-  else
-    API.createRecord<StructRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, isInSystemHeader(Decl));
+  auto RecordKind = APIRecord::RK_Struct;
+  auto FieldRecordKind = APIRecord::RK_StructField;
+
+  if (Decl->isUnion()) {
+    RecordKind = APIRecord::RK_Union;
+    FieldRecordKind = APIRecord::RK_UnionField;
+  }
+
+  RecordRecord *RecordRecord = API.addRecord(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+      Declaration, SubHeading, RecordKind, isInSystemHeader(Decl));
+
+  // Now collect information about the fields in this struct.
+  getDerivedExtractAPIVisitor().recordRecordFields(
+      RecordRecord, FieldRecordKind, Decl->fields());
 
   return true;
 }
@@ -559,8 +558,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
     return true;
 
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -582,25 +580,24 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
     Kind = APIRecord::RecordKind::RK_CXXClass;
   auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
 
-  CXXClassRecord *Record;
+  APIRecord *Parent = determineParentRecord(Decl->getDeclContext());
+  CXXClassRecord *CXXClassRecord;
   if (Decl->getDescribedClassTemplate()) {
     // Inject template fragments before class fragments.
     Declaration.insert(
         Declaration.begin(),
         DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
             Decl->getDescribedClassTemplate()));
-    Record = API.createRecord<ClassTemplateRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, Template(Decl->getDescribedClassTemplate()), Access,
-        isInSystemHeader(Decl));
+    CXXClassRecord = API.addClassTemplate(
+        Parent, Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+        Declaration, SubHeading, Template(Decl->getDescribedClassTemplate()),
+        Access, isInSystemHeader(Decl));
   } else
-    Record = API.createRecord<CXXClassRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, Kind, Access, isInSystemHeader(Decl));
+    CXXClassRecord = API.addCXXClass(
+        Parent, Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+        Declaration, SubHeading, Kind, Access, isInSystemHeader(Decl));
 
-  Record->Bases = getBases(Decl);
+  CXXClassRecord->Bases = getBases(Decl);
 
   return true;
 }
@@ -617,8 +614,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
   if (isa<CXXConstructorDecl>(Decl) || isa<CXXDestructorDecl>(Decl))
     return true;
 
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -631,10 +627,14 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
   auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
   auto Signature = DeclarationFragmentsBuilder::getFunctionSignature(Decl);
 
-  if (FunctionTemplateDecl *TemplateDecl =
-          Decl->getDescribedFunctionTemplate()) {
-    API.createRecord<CXXMethodTemplateRecord>(
-        USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
+  SmallString<128> ParentUSR;
+  index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+                            ParentUSR);
+  auto *Parent = API.findRecordForUSR(ParentUSR);
+  if (Decl->isTemplated()) {
+    FunctionTemplateDecl *TemplateDecl = Decl->getDescribedFunctionTemplate();
+    API.addCXXMethodTemplate(
+        API.findRecordForUSR(ParentUSR), Decl->getName(), USR, Loc,
         AvailabilityInfo::createFromDecl(Decl), Comment,
         DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(
             TemplateDecl),
@@ -642,27 +642,27 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
         DeclarationFragmentsBuilder::getAccessControl(TemplateDecl),
         Template(TemplateDecl), isInSystemHeader(Decl));
   } else if (Decl->getTemplateSpecializationInfo())
-    API.createRecord<CXXMethodTemplateSpecializationRecord>(
-        USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
+    API.addCXXMethodTemplateSpec(
+        Parent, Decl->getName(), USR, Loc,
         AvailabilityInfo::createFromDecl(Decl), Comment,
         DeclarationFragmentsBuilder::
             getFragmentsForFunctionTemplateSpecialization(Decl),
         SubHeading, Signature, Access, isInSystemHeader(Decl));
   else if (Decl->isOverloadedOperator())
-    API.createRecord<CXXInstanceMethodRecord>(
-        USR, Decl->getNameAsString(), createHierarchyInformationForDecl(*Decl),
-        Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+    API.addCXXInstanceMethod(
+        Parent, API.copyString(Decl->getNameAsString()), USR, Loc,
+        AvailabilityInfo::createFromDecl(Decl), Comment,
         DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(Decl),
         SubHeading, Signature, Access, isInSystemHeader(Decl));
   else if (Decl->isStatic())
-    API.createRecord<CXXStaticMethodRecord>(
-        USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
+    API.addCXXStaticMethod(
+        Parent, Decl->getName(), USR, Loc,
         AvailabilityInfo::createFromDecl(Decl), Comment,
         DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Decl), SubHeading,
         Signature, Access, isInSystemHeader(Decl));
   else
-    API.createRecord<CXXInstanceMethodRecord>(
-        USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
+    API.addCXXInstanceMethod(
+        Parent, Decl->getName(), USR, Loc,
         AvailabilityInfo::createFromDecl(Decl), Comment,
         DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Decl), SubHeading,
         Signature, Access, isInSystemHeader(Decl));
@@ -673,13 +673,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitCXXConstructorDecl(
     const CXXConstructorDecl *Decl) {
-  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) ||
-      Decl->isImplicit())
-    return true;
 
-  auto Name = Decl->getNameAsString();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef Name = API.copyString(Decl->getNameAsString());
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -696,24 +692,22 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXConstructorDecl(
   FunctionSignature Signature =
       DeclarationFragmentsBuilder::getFunctionSignature(Decl);
   AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
-
-  API.createRecord<CXXConstructorRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      Signature, Access, isInSystemHeader(Decl));
+  SmallString<128> ParentUSR;
+  index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+                            ParentUSR);
+  API.addCXXInstanceMethod(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
+                           AvailabilityInfo::createFromDecl(Decl), Comment,
+                           Declaration, SubHeading, Signature, Access,
+                           isInSystemHeader(Decl));
   return true;
 }
 
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitCXXDestructorDecl(
     const CXXDestructorDecl *Decl) {
-  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) ||
-      Decl->isImplicit())
-    return true;
 
-  auto Name = Decl->getNameAsString();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef Name = API.copyString(Decl->getNameAsString());
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -730,10 +724,13 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXDestructorDecl(
   FunctionSignature Signature =
       DeclarationFragmentsBuilder::getFunctionSignature(Decl);
   AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
-  API.createRecord<CXXDestructorRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      Signature, Access, isInSystemHeader(Decl));
+  SmallString<128> ParentUSR;
+  index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+                            ParentUSR);
+  API.addCXXInstanceMethod(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
+                           AvailabilityInfo::createFromDecl(Decl), Comment,
+                           Declaration, SubHeading, Signature, Access,
+                           isInSystemHeader(Decl));
   return true;
 }
 
@@ -743,8 +740,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitConceptDecl(const ConceptDecl *Decl) {
     return true;
 
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -756,10 +752,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitConceptDecl(const ConceptDecl *Decl) {
       DeclarationFragmentsBuilder::getFragmentsForConcept(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
-  API.createRecord<ConceptRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      Template(Decl), isInSystemHeader(Decl));
+  API.addConcept(Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl),
+                 Comment, Declaration, SubHeading, Template(Decl),
+                 isInSystemHeader(Decl));
   return true;
 }
 
@@ -770,8 +765,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitClassTemplateSpecializationDecl(
     return true;
 
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -785,13 +779,14 @@ bool ExtractAPIVisitorBase<Derived>::VisitClassTemplateSpecializationDecl(
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
 
-  auto *CTSR = API.createRecord<ClassTemplateSpecializationRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
+  APIRecord *Parent = determineParentRecord(Decl->getDeclContext());
+  auto *ClassTemplateSpecializationRecord = API.addClassTemplateSpecialization(
+      Parent, Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+      Declaration, SubHeading,
       DeclarationFragmentsBuilder::getAccessControl(Decl),
       isInSystemHeader(Decl));
 
-  CTSR->Bases = getBases(Decl);
+  ClassTemplateSpecializationRecord->Bases = getBases(Decl);
 
   return true;
 }
@@ -804,8 +799,7 @@ bool ExtractAPIVisitorBase<Derived>::
     return true;
 
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -817,13 +811,15 @@ bool ExtractAPIVisitorBase<Derived>::
       getFragmentsForClassTemplatePartialSpecialization(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
-  auto *CTPSR = API.createRecord<ClassTemplatePartialSpecializationRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      Template(Decl), DeclarationFragmentsBuilder::getAccessControl(Decl),
-      isInSystemHeader(Decl));
+  APIRecord *Parent = determineParentRecord(Decl->getDeclContext());
+  auto *ClassTemplatePartialSpecRecord =
+      API.addClassTemplatePartialSpecialization(
+          Parent, Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl),
+          Comment, Declaration, SubHeading, Template(Decl),
+          DeclarationFragmentsBuilder::getAccessControl(Decl),
+          isInSystemHeader(Decl));
 
-  CTPSR->Bases = getBases(Decl);
+  ClassTemplatePartialSpecRecord->Bases = getBases(Decl);
 
   return true;
 }
@@ -836,8 +832,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -858,17 +853,20 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateDecl(
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
 
+  SmallString<128> ParentUSR;
+  index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+                            ParentUSR);
   if (Decl->getDeclContext()->getDeclKind() == Decl::CXXRecord)
-    API.createRecord<CXXFieldTemplateRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, DeclarationFragmentsBuilder::getAccessControl(Decl),
-        Template(Decl), isInSystemHeader(Decl));
+    API.addCXXFieldTemplate(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
+                            AvailabilityInfo::createFromDecl(Decl), Comment,
+                            Declaration, SubHeading,
+                            DeclarationFragmentsBuilder::getAccessControl(Decl),
+                            Template(Decl), isInSystemHeader(Decl));
   else
-    API.createRecord<GlobalVariableTemplateRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-        SubHeading, Template(Decl), isInSystemHeader(Decl));
+    API.addGlobalVariableTemplate(Name, USR, Loc,
+                                  AvailabilityInfo::createFromDecl(Decl),
+                                  Linkage, Comment, Declaration, SubHeading,
+                                  Template(Decl), isInSystemHeader(Decl));
   return true;
 }
 
@@ -880,8 +878,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateSpecializationDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -897,10 +894,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateSpecializationDecl(
           Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
-  API.createRecord<GlobalVariableTemplateSpecializationRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-      SubHeading, isInSystemHeader(Decl));
+  API.addGlobalVariableTemplateSpecialization(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
+      Declaration, SubHeading, isInSystemHeader(Decl));
   return true;
 }
 
@@ -912,8 +908,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplatePartialSpecializationDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -928,10 +923,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplatePartialSpecializationDecl(
       getFragmentsForVarTemplatePartialSpecialization(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
-  API.createRecord<GlobalVariableTemplatePartialSpecializationRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-      SubHeading, Template(Decl), isInSystemHeader(Decl));
+  API.addGlobalVariableTemplatePartialSpecialization(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
+      Declaration, SubHeading, Template(Decl), isInSystemHeader(Decl));
   return true;
 }
 
@@ -945,8 +939,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionTemplateDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -961,9 +954,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionTemplateDecl(
   FunctionSignature Signature =
       DeclarationFragmentsBuilder::getFunctionSignature(
           Decl->getTemplatedDecl());
-  API.createRecord<GlobalFunctionTemplateRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
+  API.addGlobalFunctionTemplate(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
       DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(Decl),
       SubHeading, Signature, Template(Decl), isInSystemHeader(Decl));
 
@@ -978,8 +970,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
@@ -997,23 +988,24 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
 
   // Collect super class information.
   SymbolReference SuperClass;
-  if (const auto *SuperClassDecl = Decl->getSuperClass())
-    SuperClass = createSymbolReferenceForDecl(*SuperClassDecl);
+  if (const auto *SuperClassDecl = Decl->getSuperClass()) {
+    SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
+    SuperClass.USR = API.recordUSR(SuperClassDecl);
+  }
 
-  auto *InterfaceRecord = API.createRecord<ObjCInterfaceRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
-      SubHeading, SuperClass, isInSystemHeader(Decl));
+  ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage, Comment,
+      Declaration, SubHeading, SuperClass, isInSystemHeader(Decl));
 
   // Record all methods (selectors). This doesn't include automatically
   // synthesized property methods.
-  getDerivedExtractAPIVisitor().recordObjCMethods(InterfaceRecord,
+  getDerivedExtractAPIVisitor().recordObjCMethods(ObjCInterfaceRecord,
                                                   Decl->methods());
-  getDerivedExtractAPIVisitor().recordObjCProperties(InterfaceRecord,
+  getDerivedExtractAPIVisitor().recordObjCProperties(ObjCInterfaceRecord,
                                                      Decl->properties());
-  getDerivedExtractAPIVisitor().recordObjCInstanceVariables(InterfaceRecord,
+  getDerivedExtractAPIVisitor().recordObjCInstanceVariables(ObjCInterfaceRecord,
                                                             Decl->ivars());
-  getDerivedExtractAPIVisitor().recordObjCProtocols(InterfaceRecord,
+  getDerivedExtractAPIVisitor().recordObjCProtocols(ObjCInterfaceRecord,
                                                     Decl->protocols());
 
   return true;
@@ -1027,8 +1019,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCProtocolDecl(
 
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -1043,15 +1034,15 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCProtocolDecl(
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
 
-  auto *ProtoRecord = API.createRecord<ObjCProtocolRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      isInSystemHeader(Decl));
+  ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+      Declaration, SubHeading, isInSystemHeader(Decl));
 
-  getDerivedExtractAPIVisitor().recordObjCMethods(ProtoRecord, Decl->methods());
-  getDerivedExtractAPIVisitor().recordObjCProperties(ProtoRecord,
+  getDerivedExtractAPIVisitor().recordObjCMethods(ObjCProtocolRecord,
+                                                  Decl->methods());
+  getDerivedExtractAPIVisitor().recordObjCProperties(ObjCProtocolRecord,
                                                      Decl->properties());
-  getDerivedExtractAPIVisitor().recordObjCProtocols(ProtoRecord,
+  getDerivedExtractAPIVisitor().recordObjCProtocols(ObjCProtocolRecord,
                                                     Decl->protocols());
 
   return true;
@@ -1070,36 +1061,25 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl(
   if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
     return true;
 
-  StringRef Name = Decl->getName();
-
-  // If the underlying type was defined as part of the typedef modify it's
-  // fragments directly and pretend the typedef doesn't exist.
-  if (auto *TagDecl = Decl->getUnderlyingType()->getAsTagDecl()) {
-    if (TagDecl->getName() == Decl->getName() &&
-        TagDecl->isEmbeddedInDeclarator() && TagDecl->isCompleteDefinition()) {
-      SmallString<128> TagUSR;
-      index::generateUSRForDecl(TagDecl, TagUSR);
-      if (auto *Record = API.findRecordForUSR(TagUSR)) {
-        DeclarationFragments LeadingFragments;
-        LeadingFragments.append("typedef",
-                                DeclarationFragments::FragmentKind::Keyword, "",
-                                nullptr);
-        LeadingFragments.appendSpace();
-        Record->Declaration.removeTrailingSemicolon()
-            .insert(Record->Declaration.begin(), std::move(LeadingFragments))
-            .append(" { ... } ", DeclarationFragments::FragmentKind::Text)
-            .append(Name, DeclarationFragments::FragmentKind::Identifier)
-            .appendSemicolon();
-
-        return true;
+  // Add the notion of typedef for tag type (struct or enum) of the same name.
+  if (const ElaboratedType *ET =
+          dyn_cast<ElaboratedType>(Decl->getUnderlyingType())) {
+    if (const TagType *TagTy = dyn_cast<TagType>(ET->desugar())) {
+      if (Decl->getName() == TagTy->getDecl()->getName()) {
+        if (isa<RecordDecl>(TagTy->getDecl())) {
+          modifyRecords(API.getRecords(), Decl->getName());
+        }
+        if (TagTy->getDecl()->isEnum()) {
+          modifyRecords(API.getEnums(), Decl->getName());
+        }
       }
     }
   }
 
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
   DocComment Comment;
   if (auto *RawComment =
           getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
@@ -1111,12 +1091,11 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl(
       TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
                                                                        API);
 
-  API.createRecord<TypedefRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment,
-      DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
-      DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
-      isInSystemHeader(Decl));
+  API.addTypedef(Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl),
+                 Comment,
+                 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
+                 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
+                 isInSystemHeader(Decl));
 
   return true;
 }
@@ -1128,8 +1107,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCCategoryDecl(
     return true;
 
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -1144,20 +1122,29 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCCategoryDecl(
       DeclarationFragmentsBuilder::getSubHeading(Decl);
 
   const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
-  SymbolReference Interface = createSymbolReferenceForDecl(*InterfaceDecl);
+  SymbolReference Interface(InterfaceDecl->getName(),
+                            API.recordUSR(InterfaceDecl));
+
+  bool IsFromExternalModule = true;
+  for (const auto &Interface : API.getObjCInterfaces()) {
+    if (InterfaceDecl->getName() == Interface.second.get()->Name) {
+      IsFromExternalModule = false;
+      break;
+    }
+  }
 
-  auto *CategoryRecord = API.createRecord<ObjCCategoryRecord>(
-      USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-      AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
-      Interface, isInSystemHeader(Decl));
+  ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
+      Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
+      Declaration, SubHeading, Interface, isInSystemHeader(Decl),
+      IsFromExternalModule);
 
-  getDerivedExtractAPIVisitor().recordObjCMethods(CategoryRecord,
+  getDerivedExtractAPIVisitor().recordObjCMethods(ObjCCategoryRecord,
                                                   Decl->methods());
-  getDerivedExtractAPIVisitor().recordObjCProperties(CategoryRecord,
+  getDerivedExtractAPIVisitor().recordObjCProperties(ObjCCategoryRecord,
                                                      Decl->properties());
-  getDerivedExtractAPIVisitor().recordObjCInstanceVariables(CategoryRecord,
+  getDerivedExtractAPIVisitor().recordObjCInstanceVariables(ObjCCategoryRecord,
                                                             Decl->ivars());
-  getDerivedExtractAPIVisitor().recordObjCProtocols(CategoryRecord,
+  getDerivedExtractAPIVisitor().recordObjCProtocols(ObjCCategoryRecord,
                                                     Decl->protocols());
 
   return true;
@@ -1171,8 +1158,7 @@ void ExtractAPIVisitorBase<Derived>::recordEnumConstants(
   for (const auto *Constant : Constants) {
     // Collect symbol information.
     StringRef Name = Constant->getName();
-    SmallString<128> USR;
-    index::generateUSRForDecl(Constant, USR);
+    StringRef USR = API.recordUSR(Constant);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Constant->getLocation());
     DocComment Comment;
@@ -1187,26 +1173,51 @@ void ExtractAPIVisitorBase<Derived>::recordEnumConstants(
     DeclarationFragments SubHeading =
         DeclarationFragmentsBuilder::getSubHeading(Constant);
 
-    API.createRecord<EnumConstantRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Constant), Loc,
-        AvailabilityInfo::createFromDecl(Constant), Comment, Declaration,
-        SubHeading, isInSystemHeader(Constant));
+    API.addEnumConstant(EnumRecord, Name, USR, Loc,
+                        AvailabilityInfo::createFromDecl(Constant), Comment,
+                        Declaration, SubHeading, isInSystemHeader(Constant));
+  }
+}
+
+/// Collect API information for the struct fields and associate with the
+/// parent struct.
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordRecordFields(
+    RecordRecord *RecordRecord, APIRecord::RecordKind FieldKind,
+    const RecordDecl::field_range Fields) {
+  for (const auto *Field : Fields) {
+    // Collect symbol information.
+    StringRef Name = Field->getName();
+    StringRef USR = API.recordUSR(Field);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Field->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Field))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the struct field.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForField(Field);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Field);
+
+    API.addRecordField(
+        RecordRecord, Name, USR, Loc, AvailabilityInfo::createFromDecl(Field),
+        Comment, Declaration, SubHeading, FieldKind, isInSystemHeader(Field));
   }
 }
 
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitFieldDecl(const FieldDecl *Decl) {
-  // ObjCIvars are handled separately
-  if (isa<ObjCIvarDecl>(Decl) || isa<ObjCAtDefsFieldDecl>(Decl))
+  if (Decl->getDeclContext()->getDeclKind() == Decl::Record)
     return true;
-
-  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+  if (isa<ObjCIvarDecl>(Decl))
     return true;
-
   // Collect symbol information.
   StringRef Name = Decl->getName();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -1220,40 +1231,22 @@ bool ExtractAPIVisitorBase<Derived>::VisitFieldDecl(const FieldDecl *Decl) {
       DeclarationFragmentsBuilder::getFragmentsForField(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
+  AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
 
-  if (isa<CXXRecordDecl>(Decl->getDeclContext())) {
-    AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
-
-    API.createRecord<CXXFieldRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, Access, isInSystemHeader(Decl));
-  } else if (auto *RD = dyn_cast<RecordDecl>(Decl->getDeclContext())) {
-    if (RD->isUnion())
-      API.createRecord<UnionFieldRecord>(
-          USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-          AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-          SubHeading, isInSystemHeader(Decl));
-    else
-      API.createRecord<StructFieldRecord>(
-          USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-          AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-          SubHeading, isInSystemHeader(Decl));
-  }
-
+  SmallString<128> ParentUSR;
+  index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+                            ParentUSR);
+  API.addCXXField(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
+                  AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
+                  SubHeading, Access, isInSystemHeader(Decl));
   return true;
 }
 
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitCXXConversionDecl(
     const CXXConversionDecl *Decl) {
-  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) ||
-      Decl->isImplicit())
-    return true;
-
-  auto Name = Decl->getNameAsString();
-  SmallString<128> USR;
-  index::generateUSRForDecl(Decl, USR);
+  StringRef Name = API.copyString(Decl->getNameAsString());
+  StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   DocComment Comment;
@@ -1271,17 +1264,19 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXConversionDecl(
       DeclarationFragmentsBuilder::getFunctionSignature(Decl);
   AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
 
+  SmallString<128> ParentUSR;
+  index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+                            ParentUSR);
   if (Decl->isStatic())
-    API.createRecord<CXXStaticMethodRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, Signature, Access, isInSystemHeader(Decl));
+    API.addCXXStaticMethod(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
+                           AvailabilityInfo::createFromDecl(Decl), Comment,
+                           Declaration, SubHeading, Signature, Access,
+                           isInSystemHeader(Decl));
   else
-    API.createRecord<CXXInstanceMethodRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
-        AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
-        SubHeading, Signature, Access, isInSystemHeader(Decl));
-
+    API.addCXXInstanceMethod(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
+                             AvailabilityInfo::createFromDecl(Decl), Comment,
+                             Declaration, SubHeading, Signature, Access,
+                             isInSystemHeader(Decl));
   return true;
 }
 
@@ -1296,9 +1291,8 @@ void ExtractAPIVisitorBase<Derived>::recordObjCMethods(
     if (Method->isPropertyAccessor())
       continue;
 
-    auto Name = Method->getSelector().getAsString();
-    SmallString<128> USR;
-    index::generateUSRForDecl(Method, USR);
+    StringRef Name = API.copyString(Method->getSelector().getAsString());
+    StringRef USR = API.recordUSR(Method);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Method->getLocation());
     DocComment Comment;
@@ -1315,16 +1309,10 @@ void ExtractAPIVisitorBase<Derived>::recordObjCMethods(
     FunctionSignature Signature =
         DeclarationFragmentsBuilder::getFunctionSignature(Method);
 
-    if (Method->isInstanceMethod())
-      API.createRecord<ObjCInstanceMethodRecord>(
-          USR, Name, createHierarchyInformationForDecl(*Method), Loc,
-          AvailabilityInfo::createFromDecl(Method), Comment, Declaration,
-          SubHeading, Signature, isInSystemHeader(Method));
-    else
-      API.createRecord<ObjCClassMethodRecord>(
-          USR, Name, createHierarchyInformationForDecl(*Method), Loc,
-          AvailabilityInfo::createFromDecl(Method), Comment, Declaration,
-          SubHeading, Signature, isInSystemHeader(Method));
+    API.addObjCMethod(Container, Name, USR, Loc,
+                      AvailabilityInfo::createFromDecl(Method), Comment,
+                      Declaration, SubHeading, Signature,
+                      Method->isInstanceMethod(), isInSystemHeader(Method));
   }
 }
 
@@ -1334,8 +1322,7 @@ void ExtractAPIVisitorBase<Derived>::recordObjCProperties(
     const ObjCContainerDecl::prop_range Properties) {
   for (const auto *Property : Properties) {
     StringRef Name = Property->getName();
-    SmallString<128> USR;
-    index::generateUSRForDecl(Property, USR);
+    StringRef USR = API.recordUSR(Property);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Property->getLocation());
     DocComment Comment;
@@ -1350,8 +1337,10 @@ void ExtractAPIVisitorBase<Derived>::recordObjCProperties(
     DeclarationFragments SubHeading =
         DeclarationFragmentsBuilder::getSubHeading(Property);
 
-    auto GetterName = Property->getGetterName().getAsString();
-    auto SetterName = Property->getSetterName().getAsString();
+    StringRef GetterName =
+        API.copyString(Property->getGetterName().getAsString());
+    StringRef SetterName =
+        API.copyString(Property->getSetterName().getAsString());
 
     // Get the attributes for property.
     unsigned Attributes = ObjCPropertyRecord::NoAttr;
@@ -1359,22 +1348,14 @@ void ExtractAPIVisitorBase<Derived>::recordObjCProperties(
         ObjCPropertyAttribute::kind_readonly)
       Attributes |= ObjCPropertyRecord::ReadOnly;
 
-    if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
-      API.createRecord<ObjCClassPropertyRecord>(
-          USR, Name, createHierarchyInformationForDecl(*Property), Loc,
-          AvailabilityInfo::createFromDecl(Property), Comment, Declaration,
-          SubHeading,
-          static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
-          GetterName, SetterName, Property->isOptional(),
-          isInSystemHeader(Property));
-    else
-      API.createRecord<ObjCInstancePropertyRecord>(
-          USR, Name, createHierarchyInformationForDecl(*Property), Loc,
-          AvailabilityInfo::createFromDecl(Property), Comment, Declaration,
-          SubHeading,
-          static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
-          GetterName, SetterName, Property->isOptional(),
-          isInSystemHeader(Property));
+    API.addObjCProperty(
+        Container, Name, USR, Loc, AvailabilityInfo::createFromDecl(Property),
+        Comment, Declaration, SubHeading,
+        static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
+        SetterName, Property->isOptional(),
+        !(Property->getPropertyAttributes() &
+          ObjCPropertyAttribute::kind_class),
+        isInSystemHeader(Property));
   }
 }
 
@@ -1386,9 +1367,7 @@ void ExtractAPIVisitorBase<Derived>::recordObjCInstanceVariables(
         Ivars) {
   for (const auto *Ivar : Ivars) {
     StringRef Name = Ivar->getName();
-    SmallString<128> USR;
-    index::generateUSRForDecl(Ivar, USR);
-
+    StringRef USR = API.recordUSR(Ivar);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
     DocComment Comment;
@@ -1403,10 +1382,12 @@ void ExtractAPIVisitorBase<Derived>::recordObjCInstanceVariables(
     DeclarationFragments SubHeading =
         DeclarationFragmentsBuilder::getSubHeading(Ivar);
 
-    API.createRecord<ObjCInstanceVariableRecord>(
-        USR, Name, createHierarchyInformationForDecl(*Ivar), Loc,
-        AvailabilityInfo::createFromDecl(Ivar), Comment, Declaration,
-        SubHeading, isInSystemHeader(Ivar));
+    ObjCInstanceVariableRecord::AccessControl Access =
+        Ivar->getCanonicalAccessControl();
+
+    API.addObjCInstanceVariable(
+        Container, Name, USR, Loc, AvailabilityInfo::createFromDecl(Ivar),
+        Comment, Declaration, SubHeading, Access, isInSystemHeader(Ivar));
   }
 }
 
@@ -1415,7 +1396,8 @@ void ExtractAPIVisitorBase<Derived>::recordObjCProtocols(
     ObjCContainerRecord *Container,
     ObjCInterfaceDecl::protocol_range Protocols) {
   for (const auto *Protocol : Protocols)
-    Container->Protocols.emplace_back(createSymbolReferenceForDecl(*Protocol));
+    Container->Protocols.emplace_back(Protocol->getName(),
+                                      API.recordUSR(Protocol));
 }
 
 } // namespace impl

diff  --git a/clang/include/clang/ExtractAPI/FrontendActions.h b/clang/include/clang/ExtractAPI/FrontendActions.h
index 08045a30823db8..c67864aac9af9c 100644
--- a/clang/include/clang/ExtractAPI/FrontendActions.h
+++ b/clang/include/clang/ExtractAPI/FrontendActions.h
@@ -49,6 +49,9 @@ class ExtractAPIAction : public ASTFrontendAction,
   void EndSourceFileAction() override;
 
   static StringRef getInputBufferName() { return "<extract-api-includes>"; }
+
+  static std::unique_ptr<llvm::raw_pwrite_stream>
+  CreateOutputFile(CompilerInstance &CI, StringRef InFile);
 };
 
 /// Wrap ExtractAPIAction on top of a pre-existing action
@@ -82,6 +85,9 @@ class WrappingExtractAPIAction : public WrapperFrontendAction,
   /// actions. This is the place where all the gathered symbol graph
   /// information is emited.
   void EndSourceFileAction() override;
+
+  static std::unique_ptr<llvm::raw_pwrite_stream>
+  CreateOutputFile(CompilerInstance &CI, StringRef InFile);
 };
 
 } // namespace clang

diff  --git a/clang/include/clang/ExtractAPI/Serialization/APISetVisitor.h b/clang/include/clang/ExtractAPI/Serialization/APISetVisitor.h
deleted file mode 100644
index 07f14f349f3dd7..00000000000000
--- a/clang/include/clang/ExtractAPI/Serialization/APISetVisitor.h
+++ /dev/null
@@ -1,172 +0,0 @@
-//===- ExtractAPI/Serialization/APISetVisitor.h ----------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file defines the ExtractAPI APISetVisitor interface.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H
-#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H
-
-#include "clang/ExtractAPI/API.h"
-
-namespace clang {
-namespace extractapi {
-
-// A helper macro to implement short-circuiting when recursing.  It
-// invokes CALL_EXPR, which must be a method call, on the derived
-// object (s.t. a user of RecursiveASTVisitor can override the method
-// in CALL_EXPR).
-#define TRY_TO(CALL_EXPR)                                                      \
-  do {                                                                         \
-    if (!getDerived()->CALL_EXPR)                                              \
-      return false;                                                            \
-  } while (false)
-
-/// The base interface of visitors for API information, the interface and usage
-/// is almost identical to RecurisveASTVistor. This class performs three
-/// distinct tasks:
-/// 1. traverse the APISet (i.e. go to every record);
-/// 2. at a given record, walk up the class hierarchy starting from the record's
-/// dynamic type until APIRecord is reached.
-/// 3. given a (record, class) combination where 'class' is some base class of
-/// the dynamic type of 'record', call a user-overridable function to actually
-/// visit the record.
-///
-/// These tasks are done by three groups of methods, respectively:
-/// 1. traverseRecord(APIRecord *x) does task #1, it is the entry point for
-/// traversing the records starting from x. This method simply forwards to
-/// traverseFoo(Foo *x) where Foo is the dynamic type of *x, which calls
-/// walkUpFromFoo(x) and then recursively visits the child records of x.
-/// 2. walkUpFromFoo(Foo *x) does task #2. It doesn't visit children records of
-/// x, instead it first calls walkUpFromBar(x) where Bar is the direct parent
-/// class of Foo (unless Foo has no parent) and then calls visitFoo(x).
-/// 3. visitFoo(Foo *x) does task #3.
-///
-/// These three method groups are tiered (traverse* > walkUpFrom* >
-/// visit*).  A method (e.g. traverse*) may call methods from the same
-/// tier (e.g. other traverse*) or one tier lower (e.g. walkUpFrom*).
-/// It may not call methods from a higher tier.
-///
-/// Note that since walkUpFromFoo() calls walkUpFromBar() (where Bar
-/// is Foo's super class) before calling visitFoo(), the result is
-/// that the visit*() methods for a given record are called in the
-/// top-down order (e.g. for a record of type ObjCInstancePropertyRecord, the
-/// order will be visitRecord(), visitObjCPropertyRecord(), and then
-/// visitObjCInstancePropertyRecord()).
-///
-/// This scheme guarantees that all visit*() calls for the same record
-/// are grouped together.  In other words, visit*() methods for 
diff erent
-/// records are never interleaved.
-///
-/// Clients of this visitor should subclass the visitor (providing
-/// themselves as the template argument, using the curiously recurring
-/// template pattern) and override any of the traverse*, walkUpFrom*,
-/// and visit* methods for records where the visitor should customize
-/// behavior.  Most users only need to override visit*.  Advanced
-/// users may override traverse* and walkUpFrom* to implement custom
-/// traversal strategies.  Returning false from one of these overridden
-/// functions will abort the entire traversal.
-template <typename Derived> class APISetVisitor {
-public:
-  bool traverseAPISet() {
-    for (const APIRecord *TLR : API.getTopLevelRecords()) {
-      TRY_TO(traverseAPIRecord(TLR));
-    }
-    return true;
-  }
-
-  bool traverseAPIRecord(const APIRecord *Record);
-  bool walkUpFromAPIRecord(const APIRecord *Record) {
-    TRY_TO(visitAPIRecord(Record));
-    return true;
-  }
-  bool visitAPIRecord(const APIRecord *Record) { return true; }
-
-#define GENERATE_TRAVERSE_METHOD(CLASS, BASE)                                  \
-  bool traverse##CLASS(const CLASS *Record) {                                  \
-    TRY_TO(walkUpFrom##CLASS(Record));                                         \
-    TRY_TO(traverseRecordContext(dyn_cast<RecordContext>(Record)));            \
-    return true;                                                               \
-  }
-
-#define GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE)                         \
-  bool walkUpFrom##CLASS(const CLASS *Record) {                                \
-    TRY_TO(walkUpFrom##BASE(Record));                                          \
-    TRY_TO(visit##CLASS(Record));                                              \
-    return true;                                                               \
-  }                                                                            \
-  bool visit##CLASS(const CLASS *Record) { return true; }
-
-#define CONCRETE_RECORD(CLASS, BASE, KIND)                                     \
-  GENERATE_TRAVERSE_METHOD(CLASS, BASE)                                        \
-  GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE)
-
-#define ABSTRACT_RECORD(CLASS, BASE)                                           \
-  GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE)
-
-#include "../APIRecords.inc"
-
-#undef GENERATE_WALKUP_AND_VISIT_METHODS
-#undef GENERATE_TRAVERSE_METHOD
-
-  bool traverseRecordContext(const RecordContext *);
-
-protected:
-  const APISet &API;
-
-public:
-  APISetVisitor() = delete;
-  APISetVisitor(const APISetVisitor &) = delete;
-  APISetVisitor(APISetVisitor &&) = delete;
-  APISetVisitor &operator=(const APISetVisitor &) = delete;
-  APISetVisitor &operator=(APISetVisitor &&) = delete;
-
-protected:
-  APISetVisitor(const APISet &API) : API(API) {}
-  ~APISetVisitor() = default;
-
-  Derived *getDerived() { return static_cast<Derived *>(this); };
-};
-
-template <typename Derived>
-bool APISetVisitor<Derived>::traverseRecordContext(
-    const RecordContext *Context) {
-  if (!Context)
-    return true;
-
-  for (auto *Child : Context->records())
-    TRY_TO(traverseAPIRecord(Child));
-
-  return true;
-}
-
-template <typename Derived>
-bool APISetVisitor<Derived>::traverseAPIRecord(const APIRecord *Record) {
-  switch (Record->getKind()) {
-#define CONCRETE_RECORD(CLASS, BASE, KIND)                                     \
-  case APIRecord::KIND: {                                                      \
-    TRY_TO(traverse##CLASS(static_cast<const CLASS *>(Record)));               \
-    break;                                                                     \
-  }
-#include "../APIRecords.inc"
-  case APIRecord::RK_Unknown: {
-    TRY_TO(walkUpFromAPIRecord(static_cast<const APIRecord *>(Record)));
-    break;
-  }
-  default:
-    llvm_unreachable("API Record with uninstantiable kind");
-  }
-  return true;
-}
-
-} // namespace extractapi
-} // namespace clang
-
-#endif // LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
new file mode 100644
index 00000000000000..f0629a9ad56b03
--- /dev/null
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -0,0 +1,314 @@
+//===- ExtractAPI/Serialization/SerializerBase.h ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the ExtractAPI APISetVisitor interface.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H
+#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H
+
+#include "clang/ExtractAPI/API.h"
+
+namespace clang {
+namespace extractapi {
+
+/// The base interface of visitors for API information.
+template <typename Derived> class APISetVisitor {
+public:
+  void traverseAPISet() {
+    getDerived()->traverseNamespaces();
+
+    getDerived()->traverseGlobalVariableRecords();
+
+    getDerived()->traverseGlobalFunctionRecords();
+
+    getDerived()->traverseEnumRecords();
+
+    getDerived()->traverseStaticFieldRecords();
+
+    getDerived()->traverseCXXClassRecords();
+
+    getDerived()->traverseClassTemplateRecords();
+
+    getDerived()->traverseClassTemplateSpecializationRecords();
+
+    getDerived()->traverseClassTemplatePartialSpecializationRecords();
+
+    getDerived()->traverseCXXInstanceMethods();
+
+    getDerived()->traverseCXXStaticMethods();
+
+    getDerived()->traverseCXXMethodTemplates();
+
+    getDerived()->traverseCXXMethodTemplateSpecializations();
+
+    getDerived()->traverseCXXFields();
+
+    getDerived()->traverseCXXFieldTemplates();
+
+    getDerived()->traverseConcepts();
+
+    getDerived()->traverseGlobalVariableTemplateRecords();
+
+    getDerived()->traverseGlobalVariableTemplateSpecializationRecords();
+
+    getDerived()->traverseGlobalVariableTemplatePartialSpecializationRecords();
+
+    getDerived()->traverseGlobalFunctionTemplateRecords();
+
+    getDerived()->traverseGlobalFunctionTemplateSpecializationRecords();
+
+    getDerived()->traverseRecordRecords();
+
+    getDerived()->traverseObjCInterfaces();
+
+    getDerived()->traverseObjCProtocols();
+
+    getDerived()->traverseObjCCategories();
+
+    getDerived()->traverseMacroDefinitionRecords();
+
+    getDerived()->traverseTypedefRecords();
+  }
+
+  void traverseNamespaces() {
+    for (const auto &Namespace : API.getNamespaces())
+      getDerived()->visitNamespaceRecord(*Namespace.second);
+  }
+
+  void traverseGlobalFunctionRecords() {
+    for (const auto &GlobalFunction : API.getGlobalFunctions())
+      getDerived()->visitGlobalFunctionRecord(*GlobalFunction.second);
+  }
+
+  void traverseGlobalVariableRecords() {
+    for (const auto &GlobalVariable : API.getGlobalVariables())
+      getDerived()->visitGlobalVariableRecord(*GlobalVariable.second);
+  }
+
+  void traverseEnumRecords() {
+    for (const auto &Enum : API.getEnums())
+      getDerived()->visitEnumRecord(*Enum.second);
+  }
+
+  void traverseRecordRecords() {
+    for (const auto &Record : API.getRecords())
+      getDerived()->visitRecordRecord(*Record.second);
+  }
+
+  void traverseStaticFieldRecords() {
+    for (const auto &StaticField : API.getStaticFields())
+      getDerived()->visitStaticFieldRecord(*StaticField.second);
+  }
+
+  void traverseCXXClassRecords() {
+    for (const auto &Class : API.getCXXClasses())
+      getDerived()->visitCXXClassRecord(*Class.second);
+  }
+
+  void traverseCXXMethodTemplates() {
+    for (const auto &MethodTemplate : API.getCXXMethodTemplates())
+      getDerived()->visitMethodTemplateRecord(*MethodTemplate.second);
+  }
+
+  void traverseCXXMethodTemplateSpecializations() {
+    for (const auto &MethodTemplateSpecialization :
+         API.getCXXMethodTemplateSpecializations())
+      getDerived()->visitMethodTemplateSpecializationRecord(
+          *MethodTemplateSpecialization.second);
+  }
+
+  void traverseClassTemplateRecords() {
+    for (const auto &ClassTemplate : API.getClassTemplates())
+      getDerived()->visitClassTemplateRecord(*ClassTemplate.second);
+  }
+
+  void traverseClassTemplateSpecializationRecords() {
+    for (const auto &ClassTemplateSpecialization :
+         API.getClassTemplateSpecializations())
+      getDerived()->visitClassTemplateSpecializationRecord(
+          *ClassTemplateSpecialization.second);
+  }
+
+  void traverseClassTemplatePartialSpecializationRecords() {
+    for (const auto &ClassTemplatePartialSpecialization :
+         API.getClassTemplatePartialSpecializations())
+      getDerived()->visitClassTemplatePartialSpecializationRecord(
+          *ClassTemplatePartialSpecialization.second);
+  }
+
+  void traverseCXXInstanceMethods() {
+    for (const auto &InstanceMethod : API.getCXXInstanceMethods())
+      getDerived()->visitCXXInstanceMethodRecord(*InstanceMethod.second);
+  }
+
+  void traverseCXXStaticMethods() {
+    for (const auto &InstanceMethod : API.getCXXStaticMethods())
+      getDerived()->visitCXXStaticMethodRecord(*InstanceMethod.second);
+  }
+
+  void traverseCXXFields() {
+    for (const auto &CXXField : API.getCXXFields())
+      getDerived()->visitCXXFieldRecord(*CXXField.second);
+  }
+
+  void traverseCXXFieldTemplates() {
+    for (const auto &CXXFieldTemplate : API.getCXXFieldTemplates())
+      getDerived()->visitCXXFieldTemplateRecord(*CXXFieldTemplate.second);
+  }
+
+  void traverseGlobalVariableTemplateRecords() {
+    for (const auto &GlobalVariableTemplate : API.getGlobalVariableTemplates())
+      getDerived()->visitGlobalVariableTemplateRecord(
+          *GlobalVariableTemplate.second);
+  }
+
+  void traverseGlobalVariableTemplateSpecializationRecords() {
+    for (const auto &GlobalVariableTemplateSpecialization :
+         API.getGlobalVariableTemplateSpecializations())
+      getDerived()->visitGlobalVariableTemplateSpecializationRecord(
+          *GlobalVariableTemplateSpecialization.second);
+  }
+
+  void traverseGlobalVariableTemplatePartialSpecializationRecords() {
+    for (const auto &GlobalVariableTemplatePartialSpecialization :
+         API.getGlobalVariableTemplatePartialSpecializations())
+      getDerived()->visitGlobalVariableTemplatePartialSpecializationRecord(
+          *GlobalVariableTemplatePartialSpecialization.second);
+  }
+
+  void traverseGlobalFunctionTemplateRecords() {
+    for (const auto &GlobalFunctionTemplate : API.getGlobalFunctionTemplates())
+      getDerived()->visitGlobalFunctionTemplateRecord(
+          *GlobalFunctionTemplate.second);
+  }
+
+  void traverseGlobalFunctionTemplateSpecializationRecords() {
+    for (const auto &GlobalFunctionTemplateSpecialization :
+         API.getGlobalFunctionTemplateSpecializations())
+      getDerived()->visitGlobalFunctionTemplateSpecializationRecord(
+          *GlobalFunctionTemplateSpecialization.second);
+  }
+
+  void traverseConcepts() {
+    for (const auto &Concept : API.getConcepts())
+      getDerived()->visitConceptRecord(*Concept.second);
+  }
+
+  void traverseObjCInterfaces() {
+    for (const auto &Interface : API.getObjCInterfaces())
+      getDerived()->visitObjCContainerRecord(*Interface.second);
+  }
+
+  void traverseObjCProtocols() {
+    for (const auto &Protocol : API.getObjCProtocols())
+      getDerived()->visitObjCContainerRecord(*Protocol.second);
+  }
+
+  void traverseObjCCategories() {
+    for (const auto &Category : API.getObjCCategories())
+      getDerived()->visitObjCCategoryRecord(*Category.second);
+  }
+
+  void traverseMacroDefinitionRecords() {
+    for (const auto &Macro : API.getMacros())
+      getDerived()->visitMacroDefinitionRecord(*Macro.second);
+  }
+
+  void traverseTypedefRecords() {
+    for (const auto &Typedef : API.getTypedefs())
+      getDerived()->visitTypedefRecord(*Typedef.second);
+  }
+
+  void visitNamespaceRecord(const NamespaceRecord &Record){};
+
+  /// Visit a global function record.
+  void visitGlobalFunctionRecord(const GlobalFunctionRecord &Record){};
+
+  /// Visit a global variable record.
+  void visitGlobalVariableRecord(const GlobalVariableRecord &Record){};
+
+  /// Visit an enum record.
+  void visitEnumRecord(const EnumRecord &Record){};
+
+  /// Visit a record record.
+  void visitRecordRecord(const RecordRecord &Record){};
+
+  void visitStaticFieldRecord(const StaticFieldRecord &Record){};
+
+  void visitCXXClassRecord(const CXXClassRecord &Record){};
+
+  void visitClassTemplateRecord(const ClassTemplateRecord &Record){};
+
+  void visitClassTemplateSpecializationRecord(
+      const ClassTemplateSpecializationRecord &Record){};
+
+  void visitClassTemplatePartialSpecializationRecord(
+      const ClassTemplatePartialSpecializationRecord &Record){};
+
+  void visitCXXInstanceRecord(const CXXInstanceMethodRecord &Record){};
+
+  void visitCXXStaticRecord(const CXXStaticMethodRecord &Record){};
+
+  void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record){};
+
+  void visitMethodTemplateSpecializationRecord(
+      const CXXMethodTemplateSpecializationRecord &Record){};
+
+  void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record){};
+
+  void visitGlobalVariableTemplateRecord(
+      const GlobalVariableTemplateRecord &Record) {}
+
+  void visitGlobalVariableTemplateSpecializationRecord(
+      const GlobalVariableTemplateSpecializationRecord &Record){};
+
+  void visitGlobalVariableTemplatePartialSpecializationRecord(
+      const GlobalVariableTemplatePartialSpecializationRecord &Record){};
+
+  void visitGlobalFunctionTemplateRecord(
+      const GlobalFunctionTemplateRecord &Record){};
+
+  void visitGlobalFunctionTemplateSpecializationRecord(
+      const GlobalFunctionTemplateSpecializationRecord &Record){};
+
+  /// Visit an Objective-C container record.
+  void visitObjCContainerRecord(const ObjCContainerRecord &Record){};
+
+  /// Visit an Objective-C category record.
+  void visitObjCCategoryRecord(const ObjCCategoryRecord &Record){};
+
+  /// Visit a macro definition record.
+  void visitMacroDefinitionRecord(const MacroDefinitionRecord &Record){};
+
+  /// Visit a typedef record.
+  void visitTypedefRecord(const TypedefRecord &Record){};
+
+protected:
+  const APISet &API;
+
+public:
+  APISetVisitor() = delete;
+  APISetVisitor(const APISetVisitor &) = delete;
+  APISetVisitor(APISetVisitor &&) = delete;
+  APISetVisitor &operator=(const APISetVisitor &) = delete;
+  APISetVisitor &operator=(APISetVisitor &&) = delete;
+
+protected:
+  APISetVisitor(const APISet &API) : API(API) {}
+  ~APISetVisitor() = default;
+
+  Derived *getDerived() { return static_cast<Derived *>(this); };
+};
+
+} // namespace extractapi
+} // namespace clang
+
+#endif // LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index 724b087f7aea98..4249ac405fd262 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -17,17 +17,11 @@
 #ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H
 #define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H
 
-#include "clang/Basic/Module.h"
 #include "clang/ExtractAPI/API.h"
 #include "clang/ExtractAPI/APIIgnoresList.h"
-#include "clang/ExtractAPI/Serialization/APISetVisitor.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallString.h"
+#include "clang/ExtractAPI/Serialization/SerializerBase.h"
 #include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
-#include "llvm/ADT/Twine.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/VersionTuple.h"
 #include "llvm/Support/raw_ostream.h"
@@ -41,30 +35,7 @@ using namespace llvm::json;
 /// Common options to customize the visitor output.
 struct SymbolGraphSerializerOption {
   /// Do not include unnecessary whitespaces to save space.
-  bool Compact = true;
-  bool EmitSymbolLabelsForTesting = false;
-};
-
-/// A representation of the contents of a given module symbol graph
-struct ExtendedModule {
-  ExtendedModule() = default;
-  ExtendedModule(ExtendedModule &&EM) = default;
-  ExtendedModule &operator=(ExtendedModule &&EM) = default;
-  // Copies are expensive so disable them.
-  ExtendedModule(const ExtendedModule &EM) = delete;
-  ExtendedModule &operator=(const ExtendedModule &EM) = delete;
-
-  /// Add a symbol to the module, do not store the resulting pointer or use it
-  /// across insertions.
-  Object *addSymbol(Object &&Symbol);
-
-  void addRelationship(Object &&Relationship);
-
-  /// A JSON array of formatted symbols from an \c APISet.
-  Array Symbols;
-
-  /// A JSON array of formatted symbol relationships from an \c APISet.
-  Array Relationships;
+  bool Compact;
 };
 
 /// The visitor that organizes API information in the Symbol Graph format.
@@ -73,54 +44,28 @@ struct ExtendedModule {
 /// models an API set as a directed graph, where nodes are symbol declarations,
 /// and edges are relationships between the connected symbols.
 class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
-private:
-  using Base = APISetVisitor<SymbolGraphSerializer>;
-  /// The main symbol graph that contains symbols that are either top-level or a
-  /// are related to symbols defined in this product/module.
-  ExtendedModule MainModule;
+  /// A JSON array of formatted symbols in \c APISet.
+  Array Symbols;
 
-  /// Additional symbol graphs that contain symbols that are related to symbols
-  /// defined in another product/module. The key of this map is the module name
-  /// of the extended module.
-  llvm::StringMap<ExtendedModule> ExtendedModules;
+  /// A JSON array of formatted symbol relationships in \c APISet.
+  Array Relationships;
 
   /// The Symbol Graph format version used by this serializer.
   static const VersionTuple FormatVersion;
 
-  /// Indicates whether to take into account the extended module. This is only
+  /// Indicates whether child symbols should be visited. This is mainly
   /// useful for \c serializeSingleSymbolSGF.
-  bool ForceEmitToMainModule;
+  bool ShouldRecurse;
 
-  // Stores the references required to construct path components for the
-  // currently visited APIRecord.
-  llvm::SmallVector<SymbolReference, 8> Hierarchy;
-
-  /// The list of symbols to ignore.
+public:
+  /// Serialize the APIs in \c APISet in the Symbol Graph format.
   ///
-  /// Note: This should be consulted before emitting a symbol.
-  const APIIgnoresList &IgnoresList;
-
-  const bool EmitSymbolLabelsForTesting = false;
-
-  /// The object instantiated by the last call to serializeAPIRecord.
-  Object *CurrentSymbol = nullptr;
+  /// \returns a JSON object that contains the root of the formatted
+  /// Symbol Graph.
+  Object serialize();
 
-  /// The module to which \p CurrentSymbol belongs too.
-  ExtendedModule *ModuleForCurrentSymbol = nullptr;
-
-public:
-  static void
-  serializeMainSymbolGraph(raw_ostream &OS, const APISet &API,
-                           const APIIgnoresList &IgnoresList,
-                           SymbolGraphSerializerOption Options = {});
-
-  static void serializeWithExtensionGraphs(
-      raw_ostream &MainOutput, const APISet &API,
-      const APIIgnoresList &IgnoresList,
-      llvm::function_ref<
-          std::unique_ptr<llvm::raw_pwrite_stream>(llvm::Twine BaseFileName)>
-          CreateOutputStream,
-      SymbolGraphSerializerOption Options = {});
+  ///  Wrap serialize(void) and write out the serialized JSON object to \p os.
+  void serialize(raw_ostream &os);
 
   /// Serialize a single symbol SGF. This is primarily used for libclang.
   ///
@@ -130,7 +75,6 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
   static std::optional<Object> serializeSingleSymbolSGF(StringRef USR,
                                                         const APISet &API);
 
-private:
   /// The kind of a relationship between two symbols.
   enum RelationshipKind {
     /// The source symbol is a member of the target symbol.
@@ -150,32 +94,16 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
     ExtensionTo,
   };
 
-  /// Serialize a single record.
-  void serializeSingleRecord(const APIRecord *Record);
-
   /// Get the string representation of the relationship kind.
   static StringRef getRelationshipString(RelationshipKind Kind);
 
-  void serializeRelationship(RelationshipKind Kind,
-                             const SymbolReference &Source,
-                             const SymbolReference &Target,
-                             ExtendedModule &Into);
-
   enum ConstraintKind { Conformance, ConditionalConformance };
 
   static StringRef getConstraintString(ConstraintKind Kind);
 
-  /// Serialize the APIs in \c ExtendedModule.
-  ///
-  /// \returns a JSON object that contains the root of the formatted
-  /// Symbol Graph.
-  Object serializeGraph(StringRef ModuleName, ExtendedModule &&EM);
-
-  /// Serialize the APIs in \c ExtendedModule in the Symbol Graph format and
-  /// write them to the provide stream.
-  void serializeGraphToStream(raw_ostream &OS,
-                              SymbolGraphSerializerOption Options,
-                              StringRef ModuleName, ExtendedModule &&EM);
+private:
+  /// Just serialize the currently recorded objects in Symbol Graph format.
+  Object serializeCurrentGraph();
 
   /// Synthesize the metadata section of the Symbol Graph format.
   ///
@@ -189,92 +117,124 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
   /// by the given API set.
   /// Note that "module" here is not to be confused with the Clang/C++ module
   /// concept.
-  Object serializeModuleObject(StringRef ModuleName) const;
-
-  Array serializePathComponents(const APIRecord *Record) const;
+  Object serializeModule() const;
 
   /// Determine if the given \p Record should be skipped during serialization.
-  bool shouldSkip(const APIRecord *Record) const;
-
-  ExtendedModule &getModuleForCurrentSymbol();
+  bool shouldSkip(const APIRecord &Record) const;
 
   /// Format the common API information for \p Record.
   ///
   /// This handles the shared information of all kinds of API records,
-  /// for example identifier, source location and path components. The resulting
-  /// object is then augmented with kind-specific symbol information in
-  /// subsequent visit* methods by accessing the \p State member variable. This
-  /// method also checks if the given \p Record should be skipped during
-  /// serialization. This should be called only once per concrete APIRecord
-  /// instance and the first visit* method to be called is responsible for
-  /// calling this. This is normally visitAPIRecord unless a walkUpFromFoo
-  /// method is implemented along the inheritance hierarchy in which case the
-  /// visitFoo method needs to call this.
+  /// for example identifier and source location. The resulting object is then
+  /// augmented with kind-specific symbol information by the caller.
+  /// This method also checks if the given \p Record should be skipped during
+  /// serialization.
   ///
-  /// \returns \c nullptr if this \p Record should be skipped, or a pointer to
-  /// JSON object containing common symbol information of \p Record. Do not
-  /// store the returned pointer only use it to augment the object with record
-  /// specific information as it directly points to the object in the
-  /// \p ExtendedModule, the pointer won't be valid as soon as another object is
-  /// inserted into the module.
-  void serializeAPIRecord(const APIRecord *Record);
+  /// \returns \c std::nullopt if this \p Record should be skipped, or a JSON
+  /// object containing common symbol information of \p Record.
+  template <typename RecordTy>
+  std::optional<Object> serializeAPIRecord(const RecordTy &Record) const;
+
+  /// Helper method to serialize second-level member records of \p Record and
+  /// the member-of relationships.
+  template <typename MemberTy>
+  void serializeMembers(const APIRecord &Record,
+                        const SmallVector<std::unique_ptr<MemberTy>> &Members);
+
+  /// Serialize the \p Kind relationship between \p Source and \p Target.
+  ///
+  /// Record the relationship between the two symbols in
+  /// SymbolGraphSerializer::Relationships.
+  void serializeRelationship(RelationshipKind Kind, SymbolReference Source,
+                             SymbolReference Target);
 
-public:
-  // Handle if records should be skipped at this level of the traversal to
-  // ensure that children of skipped records aren't serialized.
-  bool traverseAPIRecord(const APIRecord *Record);
+protected:
+  /// The list of symbols to ignore.
+  ///
+  /// Note: This should be consulted before emitting a symbol.
+  const APIIgnoresList &IgnoresList;
+
+  SymbolGraphSerializerOption Options;
 
-  bool visitAPIRecord(const APIRecord *Record);
+  llvm::StringSet<> visitedCategories;
+
+public:
+  void visitNamespaceRecord(const NamespaceRecord &Record);
 
   /// Visit a global function record.
-  bool visitGlobalFunctionRecord(const GlobalFunctionRecord *Record);
+  void visitGlobalFunctionRecord(const GlobalFunctionRecord &Record);
+
+  /// Visit a global variable record.
+  void visitGlobalVariableRecord(const GlobalVariableRecord &Record);
+
+  /// Visit an enum record.
+  void visitEnumRecord(const EnumRecord &Record);
+
+  /// Visit a record record.
+  void visitRecordRecord(const RecordRecord &Record);
+
+  void visitStaticFieldRecord(const StaticFieldRecord &Record);
+
+  void visitCXXClassRecord(const CXXClassRecord &Record);
 
-  bool visitCXXClassRecord(const CXXClassRecord *Record);
+  void visitClassTemplateRecord(const ClassTemplateRecord &Record);
 
-  bool visitClassTemplateRecord(const ClassTemplateRecord *Record);
+  void visitClassTemplateSpecializationRecord(
+      const ClassTemplateSpecializationRecord &Record);
 
-  bool visitClassTemplatePartialSpecializationRecord(
-      const ClassTemplatePartialSpecializationRecord *Record);
+  void visitClassTemplatePartialSpecializationRecord(
+      const ClassTemplatePartialSpecializationRecord &Record);
 
-  bool visitCXXMethodRecord(const CXXMethodRecord *Record);
+  void visitCXXInstanceMethodRecord(const CXXInstanceMethodRecord &Record);
 
-  bool visitCXXMethodTemplateRecord(const CXXMethodTemplateRecord *Record);
+  void visitCXXStaticMethodRecord(const CXXStaticMethodRecord &Record);
 
-  bool visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord *Record);
+  void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record);
 
-  bool visitConceptRecord(const ConceptRecord *Record);
+  void visitMethodTemplateSpecializationRecord(
+      const CXXMethodTemplateSpecializationRecord &Record);
 
-  bool
-  visitGlobalVariableTemplateRecord(const GlobalVariableTemplateRecord *Record);
+  void visitCXXFieldRecord(const CXXFieldRecord &Record);
 
-  bool visitGlobalVariableTemplatePartialSpecializationRecord(
-      const GlobalVariableTemplatePartialSpecializationRecord *Record);
+  void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record);
 
-  bool
-  visitGlobalFunctionTemplateRecord(const GlobalFunctionTemplateRecord *Record);
+  void visitConceptRecord(const ConceptRecord &Record);
 
-  bool visitObjCContainerRecord(const ObjCContainerRecord *Record);
+  void
+  visitGlobalVariableTemplateRecord(const GlobalVariableTemplateRecord &Record);
 
-  bool visitObjCInterfaceRecord(const ObjCInterfaceRecord *Record);
+  void visitGlobalVariableTemplateSpecializationRecord(
+      const GlobalVariableTemplateSpecializationRecord &Record);
 
-  bool traverseObjCCategoryRecord(const ObjCCategoryRecord *Record);
-  bool walkUpFromObjCCategoryRecord(const ObjCCategoryRecord *Record);
-  bool visitObjCCategoryRecord(const ObjCCategoryRecord *Record);
+  void visitGlobalVariableTemplatePartialSpecializationRecord(
+      const GlobalVariableTemplatePartialSpecializationRecord &Record);
 
-  bool visitObjCMethodRecord(const ObjCMethodRecord *Record);
+  void
+  visitGlobalFunctionTemplateRecord(const GlobalFunctionTemplateRecord &Record);
 
-  bool
-  visitObjCInstanceVariableRecord(const ObjCInstanceVariableRecord *Record);
+  void visitGlobalFunctionTemplateSpecializationRecord(
+      const GlobalFunctionTemplateSpecializationRecord &Record);
 
-  bool walkUpFromTypedefRecord(const TypedefRecord *Record);
-  bool visitTypedefRecord(const TypedefRecord *Record);
+  /// Visit an Objective-C container record.
+  void visitObjCContainerRecord(const ObjCContainerRecord &Record);
+
+  /// Visit an Objective-C category record.
+  void visitObjCCategoryRecord(const ObjCCategoryRecord &Record);
+
+  /// Visit a macro definition record.
+  void visitMacroDefinitionRecord(const MacroDefinitionRecord &Record);
+
+  /// Visit a typedef record.
+  void visitTypedefRecord(const TypedefRecord &Record);
+
+  /// Serialize a single record.
+  void serializeSingleRecord(const APIRecord *Record);
 
   SymbolGraphSerializer(const APISet &API, const APIIgnoresList &IgnoresList,
-                        bool EmitSymbolLabelsForTesting = false,
-                        bool ForceEmitToMainModule = false)
-      : Base(API), ForceEmitToMainModule(ForceEmitToMainModule),
-        IgnoresList(IgnoresList),
-        EmitSymbolLabelsForTesting(EmitSymbolLabelsForTesting) {}
+                        SymbolGraphSerializerOption Options = {},
+                        bool ShouldRecurse = true)
+      : APISetVisitor(API), ShouldRecurse(ShouldRecurse),
+        IgnoresList(IgnoresList), Options(Options) {}
 };
 
 } // namespace extractapi

diff  --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 864af66b337069..8085dbcbf671a6 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -15,7 +15,6 @@
 #include "clang/Sema/CodeCompleteOptions.h"
 #include "clang/Serialization/ModuleFileExtension.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Compiler.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include <cassert>
 #include <map>
@@ -388,22 +387,6 @@ class FrontendOptions {
   LLVM_PREFERRED_TYPE(bool)
   unsigned ModulesShareFileManager : 1;
 
-  /// Whether to emit symbol graph files as a side effect of compilation.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned EmitSymbolGraph : 1;
-
-  /// Whether to emit additional symbol graphs for extended modules.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned EmitExtensionSymbolGraphs : 1;
-
-  /// Whether to emit symbol labels for testing in generated symbol graphs
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned EmitSymbolGraphSymbolLabelsForTesting : 1;
-
-  /// Whether to emit symbol labels for testing in generated symbol graphs
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned EmitPrettySymbolGraphs : 1;
-
   CodeCompleteOptions CodeCompleteOpts;
 
   /// Specifies the output format of the AST.
@@ -513,8 +496,10 @@ class FrontendOptions {
   // ignore when extracting documentation.
   std::vector<std::string> ExtractAPIIgnoresFileList;
 
+  // Currently this is only used as part of the `-emit-symbol-graph`
+  // action.
   // Location of output directory where symbol graph information would
-  // be dumped. This overrides regular -o output file specification
+  // be dumped
   std::string SymbolGraphOutputDir;
 
   /// Args to pass to the plugins

diff  --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index e6c1767a0082dd..1a0f5f27eda2fc 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -49,7 +49,6 @@
 #include "ToolChains/WebAssembly.h"
 #include "ToolChains/XCore.h"
 #include "ToolChains/ZOS.h"
-#include "clang/Basic/DiagnosticDriver.h"
 #include "clang/Basic/TargetID.h"
 #include "clang/Basic/Version.h"
 #include "clang/Config/config.h"
@@ -5890,12 +5889,6 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA,
         &JA);
   }
 
-  if (JA.getType() == types::TY_API_INFO &&
-      C.getArgs().hasArg(options::OPT_emit_extension_symbol_graphs) &&
-      C.getArgs().hasArg(options::OPT_o))
-    Diag(clang::diag::err_drv_unexpected_symbol_graph_output)
-        << C.getArgs().getLastArgValue(options::OPT_o);
-
   // DXC defaults to standard out when generating assembly. We check this after
   // any DXC flags that might specify a file.
   if (AtTopLevel && JA.getType() == types::TY_PP_Asm && IsDXCMode())

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 33f0b05a37696b..b03ac6018d2b80 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5037,26 +5037,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     assert(JA.getType() == types::TY_API_INFO &&
            "Extract API actions must generate a API information.");
     CmdArgs.push_back("-extract-api");
-
-    if (Arg *PrettySGFArg = Args.getLastArg(options::OPT_emit_pretty_sgf))
-      PrettySGFArg->render(Args, CmdArgs);
-
-    Arg *SymbolGraphDirArg = Args.getLastArg(options::OPT_symbol_graph_dir_EQ);
-
     if (Arg *ProductNameArg = Args.getLastArg(options::OPT_product_name_EQ))
       ProductNameArg->render(Args, CmdArgs);
     if (Arg *ExtractAPIIgnoresFileArg =
             Args.getLastArg(options::OPT_extract_api_ignores_EQ))
       ExtractAPIIgnoresFileArg->render(Args, CmdArgs);
-    if (Arg *EmitExtensionSymbolGraphs =
-            Args.getLastArg(options::OPT_emit_extension_symbol_graphs)) {
-      if (!SymbolGraphDirArg)
-        D.Diag(diag::err_drv_missing_symbol_graph_dir);
-
-      EmitExtensionSymbolGraphs->render(Args, CmdArgs);
-    }
-    if (SymbolGraphDirArg)
-      SymbolGraphDirArg->render(Args, CmdArgs);
   } else {
     assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) &&
            "Invalid action for clang tool.");

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 5a62c5deb24083..aa7a1e9360f474 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -13,67 +13,514 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ExtractAPI/API.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/CommentLexer.h"
 #include "clang/AST/RawCommentList.h"
-#include "clang/Basic/Module.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/ErrorHandling.h"
 #include <memory>
 
 using namespace clang::extractapi;
 using namespace llvm;
 
-SymbolReference::SymbolReference(const APIRecord *R)
-    : Name(R->Name), USR(R->USR), Record(R) {}
+namespace {
 
-APIRecord *APIRecord::castFromRecordContext(const RecordContext *Ctx) {
-  switch (Ctx->getKind()) {
-#define RECORD_CONTEXT(CLASS, KIND)                                            \
-  case KIND:                                                                   \
-    return static_cast<CLASS *>(const_cast<RecordContext *>(Ctx));
-#include "clang/ExtractAPI/APIRecords.inc"
-  default:
-    return nullptr;
-    // llvm_unreachable("RecordContext derived class isn't propertly
-    // implemented");
-  }
+template <typename RecordTy, typename... CtorArgsTy>
+RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
+                            APISet::RecordMap<RecordTy> &RecordMap,
+                            StringRef USR, CtorArgsTy &&...CtorArgs) {
+  auto Result = RecordMap.insert({USR, nullptr});
+
+  // Create the record if it does not already exist
+  if (Result.second)
+    Result.first->second =
+        std::make_unique<RecordTy>(USR, std::forward<CtorArgsTy>(CtorArgs)...);
+
+  auto *Record = Result.first->second.get();
+  USRLookupTable.insert({USR, Record});
+  return Record;
 }
 
-RecordContext *APIRecord::castToRecordContext(const APIRecord *Record) {
-  if (!Record)
-    return nullptr;
-  switch (Record->getKind()) {
-#define RECORD_CONTEXT(CLASS, KIND)                                            \
-  case KIND:                                                                   \
-    return static_cast<CLASS *>(const_cast<APIRecord *>(Record));
-#include "clang/ExtractAPI/APIRecords.inc"
-  default:
-    return nullptr;
-    // llvm_unreachable("RecordContext derived class isn't propertly
-    // implemented");
-  }
+} // namespace
+
+NamespaceRecord *
+APISet::addNamespace(APIRecord *Parent, StringRef Name, StringRef USR,
+                     PresumedLoc Loc, AvailabilityInfo Availability,
+                     LinkageInfo Linkage, const DocComment &Comment,
+                     DeclarationFragments Declaration,
+                     DeclarationFragments SubHeading, bool IsFromSystemHeader) {
+  auto *Record = addTopLevelRecord(
+      USRBasedLookupTable, Namespaces, USR, Name, Loc, std::move(Availability),
+      Linkage, Comment, Declaration, SubHeading, IsFromSystemHeader);
+
+  if (Parent)
+    Record->ParentInformation = APIRecord::HierarchyInformation(
+        Parent->USR, Parent->Name, Parent->getKind(), Parent);
+  return Record;
+}
+
+GlobalVariableRecord *
+APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
+                     AvailabilityInfo Availability, LinkageInfo Linkage,
+                     const DocComment &Comment, DeclarationFragments Fragments,
+                     DeclarationFragments SubHeading, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, GlobalVariables, USR, Name, Loc,
+                           std::move(Availability), Linkage, Comment, Fragments,
+                           SubHeading, IsFromSystemHeader);
+}
+
+GlobalVariableTemplateRecord *APISet::addGlobalVariableTemplate(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, Template Template,
+    bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, GlobalVariableTemplates, USR,
+                           Name, Loc, std::move(Availability), Linkage, Comment,
+                           Declaration, SubHeading, Template,
+                           IsFromSystemHeader);
+}
+
+GlobalFunctionRecord *APISet::addGlobalFunction(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Fragments,
+    DeclarationFragments SubHeading, FunctionSignature Signature,
+    bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, GlobalFunctions, USR, Name, Loc,
+                           std::move(Availability), Linkage, Comment, Fragments,
+                           SubHeading, Signature, IsFromSystemHeader);
+}
+
+GlobalFunctionTemplateRecord *APISet::addGlobalFunctionTemplate(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, FunctionSignature Signature,
+    Template Template, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, GlobalFunctionTemplates, USR,
+                           Name, Loc, std::move(Availability), Linkage, Comment,
+                           Declaration, SubHeading, Signature, Template,
+                           IsFromSystemHeader);
+}
+
+GlobalFunctionTemplateSpecializationRecord *
+APISet::addGlobalFunctionTemplateSpecialization(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, FunctionSignature Signature,
+    bool IsFromSystemHeader) {
+  return addTopLevelRecord(
+      USRBasedLookupTable, GlobalFunctionTemplateSpecializations, USR, Name,
+      Loc, std::move(Availability), Linkage, Comment, Declaration, SubHeading,
+      Signature, IsFromSystemHeader);
+}
+
+EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
+                                            StringRef USR, PresumedLoc Loc,
+                                            AvailabilityInfo Availability,
+                                            const DocComment &Comment,
+                                            DeclarationFragments Declaration,
+                                            DeclarationFragments SubHeading,
+                                            bool IsFromSystemHeader) {
+  auto Record = std::make_unique<EnumConstantRecord>(
+      USR, Name, Loc, std::move(Availability), Comment, Declaration, SubHeading,
+      IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Enum->USR, Enum->Name, Enum->getKind(), Enum);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return Enum->Constants.emplace_back(std::move(Record)).get();
+}
+
+EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+                            AvailabilityInfo Availability,
+                            const DocComment &Comment,
+                            DeclarationFragments Declaration,
+                            DeclarationFragments SubHeading,
+                            bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Enums, USR, Name, Loc,
+                           std::move(Availability), Comment, Declaration,
+                           SubHeading, IsFromSystemHeader);
 }
 
-void RecordContext::addToRecordChain(APIRecord *Record) const {
-  if (!First) {
-    First = Record;
-    Last = Record;
-    return;
-  }
+RecordFieldRecord *APISet::addRecordField(
+    RecordRecord *Record, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    APIRecord::RecordKind Kind, bool IsFromSystemHeader) {
+  auto RecordField = std::make_unique<RecordFieldRecord>(
+      USR, Name, Loc, std::move(Availability), Comment, Declaration, SubHeading,
+      Kind, IsFromSystemHeader);
+  RecordField->ParentInformation = APIRecord::HierarchyInformation(
+      Record->USR, Record->Name, Record->getKind(), Record);
+  USRBasedLookupTable.insert({USR, RecordField.get()});
+  return Record->Fields.emplace_back(std::move(RecordField)).get();
+}
+
+RecordRecord *APISet::addRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                                AvailabilityInfo Availability,
+                                const DocComment &Comment,
+                                DeclarationFragments Declaration,
+                                DeclarationFragments SubHeading,
+                                APIRecord::RecordKind Kind,
+                                bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Records, USR, Name, Loc,
+                           std::move(Availability), Comment, Declaration,
+                           SubHeading, Kind, IsFromSystemHeader);
+}
+
+StaticFieldRecord *
+APISet::addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
+                       AvailabilityInfo Availability, LinkageInfo Linkage,
+                       const DocComment &Comment,
+                       DeclarationFragments Declaration,
+                       DeclarationFragments SubHeading, SymbolReference Context,
+                       AccessControl Access, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, StaticFields, USR, Name, Loc,
+                           std::move(Availability), Linkage, Comment,
+                           Declaration, SubHeading, Context, Access,
+                           IsFromSystemHeader);
+}
+
+CXXFieldRecord *
+APISet::addCXXField(APIRecord *CXXClass, StringRef Name, StringRef USR,
+                    PresumedLoc Loc, AvailabilityInfo Availability,
+                    const DocComment &Comment, DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading, AccessControl Access,
+                    bool IsFromSystemHeader) {
+  auto *Record = addTopLevelRecord(
+      USRBasedLookupTable, CXXFields, USR, Name, Loc, std::move(Availability),
+      Comment, Declaration, SubHeading, Access, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      CXXClass->USR, CXXClass->Name, CXXClass->getKind(), CXXClass);
+  return Record;
+}
+
+CXXFieldTemplateRecord *APISet::addCXXFieldTemplate(
+    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    AccessControl Access, Template Template, bool IsFromSystemHeader) {
+  auto *Record =
+      addTopLevelRecord(USRBasedLookupTable, CXXFieldTemplates, USR, Name, Loc,
+                        std::move(Availability), Comment, Declaration,
+                        SubHeading, Access, Template, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Parent->USR, Parent->Name, Parent->getKind(), Parent);
+
+  return Record;
+}
+
+CXXClassRecord *
+APISet::addCXXClass(APIRecord *Parent, StringRef Name, StringRef USR,
+                    PresumedLoc Loc, AvailabilityInfo Availability,
+                    const DocComment &Comment, DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading, APIRecord::RecordKind Kind,
+                    AccessControl Access, bool IsFromSystemHeader) {
+  auto *Record = addTopLevelRecord(
+      USRBasedLookupTable, CXXClasses, USR, Name, Loc, std::move(Availability),
+      Comment, Declaration, SubHeading, Kind, Access, IsFromSystemHeader);
+  if (Parent)
+    Record->ParentInformation = APIRecord::HierarchyInformation(
+        Parent->USR, Parent->Name, Parent->getKind(), Parent);
+  return Record;
+}
+
+ClassTemplateRecord *APISet::addClassTemplate(
+    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    Template Template, AccessControl Access, bool IsFromSystemHeader) {
+  auto *Record =
+      addTopLevelRecord(USRBasedLookupTable, ClassTemplates, USR, Name, Loc,
+                        std::move(Availability), Comment, Declaration,
+                        SubHeading, Template, Access, IsFromSystemHeader);
+  if (Parent)
+    Record->ParentInformation = APIRecord::HierarchyInformation(
+        Parent->USR, Parent->Name, Parent->getKind(), Parent);
+  return Record;
+}
+
+ClassTemplateSpecializationRecord *APISet::addClassTemplateSpecialization(
+    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    AccessControl Access, bool IsFromSystemHeader) {
+  auto *Record =
+      addTopLevelRecord(USRBasedLookupTable, ClassTemplateSpecializations, USR,
+                        Name, Loc, std::move(Availability), Comment,
+                        Declaration, SubHeading, Access, IsFromSystemHeader);
+  if (Parent)
+    Record->ParentInformation = APIRecord::HierarchyInformation(
+        Parent->USR, Parent->Name, Parent->getKind(), Parent);
+  return Record;
+}
+
+ClassTemplatePartialSpecializationRecord *
+APISet::addClassTemplatePartialSpecialization(
+    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    Template Template, AccessControl Access, bool IsFromSystemHeader) {
+  auto *Record = addTopLevelRecord(
+      USRBasedLookupTable, ClassTemplatePartialSpecializations, USR, Name, Loc,
+      std::move(Availability), Comment, Declaration, SubHeading, Template,
+      Access, IsFromSystemHeader);
+  if (Parent)
+    Record->ParentInformation = APIRecord::HierarchyInformation(
+        Parent->USR, Parent->Name, Parent->getKind(), Parent);
+  return Record;
+}
+
+GlobalVariableTemplateSpecializationRecord *
+APISet::addGlobalVariableTemplateSpecialization(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable,
+                           GlobalVariableTemplateSpecializations, USR, Name,
+                           Loc, std::move(Availability), Linkage, Comment,
+                           Declaration, SubHeading, IsFromSystemHeader);
+}
 
-  Last->NextInContext = Record;
-  Last = Record;
+GlobalVariableTemplatePartialSpecializationRecord *
+APISet::addGlobalVariableTemplatePartialSpecialization(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, Template Template,
+    bool IsFromSystemHeader) {
+  return addTopLevelRecord(
+      USRBasedLookupTable, GlobalVariableTemplatePartialSpecializations, USR,
+      Name, Loc, std::move(Availability), Linkage, Comment, Declaration,
+      SubHeading, Template, IsFromSystemHeader);
+}
+
+ConceptRecord *APISet::addConcept(StringRef Name, StringRef USR,
+                                  PresumedLoc Loc,
+                                  AvailabilityInfo Availability,
+                                  const DocComment &Comment,
+                                  DeclarationFragments Declaration,
+                                  DeclarationFragments SubHeading,
+                                  Template Template, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Concepts, USR, Name, Loc,
+                           std::move(Availability), Comment, Declaration,
+                           SubHeading, Template, IsFromSystemHeader);
+}
+
+CXXMethodRecord *APISet::addCXXInstanceMethod(
+    APIRecord *CXXClassRecord, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, AccessControl Access,
+    bool IsFromSystemHeader) {
+  CXXMethodRecord *Record =
+      addTopLevelRecord(USRBasedLookupTable, CXXInstanceMethods, USR, Name, Loc,
+                        std::move(Availability), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader);
+
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(),
+      CXXClassRecord);
+  return Record;
+}
+
+CXXMethodRecord *APISet::addCXXStaticMethod(
+    APIRecord *CXXClassRecord, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, AccessControl Access,
+    bool IsFromSystemHeader) {
+  CXXMethodRecord *Record =
+      addTopLevelRecord(USRBasedLookupTable, CXXStaticMethods, USR, Name, Loc,
+                        std::move(Availability), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader);
+
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(),
+      CXXClassRecord);
+  return Record;
+}
+
+CXXMethodTemplateRecord *APISet::addCXXMethodTemplate(
+    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, AccessControl Access, Template Template,
+    bool IsFromSystemHeader) {
+  auto *Record = addTopLevelRecord(USRBasedLookupTable, CXXMethodTemplates, USR,
+                                   Name, Loc, std::move(Availability), Comment,
+                                   Declaration, SubHeading, Signature, Access,
+                                   Template, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Parent->USR, Parent->Name, Parent->getKind(), Parent);
+
+  return Record;
+}
+
+CXXMethodTemplateSpecializationRecord *APISet::addCXXMethodTemplateSpec(
+    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, AccessControl Access,
+    bool IsFromSystemHeader) {
+
+  auto *Record = addTopLevelRecord(
+      USRBasedLookupTable, CXXMethodTemplateSpecializations, USR, Name, Loc,
+      std::move(Availability), Comment, Declaration, SubHeading, Signature,
+      Access, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Parent->USR, Parent->Name, Parent->getKind(), Parent);
+
+  return Record;
+}
+
+ObjCCategoryRecord *APISet::addObjCCategory(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    SymbolReference Interface, bool IsFromSystemHeader,
+    bool IsFromExternalModule) {
+  // Create the category record.
+  auto *Record =
+      addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc,
+                        std::move(Availability), Comment, Declaration,
+                        SubHeading, Interface, IsFromSystemHeader);
+
+  Record->IsFromExternalModule = IsFromExternalModule;
+
+  auto It = ObjCInterfaces.find(Interface.USR);
+  if (It != ObjCInterfaces.end())
+    It->second->Categories.push_back(Record);
+
+  return Record;
+}
+
+ObjCInterfaceRecord *
+APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+                         AvailabilityInfo Availability, LinkageInfo Linkage,
+                         const DocComment &Comment,
+                         DeclarationFragments Declaration,
+                         DeclarationFragments SubHeading,
+                         SymbolReference SuperClass, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, ObjCInterfaces, USR, Name, Loc,
+                           std::move(Availability), Linkage, Comment,
+                           Declaration, SubHeading, SuperClass,
+                           IsFromSystemHeader);
+}
+
+ObjCMethodRecord *APISet::addObjCMethod(
+    ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+    PresumedLoc Loc, AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, bool IsInstanceMethod,
+    bool IsFromSystemHeader) {
+  std::unique_ptr<ObjCMethodRecord> Record;
+  if (IsInstanceMethod)
+    Record = std::make_unique<ObjCInstanceMethodRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Signature, IsFromSystemHeader);
+  else
+    Record = std::make_unique<ObjCClassMethodRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Signature, IsFromSystemHeader);
+
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Container->USR, Container->Name, Container->getKind(), Container);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return Container->Methods.emplace_back(std::move(Record)).get();
+}
+
+ObjCPropertyRecord *APISet::addObjCProperty(
+    ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+    PresumedLoc Loc, AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
+    StringRef SetterName, bool IsOptional, bool IsInstanceProperty,
+    bool IsFromSystemHeader) {
+  std::unique_ptr<ObjCPropertyRecord> Record;
+  if (IsInstanceProperty)
+    Record = std::make_unique<ObjCInstancePropertyRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Attributes, GetterName, SetterName, IsOptional,
+        IsFromSystemHeader);
+  else
+    Record = std::make_unique<ObjCClassPropertyRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Attributes, GetterName, SetterName, IsOptional,
+        IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Container->USR, Container->Name, Container->getKind(), Container);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return Container->Properties.emplace_back(std::move(Record)).get();
+}
+
+ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
+    ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+    PresumedLoc Loc, AvailabilityInfo Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) {
+  auto Record = std::make_unique<ObjCInstanceVariableRecord>(
+      USR, Name, Loc, std::move(Availability), Comment, Declaration, SubHeading,
+      Access, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Container->USR, Container->Name, Container->getKind(), Container);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return Container->Ivars.emplace_back(std::move(Record)).get();
+}
+
+ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
+                                            PresumedLoc Loc,
+                                            AvailabilityInfo Availability,
+                                            const DocComment &Comment,
+                                            DeclarationFragments Declaration,
+                                            DeclarationFragments SubHeading,
+                                            bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, ObjCProtocols, USR, Name, Loc,
+                           std::move(Availability), Comment, Declaration,
+                           SubHeading, IsFromSystemHeader);
+}
+
+MacroDefinitionRecord *
+APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
+                           DeclarationFragments Declaration,
+                           DeclarationFragments SubHeading,
+                           bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Macros, USR, Name, Loc,
+                           Declaration, SubHeading, IsFromSystemHeader);
+}
+
+TypedefRecord *
+APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+                   AvailabilityInfo Availability, const DocComment &Comment,
+                   DeclarationFragments Declaration,
+                   DeclarationFragments SubHeading,
+                   SymbolReference UnderlyingType, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Typedefs, USR, Name, Loc,
+                           std::move(Availability), Comment, Declaration,
+                           SubHeading, UnderlyingType, IsFromSystemHeader);
 }
 
 APIRecord *APISet::findRecordForUSR(StringRef USR) const {
   if (USR.empty())
     return nullptr;
 
-  auto FindIt = USRBasedLookupTable.find(USR);
-  if (FindIt != USRBasedLookupTable.end())
-    return FindIt->getSecond().get();
+  return USRBasedLookupTable.lookup(USR);
+}
+
+StringRef APISet::recordUSR(const Decl *D) {
+  SmallString<128> USR;
+  index::generateUSRForDecl(D, USR);
+  return copyString(USR);
+}
 
-  return nullptr;
+StringRef APISet::recordUSRForMacro(StringRef Name, SourceLocation SL,
+                                    const SourceManager &SM) {
+  SmallString<128> USR;
+  index::generateUSRForMacro(Name, SL, SM, USR);
+  return copyString(USR);
 }
 
 StringRef APISet::copyString(StringRef String) {
@@ -81,22 +528,15 @@ StringRef APISet::copyString(StringRef String) {
     return {};
 
   // No need to allocate memory and copy if the string has already been stored.
-  if (Allocator.identifyObject(String.data()))
+  if (StringAllocator.identifyObject(String.data()))
     return String;
 
-  void *Ptr = Allocator.Allocate(String.size(), 1);
+  void *Ptr = StringAllocator.Allocate(String.size(), 1);
   memcpy(Ptr, String.data(), String.size());
   return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
 }
 
-SymbolReference APISet::createSymbolReference(StringRef Name, StringRef USR,
-                                              StringRef Source) {
-  return SymbolReference(copyString(Name), copyString(USR), copyString(Source));
-}
-
 APIRecord::~APIRecord() {}
-RecordRecord::~RecordRecord() {}
-RecordFieldRecord::~RecordFieldRecord() {}
 ObjCContainerRecord::~ObjCContainerRecord() {}
 ObjCMethodRecord::~ObjCMethodRecord() {}
 ObjCPropertyRecord::~ObjCPropertyRecord() {}
@@ -106,10 +546,8 @@ void GlobalFunctionRecord::anchor() {}
 void GlobalVariableRecord::anchor() {}
 void EnumConstantRecord::anchor() {}
 void EnumRecord::anchor() {}
-void StructFieldRecord::anchor() {}
-void StructRecord::anchor() {}
-void UnionFieldRecord::anchor() {}
-void UnionRecord::anchor() {}
+void RecordFieldRecord::anchor() {}
+void RecordRecord::anchor() {}
 void CXXFieldRecord::anchor() {}
 void CXXClassRecord::anchor() {}
 void CXXConstructorRecord::anchor() {}

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 0f9e1eb22a1d96..80a0a498dc4001 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -60,44 +60,23 @@ void findTypeLocForBlockDecl(const clang::TypeSourceInfo *TSInfo,
 
 } // namespace
 
-DeclarationFragments &
-DeclarationFragments::appendUnduplicatedTextCharacter(char Character) {
+DeclarationFragments &DeclarationFragments::appendSpace() {
   if (!Fragments.empty()) {
     Fragment &Last = Fragments.back();
     if (Last.Kind == FragmentKind::Text) {
       // Merge the extra space into the last fragment if the last fragment is
       // also text.
-      if (Last.Spelling.back() != Character) { // avoid duplicates at end
-        Last.Spelling.push_back(Character);
+      if (Last.Spelling.back() != ' ') { // avoid extra trailing spaces.
+        Last.Spelling.push_back(' ');
       }
     } else {
-      append("", FragmentKind::Text);
-      Fragments.back().Spelling.push_back(Character);
+      append(" ", FragmentKind::Text);
     }
   }
 
   return *this;
 }
 
-DeclarationFragments &DeclarationFragments::appendSpace() {
-  return appendUnduplicatedTextCharacter(' ');
-}
-
-DeclarationFragments &DeclarationFragments::appendSemicolon() {
-  return appendUnduplicatedTextCharacter(';');
-}
-
-DeclarationFragments &DeclarationFragments::removeTrailingSemicolon() {
-  if (Fragments.empty())
-    return *this;
-
-  Fragment &Last = Fragments.back();
-  if (Last.Kind == FragmentKind::Text && Last.Spelling.back() == ';')
-    Last.Spelling.pop_back();
-
-  return *this;
-}
-
 StringRef DeclarationFragments::getFragmentKindString(
     DeclarationFragments::FragmentKind Kind) {
   switch (Kind) {
@@ -490,7 +469,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForNamespace(
   if (!Decl->isAnonymousNamespace())
     Fragments.appendSpace().append(
         Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -532,7 +511,7 @@ DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
   return Fragments
       .append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
       .append(std::move(After))
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -564,7 +543,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplate(const VarDecl *Var) {
   Fragments.append(std::move(ArgumentFragment))
       .appendSpace()
       .append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
   return Fragments;
 }
 
@@ -733,7 +712,7 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
   Fragments.append(DeclarationFragments::getExceptionSpecificationString(
       Func->getExceptionSpecType()));
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
@@ -762,7 +741,7 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
             getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
         .append(std::move(After));
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -778,7 +757,7 @@ DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
       .appendSpace()
       .append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
       .append(std::move(After))
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
@@ -796,7 +775,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
     Fragments.appendSpace().append(
         Record->getName(), DeclarationFragments::FragmentKind::Identifier);
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
@@ -811,7 +790,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
     Fragments.appendSpace().append(
         Record->getName(), DeclarationFragments::FragmentKind::Identifier);
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -841,7 +820,7 @@ DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
   Fragments.append(DeclarationFragments::getExceptionSpecificationString(
       Method->getExceptionSpecType()));
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
@@ -881,7 +860,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
   Fragments.append(DeclarationFragments::getExceptionSpecificationString(
       Method->getExceptionSpecType()));
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -912,7 +891,7 @@ DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
     Fragments.appendSpace().append("const",
                                    DeclarationFragments::FragmentKind::Keyword);
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -944,7 +923,7 @@ DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
   Fragments.append(DeclarationFragments::getExceptionSpecificationString(
       Method->getExceptionSpecType()));
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 // Get fragments for template parameters, e.g. T in tempalte<typename T> ...
@@ -1049,7 +1028,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForConcept(
       .appendSpace()
       .append(Concept->getName().str(),
               DeclarationFragments::FragmentKind::Identifier)
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -1090,7 +1069,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
           getFragmentsForTemplateArguments(Decl->getTemplateArgs().asArray(),
                                            Decl->getASTContext(), std::nullopt))
       .append(">", DeclarationFragments::FragmentKind::Text)
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -1112,7 +1091,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplatePartialSpecialization(
           Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
           Decl->getTemplateParameters()->asArray()))
       .append(">", DeclarationFragments::FragmentKind::Text)
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -1131,7 +1110,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplateSpecialization(
           getFragmentsForTemplateArguments(Decl->getTemplateArgs().asArray(),
                                            Decl->getASTContext(), std::nullopt))
       .append(">", DeclarationFragments::FragmentKind::Text)
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -1153,7 +1132,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplatePartialSpecialization(
           Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
           Decl->getTemplateParameters()->asArray()))
       .append(">", DeclarationFragments::FragmentKind::Text)
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments
@@ -1224,7 +1203,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(
 
   Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
       .appendSpace()
-      .append(Interface->getName(),
+      .append(Category->getClassInterface()->getName(),
               DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR,
               Interface)
       .append(" (", DeclarationFragments::FragmentKind::Text)
@@ -1298,7 +1277,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCMethod(
     Fragments.append(getFragmentsForParam(Param));
   }
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
@@ -1399,7 +1378,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
       .append(Property->getName(),
               DeclarationFragments::FragmentKind::Identifier)
       .append(std::move(After))
-      .appendSemicolon();
+      .append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
@@ -1443,7 +1422,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
       .appendSpace()
       .append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
 
-  return Fragments.appendSemicolon();
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
 // Instantiate template for FunctionDecl.

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index d6335854cbf262..275f49be22e15a 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -30,7 +30,6 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendOptions.h"
 #include "clang/Frontend/MultiplexConsumer.h"
-#include "clang/Index/USRGeneration.h"
 #include "clang/InstallAPI/HeaderFile.h"
 #include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/PPCallbacks.h"
@@ -40,7 +39,6 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
@@ -329,12 +327,11 @@ class MacroCallback : public PPCallbacks {
 
       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
-      SmallString<128> USR;
-      index::generateUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM,
-                                 USR);
+      StringRef USR =
+          API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
 
-      API.createRecord<extractapi::MacroDefinitionRecord>(
-          USR, Name, SymbolReference(), Loc,
+      API.addMacroDefinition(
+          Name, USR, Loc,
           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
           SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
@@ -375,57 +372,40 @@ class APIMacroCallback : public MacroCallback {
   LocationFileChecker &LCF;
 };
 
-std::unique_ptr<llvm::raw_pwrite_stream>
-createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) {
-  auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir;
-
-  SmallString<256> FileName;
-  llvm::sys::path::append(FileName, OutputDirectory,
-                          BaseName + ".symbols.json");
-  return CI.createOutputFile(
-      FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false,
-      /*UseTemporary*/ true, /*CreateMissingDirectories*/ true);
-}
-
 } // namespace
 
-void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) {
-  SymbolGraphSerializerOption SerializationOptions;
-  SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs;
-  SerializationOptions.EmitSymbolLabelsForTesting =
-      CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting;
-
-  if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) {
-    auto ConstructOutputFile = [&CI](Twine BaseName) {
-      return createAdditionalSymbolGraphFile(CI, BaseName);
-    };
-
-    SymbolGraphSerializer::serializeWithExtensionGraphs(
-        *OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions);
-  } else {
-    SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList,
-                                                    SerializationOptions);
-  }
+void ExtractAPIActionBase::ImplEndSourceFileAction() {
+  if (!OS)
+    return;
 
-  // Flush the stream and close the main output stream.
+  // Setup a SymbolGraphSerializer to write out collected API information in
+  // the Symbol Graph format.
+  // FIXME: Make the kind of APISerializer configurable.
+  SymbolGraphSerializer SGSerializer(*API, IgnoresList);
+  SGSerializer.serialize(*OS);
   OS.reset();
 }
 
+std::unique_ptr<raw_pwrite_stream>
+ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
+  std::unique_ptr<raw_pwrite_stream> OS;
+  OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile,
+                                  /*Extension=*/"json",
+                                  /*RemoveFileOnSignal=*/false);
+  if (!OS)
+    return nullptr;
+  return OS;
+}
+
 std::unique_ptr<ASTConsumer>
 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
-  auto ProductName = CI.getFrontendOpts().ProductName;
-
-  if (CI.getFrontendOpts().SymbolGraphOutputDir.empty())
-    OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile,
-                                    /*Extension*/ "symbols.json",
-                                    /*RemoveFileOnSignal*/ false,
-                                    /*CreateMissingDirectories*/ true);
-  else
-    OS = createAdditionalSymbolGraphFile(CI, ProductName);
+  OS = CreateOutputFile(CI, InFile);
 
   if (!OS)
     return nullptr;
 
+  auto ProductName = CI.getFrontendOpts().ProductName;
+
   // Now that we have enough information about the language options and the
   // target triple, let's create the APISet before anyone uses it.
   API = std::make_unique<APISet>(
@@ -515,9 +495,7 @@ bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
   return true;
 }
 
-void ExtractAPIAction::EndSourceFileAction() {
-  ImplEndSourceFileAction(getCompilerInstance());
-}
+void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); }
 
 std::unique_ptr<ASTConsumer>
 WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
@@ -528,9 +506,11 @@ WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
 
   CreatedASTConsumer = true;
 
-  ProductName = CI.getFrontendOpts().ProductName;
-  auto InputFilename = llvm::sys::path::filename(InFile);
-  OS = createAdditionalSymbolGraphFile(CI, InputFilename);
+  OS = CreateOutputFile(CI, InFile);
+  if (!OS)
+    return nullptr;
+
+  auto ProductName = CI.getFrontendOpts().ProductName;
 
   // Now that we have enough information about the language options and the
   // target triple, let's create the APISet before anyone uses it.
@@ -572,6 +552,32 @@ void WrappingExtractAPIAction::EndSourceFileAction() {
   WrapperFrontendAction::EndSourceFileAction();
 
   if (CreatedASTConsumer) {
-    ImplEndSourceFileAction(getCompilerInstance());
+    ImplEndSourceFileAction();
   }
 }
+
+std::unique_ptr<raw_pwrite_stream>
+WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI,
+                                           StringRef InFile) {
+  std::unique_ptr<raw_pwrite_stream> OS;
+  std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir;
+
+  // The symbol graphs need to be generated as a side effect of regular
+  // compilation so the output should be dumped in the directory provided with
+  // the command line option.
+  llvm::SmallString<128> OutFilePath(OutputDir);
+  auto Seperator = llvm::sys::path::get_separator();
+  auto Infilename = llvm::sys::path::filename(InFile);
+  OutFilePath.append({Seperator, Infilename});
+  llvm::sys::path::replace_extension(OutFilePath, "json");
+  // StringRef outputFilePathref = *OutFilePath;
+
+  // don't use the default output file
+  OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false,
+                           /*RemoveFileOnSignal=*/true,
+                           /*UseTemporary=*/true,
+                           /*CreateMissingDirectories=*/true);
+  if (!OS)
+    return nullptr;
+  return OS;
+}

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 57f966c8b2be35..545860acb7db80 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -14,17 +14,13 @@
 #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>
 
@@ -37,27 +33,26 @@ 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.
 ///
@@ -253,7 +248,6 @@ std::optional<Object> serializeDocComment(const DocComment &Comment) {
     return std::nullopt;
 
   Object DocComment;
-
   Array LinesArray;
   for (const auto &CommentLine : Comment) {
     Object Line;
@@ -262,8 +256,7 @@ std::optional<Object> serializeDocComment(const DocComment &Comment) {
                     serializeSourceRange(CommentLine.Begin, CommentLine.End));
     LinesArray.emplace_back(std::move(Line));
   }
-
-  serializeArray(DocComment, "lines", std::move(LinesArray));
+  serializeArray(DocComment, "lines", LinesArray);
 
   return DocComment;
 }
@@ -329,14 +322,19 @@ 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;
-  Names["title"] = Record->Name;
+  if (auto *CategoryRecord =
+          dyn_cast_or_null<const ObjCCategoryRecord>(&Record))
+    Names["title"] =
+        (CategoryRecord->Interface.Name + " (" + Record.Name + ")").str();
+  else
+    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",
@@ -353,8 +351,7 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
   Object Kind;
   switch (RK) {
   case APIRecord::RK_Unknown:
-    Kind["identifier"] = AddLangPrefix("unknown");
-    Kind["displayName"] = "Unknown";
+    llvm_unreachable("Records should have an explicit kind");
     break;
   case APIRecord::RK_Namespace:
     Kind["identifier"] = AddLangPrefix("namespace");
@@ -487,6 +484,10 @@ 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,8 +500,6 @@ 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;
@@ -515,18 +514,12 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
   return serializeSymbolKind(Record.getKind(), 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>
-void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
+std::optional<Object>
+serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
   const auto &FS = Record.Signature;
   if (FS.empty())
-    return;
+    return std::nullopt;
 
   Object Signature;
   serializeArray(Signature, "returns",
@@ -544,14 +537,63 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
   if (!Parameters.empty())
     Signature["parameters"] = std::move(Parameters);
 
-  serializeObject(Paren, "functionSignature", std::move(Signature));
+  return Signature;
 }
 
 template <typename RecordTy>
-void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
+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) {
   const auto &Template = Record.Templ;
   if (Template.empty())
-    return;
+    return std::nullopt;
 
   Object Generics;
   Array GenericParameters;
@@ -577,66 +619,97 @@ void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
   if (!GenericConstraints.empty())
     Generics["constraints"] = std::move(GenericConstraints);
 
-  serializeObject(Paren, "swiftGenerics", Generics);
+  return Generics;
 }
 
-Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents,
-                             Language Lang) {
-  Array ParentContexts;
-
-  for (const auto &Parent : Parents) {
-    Object Elem;
-    Elem["usr"] = Parent.USR;
-    Elem["name"] = Parent.Name;
-    if (Parent.Record)
-      Elem["kind"] =
-          serializeSymbolKind(Parent.Record->getKind(), Lang)["identifier"];
-    else
-      Elem["kind"] =
-          serializeSymbolKind(APIRecord::RK_Unknown, Lang)["identifier"];
-    ParentContexts.emplace_back(std::move(Elem));
-  }
+template <typename RecordTy>
+std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record,
+                                                 std::false_type) {
+  return std::nullopt;
+}
 
-  return ParentContexts;
+template <typename RecordTy>
+void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
+  serializeObject(Paren, "swiftGenerics",
+                  serializeTemplateMixinImpl(Record, has_template<RecordTy>()));
 }
 
-/// 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.
+struct PathComponent {
+  StringRef USR;
+  StringRef Name;
+  APIRecord::RecordKind Kind;
+
+  PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
+      : USR(USR), Name(Name), Kind(Kind) {}
+};
+
+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;
   }
 
-  return SymbolReference(Record);
-}
+  for (const auto &PC : reverse(ReverseComponenents))
+    ComponentTransformer(PC);
 
-} // namespace
+  return FailedToFindParent;
+}
 
-Object *ExtendedModule::addSymbol(Object &&Symbol) {
-  Symbols.emplace_back(std::move(Symbol));
-  return Symbols.back().getAsObject();
+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;
 }
 
-void ExtendedModule::addRelationship(Object &&Relationship) {
-  Relationships.emplace_back(std::move(Relationship));
+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));
+      });
+
+  return ParentContexts;
 }
+} // namespace
 
 /// Defines the format version emitted by SymbolGraphSerializer.
 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
@@ -649,44 +722,84 @@ Object SymbolGraphSerializer::serializeMetadata() const {
   return Metadata;
 }
 
-Object
-SymbolGraphSerializer::serializeModuleObject(StringRef ModuleName) const {
+Object SymbolGraphSerializer::serializeModule() const {
   Object Module;
-  Module["name"] = ModuleName;
+  // The user is expected to always pass `--product-name=` on the command line
+  // to populate this field.
+  Module["name"] = API.ProductName;
   serializeObject(Module, "platform", serializePlatform(API.getTarget()));
   return Module;
 }
 
-bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
-  if (!Record)
+bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
+  // Skip explicitly ignored symbols.
+  if (IgnoresList.shouldIgnore(Record.Name))
     return true;
 
   // Skip unconditionally unavailable symbols
-  if (Record->Availability.isUnconditionallyUnavailable())
+  if (Record.Availability.isUnconditionallyUnavailable())
     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("_"))
-    return true;
-
-  // Skip explicitly ignored symbols.
-  if (IgnoresList.shouldIgnore(Record->Name))
+  if (Record.Name.starts_with("_"))
     return true;
 
   return false;
 }
 
-ExtendedModule &SymbolGraphSerializer::getModuleForCurrentSymbol() {
-  if (!ForceEmitToMainModule && ModuleForCurrentSymbol)
-    return *ModuleForCurrentSymbol;
+template <typename RecordTy>
+std::optional<Object>
+SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
+  if (shouldSkip(Record))
+    return std::nullopt;
+
+  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));
 
-  return MainModule;
+  serializeFunctionSignatureMixin(Obj, Record);
+  serializeAccessMixin(Obj, Record);
+  serializeTemplateMixin(Obj, Record);
+
+  return Obj;
 }
 
-Array SymbolGraphSerializer::serializePathComponents(
-    const APIRecord *Record) const {
-  return Array(map_range(Hierarchy, [](auto Elt) { return Elt.Name; }));
+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);
+  }
 }
 
 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
@@ -703,33 +816,6 @@ 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:
@@ -740,324 +826,430 @@ StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {
   llvm_unreachable("Unhandled constraint kind");
 }
 
-void SymbolGraphSerializer::serializeAPIRecord(const APIRecord *Record) {
-  Object Obj;
-
-  // 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::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);
 
-  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));
+  Relationships.emplace_back(std::move(Relationship));
+}
 
-  Obj["pathComponents"] = serializePathComponents(Record);
-  Obj["accessLevel"] = Record->Access.getAccess();
+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);
+}
 
-  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);
+void SymbolGraphSerializer::visitGlobalFunctionRecord(
+    const GlobalFunctionRecord &Record) {
+  auto Obj = serializeAPIRecord(Record);
+  if (!Obj)
+    return;
 
-  CurrentSymbol = Module.addSymbol(std::move(Obj));
+  Symbols.emplace_back(std::move(*Obj));
 }
 
-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::visitGlobalVariableRecord(
+    const GlobalVariableRecord &Record) {
+  auto Obj = serializeAPIRecord(Record);
+  if (!Obj)
+    return;
+
+  Symbols.emplace_back(std::move(*Obj));
 }
 
-bool SymbolGraphSerializer::visitAPIRecord(const APIRecord *Record) {
-  serializeAPIRecord(Record);
-  return true;
+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::visitGlobalFunctionRecord(
-    const GlobalFunctionRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitRecordRecord(const RecordRecord &Record) {
+  auto SerializedRecord = serializeAPIRecord(Record);
+  if (!SerializedRecord)
+    return;
 
-  serializeFunctionSignatureMixin(*CurrentSymbol, *Record);
-  return true;
+  Symbols.emplace_back(std::move(*SerializedRecord));
+  serializeMembers(Record, Record.Fields);
 }
 
-bool SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord *Record) {
-  if (!CurrentSymbol)
-    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);
+}
 
-  for (const auto &Base : Record->Bases)
-    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base,
-                          getModuleForCurrentSymbol());
-  return true;
+void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
+  auto Class = serializeAPIRecord(Record);
+  if (!Class)
+    return;
+
+  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);
 }
 
-bool SymbolGraphSerializer::visitClassTemplateRecord(
-    const ClassTemplateRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitClassTemplateRecord(
+    const ClassTemplateRecord &Record) {
+  auto Class = serializeAPIRecord(Record);
+  if (!Class)
+    return;
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  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);
 }
 
-bool SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
-    const ClassTemplatePartialSpecializationRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitClassTemplateSpecializationRecord(
+    const ClassTemplateSpecializationRecord &Record) {
+  auto Class = serializeAPIRecord(Record);
+  if (!Class)
+    return;
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  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);
 }
 
-bool SymbolGraphSerializer::visitCXXMethodRecord(
-    const CXXMethodRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
+    const ClassTemplatePartialSpecializationRecord &Record) {
+  auto Class = serializeAPIRecord(Record);
+  if (!Class)
+    return;
+
+  Symbols.emplace_back(std::move(*Class));
 
-  serializeFunctionSignatureMixin(*CurrentSymbol, *Record);
-  return true;
+  for (const auto &Base : Record.Bases)
+    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
+  if (!Record.ParentInformation.empty())
+    serializeRelationship(RelationshipKind::MemberOf, Record,
+                          Record.ParentInformation.ParentRecord);
 }
 
-bool SymbolGraphSerializer::visitCXXMethodTemplateRecord(
-    const CXXMethodTemplateRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitCXXInstanceMethodRecord(
+    const CXXInstanceMethodRecord &Record) {
+  auto InstanceMethod = serializeAPIRecord(Record);
+  if (!InstanceMethod)
+    return;
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  return true;
+  Symbols.emplace_back(std::move(*InstanceMethod));
+  serializeRelationship(RelationshipKind::MemberOf, Record,
+                        Record.ParentInformation.ParentRecord);
 }
 
-bool SymbolGraphSerializer::visitCXXFieldTemplateRecord(
-    const CXXFieldTemplateRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitCXXStaticMethodRecord(
+    const CXXStaticMethodRecord &Record) {
+  auto StaticMethod = serializeAPIRecord(Record);
+  if (!StaticMethod)
+    return;
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  return true;
+  Symbols.emplace_back(std::move(*StaticMethod));
+  serializeRelationship(RelationshipKind::MemberOf, Record,
+                        Record.ParentInformation.ParentRecord);
 }
 
-bool SymbolGraphSerializer::visitConceptRecord(const ConceptRecord *Record) {
-  if (!CurrentSymbol)
-    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);
+}
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  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);
 }
 
-bool SymbolGraphSerializer::visitGlobalVariableTemplateRecord(
-    const GlobalVariableTemplateRecord *Record) {
-  if (!CurrentSymbol)
-    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);
+}
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  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);
 }
 
-bool SymbolGraphSerializer::
-    visitGlobalVariableTemplatePartialSpecializationRecord(
-        const GlobalVariableTemplatePartialSpecializationRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) {
+  auto Concept = serializeAPIRecord(Record);
+  if (!Concept)
+    return;
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  return true;
+  Symbols.emplace_back(std::move(*Concept));
 }
 
-bool SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(
-    const GlobalFunctionTemplateRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitGlobalVariableTemplateRecord(
+    const GlobalVariableTemplateRecord &Record) {
+  auto GlobalVariableTemplate = serializeAPIRecord(Record);
+  if (!GlobalVariableTemplate)
+    return;
+  Symbols.emplace_back(std::move(*GlobalVariableTemplate));
+}
 
-  serializeTemplateMixin(*CurrentSymbol, *Record);
-  return true;
+void SymbolGraphSerializer::visitGlobalVariableTemplateSpecializationRecord(
+    const GlobalVariableTemplateSpecializationRecord &Record) {
+  auto GlobalVariableTemplateSpecialization = serializeAPIRecord(Record);
+  if (!GlobalVariableTemplateSpecialization)
+    return;
+  Symbols.emplace_back(std::move(*GlobalVariableTemplateSpecialization));
 }
 
-bool SymbolGraphSerializer::visitObjCContainerRecord(
-    const ObjCContainerRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::
+    visitGlobalVariableTemplatePartialSpecializationRecord(
+        const GlobalVariableTemplatePartialSpecializationRecord &Record) {
+  auto GlobalVariableTemplatePartialSpecialization = serializeAPIRecord(Record);
+  if (!GlobalVariableTemplatePartialSpecialization)
+    return;
+  Symbols.emplace_back(std::move(*GlobalVariableTemplatePartialSpecialization));
+}
 
-  for (const auto &Protocol : Record->Protocols)
-    serializeRelationship(ConformsTo, Record, Protocol,
-                          getModuleForCurrentSymbol());
+void SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(
+    const GlobalFunctionTemplateRecord &Record) {
+  auto GlobalFunctionTemplate = serializeAPIRecord(Record);
+  if (!GlobalFunctionTemplate)
+    return;
+  Symbols.emplace_back(std::move(*GlobalFunctionTemplate));
+}
 
-  return true;
+void SymbolGraphSerializer::visitGlobalFunctionTemplateSpecializationRecord(
+    const GlobalFunctionTemplateSpecializationRecord &Record) {
+  auto GlobalFunctionTemplateSpecialization = serializeAPIRecord(Record);
+  if (!GlobalFunctionTemplateSpecialization)
+    return;
+  Symbols.emplace_back(std::move(*GlobalFunctionTemplateSpecialization));
 }
 
-bool SymbolGraphSerializer::visitObjCInterfaceRecord(
-    const ObjCInterfaceRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitObjCContainerRecord(
+    const ObjCContainerRecord &Record) {
+  auto ObjCContainer = serializeAPIRecord(Record);
+  if (!ObjCContainer)
+    return;
 
-  if (!Record->SuperClass.empty())
-    serializeRelationship(InheritsFrom, Record, Record->SuperClass,
-                          getModuleForCurrentSymbol());
-  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);
+    }
+  }
 }
 
-bool SymbolGraphSerializer::traverseObjCCategoryRecord(
-    const ObjCCategoryRecord *Record) {
-  auto *CurrentModule = ModuleForCurrentSymbol;
-  if (Record->isExtendingExternalModule())
-    ModuleForCurrentSymbol = &ExtendedModules[Record->Interface.Source];
+void SymbolGraphSerializer::visitObjCCategoryRecord(
+    const ObjCCategoryRecord &Record) {
+  if (!Record.IsFromExternalModule)
+    return;
 
-  if (!walkUpFromObjCCategoryRecord(Record))
-    return false;
+  // 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));
+  }
 
-  bool RetVal = traverseRecordContext(Record);
-  ModuleForCurrentSymbol = CurrentModule;
-  return RetVal;
-}
+  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));
 
-bool SymbolGraphSerializer::walkUpFromObjCCategoryRecord(
-    const ObjCCategoryRecord *Record) {
-  return visitObjCCategoryRecord(Record);
-}
+  auto ObjCCategory = serializeAPIRecord(Record);
+
+  if (!ObjCCategory)
+    return;
 
-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());
+  Symbols.emplace_back(std::move(*ObjCCategory));
+  serializeMembers(Record, Record.Methods);
+  serializeMembers(Record, Record.Properties);
 
-  return true;
+  // Surface the protocols of the category to the interface.
+  for (const auto &Protocol : Record.Protocols)
+    serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
 }
 
-bool SymbolGraphSerializer::visitObjCMethodRecord(
-    const ObjCMethodRecord *Record) {
-  if (!CurrentSymbol)
-    return true;
+void SymbolGraphSerializer::visitMacroDefinitionRecord(
+    const MacroDefinitionRecord &Record) {
+  auto Macro = serializeAPIRecord(Record);
 
-  serializeFunctionSignatureMixin(*CurrentSymbol, *Record);
-  return true;
-}
+  if (!Macro)
+    return;
 
-bool SymbolGraphSerializer::visitObjCInstanceVariableRecord(
-    const ObjCInstanceVariableRecord *Record) {
-  // FIXME: serialize ivar access control here.
-  return true;
+  Symbols.emplace_back(std::move(*Macro));
 }
 
-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::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::visitTypedefRecord(const TypedefRecord *Record) {
+void 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 true;
+    return;
 
-  // Create the symbol record if the other symbol droppping rules permit it.
-  serializeAPIRecord(Record);
-  if (!CurrentSymbol)
-    return true;
+  auto Typedef = serializeAPIRecord(Record);
+  if (!Typedef)
+    return;
 
-  (*CurrentSymbol)["type"] = Record->UnderlyingType.USR;
+  (*Typedef)["type"] = Record.UnderlyingType.USR;
 
-  return true;
+  Symbols.emplace_back(std::move(*Typedef));
 }
 
-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::serialize() {
+  traverseAPISet();
+  return serializeCurrentGraph();
 }
 
-Object SymbolGraphSerializer::serializeGraph(StringRef ModuleName,
-                                             ExtendedModule &&EM) {
+Object SymbolGraphSerializer::serializeCurrentGraph() {
   Object Root;
   serializeObject(Root, "metadata", serializeMetadata());
-  serializeObject(Root, "module", serializeModuleObject(ModuleName));
+  serializeObject(Root, "module", serializeModule());
 
-  Root["symbols"] = std::move(EM.Symbols);
-  Root["relationships"] = std::move(EM.Relationships);
+  Root["symbols"] = std::move(Symbols);
+  Root["relationships"] = std::move(Relationships);
 
   return Root;
 }
 
-void SymbolGraphSerializer::serializeGraphToStream(
-    raw_ostream &OS, SymbolGraphSerializerOption Options, StringRef ModuleName,
-    ExtendedModule &&EM) {
-  Object Root = serializeGraph(ModuleName, std::move(EM));
+void SymbolGraphSerializer::serialize(raw_ostream &os) {
+  Object root = serialize();
   if (Options.Compact)
-    OS << formatv("{0}", Value(std::move(Root))) << "\n";
+    os << formatv("{0}", Value(std::move(root))) << "\n";
   else
-    OS << formatv("{0:2}", 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);
-  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()));
-  }
+    os << formatv("{0:2}", Value(std::move(root))) << "\n";
 }
 
 std::optional<Object>
@@ -1070,20 +1262,14 @@ SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
   Object Root;
   APIIgnoresList EmptyIgnores;
   SymbolGraphSerializer Serializer(API, EmptyIgnores,
-                                   /*EmitSymbolLabelsForTesting*/ false,
-                                   /*ForceEmitToMainModule*/ true);
-
-  // Set up serializer parent chain
-  Serializer.Hierarchy = generateHierarchyFromRecord(Record);
-
+                                   /*Options.Compact*/ {true},
+                                   /*ShouldRecurse*/ false);
   Serializer.serializeSingleRecord(Record);
-  serializeObject(Root, "symbolGraph",
-                  Serializer.serializeGraph(API.ProductName,
-                                            std::move(Serializer.MainModule)));
+  serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
 
   Language Lang = API.getLanguage();
   serializeArray(Root, "parentContexts",
-                 generateParentContexts(Serializer.Hierarchy, Lang));
+                 generateParentContexts(*Record, API, Lang));
 
   Array RelatedSymbols;
 
@@ -1101,15 +1287,14 @@ SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
     Object RelatedSymbol;
     RelatedSymbol["usr"] = RelatedRecord->USR;
     RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
-    RelatedSymbol["accessLevel"] = RelatedRecord->Access.getAccess();
+    // TODO: once we record this properly let's serialize it right.
+    RelatedSymbol["accessLevel"] = "public";
     RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
     RelatedSymbol["moduleName"] = API.ProductName;
     RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
 
     serializeArray(RelatedSymbol, "parentContexts",
-                   generateParentContexts(
-                       generateHierarchyFromRecord(RelatedRecord), Lang));
-
+                   generateParentContexts(*RelatedRecord, API, Lang));
     RelatedSymbols.push_back(std::move(RelatedSymbol));
   }
 

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
index 41e4e0cf1795f9..3a5f62c9b2e6cc 100644
--- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
@@ -12,7 +12,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
-#include "clang/Basic/Module.h"
 #include "clang/Index/USRGeneration.h"
 
 using namespace clang;
@@ -51,20 +50,17 @@ TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
   SmallString<128> TypeUSR;
   const NamedDecl *TypeDecl = getUnderlyingTypeDecl(Type);
   const TypedefType *TypedefTy = Type->getAs<TypedefType>();
-  StringRef OwningModuleName;
 
   if (TypeDecl) {
     if (!TypedefTy)
       TypeName = TypeDecl->getName().str();
 
     clang::index::generateUSRForDecl(TypeDecl, TypeUSR);
-    if (auto *OwningModule = TypeDecl->getImportedOwningModule())
-      OwningModuleName = OwningModule->Name;
   } else {
     clang::index::generateUSRForType(Type, Context, TypeUSR);
   }
 
-  return API.createSymbolReference(TypeName, TypeUSR, OwningModuleName);
+  return {API.copyString(TypeName), API.copyString(TypeUSR)};
 }
 
 std::string TypedefUnderlyingTypeResolver::getUSRForType(QualType Type) const {

diff  --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index f85f0365616f9a..2446aee571f440 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -181,13 +181,9 @@ CreateFrontendAction(CompilerInstance &CI) {
 #endif
 
   // Wrap the base FE action in an extract api action to generate
-  // symbol graph as a biproduct of compilation (enabled with
-  // --emit-symbol-graph option)
-  if (FEOpts.EmitSymbolGraph) {
-    if (FEOpts.SymbolGraphOutputDir.empty()) {
-      CI.getDiagnostics().Report(diag::warn_missing_symbol_graph_dir);
-      CI.getFrontendOpts().SymbolGraphOutputDir = ".";
-    }
+  // symbol graph as a biproduct of compilation ( enabled with
+  // --emit-symbol-graph option )
+  if (!FEOpts.SymbolGraphOutputDir.empty()) {
     CI.getCodeGenOpts().ClearASTBeforeBackend = false;
     Act = std::make_unique<WrappingExtractAPIAction>(std::move(Act));
   }

diff  --git a/clang/test/ExtractAPI/anonymous_record_no_typedef.c b/clang/test/ExtractAPI/anonymous_record_no_typedef.c
index 049e8b1f85bb96..0e50f4a0948c94 100644
--- a/clang/test/ExtractAPI/anonymous_record_no_typedef.c
+++ b/clang/test/ExtractAPI/anonymous_record_no_typedef.c
@@ -1,9 +1,8 @@
-// XFAIL: *
 // RUN: rm -rf %t
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/availability.c b/clang/test/ExtractAPI/availability.c
index 12ac73f0d4295a..3c1ef5c45b634d 100644
--- a/clang/test/ExtractAPI/availability.c
+++ b/clang/test/ExtractAPI/availability.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf --product-name=Availability -triple arm64-apple-macosx -x c-header %t/input.h -o %t/output.json -verify
+// RUN: %clang_cc1 -extract-api --product-name=Availability -triple arm64-apple-macosx -x c-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.
 // RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \

diff  --git a/clang/test/ExtractAPI/bool.c b/clang/test/ExtractAPI/bool.c
index efab6dfeef03b2..f4082edeb02ede 100644
--- a/clang/test/ExtractAPI/bool.c
+++ b/clang/test/ExtractAPI/bool.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf -target arm64-apple-macosx \
+// RUN: %clang -extract-api -target arm64-apple-macosx \
 // RUN: %t/input.h -o %t/output.json
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/bool.cpp b/clang/test/ExtractAPI/bool.cpp
index f7d10c61dba4b7..1b445e220a4a0e 100644
--- a/clang/test/ExtractAPI/bool.cpp
+++ b/clang/test/ExtractAPI/bool.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/class.cpp b/clang/test/ExtractAPI/class.cpp
index 0c5db8e9c9d215..21cac43057524a 100644
--- a/clang/test/ExtractAPI/class.cpp
+++ b/clang/test/ExtractAPI/class.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/class_template.cpp b/clang/test/ExtractAPI/class_template.cpp
index 4f2670d7b69977..b04dca6bffda16 100644
--- a/clang/test/ExtractAPI/class_template.cpp
+++ b/clang/test/ExtractAPI/class_template.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/class_template_param_inheritance.cpp b/clang/test/ExtractAPI/class_template_param_inheritance.cpp
index 3d7b09f93ed6de..0d38fd1b7f5306 100644
--- a/clang/test/ExtractAPI/class_template_param_inheritance.cpp
+++ b/clang/test/ExtractAPI/class_template_param_inheritance.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/class_template_partial_spec.cpp b/clang/test/ExtractAPI/class_template_partial_spec.cpp
index c8d9cc78d41c5c..eba069319ce450 100644
--- a/clang/test/ExtractAPI/class_template_partial_spec.cpp
+++ b/clang/test/ExtractAPI/class_template_partial_spec.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.
@@ -15,7 +15,7 @@ template<typename X, typename Y> class Foo {};
 
 template<typename Z> class Foo<Z, int> {};
 
-// expected-no-diagnostics
+/// expected-no-diagnostics
 
 //--- reference.output.json.in
 {

diff  --git a/clang/test/ExtractAPI/class_template_spec.cpp b/clang/test/ExtractAPI/class_template_spec.cpp
index 06a95314dc4aa0..4b183cbb844580 100644
--- a/clang/test/ExtractAPI/class_template_spec.cpp
+++ b/clang/test/ExtractAPI/class_template_spec.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/concept.cpp b/clang/test/ExtractAPI/concept.cpp
index 443eac2971f0e5..ff4e71026e7283 100644
--- a/clang/test/ExtractAPI/concept.cpp
+++ b/clang/test/ExtractAPI/concept.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -std=c++20 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -std=c++20 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/constructor_destructor.cpp b/clang/test/ExtractAPI/constructor_destructor.cpp
index 27112c95ac45c2..9742d4bae26133 100644
--- a/clang/test/ExtractAPI/constructor_destructor.cpp
+++ b/clang/test/ExtractAPI/constructor_destructor.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.
@@ -137,7 +137,7 @@ class Foo {
         "precise": "c:@S at Foo@F at Foo#"
       },
       "kind": {
-        "displayName": "Constructor",
+        "displayName": "Instance Method",
         "identifier": "c++.method"
       },
       "location": {
@@ -193,7 +193,7 @@ class Foo {
         "precise": "c:@S at Foo@F@~Foo#"
       },
       "kind": {
-        "displayName": "Destructor",
+        "displayName": "Instance Method",
         "identifier": "c++.method"
       },
       "location": {

diff  --git a/clang/test/ExtractAPI/conversions.cpp b/clang/test/ExtractAPI/conversions.cpp
index 07688ff770979e..fc8d0675443730 100644
--- a/clang/test/ExtractAPI/conversions.cpp
+++ b/clang/test/ExtractAPI/conversions.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c b/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
index e668f69bc7e05f..e6b72d5881e7d1 100644
--- a/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
+++ b/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
@@ -5,19 +5,18 @@
 // RUN: %t/reference.main.json.in >> %t/reference.main.json
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.test.json.in >> %t/reference.test.json
-// RUN: %clang_cc1 %t/test.c %t/main.c -emit-symbol-graph --pretty-sgf \
-// RUN:   --symbol-graph-dir=%t/SymbolGraphs --product-name=multifile_test -triple=x86_64-apple-macosx12.0.0
+// RUN: %clang_cc1 %t/test.c %t/main.c --emit-symbol-graph=%t/SymbolGraphs --product-name=multifile_test -triple=x86_64-apple-macosx12.0.0
 
 // Test main.json
 // Generator version is not consistent across test runs, normalize it.
 // RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
-// RUN: %t/SymbolGraphs/main.c.symbols.json > %t/output-normalized.json
+// RUN: %t/SymbolGraphs/main.json > %t/output-normalized.json
 // RUN: 
diff  %t/reference.main.json %t/output-normalized.json
 
 // Test test.json
 // Generator version is not consistent across test runs, normalize it.
 // RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
-// RUN: %t/SymbolGraphs/test.c.symbols.json > %t/output-normalized.json
+// RUN: %t/SymbolGraphs/test.json > %t/output-normalized.json
 // RUN: 
diff  %t/reference.test.json %t/output-normalized.json
 
 // CHECK-NOT: error:

diff  --git a/clang/test/ExtractAPI/emit-symbol-graph/single_file.c b/clang/test/ExtractAPI/emit-symbol-graph/single_file.c
index b00b5f5237c9a3..8599e82e10783a 100644
--- a/clang/test/ExtractAPI/emit-symbol-graph/single_file.c
+++ b/clang/test/ExtractAPI/emit-symbol-graph/single_file.c
@@ -3,12 +3,11 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 %t/main.c -emit-symbol-graph --pretty-sgf  \
-// RUN:   --symbol-graph-dir=%t/SymbolGraphs --product-name=basicfile -triple=x86_64-apple-macosx12.0.0
+// RUN: %clang_cc1 %t/main.c --emit-symbol-graph=%t/SymbolGraphs --product-name=basicfile -triple=x86_64-apple-macosx12.0.0
 
 // Generator version is not consistent across test runs, normalize it.
 // RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
-// RUN: %t/SymbolGraphs/main.c.symbols.json >> %t/output-normalized.json
+// RUN: %t/SymbolGraphs/main.json >> %t/output-normalized.json
 // RUN: 
diff  %t/reference.output.json %t/output-normalized.json
 
 // CHECK-NOT: error:

diff  --git a/clang/test/ExtractAPI/enum.c b/clang/test/ExtractAPI/enum.c
index 1cdf45ca3cdf4b..94499d9fc3a639 100644
--- a/clang/test/ExtractAPI/enum.c
+++ b/clang/test/ExtractAPI/enum.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/field_template.cpp b/clang/test/ExtractAPI/field_template.cpp
index 2058ed008cfe40..f05e826a8eb491 100644
--- a/clang/test/ExtractAPI/field_template.cpp
+++ b/clang/test/ExtractAPI/field_template.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/function_noexcepts.cpp b/clang/test/ExtractAPI/function_noexcepts.cpp
index d95eaaa7e769a5..3fc7263cd6a186 100644
--- a/clang/test/ExtractAPI/function_noexcepts.cpp
+++ b/clang/test/ExtractAPI/function_noexcepts.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_func_template.cpp b/clang/test/ExtractAPI/global_func_template.cpp
index f43a618ec0c366..8def9745bcce8e 100644
--- a/clang/test/ExtractAPI/global_func_template.cpp
+++ b/clang/test/ExtractAPI/global_func_template.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_func_template_spec.cpp b/clang/test/ExtractAPI/global_func_template_spec.cpp
index fe046e9c3b9dac..a24263dc14584f 100644
--- a/clang/test/ExtractAPI/global_func_template_spec.cpp
+++ b/clang/test/ExtractAPI/global_func_template_spec.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_record.c b/clang/test/ExtractAPI/global_record.c
index a08d51d21f9556..623032b45bfd2c 100644
--- a/clang/test/ExtractAPI/global_record.c
+++ b/clang/test/ExtractAPI/global_record.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf --product-name=GlobalRecord -target arm64-apple-macosx \
+// RUN: %clang -extract-api --product-name=GlobalRecord -target arm64-apple-macosx \
 // RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_record_multifile.c b/clang/test/ExtractAPI/global_record_multifile.c
index ffdfbcb7eb8087..f9d3889b5d9de3 100644
--- a/clang/test/ExtractAPI/global_record_multifile.c
+++ b/clang/test/ExtractAPI/global_record_multifile.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf --product-name=GlobalRecord -target arm64-apple-macosx \
+// RUN: %clang -extract-api --product-name=GlobalRecord -target arm64-apple-macosx \
 // RUN: %t/input1.h %t/input2.h %t/input3.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_var_template.cpp b/clang/test/ExtractAPI/global_var_template.cpp
index 94f3713cd3d31b..bee2ea601bd72b 100644
--- a/clang/test/ExtractAPI/global_var_template.cpp
+++ b/clang/test/ExtractAPI/global_var_template.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_var_template_partial_spec.cpp b/clang/test/ExtractAPI/global_var_template_partial_spec.cpp
index 91084f258878ee..e98076cdb1d016 100644
--- a/clang/test/ExtractAPI/global_var_template_partial_spec.cpp
+++ b/clang/test/ExtractAPI/global_var_template_partial_spec.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/global_var_template_spec.cpp b/clang/test/ExtractAPI/global_var_template_spec.cpp
index ff4d8d17aecbe9..cca2ab3db7b8bd 100644
--- a/clang/test/ExtractAPI/global_var_template_spec.cpp
+++ b/clang/test/ExtractAPI/global_var_template_spec.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/known_files_only.c b/clang/test/ExtractAPI/known_files_only.c
index de1e786c1969dd..68881aa9e3aadb 100644
--- a/clang/test/ExtractAPI/known_files_only.c
+++ b/clang/test/ExtractAPI/known_files_only.c
@@ -1,7 +1,17 @@
 // RUN: rm -rf %t
 // RUN: split-file %s %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --product-name=GlobalRecord -triple arm64-apple-macosx \
-// RUN: %t/input1.h -verify -o - | FileCheck %s
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api --product-name=GlobalRecord -target arm64-apple-macosx \
+// RUN: %t/input1.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
 
 //--- input1.h
 int num;
@@ -14,6 +24,87 @@ char not_emitted;
 void foo(int);
 struct Foo { int a; };
 
-// CHECK-NOT: input2.h
-
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "GlobalRecord",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "num"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@num"
+      },
+      "kind": {
+        "displayName": "Global Variable",
+        "identifier": "c.var"
+      },
+      "location": {
+        "position": {
+          "character": 4,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input1.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "num"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "num"
+          }
+        ],
+        "title": "num"
+      },
+      "pathComponents": [
+        "num"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/language.c b/clang/test/ExtractAPI/language.c
index 90832fd8a2aff9..fe98626c84613e 100644
--- a/clang/test/ExtractAPI/language.c
+++ b/clang/test/ExtractAPI/language.c
@@ -7,11 +7,11 @@
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/objcpp.reference.output.json.in >> %t/objcpp.reference.output.json
 
-// RUN: %clang_cc1 -extract-api --pretty-sgf -x c-header -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -x c-header -triple arm64-apple-macosx \
 // RUN: %t/c.h -o %t/c.output.json | FileCheck -allow-empty %s
-// RUN: %clang_cc1 -extract-api --pretty-sgf -x objective-c-header -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -x objective-c-header -triple arm64-apple-macosx \
 // RUN: %t/objc.h -o %t/objc.output.json | FileCheck -allow-empty %s
-// RUN: %clang_cc1 -extract-api --pretty-sgf -x objective-c++-header -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -x objective-c++-header -triple arm64-apple-macosx \
 // RUN: %t/objcpp.h -o %t/objcpp.output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/macro_undefined.c b/clang/test/ExtractAPI/macro_undefined.c
index ec60f95d3d6c4f..1a4ed20545e0d6 100644
--- a/clang/test/ExtractAPI/macro_undefined.c
+++ b/clang/test/ExtractAPI/macro_undefined.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf --product-name=Macros -target arm64-apple-macosx \
+// RUN: %clang -extract-api --product-name=Macros -target arm64-apple-macosx \
 // RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/macros.c b/clang/test/ExtractAPI/macros.c
index 10003fe6f6e40f..d5807f6377ff63 100644
--- a/clang/test/ExtractAPI/macros.c
+++ b/clang/test/ExtractAPI/macros.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf --product-name=Macros -target arm64-apple-macosx \
+// RUN: %clang -extract-api --product-name=Macros -target arm64-apple-macosx \
 // RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/metadata_and_module.c b/clang/test/ExtractAPI/metadata_and_module.c
deleted file mode 100644
index 79574a20ed95a9..00000000000000
--- a/clang/test/ExtractAPI/metadata_and_module.c
+++ /dev/null
@@ -1,32 +0,0 @@
-// RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --product-name=module -triple arm64-apple-macosx -x c-header %s -o %t/module.symbols.json -verify
-
-// RUN: FileCheck %s --input-file %t/module.symbols.json --check-prefix METADATA
-// RUN: FileCheck %s --input-file %t/module.symbols.json --check-prefix MOD
-
-// expected-no-diagnostics
-
-// METADATA:      "metadata": {
-// METADATA-NEXT:   "formatVersion": {
-// METADATA-NEXT:     "major":
-// METADATA-NEXT:     "minor":
-// METADATA-NEXT:     "patch":
-// METADATA-NEXT:   },
-// METADATA-NEXT:   "generator":
-// METADATA-NEXT: }
-
-// MOD: "module": {
-// MOD-NEXT:   "name": "module",
-// MOD-NEXT:   "platform": {
-// MOD-NEXT:     "architecture": "arm64",
-// MOD-NEXT:     "operatingSystem": {
-// MOD-NEXT:       "minimumVersion": {
-// MOD-NEXT:         "major":
-// MOD-NEXT:         "minor":
-// MOD-NEXT:         "patch":
-// MOD-NEXT:       },
-// MOD-NEXT:       "name": "macosx"
-// MOD-NEXT:     },
-// MOD-NEXT:     "vendor": "apple"
-// MOD-NEXT:   }
-// MOD-NEXT: }

diff  --git a/clang/test/ExtractAPI/method_template.cpp b/clang/test/ExtractAPI/method_template.cpp
index 714f9cac26c208..8d832337216a28 100644
--- a/clang/test/ExtractAPI/method_template.cpp
+++ b/clang/test/ExtractAPI/method_template.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/method_template_spec.cpp b/clang/test/ExtractAPI/method_template_spec.cpp
index 8eaffdefd827a9..706d99da558fe2 100644
--- a/clang/test/ExtractAPI/method_template_spec.cpp
+++ b/clang/test/ExtractAPI/method_template_spec.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/methods.cpp b/clang/test/ExtractAPI/methods.cpp
index 412c0bb3f903c3..8b024a8c3036f1 100644
--- a/clang/test/ExtractAPI/methods.cpp
+++ b/clang/test/ExtractAPI/methods.cpp
@@ -1,221 +1,467 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -triple arm64-apple-macosx -x c++-header %s -o %t/output.symbols.json -verify
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
 class Foo {
-  // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GETCOUNT
   int getCount();
-  // GETCOUNT: "!testRelLabel": "memberOf $ c:@S at Foo@F at getCount# $ c:@S at Foo"
-  // GETCOUNT-LABEL: "!testLabel":  "c:@S at Foo@F at getCount#"
-  // GETCOUNT: "accessLevel": "private",
-  // GETCOUNT:      "declarationFragments": [
-  // GETCOUNT-NEXT:   {
-  // GETCOUNT-NEXT:     "kind": "typeIdentifier",
-  // GETCOUNT-NEXT:     "preciseIdentifier": "c:I",
-  // GETCOUNT-NEXT:     "spelling": "int"
-  // GETCOUNT-NEXT:   },
-  // GETCOUNT-NEXT:   {
-  // GETCOUNT-NEXT:     "kind": "text",
-  // GETCOUNT-NEXT:     "spelling": " "
-  // GETCOUNT-NEXT:   },
-  // GETCOUNT-NEXT:   {
-  // GETCOUNT-NEXT:     "kind": "identifier",
-  // GETCOUNT-NEXT:     "spelling": "getCount"
-  // GETCOUNT-NEXT:   },
-  // GETCOUNT-NEXT:   {
-  // GETCOUNT-NEXT:     "kind": "text",
-  // GETCOUNT-NEXT:     "spelling": "();"
-  // GETCOUNT-NEXT:   }
-  // GETCOUNT-NEXT: ],
-  // GETCOUNT:      "functionSignature": {
-  // GETCOUNT-NEXT:   "returns": [
-  // GETCOUNT-NEXT:     {
-  // GETCOUNT-NEXT:       "kind": "typeIdentifier",
-  // GETCOUNT-NEXT:       "preciseIdentifier": "c:I",
-  // GETCOUNT-NEXT:       "spelling": "int"
-  // GETCOUNT-NEXT:     }
-  // GETCOUNT-NEXT:   ]
-  // GETCOUNT-NEXT: },
-  // GETCOUNT: "displayName": "Instance Method",
-  // GETCOUNT-NEXT: "identifier": "c++.method"
-  // GETCOUNT: "title": "getCount"
-  // GETCOUNT: "pathComponents": [
-  // GETCOUNT-NEXT:   "Foo",
-  // GETCOUNT-NEXT:   "getCount"
-  // GETCOUNT-NEXT: ]
 
-  // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix SETL
   void setLength(int length) noexcept;
-  // SETL: "!testRelLabel": "memberOf $ c:@S at Foo@F at setLength#I# $ c:@S at Foo"
-  // SETL-LABEL: "!testLabel": "c:@S at Foo@F at setLength#I#"
-  // SETL:      "declarationFragments": [
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "typeIdentifier",
-  // SETL-NEXT:     "preciseIdentifier": "c:v",
-  // SETL-NEXT:     "spelling": "void"
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "text",
-  // SETL-NEXT:     "spelling": " "
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "identifier",
-  // SETL-NEXT:     "spelling": "setLength"
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "text",
-  // SETL-NEXT:     "spelling": "("
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "typeIdentifier",
-  // SETL-NEXT:     "preciseIdentifier": "c:I",
-  // SETL-NEXT:     "spelling": "int"
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "text",
-  // SETL-NEXT:     "spelling": " "
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "internalParam",
-  // SETL-NEXT:     "spelling": "length"
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "text",
-  // SETL-NEXT:     "spelling": ")"
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "text",
-  // SETL-NEXT:     "spelling": " "
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "keyword",
-  // SETL-NEXT:     "spelling": "noexcept"
-  // SETL-NEXT:   },
-  // SETL-NEXT:   {
-  // SETL-NEXT:     "kind": "text",
-  // SETL-NEXT:     "spelling": ";"
-  // SETL-NEXT:   }
-  // SETL-NEXT: ],
-  // SETL:      "functionSignature": {
-  // SETL-NEXT:   "parameters": [
-  // SETL-NEXT:     {
-  // SETL-NEXT:       "declarationFragments": [
-  // SETL-NEXT:         {
-  // SETL-NEXT:           "kind": "typeIdentifier",
-  // SETL-NEXT:           "preciseIdentifier": "c:I",
-  // SETL-NEXT:           "spelling": "int"
-  // SETL-NEXT:         },
-  // SETL-NEXT:         {
-  // SETL-NEXT:           "kind": "text",
-  // SETL-NEXT:           "spelling": " "
-  // SETL-NEXT:         },
-  // SETL-NEXT:         {
-  // SETL-NEXT:           "kind": "internalParam",
-  // SETL-NEXT:           "spelling": "length"
-  // SETL-NEXT:         }
-  // SETL-NEXT:       ],
-  // SETL-NEXT:       "name": "length"
-  // SETL-NEXT:     }
-  // SETL-NEXT:   ],
-  // SETL-NEXT:   "returns": [
-  // SETL-NEXT:     {
-  // SETL-NEXT:       "kind": "typeIdentifier",
-  // SETL-NEXT:       "preciseIdentifier": "c:v",
-  // SETL-NEXT:       "spelling": "void"
-  // SETL-NEXT:     }
-  // SETL-NEXT:   ]
-  // SETL-NEXT: },
 
 public:
-  // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GETFOO
   static double getFoo();
-  // GETFOO: "!testRelLabel": "memberOf $ c:@S at Foo@F at getFoo#S $ c:@S at Foo"
-
-  // GETFOO-LABEL: "!testLabel": "c:@S at Foo@F at getFoo#S"
-  // GETFOO: "accessLevel": "public",
-  // GETFOO:      "declarationFragments": [
-  // GETFOO-NEXT:   {
-  // GETFOO-NEXT:     "kind": "keyword",
-  // GETFOO-NEXT:     "spelling": "static"
-  // GETFOO-NEXT:   },
-  // GETFOO-NEXT:   {
-  // GETFOO-NEXT:     "kind": "text",
-  // GETFOO-NEXT:     "spelling": " "
-  // GETFOO-NEXT:   },
-  // GETFOO-NEXT:   {
-  // GETFOO-NEXT:     "kind": "typeIdentifier",
-  // GETFOO-NEXT:     "preciseIdentifier": "c:d",
-  // GETFOO-NEXT:     "spelling": "double"
-  // GETFOO-NEXT:   },
-  // GETFOO-NEXT:   {
-  // GETFOO-NEXT:     "kind": "text",
-  // GETFOO-NEXT:     "spelling": " "
-  // GETFOO-NEXT:   },
-  // GETFOO-NEXT:   {
-  // GETFOO-NEXT:     "kind": "identifier",
-  // GETFOO-NEXT:     "spelling": "getFoo"
-  // GETFOO-NEXT:   },
-  // GETFOO-NEXT:   {
-  // GETFOO-NEXT:     "kind": "text",
-  // GETFOO-NEXT:     "spelling": "();"
-  // GETFOO-NEXT:   }
-  // GETFOO-NEXT: ],
-  // GETFOO:      "functionSignature": {
-  // GETFOO-NEXT:   "returns": [
-  // GETFOO-NEXT:     {
-  // GETFOO-NEXT:       "kind": "typeIdentifier",
-  // GETFOO-NEXT:       "preciseIdentifier": "c:d",
-  // GETFOO-NEXT:       "spelling": "double"
-  // GETFOO-NEXT:     }
-  // GETFOO-NEXT:   ]
-  // GETFOO-NEXT: },
-  // GETFOO:      "kind": {
-  // GETFOO-NEXT:   "displayName": "Static Method",
-  // GETFOO-NEXT:   "identifier": "c++.type.method"
-  // GETFOO-NEXT: },
 
 protected:
-  // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GETBAR
   constexpr int getBar() const;
-  // GETBAR: "!testRelLabel": "memberOf $ c:@S at Foo@F at getBar#1 $ c:@S at Foo"
-
-  // GETBAR-LABEL: "!testLabel": "c:@S at Foo@F at getBar#1"
-  // GETBAR: "accessLevel": "protected"
-  // GETBAR:      "declarationFragments": [
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "keyword",
-  // GETBAR-NEXT:     "spelling": "constexpr"
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "text",
-  // GETBAR-NEXT:     "spelling": " "
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "typeIdentifier",
-  // GETBAR-NEXT:     "preciseIdentifier": "c:I",
-  // GETBAR-NEXT:     "spelling": "int"
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "text",
-  // GETBAR-NEXT:     "spelling": " "
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "identifier",
-  // GETBAR-NEXT:     "spelling": "getBar"
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "text",
-  // GETBAR-NEXT:     "spelling": "() "
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "keyword",
-  // GETBAR-NEXT:     "spelling": "const"
-  // GETBAR-NEXT:   },
-  // GETBAR-NEXT:   {
-  // GETBAR-NEXT:     "kind": "text",
-  // GETBAR-NEXT:     "spelling": ";"
-  // GETBAR-NEXT:   }
-  // GETBAR-NEXT: ],
 };
+/// expected-no-diagnostics
 
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at getCount#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at setLength#I#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at getBar#1",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at getFoo#S",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getCount"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at getCount#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getCount"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getCount"
+          }
+        ],
+        "title": "getCount"
+      },
+      "pathComponents": [
+        "Foo",
+        "getCount"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "setLength"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "length"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "noexcept"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "length"
+              }
+            ],
+            "name": "length"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at setLength#I#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "setLength"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "setLength"
+          }
+        ],
+        "title": "setLength"
+      },
+      "pathComponents": [
+        "Foo",
+        "setLength"
+      ]
+    },
+    {
+      "accessLevel": "protected",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "constexpr"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getBar"
+        },
+        {
+          "kind": "text",
+          "spelling": "() "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "const"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at getBar#1"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 16,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getBar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getBar"
+          }
+        ],
+        "title": "getBar"
+      },
+      "pathComponents": [
+        "Foo",
+        "getBar"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "static"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:d",
+          "spelling": "double"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getFoo"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:d",
+            "spelling": "double"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at getFoo#S"
+      },
+      "kind": {
+        "displayName": "Static Method",
+        "identifier": "c++.type.method"
+      },
+      "location": {
+        "position": {
+          "character": 16,
+          "line": 6
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getFoo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getFoo"
+          }
+        ],
+        "title": "getFoo"
+      },
+      "pathComponents": [
+        "Foo",
+        "getFoo"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/multiple_inheritance.cpp b/clang/test/ExtractAPI/multiple_inheritance.cpp
index 7d49cf4326465e..a1f069be0de617 100644
--- a/clang/test/ExtractAPI/multiple_inheritance.cpp
+++ b/clang/test/ExtractAPI/multiple_inheritance.cpp
@@ -3,7 +3,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/namespace.cpp b/clang/test/ExtractAPI/namespace.cpp
index 73e0728b9a4416..e0c36dd3d60fed 100644
--- a/clang/test/ExtractAPI/namespace.cpp
+++ b/clang/test/ExtractAPI/namespace.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -std=c++20 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/nested_namespaces.cpp b/clang/test/ExtractAPI/nested_namespaces.cpp
index c6912cfb46312f..bd13ef93807c01 100644
--- a/clang/test/ExtractAPI/nested_namespaces.cpp
+++ b/clang/test/ExtractAPI/nested_namespaces.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -std=c++20 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/objc_block.m b/clang/test/ExtractAPI/objc_block.m
index 4a4335ec09832d..a7a4f5696333c1 100644
--- a/clang/test/ExtractAPI/objc_block.m
+++ b/clang/test/ExtractAPI/objc_block.m
@@ -1,630 +1,965 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -fblocks -triple arm64-apple-macosx -x objective-c-header %s -o %t/output.symbols.json -verify
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -fblocks -triple arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json -verify
 
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
 @interface Foo
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix NOPARAM
 -(void)methodBlockNoParam:(void (^)())block;
-// NOPARAM-LABEL: "!testLabel": "c:objc(cs)Foo(im)methodBlockNoParam:"
-// NOPARAM:      "declarationFragments": [
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "text",
-// NOPARAM-NEXT:     "spelling": "- ("
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "typeIdentifier",
-// NOPARAM-NEXT:     "preciseIdentifier": "c:v",
-// NOPARAM-NEXT:     "spelling": "void"
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "text",
-// NOPARAM-NEXT:     "spelling": ") "
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "identifier",
-// NOPARAM-NEXT:     "spelling": "methodBlockNoParam:"
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "text",
-// NOPARAM-NEXT:     "spelling": "("
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "typeIdentifier",
-// NOPARAM-NEXT:     "preciseIdentifier": "c:v",
-// NOPARAM-NEXT:     "spelling": "void"
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "text",
-// NOPARAM-NEXT:     "spelling": " (^"
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "text",
-// NOPARAM-NEXT:     "spelling": ")()) "
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "internalParam",
-// NOPARAM-NEXT:     "spelling": "block"
-// NOPARAM-NEXT:   },
-// NOPARAM-NEXT:   {
-// NOPARAM-NEXT:     "kind": "text",
-// NOPARAM-NEXT:     "spelling": ";"
-// NOPARAM-NEXT:   }
-// NOPARAM-NEXT: ],
-// NOPARAM:      "functionSignature": {
-// NOPARAM-NEXT:   "parameters": [
-// NOPARAM-NEXT:     {
-// NOPARAM-NEXT:       "declarationFragments": [
-// NOPARAM-NEXT:         {
-// NOPARAM-NEXT:           "kind": "text",
-// NOPARAM-NEXT:           "spelling": "("
-// NOPARAM-NEXT:         },
-// NOPARAM-NEXT:         {
-// NOPARAM-NEXT:           "kind": "typeIdentifier",
-// NOPARAM-NEXT:           "preciseIdentifier": "c:v",
-// NOPARAM-NEXT:           "spelling": "void"
-// NOPARAM-NEXT:         },
-// NOPARAM-NEXT:         {
-// NOPARAM-NEXT:           "kind": "text",
-// NOPARAM-NEXT:           "spelling": " (^"
-// NOPARAM-NEXT:         },
-// NOPARAM-NEXT:         {
-// NOPARAM-NEXT:           "kind": "text",
-// NOPARAM-NEXT:           "spelling": ")()) "
-// NOPARAM-NEXT:         },
-// NOPARAM-NEXT:         {
-// NOPARAM-NEXT:           "kind": "internalParam",
-// NOPARAM-NEXT:           "spelling": "block"
-// NOPARAM-NEXT:         }
-// NOPARAM-NEXT:       ],
-// NOPARAM-NEXT:       "name": "block"
-// NOPARAM-NEXT:     }
-// NOPARAM-NEXT:   ],
-// NOPARAM-NEXT:   "returns": [
-// NOPARAM-NEXT:     {
-// NOPARAM-NEXT:       "kind": "typeIdentifier",
-// NOPARAM-NEXT:       "preciseIdentifier": "c:v",
-// NOPARAM-NEXT:       "spelling": "void"
-// NOPARAM-NEXT:     }
-// NOPARAM-NEXT:   ]
-// NOPARAM-NEXT: }
-
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix PARAM
 -(void)methodBlockWithParam:(int (^)(int foo))block;
-// PARAM-LABEL: "!testLabel": "c:objc(cs)Foo(im)methodBlockWithParam:"
-// PARAM:      "declarationFragments": [
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": "- ("
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "typeIdentifier",
-// PARAM-NEXT:     "preciseIdentifier": "c:v",
-// PARAM-NEXT:     "spelling": "void"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": ") "
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "identifier",
-// PARAM-NEXT:     "spelling": "methodBlockWithParam:"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": "("
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "typeIdentifier",
-// PARAM-NEXT:     "preciseIdentifier": "c:I",
-// PARAM-NEXT:     "spelling": "int"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": " (^"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": ")("
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "typeIdentifier",
-// PARAM-NEXT:     "preciseIdentifier": "c:I",
-// PARAM-NEXT:     "spelling": "int"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": " "
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "internalParam",
-// PARAM-NEXT:     "spelling": "foo"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": ")) "
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "internalParam",
-// PARAM-NEXT:     "spelling": "block"
-// PARAM-NEXT:   },
-// PARAM-NEXT:   {
-// PARAM-NEXT:     "kind": "text",
-// PARAM-NEXT:     "spelling": ";"
-// PARAM-NEXT:   }
-// PARAM-NEXT: ],
-// PARAM:      "functionSignature": {
-// PARAM-NEXT:   "parameters": [
-// PARAM-NEXT:     {
-// PARAM-NEXT:       "declarationFragments": [
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "text",
-// PARAM-NEXT:           "spelling": "("
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "typeIdentifier",
-// PARAM-NEXT:           "preciseIdentifier": "c:I",
-// PARAM-NEXT:           "spelling": "int"
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "text",
-// PARAM-NEXT:           "spelling": " (^"
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "text",
-// PARAM-NEXT:           "spelling": ")("
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "typeIdentifier",
-// PARAM-NEXT:           "preciseIdentifier": "c:I",
-// PARAM-NEXT:           "spelling": "int"
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "text",
-// PARAM-NEXT:           "spelling": " "
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "internalParam",
-// PARAM-NEXT:           "spelling": "foo"
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "text",
-// PARAM-NEXT:           "spelling": ")) "
-// PARAM-NEXT:         },
-// PARAM-NEXT:         {
-// PARAM-NEXT:           "kind": "internalParam",
-// PARAM-NEXT:           "spelling": "block"
-// PARAM-NEXT:         }
-// PARAM-NEXT:       ],
-// PARAM-NEXT:       "name": "block"
-// PARAM-NEXT:     }
-// PARAM-NEXT:   ],
-// PARAM-NEXT:   "returns": [
-// PARAM-NEXT:     {
-// PARAM-NEXT:       "kind": "typeIdentifier",
-// PARAM-NEXT:       "preciseIdentifier": "c:v",
-// PARAM-NEXT:       "spelling": "void"
-// PARAM-NEXT:     }
-// PARAM-NEXT:   ]
-// PARAM-NEXT: }
-
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix MULTIPARAM
 -(void)methodBlockWithMultipleParam:(int (^)(int foo, unsigned baz))block;
-// MULTIPARAM-LABEL: "!testLabel": "c:objc(cs)Foo(im)methodBlockWithMultipleParam:"
-// MULTIPARAM:      "declarationFragments": [
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": "- ("
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:     "preciseIdentifier": "c:v",
-// MULTIPARAM-NEXT:     "spelling": "void"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": ") "
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "identifier",
-// MULTIPARAM-NEXT:     "spelling": "methodBlockWithMultipleParam:"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": "("
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:     "preciseIdentifier": "c:I",
-// MULTIPARAM-NEXT:     "spelling": "int"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": " (^"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": ")("
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:     "preciseIdentifier": "c:I",
-// MULTIPARAM-NEXT:     "spelling": "int"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": " "
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "internalParam",
-// MULTIPARAM-NEXT:     "spelling": "foo"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": ", "
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:     "preciseIdentifier": "c:i",
-// MULTIPARAM-NEXT:     "spelling": "unsigned int"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": " "
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "internalParam",
-// MULTIPARAM-NEXT:     "spelling": "baz"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": ")) "
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "internalParam",
-// MULTIPARAM-NEXT:     "spelling": "block"
-// MULTIPARAM-NEXT:   },
-// MULTIPARAM-NEXT:   {
-// MULTIPARAM-NEXT:     "kind": "text",
-// MULTIPARAM-NEXT:     "spelling": ";"
-// MULTIPARAM-NEXT:   }
-// MULTIPARAM-NEXT: ],
-// MULTIPARAM:      "functionSignature": {
-// MULTIPARAM-NEXT:   "parameters": [
-// MULTIPARAM-NEXT:     {
-// MULTIPARAM-NEXT:       "declarationFragments": [
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": "("
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:           "preciseIdentifier": "c:I",
-// MULTIPARAM-NEXT:           "spelling": "int"
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": " (^"
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": ")("
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:           "preciseIdentifier": "c:I",
-// MULTIPARAM-NEXT:           "spelling": "int"
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": " "
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "internalParam",
-// MULTIPARAM-NEXT:           "spelling": "foo"
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": ", "
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:           "preciseIdentifier": "c:i",
-// MULTIPARAM-NEXT:           "spelling": "unsigned int"
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": " "
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "internalParam",
-// MULTIPARAM-NEXT:           "spelling": "baz"
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "text",
-// MULTIPARAM-NEXT:           "spelling": ")) "
-// MULTIPARAM-NEXT:         },
-// MULTIPARAM-NEXT:         {
-// MULTIPARAM-NEXT:           "kind": "internalParam",
-// MULTIPARAM-NEXT:           "spelling": "block"
-// MULTIPARAM-NEXT:         }
-// MULTIPARAM-NEXT:       ],
-// MULTIPARAM-NEXT:       "name": "block"
-// MULTIPARAM-NEXT:     }
-// MULTIPARAM-NEXT:   ],
-// MULTIPARAM-NEXT:   "returns": [
-// MULTIPARAM-NEXT:     {
-// MULTIPARAM-NEXT:       "kind": "typeIdentifier",
-// MULTIPARAM-NEXT:       "preciseIdentifier": "c:v",
-// MULTIPARAM-NEXT:       "spelling": "void"
-// MULTIPARAM-NEXT:     }
-// MULTIPARAM-NEXT:   ]
-// MULTIPARAM-NEXT: },
-
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix VARIADIC
 -(void)methodBlockVariadic:(int (^)(int foo, ...))block;
-// VARIADIC-LABEL: "!testLabel": "c:objc(cs)Foo(im)methodBlockVariadic:"
-// VARIADIC:      "declarationFragments": [
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": "- ("
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "typeIdentifier",
-// VARIADIC-NEXT:     "preciseIdentifier": "c:v",
-// VARIADIC-NEXT:     "spelling": "void"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": ") "
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "identifier",
-// VARIADIC-NEXT:     "spelling": "methodBlockVariadic:"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": "("
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "typeIdentifier",
-// VARIADIC-NEXT:     "preciseIdentifier": "c:I",
-// VARIADIC-NEXT:     "spelling": "int"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": " (^"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": ")("
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "typeIdentifier",
-// VARIADIC-NEXT:     "preciseIdentifier": "c:I",
-// VARIADIC-NEXT:     "spelling": "int"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": " "
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "internalParam",
-// VARIADIC-NEXT:     "spelling": "foo"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": ", ...)) "
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "internalParam",
-// VARIADIC-NEXT:     "spelling": "block"
-// VARIADIC-NEXT:   },
-// VARIADIC-NEXT:   {
-// VARIADIC-NEXT:     "kind": "text",
-// VARIADIC-NEXT:     "spelling": ";"
-// VARIADIC-NEXT:   }
-// VARIADIC-NEXT: ],
-// VARIADIC:      "functionSignature": {
-// VARIADIC-NEXT:   "parameters": [
-// VARIADIC-NEXT:     {
-// VARIADIC-NEXT:       "declarationFragments": [
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "text",
-// VARIADIC-NEXT:           "spelling": "("
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "typeIdentifier",
-// VARIADIC-NEXT:           "preciseIdentifier": "c:I",
-// VARIADIC-NEXT:           "spelling": "int"
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "text",
-// VARIADIC-NEXT:           "spelling": " (^"
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "text",
-// VARIADIC-NEXT:           "spelling": ")("
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "typeIdentifier",
-// VARIADIC-NEXT:           "preciseIdentifier": "c:I",
-// VARIADIC-NEXT:           "spelling": "int"
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "text",
-// VARIADIC-NEXT:           "spelling": " "
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "internalParam",
-// VARIADIC-NEXT:           "spelling": "foo"
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "text",
-// VARIADIC-NEXT:           "spelling": ", ...)) "
-// VARIADIC-NEXT:         },
-// VARIADIC-NEXT:         {
-// VARIADIC-NEXT:           "kind": "internalParam",
-// VARIADIC-NEXT:           "spelling": "block"
-// VARIADIC-NEXT:         }
-// VARIADIC-NEXT:       ],
-// VARIADIC-NEXT:       "name": "block"
-// VARIADIC-NEXT:     }
-// VARIADIC-NEXT:   ],
-// VARIADIC-NEXT:   "returns": [
-// VARIADIC-NEXT:     {
-// VARIADIC-NEXT:       "kind": "typeIdentifier",
-// VARIADIC-NEXT:       "preciseIdentifier": "c:v",
-// VARIADIC-NEXT:       "spelling": "void"
-// VARIADIC-NEXT:     }
-// VARIADIC-NEXT:   ]
-// VARIADIC-NEXT: },
 @end
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix FUNC
 void func(int (^arg)(int foo));
-// FUNC-LABEL: "!testLabel": "c:@F at func"
-// FUNC:      "declarationFragments": [
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "typeIdentifier",
-// FUNC-NEXT:     "preciseIdentifier": "c:v",
-// FUNC-NEXT:     "spelling": "void"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "text",
-// FUNC-NEXT:     "spelling": " "
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "identifier",
-// FUNC-NEXT:     "spelling": "func"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "text",
-// FUNC-NEXT:     "spelling": "("
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "typeIdentifier",
-// FUNC-NEXT:     "preciseIdentifier": "c:I",
-// FUNC-NEXT:     "spelling": "int"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "text",
-// FUNC-NEXT:     "spelling": " (^"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "internalParam",
-// FUNC-NEXT:     "spelling": "arg"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "text",
-// FUNC-NEXT:     "spelling": ")("
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "typeIdentifier",
-// FUNC-NEXT:     "preciseIdentifier": "c:I",
-// FUNC-NEXT:     "spelling": "int"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "text",
-// FUNC-NEXT:     "spelling": " "
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "internalParam",
-// FUNC-NEXT:     "spelling": "foo"
-// FUNC-NEXT:   },
-// FUNC-NEXT:   {
-// FUNC-NEXT:     "kind": "text",
-// FUNC-NEXT:     "spelling": "));"
-// FUNC-NEXT:   }
-// FUNC-NEXT: ],
-// FUNC:      "functionSignature": {
-// FUNC-NEXT:   "parameters": [
-// FUNC-NEXT:     {
-// FUNC-NEXT:       "declarationFragments": [
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "typeIdentifier",
-// FUNC-NEXT:           "preciseIdentifier": "c:I",
-// FUNC-NEXT:           "spelling": "int"
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "text",
-// FUNC-NEXT:           "spelling": " (^"
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "internalParam",
-// FUNC-NEXT:           "spelling": "arg"
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "text",
-// FUNC-NEXT:           "spelling": ")("
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "typeIdentifier",
-// FUNC-NEXT:           "preciseIdentifier": "c:I",
-// FUNC-NEXT:           "spelling": "int"
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "text",
-// FUNC-NEXT:           "spelling": " "
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "internalParam",
-// FUNC-NEXT:           "spelling": "foo"
-// FUNC-NEXT:         },
-// FUNC-NEXT:         {
-// FUNC-NEXT:           "kind": "text",
-// FUNC-NEXT:           "spelling": ")"
-// FUNC-NEXT:         }
-// FUNC-NEXT:       ],
-// FUNC-NEXT:       "name": "arg"
-// FUNC-NEXT:     }
-// FUNC-NEXT:   ],
-// FUNC-NEXT:   "returns": [
-// FUNC-NEXT:     {
-// FUNC-NEXT:       "kind": "typeIdentifier",
-// FUNC-NEXT:       "preciseIdentifier": "c:v",
-// FUNC-NEXT:       "spelling": "void"
-// FUNC-NEXT:     }
-// FUNC-NEXT:   ]
-// FUNC-NEXT: },
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GLOBAL
 int (^global)(int foo);
-// GLOBAL-LABEL: "!testLabel": "c:@global"
-// GLOBAL:      "declarationFragments": [
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "typeIdentifier",
-// GLOBAL-NEXT:     "preciseIdentifier": "c:I",
-// GLOBAL-NEXT:     "spelling": "int"
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "text",
-// GLOBAL-NEXT:     "spelling": " (^"
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "identifier",
-// GLOBAL-NEXT:     "spelling": "global"
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "text",
-// GLOBAL-NEXT:     "spelling": ")("
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "typeIdentifier",
-// GLOBAL-NEXT:     "preciseIdentifier": "c:I",
-// GLOBAL-NEXT:     "spelling": "int"
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "text",
-// GLOBAL-NEXT:     "spelling": " "
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "internalParam",
-// GLOBAL-NEXT:     "spelling": "foo"
-// GLOBAL-NEXT:   },
-// GLOBAL-NEXT:   {
-// GLOBAL-NEXT:     "kind": "text",
-// GLOBAL-NEXT:     "spelling": ");"
-// GLOBAL-NEXT:   }
-// GLOBAL-NEXT: ],
 
 ///expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Foo(im)methodBlockNoParam:",
+      "target": "c:objc(cs)Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Foo(im)methodBlockWithParam:",
+      "target": "c:objc(cs)Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Foo(im)methodBlockWithMultipleParam:",
+      "target": "c:objc(cs)Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Foo(im)methodBlockVariadic:",
+      "target": "c:objc(cs)Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " (^"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "global"
+        },
+        {
+          "kind": "text",
+          "spelling": ")("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ");"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:@global"
+      },
+      "kind": {
+        "displayName": "Global Variable",
+        "identifier": "objective-c.var"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "global"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "global"
+          }
+        ],
+        "title": "global"
+      },
+      "pathComponents": [
+        "global"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "func"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " (^"
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "arg"
+        },
+        {
+          "kind": "text",
+          "spelling": ")("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "foo"
+        },
+        {
+          "kind": "text",
+          "spelling": "));"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " (^"
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "arg"
+              },
+              {
+                "kind": "text",
+                "spelling": ")("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "foo"
+              },
+              {
+                "kind": "text",
+                "spelling": ")"
+              }
+            ],
+            "name": "arg"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:@F at func"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "objective-c.func"
+      },
+      "location": {
+        "position": {
+          "character": 5,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "func"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "func"
+          }
+        ],
+        "title": "func"
+      },
+      "pathComponents": [
+        "func"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "methodBlockNoParam:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " (^"
+        },
+        {
+          "kind": "text",
+          "spelling": ")()) "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "block"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:v",
+                "spelling": "void"
+              },
+              {
+                "kind": "text",
+                "spelling": " (^"
+              },
+              {
+                "kind": "text",
+                "spelling": ")()) "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "block"
+              }
+            ],
+            "name": "block"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Foo(im)methodBlockNoParam:"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockNoParam:"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockNoParam:"
+          }
+        ],
+        "title": "methodBlockNoParam:"
+      },
+      "pathComponents": [
+        "Foo",
+        "methodBlockNoParam:"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "methodBlockWithParam:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " (^"
+        },
+        {
+          "kind": "text",
+          "spelling": ")("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ")) "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "block"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " (^"
+              },
+              {
+                "kind": "text",
+                "spelling": ")("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "foo"
+              },
+              {
+                "kind": "text",
+                "spelling": ")) "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "block"
+              }
+            ],
+            "name": "block"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Foo(im)methodBlockWithParam:"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockWithParam:"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockWithParam:"
+          }
+        ],
+        "title": "methodBlockWithParam:"
+      },
+      "pathComponents": [
+        "Foo",
+        "methodBlockWithParam:"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "methodBlockWithMultipleParam:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " (^"
+        },
+        {
+          "kind": "text",
+          "spelling": ")("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "baz"
+        },
+        {
+          "kind": "text",
+          "spelling": ")) "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "block"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " (^"
+              },
+              {
+                "kind": "text",
+                "spelling": ")("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "foo"
+              },
+              {
+                "kind": "text",
+                "spelling": ", "
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:i",
+                "spelling": "unsigned int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "baz"
+              },
+              {
+                "kind": "text",
+                "spelling": ")) "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "block"
+              }
+            ],
+            "name": "block"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Foo(im)methodBlockWithMultipleParam:"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockWithMultipleParam:"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockWithMultipleParam:"
+          }
+        ],
+        "title": "methodBlockWithMultipleParam:"
+      },
+      "pathComponents": [
+        "Foo",
+        "methodBlockWithMultipleParam:"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "methodBlockVariadic:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " (^"
+        },
+        {
+          "kind": "text",
+          "spelling": ")("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ", ...)) "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "block"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " (^"
+              },
+              {
+                "kind": "text",
+                "spelling": ")("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "foo"
+              },
+              {
+                "kind": "text",
+                "spelling": ", ...)) "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "block"
+              }
+            ],
+            "name": "block"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Foo(im)methodBlockVariadic:"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockVariadic:"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "methodBlockVariadic:"
+          }
+        ],
+        "title": "methodBlockVariadic:"
+      },
+      "pathComponents": [
+        "Foo",
+        "methodBlockVariadic:"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/objc_category.m b/clang/test/ExtractAPI/objc_category.m
index 9177d40b82644a..34b0a9e31f553c 100644
--- a/clang/test/ExtractAPI/objc_category.m
+++ b/clang/test/ExtractAPI/objc_category.m
@@ -1,21 +1,341 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -triple arm64-apple-macosx -x objective-c-header %s -o - -verify | FileCheck %s
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
- at protocol Protocol
- at end
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+ at protocol Protocol;
 
 @interface Interface
 @end
 
 @interface Interface (Category) <Protocol>
-// CHECK-DAG: "!testRelLabel": "conformsTo $ c:objc(cs)Interface $ c:objc(pl)Protocol"
 @property int Property;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(cs)Interface(py)Property $ c:objc(cs)Interface"
 - (void)InstanceMethod;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(cs)Interface(im)InstanceMethod $ c:objc(cs)Interface"
 + (void)ClassMethod;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(cs)Interface(cm)ClassMethod $ c:objc(cs)Interface"
 @end
 
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(im)InstanceMethod",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(cm)ClassMethod",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(py)Property",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "conformsTo",
+      "source": "c:objc(cs)Interface",
+      "target": "c:objc(pl)Protocol",
+      "targetFallback": "Protocol"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Interface"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Interface"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Interface"
+          }
+        ],
+        "title": "Interface"
+      },
+      "pathComponents": [
+        "Interface"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "InstanceMethod"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(im)InstanceMethod"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "InstanceMethod"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "InstanceMethod"
+          }
+        ],
+        "title": "InstanceMethod"
+      },
+      "pathComponents": [
+        "Interface",
+        "InstanceMethod"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "+ ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "ClassMethod"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(cm)ClassMethod"
+      },
+      "kind": {
+        "displayName": "Type Method",
+        "identifier": "objective-c.type.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 8
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "ClassMethod"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "+ "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "ClassMethod"
+          }
+        ],
+        "title": "ClassMethod"
+      },
+      "pathComponents": [
+        "Interface",
+        "ClassMethod"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Property"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(py)Property"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 14,
+          "line": 6
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Property"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Property"
+          }
+        ],
+        "title": "Property"
+      },
+      "pathComponents": [
+        "Interface",
+        "Property"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/objc_external_category.m b/clang/test/ExtractAPI/objc_external_category.m
deleted file mode 100644
index 47e699cb91c0e4..00000000000000
--- a/clang/test/ExtractAPI/objc_external_category.m
+++ /dev/null
@@ -1,49 +0,0 @@
-// RUN: rm -rf %t
-// RUN: split-file %s %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   --emit-extension-symbol-graphs --symbol-graph-dir=%t/symbols \
-// RUN:   --product-name=Module -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules-cache \
-// RUN:   -triple arm64-apple-macosx -x objective-c-header %t/input.h -verify
-
-//--- input.h
-#include "ExternalModule.h"
-
- at interface ExtInterface (Category)
- at property int Property;
-- (void)InstanceMethod;
-+ (void)ClassMethod;
- at end
-
- at interface ModInterface
- at end
-
-// expected-no-diagnostics
-
-//--- ExternalModule.h
- at interface ExtInterface
- at end
-
-//--- module.modulemap
-module ExternalModule {
-    header "ExternalModule.h"
-}
-
-// RUN: FileCheck %s --input-file  %t/symbols/Module.symbols.json --check-prefix MOD
-// MOD-NOT: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(py)Property $ c:objc(cs)ExtInterface"
-// MOD-NOT: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(im)InstanceMethod $ c:objc(cs)ExtInterface"
-// MOD-NOT: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(cm)ClassMethod $ c:objc(cs)ExtInterface"
-// MOD-NOT: "!testLabel": "c:objc(cs)ExtInterface(py)Property"
-// MOD-NOT: "!testLabel": "c:objc(cs)ExtInterface(im)InstanceMethod"
-// MOD-NOT: "!testLabel": "c:objc(cs)ExtInterface(cm)ClassMethod"
-// MOD-NOT: "!testLabel": "c:objc(cs)ExtInterface"
-// MOD-DAG: "!testLabel": "c:objc(cs)ModInterface"
-
-// RUN: FileCheck %s --input-file %t/symbols/ExternalModule at Module.symbols.json --check-prefix EXT
-// EXT-DAG: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(py)Property $ c:objc(cs)ExtInterface"
-// EXT-DAG: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(im)InstanceMethod $ c:objc(cs)ExtInterface"
-// EXT-DAG: "!testRelLabel": "memberOf $ c:objc(cs)ExtInterface(cm)ClassMethod $ c:objc(cs)ExtInterface"
-// EXT-DAG: "!testLabel": "c:objc(cs)ExtInterface(py)Property"
-// EXT-DAG: "!testLabel": "c:objc(cs)ExtInterface(im)InstanceMethod"
-// EXT-DAG: "!testLabel": "c:objc(cs)ExtInterface(cm)ClassMethod"
-// EXT-NOT: "!testLabel": "c:objc(cs)ExtInterface"
-// EXT-NOT: "!testLabel": "c:objc(cs)ModInterface"

diff  --git a/clang/test/ExtractAPI/objc_id_protocol.m b/clang/test/ExtractAPI/objc_id_protocol.m
index f2a03a9c575857..0b0f1b39d2bd61 100644
--- a/clang/test/ExtractAPI/objc_id_protocol.m
+++ b/clang/test/ExtractAPI/objc_id_protocol.m
@@ -1,56 +1,317 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -x objective-c-header -triple arm64-apple-macosx %s -o - -verify | FileCheck %s
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
 @protocol MyProtocol
 @end
 
 @interface MyInterface
 @property(copy, readwrite) id<MyProtocol> obj1;
-// CHECK-LABEL: "!testLabel": "c:objc(cs)MyInterface(py)obj1"
-// CHECK:      "declarationFragments": [
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "keyword",
-// CHECK-NEXT:     "spelling": "@property"
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "text",
-// CHECK-NEXT:     "spelling": " ("
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "keyword",
-// CHECK-NEXT:     "spelling": "copy"
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "text",
-// CHECK-NEXT:     "spelling": ", "
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "keyword",
-// CHECK-NEXT:     "spelling": "readwrite"
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "text",
-// CHECK-NEXT:     "spelling": ") "
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "typeIdentifier",
-// CHECK-NEXT:     "preciseIdentifier": "c:Qoobjc(pl)MyProtocol",
-// CHECK-NEXT:     "spelling": "id<MyProtocol>"
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "text",
-// CHECK-NEXT:     "spelling": " "
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "identifier",
-// CHECK-NEXT:     "spelling": "obj1"
-// CHECK-NEXT:   },
-// CHECK-NEXT:   {
-// CHECK-NEXT:     "kind": "text",
-// CHECK-NEXT:     "spelling": ";"
-// CHECK-NEXT:   }
-// CHECK-NEXT: ],
+ at property(readwrite) id<MyProtocol> *obj2;
 @end
-
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)MyInterface(py)obj1",
+      "target": "c:objc(cs)MyInterface",
+      "targetFallback": "MyInterface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)MyInterface(py)obj2",
+      "target": "c:objc(cs)MyInterface",
+      "targetFallback": "MyInterface"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyInterface"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)MyInterface"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyInterface"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyInterface"
+          }
+        ],
+        "title": "MyInterface"
+      },
+      "pathComponents": [
+        "MyInterface"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "copy"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "readwrite"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:Qoobjc(pl)MyProtocol",
+          "spelling": "id<MyProtocol>"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "obj1"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)MyInterface(py)obj1"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 42,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "obj1"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "obj1"
+          }
+        ],
+        "title": "obj1"
+      },
+      "pathComponents": [
+        "MyInterface",
+        "obj1"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "readwrite"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:Qoobjc(pl)MyProtocol",
+          "spelling": "id<MyProtocol>"
+        },
+        {
+          "kind": "text",
+          "spelling": " * "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "obj2"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)MyInterface(py)obj2"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 37,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "obj2"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "obj2"
+          }
+        ],
+        "title": "obj2"
+      },
+      "pathComponents": [
+        "MyInterface",
+        "obj2"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@protocol"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyProtocol"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(pl)MyProtocol"
+      },
+      "kind": {
+        "displayName": "Protocol",
+        "identifier": "objective-c.protocol"
+      },
+      "location": {
+        "position": {
+          "character": 10,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyProtocol"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyProtocol"
+          }
+        ],
+        "title": "MyProtocol"
+      },
+      "pathComponents": [
+        "MyProtocol"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/objc_instancetype.m b/clang/test/ExtractAPI/objc_instancetype.m
index 071ebe440918aa..d9d259f2d56028 100644
--- a/clang/test/ExtractAPI/objc_instancetype.m
+++ b/clang/test/ExtractAPI/objc_instancetype.m
@@ -1,8 +1,8 @@
 // RUN: rm -rf %t
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
-// RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx -x objective-c-header %t/input.h -o %t/output.json -verify
+             // RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx -x objective-c-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.
 // RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \

diff  --git a/clang/test/ExtractAPI/objc_interface.m b/clang/test/ExtractAPI/objc_interface.m
index 4abccddc3b5c8e..ab1772a0c529bb 100644
--- a/clang/test/ExtractAPI/objc_interface.m
+++ b/clang/test/ExtractAPI/objc_interface.m
@@ -1,360 +1,701 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -x objective-c-header -triple arm64-apple-macosx %s -o %t/output.symbols.json -verify
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
- at protocol Protocol
- at end
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix SUPER
- at interface Super <Protocol>
-// SUPER: "!testRelLabel": "conformsTo $ c:objc(cs)Super $ c:objc(pl)Protocol"
-// SUPER-LABEL: "!testLabel": "c:objc(cs)Super"
-// SUPER: "accessLevel": "public",
-// SUPER:      "declarationFragments": [
-// SUPER-NEXT:   {
-// SUPER-NEXT:     "kind": "keyword",
-// SUPER-NEXT:     "spelling": "@interface"
-// SUPER-NEXT:   },
-// SUPER-NEXT:   {
-// SUPER-NEXT:     "kind": "text",
-// SUPER-NEXT:     "spelling": " "
-// SUPER-NEXT:   },
-// SUPER-NEXT:   {
-// SUPER-NEXT:     "kind": "identifier",
-// SUPER-NEXT:     "spelling": "Super"
-// SUPER-NEXT:   }
-// SUPER-NEXT: ],
-// SUPER:      "kind": {
-// SUPER-NEXT:   "displayName": "Class",
-// SUPER-NEXT:   "identifier": "objective-c.class"
-// SUPER-NEXT: },
-// SUPER:   "title": "Super"
-// SUPER:      "pathComponents": [
-// SUPER-NEXT:   "Super"
-// SUPER-NEXT: ]
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix PROP
- at property(readonly, getter=getProperty) unsigned Property;
-// PROP: "!testRelLabel": "memberOf $ c:objc(cs)Super(py)Property $ c:objc(cs)Super"
-// PROP: "!testLabel": "c:objc(cs)Super(py)Property"
-// PROP: "accessLevel": "public",
-// PROP:      "declarationFragments": [
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "keyword",
-// PROP-NEXT:     "spelling": "@property"
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "text",
-// PROP-NEXT:     "spelling": " ("
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "keyword",
-// PROP-NEXT:     "spelling": "readonly"
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "text",
-// PROP-NEXT:     "spelling": ", "
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "keyword",
-// PROP-NEXT:     "spelling": "getter"
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "text",
-// PROP-NEXT:     "spelling": "="
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "identifier",
-// PROP-NEXT:     "spelling": "getProperty"
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "text",
-// PROP-NEXT:     "spelling": ") "
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "typeIdentifier",
-// PROP-NEXT:     "preciseIdentifier": "c:i",
-// PROP-NEXT:     "spelling": "unsigned int"
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "text",
-// PROP-NEXT:     "spelling": " "
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "identifier",
-// PROP-NEXT:     "spelling": "Property"
-// PROP-NEXT:   },
-// PROP-NEXT:   {
-// PROP-NEXT:     "kind": "text",
-// PROP-NEXT:     "spelling": ";"
-// PROP-NEXT:   }
-// PROP-NEXT: ],
-// PROP:      "kind": {
-// PROP-NEXT:   "displayName": "Instance Property",
-// PROP-NEXT:   "identifier": "objective-c.property"
-// PROP-NEXT: },
-// PROP:   "title": "Property"
-// PROP:      "pathComponents": [
-// PROP-NEXT:   "Super",
-// PROP-NEXT:   "Property"
-// PROP-NEXT: ]
+//--- input.h
+ at protocol Protocol;
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GET
+ at interface Super <Protocol>
+ at property(readonly, getter=getProperty) unsigned Property;
 + (id)getWithProperty:(unsigned) Property;
-// GET: "!testRelLabel": "memberOf $ c:objc(cs)Super(cm)getWithProperty: $ c:objc(cs)Super"
-// GET-LABEL: "!testLabel": "c:objc(cs)Super(cm)getWithProperty:"
-// GET: "accessLevel": "public",
-// GET:      "declarationFragments": [
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "text",
-// GET-NEXT:     "spelling": "+ ("
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "keyword",
-// GET-NEXT:     "spelling": "id"
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "text",
-// GET-NEXT:     "spelling": ") "
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "identifier",
-// GET-NEXT:     "spelling": "getWithProperty:"
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "text",
-// GET-NEXT:     "spelling": "("
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "typeIdentifier",
-// GET-NEXT:     "preciseIdentifier": "c:i",
-// GET-NEXT:     "spelling": "unsigned int"
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "text",
-// GET-NEXT:     "spelling": ") "
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "internalParam",
-// GET-NEXT:     "spelling": "Property"
-// GET-NEXT:   },
-// GET-NEXT:   {
-// GET-NEXT:     "kind": "text",
-// GET-NEXT:     "spelling": ";"
-// GET-NEXT:   }
-// GET-NEXT: ],
-// GET:      "functionSignature": {
-// GET-NEXT:   "parameters": [
-// GET-NEXT:     {
-// GET-NEXT:       "declarationFragments": [
-// GET-NEXT:         {
-// GET-NEXT:           "kind": "text",
-// GET-NEXT:           "spelling": "("
-// GET-NEXT:         },
-// GET-NEXT:         {
-// GET-NEXT:           "kind": "typeIdentifier",
-// GET-NEXT:           "preciseIdentifier": "c:i",
-// GET-NEXT:           "spelling": "unsigned int"
-// GET-NEXT:         },
-// GET-NEXT:         {
-// GET-NEXT:           "kind": "text",
-// GET-NEXT:           "spelling": ") "
-// GET-NEXT:         },
-// GET-NEXT:         {
-// GET-NEXT:           "kind": "internalParam",
-// GET-NEXT:           "spelling": "Property"
-// GET-NEXT:         }
-// GET-NEXT:       ],
-// GET-NEXT:       "name": "Property"
-// GET-NEXT:     }
-// GET-NEXT:   ],
-// GET-NEXT:   "returns": [
-// GET-NEXT:     {
-// GET-NEXT:       "kind": "keyword",
-// GET-NEXT:       "spelling": "id"
-// GET-NEXT:     }
-// GET-NEXT:   ]
-// GET-NEXT: },
-// GET:      "kind": {
-// GET-NEXT:   "displayName": "Type Method",
-// GET-NEXT:   "identifier": "objective-c.type.method"
-// GET-NEXT: },
-// GET:   "title": "getWithProperty:"
-// GET:      "pathComponents": [
-// GET-NEXT:   "Super",
-// GET-NEXT:   "getWithProperty:"
-// GET-NEXT: ]
-
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix SET
 - (void)setProperty:(unsigned) Property andOtherThing: (unsigned) Thing;
-// SET: "!testRelLabel": "memberOf $ c:objc(cs)Super(im)setProperty:andOtherThing: $ c:objc(cs)Super"
-// SET-LABEL: "!testLabel": "c:objc(cs)Super(im)setProperty:andOtherThing:"
-// SET: "accessLevel": "public",
-// SET:      "declarationFragments": [
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": "- ("
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "typeIdentifier",
-// SET-NEXT:     "preciseIdentifier": "c:v",
-// SET-NEXT:     "spelling": "void"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": ") "
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "identifier",
-// SET-NEXT:     "spelling": "setProperty:"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": "("
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "typeIdentifier",
-// SET-NEXT:     "preciseIdentifier": "c:i",
-// SET-NEXT:     "spelling": "unsigned int"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": ") "
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "internalParam",
-// SET-NEXT:     "spelling": "Property"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": " "
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "identifier",
-// SET-NEXT:     "spelling": "andOtherThing:"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": "("
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "typeIdentifier",
-// SET-NEXT:     "preciseIdentifier": "c:i",
-// SET-NEXT:     "spelling": "unsigned int"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": ") "
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "internalParam",
-// SET-NEXT:     "spelling": "Thing"
-// SET-NEXT:   },
-// SET-NEXT:   {
-// SET-NEXT:     "kind": "text",
-// SET-NEXT:     "spelling": ";"
-// SET-NEXT:   }
-// SET-NEXT: ],
-// SET:      "functionSignature": {
-// SET-NEXT:   "parameters": [
-// SET-NEXT:     {
-// SET-NEXT:       "declarationFragments": [
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "text",
-// SET-NEXT:           "spelling": "("
-// SET-NEXT:         },
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "typeIdentifier",
-// SET-NEXT:           "preciseIdentifier": "c:i",
-// SET-NEXT:           "spelling": "unsigned int"
-// SET-NEXT:         },
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "text",
-// SET-NEXT:           "spelling": ") "
-// SET-NEXT:         },
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "internalParam",
-// SET-NEXT:           "spelling": "Property"
-// SET-NEXT:         }
-// SET-NEXT:       ],
-// SET-NEXT:       "name": "Property"
-// SET-NEXT:     },
-// SET-NEXT:     {
-// SET-NEXT:       "declarationFragments": [
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "text",
-// SET-NEXT:           "spelling": "("
-// SET-NEXT:         },
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "typeIdentifier",
-// SET-NEXT:           "preciseIdentifier": "c:i",
-// SET-NEXT:           "spelling": "unsigned int"
-// SET-NEXT:         },
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "text",
-// SET-NEXT:           "spelling": ") "
-// SET-NEXT:         },
-// SET-NEXT:         {
-// SET-NEXT:           "kind": "internalParam",
-// SET-NEXT:           "spelling": "Thing"
-// SET-NEXT:         }
-// SET-NEXT:       ],
-// SET-NEXT:       "name": "Thing"
-// SET-NEXT:     }
-// SET-NEXT:   ],
-// SET-NEXT:   "returns": [
-// SET-NEXT:     {
-// SET-NEXT:       "kind": "typeIdentifier",
-// SET-NEXT:       "preciseIdentifier": "c:v",
-// SET-NEXT:       "spelling": "void"
-// SET-NEXT:     }
-// SET-NEXT:   ]
-// SET-NEXT: },
-// SET:      "kind": {
-// SET-NEXT:   "displayName": "Instance Method",
-// SET-NEXT:   "identifier": "objective-c.method"
-// SET-NEXT: },
-// SET:   "title": "setProperty:andOtherThing:"
 @end
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix DERIVED
 @interface Derived : Super {
-// DERIVED: "!testRelLabel": "inheritsFrom $ c:objc(cs)Derived $ c:objc(cs)Super"
-
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix IVAR
   char Ivar;
-// IVAR: "!testRelLabel": "memberOf $ c:objc(cs)Derived at Ivar $ c:objc(cs)Derived"
-// IVAR-LABEL: "!testLabel": "c:objc(cs)Derived at Ivar"
-// IVAR: "accessLevel": "public",
-// IVAR:      "declarationFragments": [
-// IVAR-NEXT:   {
-// IVAR-NEXT:     "kind": "typeIdentifier",
-// IVAR-NEXT:     "preciseIdentifier": "c:C",
-// IVAR-NEXT:     "spelling": "char"
-// IVAR-NEXT:   },
-// IVAR-NEXT:   {
-// IVAR-NEXT:     "kind": "text",
-// IVAR-NEXT:     "spelling": " "
-// IVAR-NEXT:   },
-// IVAR-NEXT:   {
-// IVAR-NEXT:     "kind": "identifier",
-// IVAR-NEXT:     "spelling": "Ivar"
-// IVAR-NEXT:   },
-// IVAR-NEXT:   {
-// IVAR-NEXT:     "kind": "text",
-// IVAR-NEXT:     "spelling": ";"
-// IVAR-NEXT:   }
-// IVAR-NEXT: ],
-// IVAR:      "kind": {
-// IVAR-NEXT:   "displayName": "Instance Variable",
-// IVAR-NEXT:   "identifier": "objective-c.ivar"
-// IVAR-NEXT: },
-// IVAR: "title": "Ivar"
-// IVAR:      "pathComponents": [
-// IVAR-NEXT:   "Derived",
-// IVAR-NEXT:   "Ivar"
-// IVAR-NEXT: ]
 }
+- (char)getIvar;
 @end
 
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Super(cm)getWithProperty:",
+      "target": "c:objc(cs)Super",
+      "targetFallback": "Super"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Super(im)setProperty:andOtherThing:",
+      "target": "c:objc(cs)Super",
+      "targetFallback": "Super"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Super(py)Property",
+      "target": "c:objc(cs)Super",
+      "targetFallback": "Super"
+    },
+    {
+      "kind": "conformsTo",
+      "source": "c:objc(cs)Super",
+      "target": "c:objc(pl)Protocol",
+      "targetFallback": "Protocol"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Derived at Ivar",
+      "target": "c:objc(cs)Derived",
+      "targetFallback": "Derived"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Derived(im)getIvar",
+      "target": "c:objc(cs)Derived",
+      "targetFallback": "Derived"
+    },
+    {
+      "kind": "inheritsFrom",
+      "source": "c:objc(cs)Derived",
+      "target": "c:objc(cs)Super",
+      "targetFallback": "Super"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Super"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Super"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Super"
+          }
+        ],
+        "title": "Super"
+      },
+      "pathComponents": [
+        "Super"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "+ ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "id"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getWithProperty:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "Property"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:i",
+                "spelling": "unsigned int"
+              },
+              {
+                "kind": "text",
+                "spelling": ") "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "Property"
+              }
+            ],
+            "name": "Property"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "keyword",
+            "spelling": "id"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super(cm)getWithProperty:"
+      },
+      "kind": {
+        "displayName": "Type Method",
+        "identifier": "objective-c.type.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getWithProperty:"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "+ "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "getWithProperty:"
+          }
+        ],
+        "title": "getWithProperty:"
+      },
+      "pathComponents": [
+        "Super",
+        "getWithProperty:"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "setProperty:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "Property"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "andOtherThing:"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "Thing"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:i",
+                "spelling": "unsigned int"
+              },
+              {
+                "kind": "text",
+                "spelling": ") "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "Property"
+              }
+            ],
+            "name": "Property"
+          },
+          {
+            "declarationFragments": [
+              {
+                "kind": "text",
+                "spelling": "("
+              },
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:i",
+                "spelling": "unsigned int"
+              },
+              {
+                "kind": "text",
+                "spelling": ") "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "Thing"
+              }
+            ],
+            "name": "Thing"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super(im)setProperty:andOtherThing:"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "setProperty:andOtherThing:"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "setProperty:andOtherThing:"
+          }
+        ],
+        "title": "setProperty:andOtherThing:"
+      },
+      "pathComponents": [
+        "Super",
+        "setProperty:andOtherThing:"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "readonly"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "getter"
+        },
+        {
+          "kind": "text",
+          "spelling": "="
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getProperty"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Property"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super(py)Property"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 49,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Property"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Property"
+          }
+        ],
+        "title": "Property"
+      },
+      "pathComponents": [
+        "Super",
+        "Property"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Derived"
+        },
+        {
+          "kind": "text",
+          "spelling": " : "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(cs)Super",
+          "spelling": "Super"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Derived"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 8
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Derived"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Derived"
+          }
+        ],
+        "title": "Derived"
+      },
+      "pathComponents": [
+        "Derived"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:C",
+          "spelling": "char"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Ivar"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Derived at Ivar"
+      },
+      "kind": {
+        "displayName": "Instance Variable",
+        "identifier": "objective-c.ivar"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Ivar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Ivar"
+          }
+        ],
+        "title": "Ivar"
+      },
+      "pathComponents": [
+        "Derived",
+        "Ivar"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:C",
+          "spelling": "char"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getIvar"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:C",
+            "spelling": "char"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Derived(im)getIvar"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 11
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getIvar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "getIvar"
+          }
+        ],
+        "title": "getIvar"
+      },
+      "pathComponents": [
+        "Derived",
+        "getIvar"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/objc_module_category.m b/clang/test/ExtractAPI/objc_module_category.m
new file mode 100644
index 00000000000000..708ed10be821d7
--- /dev/null
+++ b/clang/test/ExtractAPI/objc_module_category.m
@@ -0,0 +1,404 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header \
+// RUN: -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+#import "Foundation.h"
+
+/// Doc comment 1
+ at interface NSString (Category1)
+-(void)method1;
+ at end
+
+/// Doc comment 2
+ at interface NSString (Category2)
+-(void)method2;
+ at end
+
+//--- Foundation.h
+ at interface NSString
+ at end
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "extensionTo",
+      "source": "c:objc(cy)NSString at Category1",
+      "target": "c:objc(cs)NSString",
+      "targetFallback": "NSString"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)NSString(im)method1",
+      "target": "c:objc(cy)NSString at Category1",
+      "targetFallback": "Category1"
+    },
+    {
+      "kind": "extensionTo",
+      "source": "c:objc(cy)NSString at Category2",
+      "target": "c:objc(cs)NSString",
+      "targetFallback": "NSString"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)NSString(im)method2",
+      "target": "c:objc(cy)NSString at Category2",
+      "targetFallback": "Category2"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cy)NSString at Category1"
+      },
+      "kind": {
+        "displayName": "Module Extension",
+        "identifier": "objective-c.module.extension"
+      }
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(cs)NSString",
+          "spelling": "NSString"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Category1"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        }
+      ],
+      "docComment": {
+        "lines": [
+          {
+            "range": {
+              "end": {
+                "character": 17,
+                "line": 2
+              },
+              "start": {
+                "character": 4,
+                "line": 2
+              }
+            },
+            "text": "Doc comment 1"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cy)NSString at Category1"
+      },
+      "kind": {
+        "displayName": "Class Extension",
+        "identifier": "objective-c.class.extension"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Category1"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Category1"
+          }
+        ],
+        "title": "NSString (Category1)"
+      },
+      "pathComponents": [
+        "Category1"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "method1"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)NSString(im)method1"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "method1"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "method1"
+          }
+        ],
+        "title": "method1"
+      },
+      "pathComponents": [
+        "Category1",
+        "method1"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(cs)NSString",
+          "spelling": "NSString"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Category2"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        }
+      ],
+      "docComment": {
+        "lines": [
+          {
+            "range": {
+              "end": {
+                "character": 17,
+                "line": 7
+              },
+              "start": {
+                "character": 4,
+                "line": 7
+              }
+            },
+            "text": "Doc comment 2"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cy)NSString at Category2"
+      },
+      "kind": {
+        "displayName": "Class Extension",
+        "identifier": "objective-c.class.extension"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 8
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Category2"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Category2"
+          }
+        ],
+        "title": "NSString (Category2)"
+      },
+      "pathComponents": [
+        "Category2"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "method2"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)NSString(im)method2"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "method2"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "method2"
+          }
+        ],
+        "title": "method2"
+      },
+      "pathComponents": [
+        "Category2",
+        "method2"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/objc_property.m b/clang/test/ExtractAPI/objc_property.m
index f05584c885d91f..5712abc15393b6 100644
--- a/clang/test/ExtractAPI/objc_property.m
+++ b/clang/test/ExtractAPI/objc_property.m
@@ -1,26 +1,608 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -triple arm64-apple-macosx -x objective-c-header %s -o - -verify | FileCheck %s
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx -x objective-c-header %t/input.h -o %t/output.json -verify
 
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
 @protocol Protocol
 @property(class) int myProtocolTypeProp;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(pl)Protocol(cpy)myProtocolTypeProp $ c:objc(pl)Protocol"
 @property int myProtocolInstanceProp;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(pl)Protocol(py)myProtocolInstanceProp $ c:objc(pl)Protocol"
 @end
 
 @interface Interface
 @property(class) int myInterfaceTypeProp;
-// CHECk-DAG: "!testRelLabel": "memberOf $ c:objc(cs)Interface(cpy)myInterfaceTypeProp $ c:objc(cs)Interface"
 @property int myInterfaceInstanceProp;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(cs)Interface(py)myInterfaceInstanceProp $ c:objc(cs)Interface"
 @end
 
 @interface Interface (Category) <Protocol>
 @property(class) int myCategoryTypeProp;
-// CHECK-DAG: "!testRelLabel": "memberOf $ c:objc(cs)Interface(cpy)myCategoryTypeProp $ c:objc(cs)Interface"
 @property int myCategoryInstanceProp;
-// CHECK-DAG "!testRelLabel": "memberOf $ c:objc(cs)Interface(py)myCategoryInstanceProp $ c:objc(cs)Interface"
 @end
-
 // expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(cpy)myInterfaceTypeProp",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(py)myInterfaceInstanceProp",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(cpy)myCategoryTypeProp",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(py)myCategoryInstanceProp",
+      "target": "c:objc(cs)Interface",
+      "targetFallback": "Interface"
+    },
+    {
+      "kind": "conformsTo",
+      "source": "c:objc(cs)Interface",
+      "target": "c:objc(pl)Protocol",
+      "targetFallback": "Protocol"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(pl)Protocol(cpy)myProtocolTypeProp",
+      "target": "c:objc(pl)Protocol",
+      "targetFallback": "Protocol"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(pl)Protocol(py)myProtocolInstanceProp",
+      "target": "c:objc(pl)Protocol",
+      "targetFallback": "Protocol"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Interface"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Interface"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Interface"
+          }
+        ],
+        "title": "Interface"
+      },
+      "pathComponents": [
+        "Interface"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "myInterfaceTypeProp"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(cpy)myInterfaceTypeProp"
+      },
+      "kind": {
+        "displayName": "Type Property",
+        "identifier": "objective-c.type.property"
+      },
+      "location": {
+        "position": {
+          "character": 21,
+          "line": 6
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "myInterfaceTypeProp"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "myInterfaceTypeProp"
+          }
+        ],
+        "title": "myInterfaceTypeProp"
+      },
+      "pathComponents": [
+        "Interface",
+        "myInterfaceTypeProp"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "myInterfaceInstanceProp"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(py)myInterfaceInstanceProp"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 14,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "myInterfaceInstanceProp"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "myInterfaceInstanceProp"
+          }
+        ],
+        "title": "myInterfaceInstanceProp"
+      },
+      "pathComponents": [
+        "Interface",
+        "myInterfaceInstanceProp"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "myCategoryTypeProp"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(cpy)myCategoryTypeProp"
+      },
+      "kind": {
+        "displayName": "Type Property",
+        "identifier": "objective-c.type.property"
+      },
+      "location": {
+        "position": {
+          "character": 21,
+          "line": 11
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "myCategoryTypeProp"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "myCategoryTypeProp"
+          }
+        ],
+        "title": "myCategoryTypeProp"
+      },
+      "pathComponents": [
+        "Interface",
+        "myCategoryTypeProp"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "myCategoryInstanceProp"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(py)myCategoryInstanceProp"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 14,
+          "line": 12
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "myCategoryInstanceProp"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "myCategoryInstanceProp"
+          }
+        ],
+        "title": "myCategoryInstanceProp"
+      },
+      "pathComponents": [
+        "Interface",
+        "myCategoryInstanceProp"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@protocol"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Protocol"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(pl)Protocol"
+      },
+      "kind": {
+        "displayName": "Protocol",
+        "identifier": "objective-c.protocol"
+      },
+      "location": {
+        "position": {
+          "character": 10,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Protocol"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Protocol"
+          }
+        ],
+        "title": "Protocol"
+      },
+      "pathComponents": [
+        "Protocol"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "myProtocolTypeProp"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(pl)Protocol(cpy)myProtocolTypeProp"
+      },
+      "kind": {
+        "displayName": "Type Property",
+        "identifier": "objective-c.type.property"
+      },
+      "location": {
+        "position": {
+          "character": 21,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "myProtocolTypeProp"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "myProtocolTypeProp"
+          }
+        ],
+        "title": "myProtocolTypeProp"
+      },
+      "pathComponents": [
+        "Protocol",
+        "myProtocolTypeProp"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "myProtocolInstanceProp"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(pl)Protocol(py)myProtocolInstanceProp"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 14,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "myProtocolInstanceProp"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "myProtocolInstanceProp"
+          }
+        ],
+        "title": "myProtocolInstanceProp"
+      },
+      "pathComponents": [
+        "Protocol",
+        "myProtocolInstanceProp"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/objc_protocol.m b/clang/test/ExtractAPI/objc_protocol.m
index 06f7ee3d20363e..a04936fe041234 100644
--- a/clang/test/ExtractAPI/objc_protocol.m
+++ b/clang/test/ExtractAPI/objc_protocol.m
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf -x objective-c-header -target arm64-apple-macosx \
+// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
 // RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/objc_various_categories.m b/clang/test/ExtractAPI/objc_various_categories.m
new file mode 100644
index 00000000000000..adaef5a7b31a99
--- /dev/null
+++ b/clang/test/ExtractAPI/objc_various_categories.m
@@ -0,0 +1,507 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header \
+// RUN: -target arm64-apple-macosx \
+// RUN: %t/myclass_1.h \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+#import "myclass_1.h"
+#import "Foundation.h"
+
+ at interface MyClass1 (MyCategory1)
+- (int) SomeMethod;
+ at end
+
+ at interface NSString (Category1)
+-(void) StringMethod;
+ at end
+
+ at interface NSString (Category2)
+-(void) StringMethod2;
+ at end
+
+//--- myclass_1.h
+ at interface MyClass1
+ at end
+
+//--- Foundation.h
+ at interface NSString
+ at end
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)MyClass1(im)SomeMethod",
+      "target": "c:objc(cs)MyClass1",
+      "targetFallback": "MyClass1"
+    },
+    {
+      "kind": "extensionTo",
+      "source": "c:objc(cy)NSString at Category1",
+      "target": "c:objc(cs)NSString",
+      "targetFallback": "NSString"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)NSString(im)StringMethod",
+      "target": "c:objc(cy)NSString at Category1",
+      "targetFallback": "Category1"
+    },
+    {
+      "kind": "extensionTo",
+      "source": "c:objc(cy)NSString at Category2",
+      "target": "c:objc(cs)NSString",
+      "targetFallback": "NSString"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)NSString(im)StringMethod2",
+      "target": "c:objc(cy)NSString at Category2",
+      "targetFallback": "Category2"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyClass1"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)MyClass1"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/myclass_1.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyClass1"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyClass1"
+          }
+        ],
+        "title": "MyClass1"
+      },
+      "pathComponents": [
+        "MyClass1"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "SomeMethod"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)MyClass1(im)SomeMethod"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "SomeMethod"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "SomeMethod"
+          }
+        ],
+        "title": "SomeMethod"
+      },
+      "pathComponents": [
+        "MyClass1",
+        "SomeMethod"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cy)NSString at Category1"
+      },
+      "kind": {
+        "displayName": "Module Extension",
+        "identifier": "objective-c.module.extension"
+      }
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(cs)NSString",
+          "spelling": "NSString"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Category1"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cy)NSString at Category1"
+      },
+      "kind": {
+        "displayName": "Class Extension",
+        "identifier": "objective-c.class.extension"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Category1"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Category1"
+          }
+        ],
+        "title": "NSString (Category1)"
+      },
+      "pathComponents": [
+        "Category1"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "StringMethod"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)NSString(im)StringMethod"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 8
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "StringMethod"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "StringMethod"
+          }
+        ],
+        "title": "StringMethod"
+      },
+      "pathComponents": [
+        "Category1",
+        "StringMethod"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(cs)NSString",
+          "spelling": "NSString"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Category2"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cy)NSString at Category2"
+      },
+      "kind": {
+        "displayName": "Class Extension",
+        "identifier": "objective-c.class.extension"
+      },
+      "location": {
+        "position": {
+          "character": 11,
+          "line": 11
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Category2"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Category2"
+          }
+        ],
+        "title": "NSString (Category2)"
+      },
+      "pathComponents": [
+        "Category2"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ") "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "StringMethod2"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)NSString(im)StringMethod2"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 0,
+          "line": 12
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "StringMethod2"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "StringMethod2"
+          }
+        ],
+        "title": "StringMethod2"
+      },
+      "pathComponents": [
+        "Category2",
+        "StringMethod2"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/operator_overload.cpp b/clang/test/ExtractAPI/operator_overload.cpp
index 9430c58a991e17..511a5a7ae8fdf0 100644
--- a/clang/test/ExtractAPI/operator_overload.cpp
+++ b/clang/test/ExtractAPI/operator_overload.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/relative_include.m b/clang/test/ExtractAPI/relative_include.m
index e5a02683cbd816..46cbdaeeb280c1 100644
--- a/clang/test/ExtractAPI/relative_include.m
+++ b/clang/test/ExtractAPI/relative_include.m
@@ -15,7 +15,7 @@
 // RUN: %hmaptool write %t/headermap.hmap.json %t/headermap.hmap
 
 // Input headers use paths to the framework root/DSTROOT
-// RUN: %clang_cc1 -extract-api --pretty-sgf -v --product-name=MyFramework \
+// RUN: %clang_cc1 -extract-api -v --product-name=MyFramework \
 // RUN: -triple arm64-apple-macosx \
 // RUN: -iquote%t -I%t/headermap.hmap -F%t/Frameworks \
 // RUN: -x objective-c-header \

diff  --git a/clang/test/ExtractAPI/simple_inheritance.cpp b/clang/test/ExtractAPI/simple_inheritance.cpp
index 58c3c4e1e5cb8a..5fe99afe087633 100644
--- a/clang/test/ExtractAPI/simple_inheritance.cpp
+++ b/clang/test/ExtractAPI/simple_inheritance.cpp
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
 // RUN:   -x c++-header %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/struct.c b/clang/test/ExtractAPI/struct.c
index 1995a6aedbfd35..4284b734cd0595 100644
--- a/clang/test/ExtractAPI/struct.c
+++ b/clang/test/ExtractAPI/struct.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf -target arm64-apple-macosx \
+// RUN: %clang -extract-api -target arm64-apple-macosx \
 // RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/typedef.c b/clang/test/ExtractAPI/typedef.c
index a4c3619bfd210a..c30e65549f2b74 100644
--- a/clang/test/ExtractAPI/typedef.c
+++ b/clang/test/ExtractAPI/typedef.c
@@ -1,93 +1,391 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -triple arm64-apple-macosx -x objective-c-header %s -o %t/output.symbols.json -verify
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api --product-name=Typedef -target arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix MYINT
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
 typedef int MyInt;
-// MYINT-LABEL: "!testLabel": "c:typedef.c at T@MyInt"
-// MYINT: "accessLevel": "public",
-// MYINT:      "declarationFragments": [
-// MYINT-NEXT:   {
-// MYINT-NEXT:     "kind": "keyword",
-// MYINT-NEXT:     "spelling": "typedef"
-// MYINT-NEXT:   },
-// MYINT-NEXT:   {
-// MYINT-NEXT:     "kind": "text",
-// MYINT-NEXT:     "spelling": " "
-// MYINT-NEXT:   },
-// MYINT-NEXT:   {
-// MYINT-NEXT:     "kind": "typeIdentifier",
-// MYINT-NEXT:     "preciseIdentifier": "c:I",
-// MYINT-NEXT:     "spelling": "int"
-// MYINT-NEXT:   },
-// MYINT-NEXT:   {
-// MYINT-NEXT:     "kind": "text",
-// MYINT-NEXT:     "spelling": " "
-// MYINT-NEXT:   },
-// MYINT-NEXT:   {
-// MYINT-NEXT:     "kind": "identifier",
-// MYINT-NEXT:     "spelling": "MyInt"
-// MYINT-NEXT:   },
-// MYINT-NEXT:   {
-// MYINT-NEXT:     "kind": "text",
-// MYINT-NEXT:     "spelling": ";"
-// MYINT-NEXT:   }
-// MYINT-NEXT: ],
-// MYINT:      "kind": {
-// MYINT-NEXT:   "displayName": "Type Alias",
-// MYINT-NEXT:   "identifier": "objective-c.typealias"
-// MYINT-NEXT: },
-// MYINT: "title": "MyInt"
-// MYINT:      "pathComponents": [
-// MYINT-NEXT:   "MyInt"
-// MYINT-NEXT: ],
-// MYINT: "type": "c:I"
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix BARPTR
 typedef struct Bar *BarPtr;
-// BARPTR-LABEL: "!testLabel": "c:typedef.c at T@BarPtr"
-// BARPTR: "accessLevel": "public",
-// BARPTR:      "declarationFragments": [
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "keyword",
-// BARPTR-NEXT:     "spelling": "typedef"
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "text",
-// BARPTR-NEXT:     "spelling": " "
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "keyword",
-// BARPTR-NEXT:     "spelling": "struct"
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "text",
-// BARPTR-NEXT:     "spelling": " "
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "typeIdentifier",
-// BARPTR-NEXT:     "preciseIdentifier": "c:@S at Bar",
-// BARPTR-NEXT:     "spelling": "Bar"
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "text",
-// BARPTR-NEXT:     "spelling": " * "
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "identifier",
-// BARPTR-NEXT:     "spelling": "BarPtr"
-// BARPTR-NEXT:   },
-// BARPTR-NEXT:   {
-// BARPTR-NEXT:     "kind": "text",
-// BARPTR-NEXT:     "spelling": ";"
-// BARPTR-NEXT:   }
-// BARPTR-NEXT: ],
-// BARPTR: "type": "c:*$@S at Bar"
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json
 void foo(BarPtr value);
 
 void baz(BarPtr *value);
-// CHECK-NOT: struct Bar *
 
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "Typedef",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "foo"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@BarPtr",
+          "spelling": "BarPtr"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "value"
+        },
+        {
+          "kind": "text",
+          "spelling": ");"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:input.h at T@BarPtr",
+                "spelling": "BarPtr"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "value"
+              }
+            ],
+            "name": "value"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:@F at foo"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "objective-c.func"
+      },
+      "location": {
+        "position": {
+          "character": 5,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "foo"
+          }
+        ],
+        "title": "foo"
+      },
+      "pathComponents": [
+        "foo"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "baz"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@BarPtr",
+          "spelling": "BarPtr"
+        },
+        {
+          "kind": "text",
+          "spelling": " * "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "value"
+        },
+        {
+          "kind": "text",
+          "spelling": ");"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:input.h at T@BarPtr",
+                "spelling": "BarPtr"
+              },
+              {
+                "kind": "text",
+                "spelling": " * "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "value"
+              }
+            ],
+            "name": "value"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:@F at baz"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "objective-c.func"
+      },
+      "location": {
+        "position": {
+          "character": 5,
+          "line": 6
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "baz"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "baz"
+          }
+        ],
+        "title": "baz"
+      },
+      "pathComponents": [
+        "baz"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyInt"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@MyInt"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 12,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyInt"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyInt"
+          }
+        ],
+        "title": "MyInt"
+      },
+      "pathComponents": [
+        "MyInt"
+      ],
+      "type": "c:I"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:@S at Bar",
+          "spelling": "Bar"
+        },
+        {
+          "kind": "text",
+          "spelling": " * "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "BarPtr"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:input.h at T@BarPtr"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "objective-c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 20,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "BarPtr"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "BarPtr"
+          }
+        ],
+        "title": "BarPtr"
+      },
+      "pathComponents": [
+        "BarPtr"
+      ],
+      "type": "c:*$@S at Bar"
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/typedef_anonymous_record.c b/clang/test/ExtractAPI/typedef_anonymous_record.c
index 9e00ff75254654..3e4c3e1dd60c4f 100644
--- a/clang/test/ExtractAPI/typedef_anonymous_record.c
+++ b/clang/test/ExtractAPI/typedef_anonymous_record.c
@@ -1,158 +1,468 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   --product-name=TypedefChain -triple arm64-apple-macosx -x c-header %s -o %t/typedefchain.symbols.json -verify
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api --product-name=TypedefChain -triple arm64-apple-macosx \
+// RUN:   -x c-header %t/input.h -o %t/output.json -verify
 
-// RUN: FileCheck %s --input-file %t/typedefchain.symbols.json --check-prefix MYSTRUCT
-typedef struct { } MyStruct;
-// MYSTRUCT-LABEL: "!testLabel": "c:@SA at MyStruct"
-// MYSTRUCT:      "accessLevel": "public",
-// MYSTRUCT:      "declarationFragments": [
-// MYSTRUCT-NEXT:   {
-// MYSTRUCT-NEXT:     "kind": "keyword",
-// MYSTRUCT-NEXT:     "spelling": "typedef"
-// MYSTRUCT-NEXT:   },
-// MYSTRUCT-NEXT:   {
-// MYSTRUCT-NEXT:     "kind": "text",
-// MYSTRUCT-NEXT:     "spelling": " "
-// MYSTRUCT-NEXT:   },
-// MYSTRUCT-NEXT:   {
-// MYSTRUCT-NEXT:     "kind": "keyword",
-// MYSTRUCT-NEXT:     "spelling": "struct"
-// MYSTRUCT-NEXT:   },
-// MYSTRUCT-NEXT:   {
-// MYSTRUCT-NEXT:     "kind": "text",
-// MYSTRUCT-NEXT:     "spelling": " "
-// MYSTRUCT-NEXT:   },
-// MYSTRUCT-NEXT:   {
-// MYSTRUCT-NEXT:     "kind": "identifier",
-// MYSTRUCT-NEXT:     "spelling": "MyStruct"
-// MYSTRUCT-NEXT:   },
-// MYSTRUCT-NEXT:   {
-// MYSTRUCT-NEXT:     "kind": "text",
-// MYSTRUCT-NEXT:     "spelling": ";"
-// MYSTRUCT-NEXT:   }
-// MYSTRUCT-NEXT: ]
-// MYSTRUCT:      "kind": {
-// MYSTRUCT-NEXT:   "displayName": "Structure",
-// MYSTRUCT-NEXT:   "identifier": "c.struct"
-// MYSTRUCT: "title": "MyStruct"
-// MYSTRUCT:      "pathComponents": [
-// MYSTRUCT-NEXT:    "MyStruct"
-// MYSTRUCT-NEXT:  ]
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
 
-// RUN: FileCheck %s --input-file %t/typedefchain.symbols.json --check-prefix MYSTRUCTSTRUCT
+//--- input.h
+typedef struct { } MyStruct;
 typedef MyStruct MyStructStruct;
-// MYSTRUCTSTRUCT-LABEL: "!testLabel": "c:typedef_anonymous_record.c at T@MyStructStruct"
-// MYSTRUCTSTRUCT: "accessLevel": "public",
-// MYSTRUCTSTRUCT:     "declarationFragments": [
-// MYSTRUCTSTRUCT-NEXT:  {
-// MYSTRUCTSTRUCT-NEXT:    "kind": "keyword",
-// MYSTRUCTSTRUCT-NEXT:    "spelling": "typedef"
-// MYSTRUCTSTRUCT-NEXT:  },
-// MYSTRUCTSTRUCT-NEXT:  {
-// MYSTRUCTSTRUCT-NEXT:    "kind": "text",
-// MYSTRUCTSTRUCT-NEXT:    "spelling": " "
-// MYSTRUCTSTRUCT-NEXT:  },
-// MYSTRUCTSTRUCT-NEXT:  {
-// MYSTRUCTSTRUCT-NEXT:    "kind": "typeIdentifier",
-// MYSTRUCTSTRUCT-NEXT:    "preciseIdentifier": "c:@SA at MyStruct",
-// MYSTRUCTSTRUCT-NEXT:    "spelling": "MyStruct"
-// MYSTRUCTSTRUCT-NEXT:  },
-// MYSTRUCTSTRUCT-NEXT:  {
-// MYSTRUCTSTRUCT-NEXT:    "kind": "text",
-// MYSTRUCTSTRUCT-NEXT:    "spelling": " "
-// MYSTRUCTSTRUCT-NEXT:  },
-// MYSTRUCTSTRUCT-NEXT:  {
-// MYSTRUCTSTRUCT-NEXT:    "kind": "identifier",
-// MYSTRUCTSTRUCT-NEXT:    "spelling": "MyStructStruct"
-// MYSTRUCTSTRUCT-NEXT:  },
-// MYSTRUCTSTRUCT-NEXT:  {
-// MYSTRUCTSTRUCT-NEXT:    "kind": "text",
-// MYSTRUCTSTRUCT-NEXT:    "spelling": ";"
-// MYSTRUCTSTRUCT-NEXT:  }
-// MYSTRUCTSTRUCT-NEXT:],
-// MYSTRUCTSTRUCT:     "kind": {
-// MYSTRUCTSTRUCT-NEXT:  "displayName": "Type Alias",
-// MYSTRUCTSTRUCT-NEXT:  "identifier": "c.typealias"
-
-// RUN: FileCheck %s --input-file %t/typedefchain.symbols.json --check-prefix MYENUM
-// RUN: FileCheck %s --input-file %t/typedefchain.symbols.json --check-prefix CASE
+typedef MyStructStruct MyStructStructStruct;
 typedef enum { Case } MyEnum;
-// MYENUM: "source": "c:@EA at MyEnum@Case",
-// MYENUM-NEXT: "target": "c:@EA at MyEnum",
-// MYENUM-NEXT: "targetFallback": "MyEnum"
-// MYENUM-LABEL: "!testLabel": "c:@EA at MyEnum"
-// MYENUM:     "declarationFragments": [
-// MYENUM-NEXT:  {
-// MYENUM-NEXT:    "kind": "keyword",
-// MYENUM-NEXT:    "spelling": "typedef"
-// MYENUM-NEXT:  },
-// MYENUM-NEXT:  {
-// MYENUM-NEXT:    "kind": "text",
-// MYENUM-NEXT:    "spelling": " "
-// MYENUM-NEXT:  },
-// MYENUM-NEXT:  {
-// MYENUM-NEXT:    "kind": "keyword",
-// MYENUM-NEXT:    "spelling": "enum"
-// MYENUM-NEXT:  },
-// MYENUM-NEXT:  {
-// MYENUM-NEXT:    "kind": "text",
-// MYENUM-NEXT:    "spelling": " "
-// MYENUM-NEXT:  },
-// MYENUM-NEXT:  {
-// MYENUM-NEXT:    "kind": "identifier",
-// MYENUM-NEXT:    "spelling": "MyEnum"
-// MYENUM-NEXT:  },
-// MYENUM-NEXT:  {
-// MYENUM-NEXT:    "kind": "text",
-// MYENUM-NEXT:    "spelling": ";"
-// MYENUM-NEXT:  }
-// MYENUM-NEXT:],
-// MYENUM:     "kind": {
-// MYENUM-NEXT:  "displayName": "Enumeration",
-// MYENUM-NEXT:  "identifier": "c.enum"
-// MYENUM: "title": "MyEnum"
-
-// CASE-LABEL: "!testLabel": "c:@EA at MyEnum@Case"
-// CASE:      "pathComponents": [
-// CASE-NEXT:   "MyEnum",
-// CASE-NEXT:   "Case"
-// CASE-NEXT: ]
-
-// RUN: FileCheck %s --input-file %t/typedefchain.symbols.json --check-prefix MYENUMENUM
 typedef MyEnum MyEnumEnum;
-// MYENUMENUM-LABEL: "!testLabel": "c:typedef_anonymous_record.c at T@MyEnumEnum"
-// MYENUMENUM:      "declarationFragments": [
-// MYENUMENUM-NEXT:   {
-// MYENUMENUM-NEXT:     "kind": "keyword",
-// MYENUMENUM-NEXT:     "spelling": "typedef"
-// MYENUMENUM-NEXT:   },
-// MYENUMENUM-NEXT:   {
-// MYENUMENUM-NEXT:     "kind": "text",
-// MYENUMENUM-NEXT:     "spelling": " "
-// MYENUMENUM-NEXT:   },
-// MYENUMENUM-NEXT:   {
-// MYENUMENUM-NEXT:     "kind": "typeIdentifier",
-// MYENUMENUM-NEXT:     "preciseIdentifier": "c:@EA at MyEnum",
-// MYENUMENUM-NEXT:     "spelling": "MyEnum"
-// MYENUMENUM-NEXT:   },
-// MYENUMENUM-NEXT:   {
-// MYENUMENUM-NEXT:     "kind": "text",
-// MYENUMENUM-NEXT:     "spelling": " "
-// MYENUMENUM-NEXT:   },
-// MYENUMENUM-NEXT:   {
-// MYENUMENUM-NEXT:     "kind": "identifier",
-// MYENUMENUM-NEXT:     "spelling": "MyEnumEnum"
-// MYENUMENUM-NEXT:   },
-// MYENUMENUM-NEXT:   {
-// MYENUMENUM-NEXT:     "kind": "text",
-// MYENUMENUM-NEXT:     "spelling": ";"
-// MYENUMENUM-NEXT:   }
-// MYENUMENUM-NEXT: ],
-// MYENUMENUM:      "kind": {
-// MYENUMENUM-NEXT:   "displayName": "Type Alias",
-// MYENUMENUM-NEXT:   "identifier": "c.typealias"
-// MYENUMENUM-NEXT: },
-// MYENUMENUM: "title": "MyEnumEnum"
-
+typedef MyEnumEnum MyEnumEnumEnum;
 // expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "TypedefChain",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@EA at MyEnum@Case",
+      "target": "c:@EA at MyEnum",
+      "targetFallback": "MyEnum"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "enum"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyEnum"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@EA at MyEnum"
+      },
+      "kind": {
+        "displayName": "Enumeration",
+        "identifier": "c.enum"
+      },
+      "location": {
+        "position": {
+          "character": 8,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyEnum"
+          }
+        ],
+        "title": "MyEnum"
+      },
+      "pathComponents": [
+        "MyEnum"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Case"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@EA at MyEnum@Case"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "position": {
+          "character": 15,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Case"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Case"
+          }
+        ],
+        "title": "Case"
+      },
+      "pathComponents": [
+        "MyEnum",
+        "Case"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@SA at MyStruct"
+      },
+      "kind": {
+        "displayName": "Structure",
+        "identifier": "c.struct"
+      },
+      "location": {
+        "position": {
+          "character": 8,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStruct"
+          }
+        ],
+        "title": "MyStruct"
+      },
+      "pathComponents": [
+        "MyStruct"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:@SA at MyStruct",
+          "spelling": "MyStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyStructStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@MyStructStruct"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 17,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStructStruct"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStructStruct"
+          }
+        ],
+        "title": "MyStructStruct"
+      },
+      "pathComponents": [
+        "MyStructStruct"
+      ],
+      "type": "c:@SA at MyStruct"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@MyStructStruct",
+          "spelling": "MyStructStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyStructStructStruct"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@MyStructStructStruct"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 23,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStructStructStruct"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyStructStructStruct"
+          }
+        ],
+        "title": "MyStructStructStruct"
+      },
+      "pathComponents": [
+        "MyStructStructStruct"
+      ],
+      "type": "c:input.h at T@MyStructStruct"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:@EA at MyEnum",
+          "spelling": "MyEnum"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyEnumEnum"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@MyEnumEnum"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 15,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyEnumEnum"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyEnumEnum"
+          }
+        ],
+        "title": "MyEnumEnum"
+      },
+      "pathComponents": [
+        "MyEnumEnum"
+      ],
+      "type": "c:@EA at MyEnum"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:input.h at T@MyEnumEnum",
+          "spelling": "MyEnumEnum"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "MyEnumEnumEnum"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@MyEnumEnumEnum"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 19,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "MyEnumEnumEnum"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "MyEnumEnumEnum"
+          }
+        ],
+        "title": "MyEnumEnumEnum"
+      },
+      "pathComponents": [
+        "MyEnumEnumEnum"
+      ],
+      "type": "c:input.h at T@MyEnumEnum"
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/typedef_chain.c b/clang/test/ExtractAPI/typedef_chain.c
index 05d4eb52cef36b..9e6151c8ebd905 100644
--- a/clang/test/ExtractAPI/typedef_chain.c
+++ b/clang/test/ExtractAPI/typedef_chain.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang -extract-api --pretty-sgf --product-name=TypedefChain -target arm64-apple-macosx \
+// RUN: %clang -extract-api --product-name=TypedefChain -target arm64-apple-macosx \
 // RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
 // Generator version is not consistent across test runs, normalize it.

diff  --git a/clang/test/ExtractAPI/typedef_struct_enum.c b/clang/test/ExtractAPI/typedef_struct_enum.c
index fb6fbe987624f8..15357d5b055fb2 100644
--- a/clang/test/ExtractAPI/typedef_struct_enum.c
+++ b/clang/test/ExtractAPI/typedef_struct_enum.c
@@ -1,146 +1,445 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN:   -x c-header %s -triple arm64-apple-macos -o %t/output.symbols.json -verify
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang -extract-api -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix TEST
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
 typedef struct Test {
 } Test;
-// TEST-LABEL: "!testLabel": "c:@S at Test"
-// TEST:      "declarationFragments": [
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "keyword",
-// TEST-NEXT:     "spelling": "typedef"
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "text",
-// TEST-NEXT:     "spelling": " "
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "keyword",
-// TEST-NEXT:     "spelling": "struct"
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "text",
-// TEST-NEXT:     "spelling": " "
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "identifier",
-// TEST-NEXT:     "spelling": "Test"
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "text",
-// TEST-NEXT:     "spelling": " { ... } "
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "identifier",
-// TEST-NEXT:     "spelling": "Test"
-// TEST-NEXT:   },
-// TEST-NEXT:   {
-// TEST-NEXT:     "kind": "text",
-// TEST-NEXT:     "spelling": ";"
-// TEST-NEXT:   }
-// TEST-NEXT: ],
-// TEST: "displayName": "Structure",
-// TEST: "title": "Test"
 
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix TEST2
 typedef enum Test2 {
   simple
 } Test2;
 
-// TEST2-LABEL: "!testLabel": "c:@E at Test2"
-// TEST2:      "declarationFragments": [
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "keyword",
-// TEST2-NEXT:     "spelling": "typedef"
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "text",
-// TEST2-NEXT:     "spelling": " "
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "keyword",
-// TEST2-NEXT:     "spelling": "enum"
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "text",
-// TEST2-NEXT:     "spelling": " "
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "identifier",
-// TEST2-NEXT:     "spelling": "Test2"
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "text",
-// TEST2-NEXT:     "spelling": ": "
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "typeIdentifier",
-// TEST2-NEXT:     "preciseIdentifier": "c:i",
-// TEST2-NEXT:     "spelling": "unsigned int"
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "text",
-// TEST2-NEXT:     "spelling": " { ... } "
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "identifier",
-// TEST2-NEXT:     "spelling": "Test2"
-// TEST2-NEXT:   },
-// TEST2-NEXT:   {
-// TEST2-NEXT:     "kind": "text",
-// TEST2-NEXT:     "spelling": ";"
-// TEST2-NEXT:   }
-// TEST2-NEXT: ],
-// TEST2: "displayName": "Enumeration",
-// TEST2: "title": "Test2"
-
 struct Foo;
-
-// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix TYPEDEF
 typedef struct Foo TypedefedFoo;
-// TYPEDEF-LABEL: "!testLabel": "c:typedef_struct_enum.c at T@TypedefedFoo"
-// TYPEDEF:      "declarationFragments": [
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "keyword",
-// TYPEDEF-NEXT:     "spelling": "typedef"
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "text",
-// TYPEDEF-NEXT:     "spelling": " "
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "keyword",
-// TYPEDEF-NEXT:     "spelling": "struct"
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "text",
-// TYPEDEF-NEXT:     "spelling": " "
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "typeIdentifier",
-// TYPEDEF-NEXT:     "preciseIdentifier": "c:@S at Foo",
-// TYPEDEF-NEXT:     "spelling": "Foo"
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "text",
-// TYPEDEF-NEXT:     "spelling": " "
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "identifier",
-// TYPEDEF-NEXT:     "spelling": "TypedefedFoo"
-// TYPEDEF-NEXT:   },
-// TYPEDEF-NEXT:   {
-// TYPEDEF-NEXT:     "kind": "text",
-// TYPEDEF-NEXT:     "spelling": ";"
-// TYPEDEF-NEXT:   }
-// TYPEDEF-NEXT: ],
-// TYPEDEF: "displayName": "Type Alias",
-// TYPEDEF: "title": "TypedefedFoo"
-// TYPEDEF: "type": "c:@S at Foo"
-
 struct Foo {
     int bar;
 };
 
-// expected-no-diagnostics
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Test2@simple",
+      "target": "c:@E at Test2",
+      "targetFallback": "Test2"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@FI at bar",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "enum"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Test2"
+        },
+        {
+          "kind": "text",
+          "spelling": ": "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": " { ... } "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Test2"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Test2"
+      },
+      "kind": {
+        "displayName": "Enumeration",
+        "identifier": "c.enum"
+      },
+      "location": {
+        "position": {
+          "character": 13,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Test2"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Test2"
+          }
+        ],
+        "title": "Test2"
+      },
+      "pathComponents": [
+        "Test2"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "simple"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Test2@simple"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "position": {
+          "character": 2,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "simple"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "simple"
+          }
+        ],
+        "title": "simple"
+      },
+      "pathComponents": [
+        "Test2",
+        "simple"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Test"
+        },
+        {
+          "kind": "text",
+          "spelling": " { ... } "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Test"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@S at Test"
+      },
+      "kind": {
+        "displayName": "Structure",
+        "identifier": "c.struct"
+      },
+      "location": {
+        "position": {
+          "character": 15,
+          "line": 0
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Test"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Test"
+          }
+        ],
+        "title": "Test"
+      },
+      "pathComponents": [
+        "Test"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Structure",
+        "identifier": "c.struct"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "bar"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@S at Foo@FI at bar"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "c.property"
+      },
+      "location": {
+        "position": {
+          "character": 8,
+          "line": 10
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "bar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "bar"
+          }
+        ],
+        "title": "bar"
+      },
+      "pathComponents": [
+        "Foo",
+        "bar"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:@S at Foo",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "TypedefedFoo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@TypedefedFoo"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 19,
+          "line": 8
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "TypedefedFoo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "TypedefedFoo"
+          }
+        ],
+        "title": "TypedefedFoo"
+      },
+      "pathComponents": [
+        "TypedefedFoo"
+      ],
+      "type": "c:@S at Foo"
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/underscored.c b/clang/test/ExtractAPI/underscored.c
index 204ec36f1fab1f..30d2b63f763eff 100644
--- a/clang/test/ExtractAPI/underscored.c
+++ b/clang/test/ExtractAPI/underscored.c
@@ -1,5 +1,17 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
 // RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
-// RUN:   -x c-header %s -o - -verify | FileCheck %s
+// RUN:   -x c-header %t/input.h -o %t/output.json -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+// expected-no-diagnostics
 
 // Global record
 int _HiddenGlobal;
@@ -7,22 +19,399 @@ int exposed_global;
 
 // Record type
 struct _HiddenRecord {
-  int HiddenRecordMember;
+  int a;
 };
 
 struct ExposedRecord {
-  int ExposedRecordMember;
+  int a;
 };
 
+// Typedef
+typedef struct {} _HiddenTypedef;
+typedef int ExposedTypedef;
+typedef _HiddenTypedef ExposedTypedefToHidden;
+
 // Macros
 #define _HIDDEN_MACRO 5
 #define EXPOSED_MACRO 5
 
-// expected-no-diagnostics
-
-// CHECK-NOT: _HiddenRecord
-// CHECK-NOT: HiddenRecordMember
-// CHECK: ExposedRecord
-// CHECK: ExposedRecordMember
-// CHECK-NOT: _HIDDEN_MACRO
-// CHECK: EXPOSED_MACRO
+// Symbols that start with '_' should not appear in the reference output
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at ExposedRecord@FI at a",
+      "target": "c:@S at ExposedRecord",
+      "targetFallback": "ExposedRecord"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "exposed_global"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@exposed_global"
+      },
+      "kind": {
+        "displayName": "Global Variable",
+        "identifier": "c.var"
+      },
+      "location": {
+        "position": {
+          "character": 4,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "exposed_global"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "exposed_global"
+          }
+        ],
+        "title": "exposed_global"
+      },
+      "pathComponents": [
+        "exposed_global"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "struct"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "ExposedRecord"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@S at ExposedRecord"
+      },
+      "kind": {
+        "displayName": "Structure",
+        "identifier": "c.struct"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 11
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "ExposedRecord"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "ExposedRecord"
+          }
+        ],
+        "title": "ExposedRecord"
+      },
+      "pathComponents": [
+        "ExposedRecord"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "a"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@S at ExposedRecord@FI at a"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "c.property"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 12
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "a"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "a"
+          }
+        ],
+        "title": "a"
+      },
+      "pathComponents": [
+        "ExposedRecord",
+        "a"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "#define"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "EXPOSED_MACRO"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at 335@macro at EXPOSED_MACRO"
+      },
+      "kind": {
+        "displayName": "Macro",
+        "identifier": "c.macro"
+      },
+      "location": {
+        "position": {
+          "character": 8,
+          "line": 22
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "EXPOSED_MACRO"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "EXPOSED_MACRO"
+          }
+        ],
+        "title": "EXPOSED_MACRO"
+      },
+      "pathComponents": [
+        "EXPOSED_MACRO"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "ExposedTypedef"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@ExposedTypedef"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 12,
+          "line": 17
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "ExposedTypedef"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "ExposedTypedef"
+          }
+        ],
+        "title": "ExposedTypedef"
+      },
+      "pathComponents": [
+        "ExposedTypedef"
+      ],
+      "type": "c:I"
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "typedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:@SA at _HiddenTypedef",
+          "spelling": "_HiddenTypedef"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "ExposedTypedefToHidden"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:input.h at T@ExposedTypedefToHidden"
+      },
+      "kind": {
+        "displayName": "Type Alias",
+        "identifier": "c.typealias"
+      },
+      "location": {
+        "position": {
+          "character": 23,
+          "line": 18
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "ExposedTypedefToHidden"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "ExposedTypedefToHidden"
+          }
+        ],
+        "title": "ExposedTypedefToHidden"
+      },
+      "pathComponents": [
+        "ExposedTypedefToHidden"
+      ],
+      "type": "c:@SA at _HiddenTypedef"
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/union.c b/clang/test/ExtractAPI/union.c
index 8f8300b2c9a52d..6ec9fd3ddf6e99 100644
--- a/clang/test/ExtractAPI/union.c
+++ b/clang/test/ExtractAPI/union.c
@@ -2,7 +2,7 @@
 // RUN: split-file %s %t
 // RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
 // RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx -x c-header\
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx -x c-header\
 // RUN:   %t/input.h -o %t/output.json -verify
 
 // Generator version is not consistent across test runs, normalize it.
@@ -12,7 +12,7 @@
 
 //--- input.h
 /// My Union
-union Union {
+union Union{
     /// the a option
     int a;
     /// the b option

diff  --git a/clang/test/ExtractAPI/vfs_redirected_include.m b/clang/test/ExtractAPI/vfs_redirected_include.m
index db0382052ba3ef..9ba7e1dedb601e 100644
--- a/clang/test/ExtractAPI/vfs_redirected_include.m
+++ b/clang/test/ExtractAPI/vfs_redirected_include.m
@@ -14,7 +14,7 @@
 // RUN: %t/vfsoverlay.yaml.in >> %t/vfsoverlay.yaml
 
 // Input headers use paths to the framework root/DSTROOT
-// RUN: %clang_cc1 -extract-api --pretty-sgf -v --product-name=MyFramework \
+// RUN: %clang_cc1 -extract-api -v --product-name=MyFramework \
 // RUN: -triple arm64-apple-macosx \
 // RUN: -iquote%t -ivfsoverlay %t/vfsoverlay.yaml -F%t/Frameworks \
 // RUN: -x objective-c-header \

diff  --git a/clang/test/Index/extract-api-cursor.m b/clang/test/Index/extract-api-cursor.m
index 9d9d3a1e40f145..1b27b6f61437be 100644
--- a/clang/test/Index/extract-api-cursor.m
+++ b/clang/test/Index/extract-api-cursor.m
@@ -31,8 +31,6 @@ @implementation Derived
 - (void)derivedMethodWithValue:(id<Protocol>)value {
     int a = 5;
 }
-/// Impl only docs
-- (void)implOnlyMethod { }
 @end
 
 // RUN: c-index-test -single-symbol-sgf-at=%s:4:9 local %s | FileCheck -check-prefix=CHECK-FOO %s
@@ -120,10 +118,3 @@ - (void)implOnlyMethod { }
 // CHECK-DERIVED-METHOD-IMPL: "text":"Derived method docs"
 // CHECK-DERIVED-METHOD-IMPL: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
 // CHECK-DERIVED-METHOD-IMPL: "title":"derivedMethodWithValue:"
-
-// RUN: c-index-test -single-symbol-sgf-at=%s:35:11 local %s | FileCheck -check-prefix=CHECK-IMPL-ONLY %s
-// CHECK-IMPL-ONLY: "relatedSymbols":[]
-// CHECK-IMPL-ONLY: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)implOnlyMethod","target":"c:objc(cs)Derived"
-// CHECK-IMPL-ONLY: "text":"Impl only docs"
-// CHECK-IMPL-ONLY: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
-// CHECK-IMPL-ONLY: "title":"implOnlyMethod"

diff  --git a/clang/tools/libclang/CXExtractAPI.cpp b/clang/tools/libclang/CXExtractAPI.cpp
index d74f3740406c5c..05098c96829fcd 100644
--- a/clang/tools/libclang/CXExtractAPI.cpp
+++ b/clang/tools/libclang/CXExtractAPI.cpp
@@ -18,7 +18,6 @@
 #include "clang-c/Index.h"
 #include "clang-c/Platform.h"
 #include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/ExtractAPI/API.h"
@@ -55,20 +54,41 @@ struct LibClangExtractAPIVisitor
     if (!shouldDeclBeIncluded(Decl))
       return true;
 
-    auto *Interface = Decl->getClassInterface();
+    const ObjCInterfaceDecl *Interface = Decl->getClassInterface();
+    StringRef Name = Interface->getName();
+    StringRef USR = API.recordUSR(Decl);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+    DocComment Comment;
+    if (auto *RawComment = fetchRawCommentForDecl(Interface))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading by generating them for the
+    // interface.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Interface);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+    // Collect super class information.
+    SymbolReference SuperClass;
+    if (const auto *SuperClassDecl = Decl->getSuperClass()) {
+      SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
+      SuperClass.USR = API.recordUSR(SuperClassDecl);
+    }
 
-    if (!VisitObjCInterfaceDecl(Interface))
-      return false;
+    ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
+        Name, USR, Loc, AvailabilityInfo::createFromDecl(Decl), Linkage,
+        Comment, Declaration, SubHeading, SuperClass, isInSystemHeader(Decl));
 
-    SmallString<128> USR;
-    index::generateUSRForDecl(Interface, USR);
+    // Record all methods (selectors). This doesn't include automatically
+    // synthesized property methods.
+    recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
+    recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
+    recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
 
-    if (auto *InterfaceRecord = dyn_cast_if_present<ObjCInterfaceRecord>(
-            API.findRecordForUSR(USR))) {
-      recordObjCMethods(InterfaceRecord, Decl->methods());
-      recordObjCProperties(InterfaceRecord, Decl->properties());
-      recordObjCInstanceVariables(InterfaceRecord, Decl->ivars());
-    }
     return true;
   }
 };
@@ -76,14 +96,21 @@ struct LibClangExtractAPIVisitor
 
 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet)
 
-// Visits the Decl D and it's transitive DeclContexts recursively, starting from
-// the outer-most context. This is guaranteed to visit every Decl we need in the
-// right order to generate symbol graph information for D.
 static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor,
-                                      Decl *D) {
-  if (auto *Parent = D->getDeclContext())
-    WalkupFromMostDerivedType(Visitor, cast<Decl>(Parent));
+                                      Decl *D);
 
+template <typename DeclTy>
+static bool WalkupParentContext(DeclContext *Parent,
+                                LibClangExtractAPIVisitor &Visitor) {
+  if (auto *D = dyn_cast<DeclTy>(Parent)) {
+    WalkupFromMostDerivedType(Visitor, D);
+    return true;
+  }
+  return false;
+}
+
+static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor,
+                                      Decl *D) {
   switch (D->getKind()) {
 #define ABSTRACT_DECL(DECL)
 #define DECL(CLASS, BASE)                                                      \
@@ -92,12 +119,20 @@ static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor,
     break;
 #include "clang/AST/DeclNodes.inc"
   }
+
+  for (auto *Parent = D->getDeclContext(); Parent != nullptr;
+       Parent = Parent->getParent()) {
+    if (WalkupParentContext<ObjCContainerDecl>(Parent, Visitor))
+      return;
+    if (WalkupParentContext<TagDecl>(Parent, Visitor))
+      return;
+  }
 }
 
 static CXString GenerateCXStringFromSymbolGraphData(llvm::json::Object Obj) {
   llvm::SmallString<0> BackingString;
   llvm::raw_svector_ostream OS(BackingString);
-  OS << llvm::formatv("{0}", Value(std::move(Obj)));
+  OS << Value(std::move(Obj));
   return cxstring::createDup(BackingString.str());
 }
 


        


More information about the cfe-commits mailing list