[clang] 7a85192 - [clang][ExtractAPI] Add support for single symbol SGF and libclang support

Daniel Grumberg via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 13 03:19:22 PST 2022


Author: Daniel Grumberg
Date: 2022-12-13T11:18:11Z
New Revision: 7a85192166b551929d413e8a38549375503371db

URL: https://github.com/llvm/llvm-project/commit/7a85192166b551929d413e8a38549375503371db
DIFF: https://github.com/llvm/llvm-project/commit/7a85192166b551929d413e8a38549375503371db.diff

LOG: [clang][ExtractAPI] Add support for single symbol SGF and libclang support

This is mainly adding an entry point to `SymbolGraphSerializer` at
`serializeSingleSymbolSGF` and exposing the necessary data to make this
possible.  Additionaly there are some changes to how symbol kinds and
path components are serialized to make the usage more ergonomic in
`serializeSingleSymbolSGF`.

On the libclang side this introduces APIs to:
- create an APISet from a TU
- dispose of an APISet
- query an APISet for a single symbol SGF for a given USR.
- generate a single symbol SGF for a given CXCursor, this only traverses
the necessary AST nodes to construct the result as oppposed as going
through the entire AST.

Differential Revision: https://reviews.llvm.org/D139115

Added: 
    clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
    clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
    clang/test/Index/extract-api-cursor.m
    clang/test/Index/extract-api-usr.m
    clang/tools/libclang/CXExtractAPI.cpp

Modified: 
    clang/include/clang-c/Documentation.h
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/DeclarationFragments.h
    clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
    clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/CMakeLists.txt
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
    clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
    clang/tools/c-index-test/c-index-test.c
    clang/tools/libclang/CMakeLists.txt
    clang/tools/libclang/libclang.map

Removed: 
    


################################################################################
diff  --git a/clang/include/clang-c/Documentation.h b/clang/include/clang-c/Documentation.h
index 5bece2cb6758f..e04c50a0e68b6 100644
--- a/clang/include/clang-c/Documentation.h
+++ b/clang/include/clang-c/Documentation.h
@@ -15,6 +15,7 @@
 #ifndef LLVM_CLANG_C_DOCUMENTATION_H
 #define LLVM_CLANG_C_DOCUMENTATION_H
 
+#include "clang-c/CXErrorCode.h"
 #include "clang-c/ExternC.h"
 #include "clang-c/Index.h"
 
@@ -545,6 +546,69 @@ CINDEX_LINKAGE CXString clang_FullComment_getAsHTML(CXComment Comment);
  */
 CINDEX_LINKAGE CXString clang_FullComment_getAsXML(CXComment Comment);
 
+/**
+ * CXAPISet is an opaque type that represents a data structure containing all
+ * the API information for a given translation unit. This can be used for a
+ * single symbol symbol graph for a given symbol.
+ */
+typedef struct CXAPISetImpl *CXAPISet;
+
+/**
+ * Traverses the translation unit to create a \c CXAPISet.
+ *
+ * \param tu is the \c CXTranslationUnit to build the \c CXAPISet for.
+ *
+ * \param out_api is a pointer to the output of this function. It is needs to be
+ * disposed of by calling clang_disposeAPISet.
+ *
+ * \returns Error code indicating success or failure of the APISet creation.
+ */
+CINDEX_LINKAGE enum CXErrorCode clang_createAPISet(CXTranslationUnit tu,
+                                                   CXAPISet *out_api);
+
+/**
+ * Dispose of an APISet.
+ *
+ * The provided \c CXAPISet can not be used after this function is called.
+ */
+CINDEX_LINKAGE void clang_disposeAPISet(CXAPISet api);
+
+/**
+ * Generate a single symbol symbol graph for the given USR. Returns a null
+ * string if the associated symbol can not be found in the provided \c CXAPISet.
+ *
+ * The output contains the symbol graph as well as some additional information
+ * about related symbols.
+ *
+ * \param usr is a string containing the USR of the symbol to generate the
+ * symbol graph for.
+ *
+ * \param api the \c CXAPISet to look for the symbol in.
+ *
+ * \returns a string containing the serialized symbol graph representation for
+ * the symbol being queried or a null string if it can not be found in the
+ * APISet.
+ */
+CINDEX_LINKAGE CXString clang_getSymbolGraphForUSR(const char *usr,
+                                                   CXAPISet api);
+
+/**
+ * Generate a single symbol symbol graph for the declaration at the given
+ * cursor. Returns a null string if the AST node for the cursor isn't a
+ * declaration.
+ *
+ * The output contains the symbol graph as well as some additional information
+ * about related symbols.
+ *
+ * \param cursor the declaration for which to generate the single symbol symbol
+ * graph.
+ *
+ * \returns a string containing the serialized symbol graph representation for
+ * the symbol being queried or a null string if it can not be found in the
+ * APISet.
+ */
+CINDEX_LINKAGE CXString clang_getSymbolGraphForCursor(CXCursor cursor);
+
 /**
  * @}
  */

diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index b77d76d500df6..f3c1cce3fe403 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -55,6 +55,51 @@ using DocComment = std::vector<RawComment::CommentLine>;
 // 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,
+    RK_GlobalFunction,
+    RK_GlobalVariable,
+    RK_EnumConstant,
+    RK_Enum,
+    RK_StructField,
+    RK_Struct,
+    RK_ObjCInstanceProperty,
+    RK_ObjCClassProperty,
+    RK_ObjCIvar,
+    RK_ObjCClassMethod,
+    RK_ObjCInstanceMethod,
+    RK_ObjCInterface,
+    RK_ObjCCategory,
+    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;
   PresumedLoc Location;
@@ -75,23 +120,11 @@ struct APIRecord {
   /// Objective-C class/instance methods).
   DeclarationFragments SubHeading;
 
-  /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
-  enum RecordKind {
-    RK_GlobalFunction,
-    RK_GlobalVariable,
-    RK_EnumConstant,
-    RK_Enum,
-    RK_StructField,
-    RK_Struct,
-    RK_ObjCProperty,
-    RK_ObjCIvar,
-    RK_ObjCMethod,
-    RK_ObjCInterface,
-    RK_ObjCCategory,
-    RK_ObjCProtocol,
-    RK_MacroDefinition,
-    RK_Typedef,
-  };
+  /// Information about the parent record of this record.
+  HierarchyInformation ParentInformation;
+
+  /// Whether the symbol was defined in a system header.
+  bool IsFromSystemHeader;
 
 private:
   const RecordKind Kind;
@@ -104,11 +137,12 @@ struct APIRecord {
   APIRecord(RecordKind Kind, StringRef USR, StringRef Name,
             PresumedLoc Location, AvailabilitySet Availabilities,
             LinkageInfo Linkage, const DocComment &Comment,
-            DeclarationFragments Declaration, DeclarationFragments SubHeading)
+            DeclarationFragments Declaration, DeclarationFragments SubHeading,
+            bool IsFromSystemHeader)
       : USR(USR), Name(Name), Location(Location),
         Availabilities(std::move(Availabilities)), Linkage(Linkage),
         Comment(Comment), Declaration(Declaration), SubHeading(SubHeading),
-        Kind(Kind) {}
+        IsFromSystemHeader(IsFromSystemHeader), Kind(Kind) {}
 
   // Pure virtual destructor to make APIRecord abstract
   virtual ~APIRecord() = 0;
@@ -123,9 +157,10 @@ struct GlobalFunctionRecord : APIRecord {
                        const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
-                       FunctionSignature Signature)
+                       FunctionSignature Signature, bool IsFromSystemHeader)
       : APIRecord(RK_GlobalFunction, USR, Name, Loc, std::move(Availabilities),
-                  Linkage, Comment, Declaration, SubHeading),
+                  Linkage, Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
         Signature(Signature) {}
 
   static bool classof(const APIRecord *Record) {
@@ -142,9 +177,10 @@ struct GlobalVariableRecord : APIRecord {
                        AvailabilitySet Availabilities, LinkageInfo Linkage,
                        const DocComment &Comment,
                        DeclarationFragments Declaration,
-                       DeclarationFragments SubHeading)
+                       DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : APIRecord(RK_GlobalVariable, USR, Name, Loc, std::move(Availabilities),
-                  Linkage, Comment, Declaration, SubHeading) {}
+                  Linkage, Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_GlobalVariable;
@@ -159,9 +195,10 @@ struct EnumConstantRecord : APIRecord {
   EnumConstantRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                      AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
-                     DeclarationFragments SubHeading)
+                     DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : APIRecord(RK_EnumConstant, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_EnumConstant;
@@ -177,9 +214,11 @@ struct EnumRecord : APIRecord {
 
   EnumRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
              AvailabilitySet Availabilities, const DocComment &Comment,
-             DeclarationFragments Declaration, DeclarationFragments SubHeading)
+             DeclarationFragments Declaration, DeclarationFragments SubHeading,
+             bool IsFromSystemHeader)
       : APIRecord(RK_Enum, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_Enum;
@@ -194,9 +233,10 @@ struct StructFieldRecord : APIRecord {
   StructFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                     AvailabilitySet Availabilities, const DocComment &Comment,
                     DeclarationFragments Declaration,
-                    DeclarationFragments SubHeading)
+                    DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : APIRecord(RK_StructField, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_StructField;
@@ -213,9 +253,10 @@ struct StructRecord : APIRecord {
   StructRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                AvailabilitySet Availabilities, const DocComment &Comment,
                DeclarationFragments Declaration,
-               DeclarationFragments SubHeading)
+               DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : APIRecord(RK_Struct, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_Struct;
@@ -231,7 +272,6 @@ struct ObjCPropertyRecord : APIRecord {
   enum AttributeKind : unsigned {
     NoAttr = 0,
     ReadOnly = 1,
-    Class = 1 << 1,
     Dynamic = 1 << 2,
   };
 
@@ -240,23 +280,63 @@ struct ObjCPropertyRecord : APIRecord {
   StringRef SetterName;
   bool IsOptional;
 
-  ObjCPropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                     AvailabilitySet Availabilities, const DocComment &Comment,
+  ObjCPropertyRecord(RecordKind Kind, StringRef USR, StringRef Name,
+                     PresumedLoc Loc, AvailabilitySet Availabilities,
+                     const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, AttributeKind Attributes,
                      StringRef GetterName, StringRef SetterName,
-                     bool IsOptional)
-      : APIRecord(RK_ObjCProperty, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading),
+                     bool IsOptional, bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
         Attributes(Attributes), GetterName(GetterName), SetterName(SetterName),
         IsOptional(IsOptional) {}
 
   bool isReadOnly() const { return Attributes & ReadOnly; }
   bool isDynamic() const { return Attributes & Dynamic; }
-  bool isClassProperty() const { return Attributes & Class; }
+
+  virtual ~ObjCPropertyRecord() = 0;
+};
+
+struct ObjCInstancePropertyRecord : ObjCPropertyRecord {
+  ObjCInstancePropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                             AvailabilitySet Availabilities,
+                             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(Availabilities), Comment, Declaration,
+                           SubHeading, Attributes, GetterName, SetterName,
+                           IsOptional, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
-    return Record->getKind() == RK_ObjCProperty;
+    return Record->getKind() == RK_ObjCInstanceProperty;
+  }
+
+private:
+  virtual void anchor();
+};
+
+struct ObjCClassPropertyRecord : ObjCPropertyRecord {
+  ObjCClassPropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                          AvailabilitySet Availabilities,
+                          const DocComment &Comment,
+                          DeclarationFragments Declaration,
+                          DeclarationFragments SubHeading,
+                          AttributeKind Attributes, StringRef GetterName,
+                          StringRef SetterName, bool IsOptional,
+                          bool IsFromSystemHeader)
+      : ObjCPropertyRecord(RK_ObjCClassProperty, USR, Name, Loc,
+                           std::move(Availabilities), Comment, Declaration,
+                           SubHeading, Attributes, GetterName, SetterName,
+                           IsOptional, IsFromSystemHeader) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCClassProperty;
   }
 
 private:
@@ -273,9 +353,10 @@ struct ObjCInstanceVariableRecord : APIRecord {
                              const DocComment &Comment,
                              DeclarationFragments Declaration,
                              DeclarationFragments SubHeading,
-                             AccessControl Access)
+                             AccessControl Access, bool IsFromSystemHeader)
       : APIRecord(RK_ObjCIvar, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
         Access(Access) {}
 
   static bool classof(const APIRecord *Record) {
@@ -289,19 +370,53 @@ struct ObjCInstanceVariableRecord : APIRecord {
 /// This holds information associated with Objective-C methods.
 struct ObjCMethodRecord : APIRecord {
   FunctionSignature Signature;
-  bool IsInstanceMethod;
 
-  ObjCMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
-                   AvailabilitySet Availabilities, const DocComment &Comment,
-                   DeclarationFragments Declaration,
+  ObjCMethodRecord() = delete;
+
+  ObjCMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
+                   PresumedLoc Loc, AvailabilitySet Availabilities,
+                   const DocComment &Comment, DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, FunctionSignature Signature,
-                   bool IsInstanceMethod)
-      : APIRecord(RK_ObjCMethod, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading),
-        Signature(Signature), IsInstanceMethod(IsInstanceMethod) {}
+                   bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
+        Signature(Signature) {}
+
+  virtual ~ObjCMethodRecord() = 0;
+};
 
+struct ObjCInstanceMethodRecord : ObjCMethodRecord {
+  ObjCInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                           AvailabilitySet Availabilities,
+                           const DocComment &Comment,
+                           DeclarationFragments Declaration,
+                           DeclarationFragments SubHeading,
+                           FunctionSignature Signature, bool IsFromSystemHeader)
+      : ObjCMethodRecord(RK_ObjCInstanceMethod, USR, Name, Loc,
+                         std::move(Availabilities), Comment, Declaration,
+                         SubHeading, Signature, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
-    return Record->getKind() == RK_ObjCMethod;
+    return Record->getKind() == RK_ObjCInstanceMethod;
+  }
+
+private:
+  virtual void anchor();
+};
+
+struct ObjCClassMethodRecord : ObjCMethodRecord {
+  ObjCClassMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                        AvailabilitySet Availabilities,
+                        const DocComment &Comment,
+                        DeclarationFragments Declaration,
+                        DeclarationFragments SubHeading,
+                        FunctionSignature Signature, bool IsFromSystemHeader)
+      : ObjCMethodRecord(RK_ObjCClassMethod, USR, Name, Loc,
+                         std::move(Availabilities), Comment, Declaration,
+                         SubHeading, Signature, IsFromSystemHeader) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCClassMethod;
   }
 
 private:
@@ -343,9 +458,9 @@ struct ObjCContainerRecord : APIRecord {
                       PresumedLoc Loc, AvailabilitySet Availabilities,
                       LinkageInfo Linkage, const DocComment &Comment,
                       DeclarationFragments Declaration,
-                      DeclarationFragments SubHeading)
+                      DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), Linkage,
-                  Comment, Declaration, SubHeading) {}
+                  Comment, Declaration, SubHeading, IsFromSystemHeader) {}
 
   virtual ~ObjCContainerRecord() = 0;
 };
@@ -357,10 +472,12 @@ struct ObjCCategoryRecord : ObjCContainerRecord {
   ObjCCategoryRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                      AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
-                     DeclarationFragments SubHeading, SymbolReference Interface)
+                     DeclarationFragments SubHeading, SymbolReference Interface,
+                     bool IsFromSystemHeader)
       : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Loc,
                             std::move(Availabilities), LinkageInfo::none(),
-                            Comment, Declaration, SubHeading),
+                            Comment, Declaration, SubHeading,
+                            IsFromSystemHeader),
         Interface(Interface) {}
 
   static bool classof(const APIRecord *Record) {
@@ -382,10 +499,10 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
                       const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading,
-                      SymbolReference SuperClass)
+                      SymbolReference SuperClass, bool IsFromSystemHeader)
       : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Loc,
                             std::move(Availabilities), Linkage, Comment,
-                            Declaration, SubHeading),
+                            Declaration, SubHeading, IsFromSystemHeader),
         SuperClass(SuperClass) {}
 
   static bool classof(const APIRecord *Record) {
@@ -401,10 +518,11 @@ struct ObjCProtocolRecord : ObjCContainerRecord {
   ObjCProtocolRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                      AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
-                     DeclarationFragments SubHeading)
+                     DeclarationFragments SubHeading, bool IsFromSystemHeader)
       : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Loc,
                             std::move(Availabilities), LinkageInfo::none(),
-                            Comment, Declaration, SubHeading) {}
+                            Comment, Declaration, SubHeading,
+                            IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_ObjCProtocol;
@@ -418,9 +536,11 @@ struct ObjCProtocolRecord : ObjCContainerRecord {
 struct MacroDefinitionRecord : APIRecord {
   MacroDefinitionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                         DeclarationFragments Declaration,
-                        DeclarationFragments SubHeading)
+                        DeclarationFragments SubHeading,
+                        bool IsFromSystemHeader)
       : APIRecord(RK_MacroDefinition, USR, Name, Loc, AvailabilitySet(),
-                  LinkageInfo(), {}, Declaration, SubHeading) {}
+                  LinkageInfo(), {}, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_MacroDefinition;
@@ -441,9 +561,11 @@ struct TypedefRecord : APIRecord {
   TypedefRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
                 AvailabilitySet Availabilities, const DocComment &Comment,
                 DeclarationFragments Declaration,
-                DeclarationFragments SubHeading, SymbolReference UnderlyingType)
+                DeclarationFragments SubHeading, SymbolReference UnderlyingType,
+                bool IsFromSystemHeader)
       : APIRecord(RK_Typedef, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo(), Comment, Declaration, SubHeading),
+                  LinkageInfo(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
         UnderlyingType(UnderlyingType) {}
 
   static bool classof(const APIRecord *Record) {
@@ -464,6 +586,11 @@ 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 {};
 
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
@@ -478,7 +605,7 @@ class APISet {
   addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
                AvailabilitySet Availability, LinkageInfo Linkage,
                const DocComment &Comment, DeclarationFragments Declaration,
-               DeclarationFragments SubHeading);
+               DeclarationFragments SubHeadin, bool IsFromSystemHeaderg);
 
   /// Create and add a function record into the API set.
   ///
@@ -491,7 +618,7 @@ class APISet {
                     AvailabilitySet Availability, LinkageInfo Linkage,
                     const DocComment &Comment, DeclarationFragments Declaration,
                     DeclarationFragments SubHeading,
-                    FunctionSignature Signature);
+                    FunctionSignature Signature, bool IsFromSystemHeader);
 
   /// Create and add an enum constant record into the API set.
   ///
@@ -499,12 +626,11 @@ class APISet {
   /// \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,
-                                      AvailabilitySet Availability,
-                                      const DocComment &Comment,
-                                      DeclarationFragments Declaration,
-                                      DeclarationFragments SubHeading);
+  EnumConstantRecord *
+  addEnumConstant(EnumRecord *Enum, StringRef Name, StringRef USR,
+                  PresumedLoc Loc, AvailabilitySet Availability,
+                  const DocComment &Comment, DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
   /// Create and add an enum record into the API set.
   ///
@@ -515,7 +641,7 @@ class APISet {
   EnumRecord *addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
                       AvailabilitySet Availability, const DocComment &Comment,
                       DeclarationFragments Declaration,
-                      DeclarationFragments SubHeading);
+                      DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
   /// Create and add a struct field record into the API set.
   ///
@@ -523,12 +649,11 @@ class APISet {
   /// \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.
-  StructFieldRecord *addStructField(StructRecord *Struct, StringRef Name,
-                                    StringRef USR, PresumedLoc Loc,
-                                    AvailabilitySet Availability,
-                                    const DocComment &Comment,
-                                    DeclarationFragments Declaration,
-                                    DeclarationFragments SubHeading);
+  StructFieldRecord *
+  addStructField(StructRecord *Struct, StringRef Name, StringRef USR,
+                 PresumedLoc Loc, AvailabilitySet Availability,
+                 const DocComment &Comment, DeclarationFragments Declaration,
+                 DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
   /// Create and add a struct record into the API set.
   ///
@@ -540,7 +665,8 @@ class APISet {
                           AvailabilitySet Availability,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
-                          DeclarationFragments SubHeading);
+                          DeclarationFragments SubHeading,
+                          bool IsFromSystemHeader);
 
   /// Create and add an Objective-C category record into the API set.
   ///
@@ -552,7 +678,8 @@ class APISet {
   addObjCCategory(StringRef Name, StringRef USR, PresumedLoc Loc,
                   AvailabilitySet Availability, const DocComment &Comment,
                   DeclarationFragments Declaration,
-                  DeclarationFragments SubHeading, SymbolReference Interface);
+                  DeclarationFragments SubHeading, SymbolReference Interface,
+                  bool IsFromSystemHeader);
 
   /// Create and add an Objective-C interface record into the API set.
   ///
@@ -564,7 +691,8 @@ class APISet {
   addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
                    AvailabilitySet Availability, LinkageInfo Linkage,
                    const DocComment &Comment, DeclarationFragments Declaration,
-                   DeclarationFragments SubHeading, SymbolReference SuperClass);
+                   DeclarationFragments SubHeading, SymbolReference SuperClass,
+                   bool IsFromSystemHeader);
 
   /// Create and add an Objective-C method record into the API set.
   ///
@@ -577,7 +705,7 @@ class APISet {
                 PresumedLoc Loc, AvailabilitySet Availability,
                 const DocComment &Comment, DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, FunctionSignature Signature,
-                bool IsInstanceMethod);
+                bool IsInstanceMethod, bool IsFromSystemHeader);
 
   /// Create and add an Objective-C property record into the API set.
   ///
@@ -591,7 +719,8 @@ class APISet {
                   const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading,
                   ObjCPropertyRecord::AttributeKind Attributes,
-                  StringRef GetterName, StringRef SetterName, bool IsOptional);
+                  StringRef GetterName, StringRef SetterName, bool IsOptional,
+                  bool IsInstanceProperty, bool IsFromSystemHeader);
 
   /// Create and add an Objective-C instance variable record into the API set.
   ///
@@ -603,7 +732,8 @@ class APISet {
       ObjCContainerRecord *Container, StringRef Name, StringRef USR,
       PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
-      ObjCInstanceVariableRecord::AccessControl Access);
+      ObjCInstanceVariableRecord::AccessControl Access,
+      bool IsFromSystemHeader);
 
   /// Create and add an Objective-C protocol record into the API set.
   ///
@@ -611,12 +741,11 @@ class APISet {
   /// \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,
-                                      AvailabilitySet Availability,
-                                      const DocComment &Comment,
-                                      DeclarationFragments Declaration,
-                                      DeclarationFragments SubHeading);
+  ObjCProtocolRecord *
+  addObjCProtocol(StringRef Name, StringRef USR, PresumedLoc Loc,
+                  AvailabilitySet Availability, const DocComment &Comment,
+                  DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
   /// Create a macro definition record into the API set.
   ///
@@ -628,7 +757,8 @@ class APISet {
   MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR,
                                             PresumedLoc Loc,
                                             DeclarationFragments Declaration,
-                                            DeclarationFragments SubHeading);
+                                            DeclarationFragments SubHeading,
+                                            bool IsFromSystemHeader);
 
   /// Create a typedef record into the API set.
   ///
@@ -636,12 +766,11 @@ class APISet {
   /// \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,
-                            AvailabilitySet Availability,
-                            const DocComment &Comment,
-                            DeclarationFragments Declaration,
-                            DeclarationFragments SubHeading,
-                            SymbolReference UnderlyingType);
+  TypedefRecord *
+  addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+             AvailabilitySet 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,
@@ -675,6 +804,11 @@ class APISet {
   const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }
   const RecordMap<TypedefRecord> &getTypedefs() const { return Typedefs; }
 
+  /// Finds the APIRecord for a given USR.
+  ///
+  /// \returns a pointer to the APIRecord associated with that USR or nullptr.
+  APIRecord *findRecordForUSR(StringRef USR) const;
+
   /// Generate and store the USR of declaration \p D.
   ///
   /// Note: The USR string is stored in and owned by Allocator.
@@ -695,8 +829,9 @@ class APISet {
   /// \returns a StringRef of the copied string in APISet::Allocator.
   StringRef copyString(StringRef String);
 
-  APISet(const llvm::Triple &Target, Language Lang)
-      : Target(Target), Lang(Lang) {}
+  APISet(const llvm::Triple &Target, Language Lang,
+         const std::string &ProductName)
+      : Target(Target), Lang(Lang), ProductName(ProductName) {}
 
 private:
   /// BumpPtrAllocator to store generated/copied strings.
@@ -707,6 +842,7 @@ class APISet {
   const llvm::Triple Target;
   const Language Lang;
 
+  llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
   RecordMap<GlobalFunctionRecord> GlobalFunctions;
   RecordMap<GlobalVariableRecord> GlobalVariables;
   RecordMap<EnumRecord> Enums;
@@ -716,6 +852,9 @@ class APISet {
   RecordMap<ObjCProtocolRecord> ObjCProtocols;
   RecordMap<MacroDefinitionRecord> Macros;
   RecordMap<TypedefRecord> Typedefs;
+
+public:
+  const std::string ProductName;
 };
 
 } // namespace extractapi

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 55a6768eed9b9..a5db4d23e8b55 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -87,9 +87,14 @@ class DeclarationFragments {
     /// The USR of the fragment symbol, if applicable.
     std::string PreciseIdentifier;
 
-    Fragment(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier)
-        : Spelling(Spelling), Kind(Kind), PreciseIdentifier(PreciseIdentifier) {
-    }
+    /// The associated declaration, if applicable. This is not intended to be
+    /// used outside of libclang.
+    const Decl *Declaration;
+
+    Fragment(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier,
+             const Decl *Declaration)
+        : Spelling(Spelling), Kind(Kind), PreciseIdentifier(PreciseIdentifier),
+          Declaration(Declaration) {}
   };
 
   const std::vector<Fragment> &getFragments() const { return Fragments; }
@@ -99,14 +104,15 @@ class DeclarationFragments {
   /// \returns a reference to the DeclarationFragments object itself after
   /// appending to chain up consecutive appends.
   DeclarationFragments &append(StringRef Spelling, FragmentKind Kind,
-                               StringRef PreciseIdentifier = "") {
+                               StringRef PreciseIdentifier = "",
+                               const Decl *Declaration = nullptr) {
     if (Kind == FragmentKind::Text && !Fragments.empty() &&
         Fragments.back().Kind == FragmentKind::Text) {
       // If appending a text fragment, and the last fragment is also text,
       // merge into the last fragment.
       Fragments.back().Spelling.append(Spelling.data(), Spelling.size());
     } else {
-      Fragments.emplace_back(Spelling, Kind, PreciseIdentifier);
+      Fragments.emplace_back(Spelling, Kind, PreciseIdentifier, Declaration);
     }
     return *this;
   }

diff  --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
new file mode 100644
index 0000000000000..f6546fb4776a6
--- /dev/null
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -0,0 +1,88 @@
+//===- ExtractAPI/ExtractAPIVisitor.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 ExtractAPVisitor AST visitation interface.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
+#define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
+
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/ExtractAPI/API.h"
+#include "llvm/ADT/FunctionExtras.h"
+
+namespace clang {
+namespace extractapi {
+
+/// The RecursiveASTVisitor to traverse symbol declarations and collect API
+/// information.
+class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
+public:
+  ExtractAPIVisitor(ASTContext &Context,
+                    llvm::unique_function<bool(SourceLocation)> LocationChecker,
+                    APISet &API)
+      : Context(Context), API(API),
+        LocationChecker(std::move(LocationChecker)) {}
+
+  const APISet &getAPI() const { return API; }
+
+  bool VisitVarDecl(const VarDecl *Decl);
+
+  bool VisitFunctionDecl(const FunctionDecl *Decl);
+
+  bool VisitEnumDecl(const EnumDecl *Decl);
+
+  bool VisitRecordDecl(const RecordDecl *Decl);
+
+  bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl);
+
+  bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl);
+
+  bool VisitTypedefNameDecl(const TypedefNameDecl *Decl);
+
+  bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl);
+
+private:
+  /// Collect API information for the enum constants and associate with the
+  /// parent enum.
+  void recordEnumConstants(EnumRecord *EnumRecord,
+                           const EnumDecl::enumerator_range Constants);
+
+  /// Collect API information for the struct fields and associate with the
+  /// parent struct.
+  void recordStructFields(StructRecord *StructRecord,
+                          const RecordDecl::field_range Fields);
+
+  /// Collect API information for the Objective-C methods and associate with the
+  /// parent container.
+  void recordObjCMethods(ObjCContainerRecord *Container,
+                         const ObjCContainerDecl::method_range Methods);
+
+  void recordObjCProperties(ObjCContainerRecord *Container,
+                            const ObjCContainerDecl::prop_range Properties);
+
+  void recordObjCInstanceVariables(
+      ObjCContainerRecord *Container,
+      const llvm::iterator_range<
+          DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+          Ivars);
+
+  void recordObjCProtocols(ObjCContainerRecord *Container,
+                           ObjCInterfaceDecl::protocol_range Protocols);
+  ASTContext &Context;
+  APISet &API;
+  llvm::unique_function<bool(SourceLocation)> LocationChecker;
+};
+
+} // namespace extractapi
+} // namespace clang
+
+#endif // LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
index 0510dacb06495..d8aa826e3f4f6 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -36,11 +36,6 @@ class APISerializer {
 protected:
   const APISet &API;
 
-  /// The product name of API.
-  ///
-  /// Note: This should be used for populating metadata about the API.
-  StringRef ProductName;
-
   /// The list of symbols to ignore.
   ///
   /// Note: This should be consulted before emitting a symbol.
@@ -56,11 +51,9 @@ class APISerializer {
   APISerializer &operator=(APISerializer &&) = delete;
 
 protected:
-  APISerializer(const APISet &API, StringRef ProductName,
-                const APIIgnoresList &IgnoresList,
+  APISerializer(const APISet &API, const APIIgnoresList &IgnoresList,
                 APISerializerOption Options = {})
-      : API(API), ProductName(ProductName), IgnoresList(IgnoresList),
-        Options(Options) {}
+      : API(API), IgnoresList(IgnoresList), Options(Options) {}
 
   virtual ~APISerializer() = default;
 };

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index e20ca85fc8d6e..9bf6083fe901e 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -47,24 +47,9 @@ class SymbolGraphSerializer : public APISerializer {
   /// The Symbol Graph format version used by this serializer.
   static const VersionTuple FormatVersion;
 
-  using PathComponentStack = llvm::SmallVector<llvm::StringRef, 4>;
-  /// The current path component stack.
-  ///
-  /// Note: this is used to serialize the ``pathComponents`` field of symbols in
-  /// the Symbol Graph.
-  PathComponentStack PathComponents;
-
-  /// A helper type to manage PathComponents correctly using RAII.
-  struct PathComponentGuard {
-    PathComponentGuard(PathComponentStack &PC, StringRef Component) : PC(PC) {
-      PC.emplace_back(Component);
-    }
-
-    ~PathComponentGuard() { PC.pop_back(); }
-
-  private:
-    PathComponentStack &PC;
-  };
+  /// Indicates whether child symbols should be serialized. This is mainly
+  /// useful for \c serializeSingleSymbolSGF.
+  bool ShouldRecurse;
 
 public:
   /// Serialize the APIs in \c APISet in the Symbol Graph format.
@@ -77,6 +62,14 @@ class SymbolGraphSerializer : public APISerializer {
   /// write out the serialized JSON object to \p os.
   void serialize(raw_ostream &os) override;
 
+  /// Serialize a single symbol SGF. This is primarily used for libclang.
+  ///
+  /// \returns an optional JSON Object representing the payload that libclang
+  /// expects for providing symbol information for a single symbol. If this is
+  /// not a known symbol returns \c None.
+  static Optional<Object> serializeSingleSymbolSGF(StringRef USR,
+                                                   const APISet &API);
+
   /// The kind of a relationship between two symbols.
   enum RelationshipKind {
     /// The source symbol is a member of the target symbol.
@@ -96,6 +89,9 @@ class SymbolGraphSerializer : public APISerializer {
   static StringRef getRelationshipString(RelationshipKind Kind);
 
 private:
+  /// Just serialize the currently recorded objects in Symbol Graph format.
+  Object serializeCurrentGraph();
+
   /// Synthesize the metadata section of the Symbol Graph format.
   ///
   /// The metadata section describes information about the Symbol Graph itself,
@@ -160,18 +156,14 @@ class SymbolGraphSerializer : public APISerializer {
   /// Serialize a typedef record.
   void serializeTypedefRecord(const TypedefRecord &Record);
 
-  /// Push a component to the current path components stack.
-  ///
-  /// \param Component The component to push onto the path components stack.
-  /// \return A PathComponentGuard responsible for removing the latest
-  /// component from the stack on scope exit.
-  [[nodiscard]] PathComponentGuard makePathComponentGuard(StringRef Component);
+  void serializeSingleRecord(const APIRecord *Record);
 
 public:
-  SymbolGraphSerializer(const APISet &API, StringRef ProductName,
-                        const APIIgnoresList &IgnoresList,
-                        APISerializerOption Options = {})
-      : APISerializer(API, ProductName, IgnoresList, Options) {}
+  SymbolGraphSerializer(const APISet &API, const APIIgnoresList &IgnoresList,
+                        APISerializerOption Options = {},
+                        bool ShouldRecurse = true)
+      : APISerializer(API, IgnoresList, Options), ShouldRecurse(ShouldRecurse) {
+  }
 };
 
 } // namespace extractapi

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 8ab03a833e3c2..553b7bbe710f8 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -27,7 +27,8 @@ using namespace llvm;
 namespace {
 
 template <typename RecordTy, typename... CtorArgsTy>
-RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
+RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
+                            APISet::RecordMap<RecordTy> &RecordMap,
                             StringRef USR, CtorArgsTy &&...CtorArgs) {
   auto Result = RecordMap.insert({USR, nullptr});
 
@@ -36,7 +37,9 @@ RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
     Result.first->second =
         std::make_unique<RecordTy>(USR, std::forward<CtorArgsTy>(CtorArgs)...);
 
-  return Result.first->second.get();
+  auto *Record = Result.first->second.get();
+  USRLookupTable.insert({USR, Record});
+  return Record;
 }
 
 } // namespace
@@ -45,20 +48,22 @@ GlobalVariableRecord *
 APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
                      AvailabilitySet Availabilities, LinkageInfo Linkage,
                      const DocComment &Comment, DeclarationFragments Fragments,
-                     DeclarationFragments SubHeading) {
-  return addTopLevelRecord(GlobalVariables, USR, Name, Loc,
+                     DeclarationFragments SubHeading, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, GlobalVariables, USR, Name, Loc,
                            std::move(Availabilities), Linkage, Comment,
-                           Fragments, SubHeading);
+                           Fragments, SubHeading, IsFromSystemHeader);
 }
 
 GlobalFunctionRecord *APISet::addGlobalFunction(
     StringRef Name, StringRef USR, PresumedLoc Loc,
     AvailabilitySet Availabilities, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Fragments,
-    DeclarationFragments SubHeading, FunctionSignature Signature) {
-  return addTopLevelRecord(GlobalFunctions, USR, Name, Loc,
+    DeclarationFragments SubHeading, FunctionSignature Signature,
+    bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, GlobalFunctions, USR, Name, Loc,
                            std::move(Availabilities), Linkage, Comment,
-                           Fragments, SubHeading, Signature);
+                           Fragments, SubHeading, Signature,
+                           IsFromSystemHeader);
 }
 
 EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
@@ -66,10 +71,14 @@ EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
                                             AvailabilitySet Availabilities,
                                             const DocComment &Comment,
                                             DeclarationFragments Declaration,
-                                            DeclarationFragments SubHeading) {
+                                            DeclarationFragments SubHeading,
+                                            bool IsFromSystemHeader) {
   auto Record = std::make_unique<EnumConstantRecord>(
       USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
-      SubHeading);
+      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();
 }
 
@@ -77,9 +86,11 @@ EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
                             AvailabilitySet Availabilities,
                             const DocComment &Comment,
                             DeclarationFragments Declaration,
-                            DeclarationFragments SubHeading) {
-  return addTopLevelRecord(Enums, USR, Name, Loc, std::move(Availabilities),
-                           Comment, Declaration, SubHeading);
+                            DeclarationFragments SubHeading,
+                            bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Enums, USR, Name, Loc,
+                           std::move(Availabilities), Comment, Declaration,
+                           SubHeading, IsFromSystemHeader);
 }
 
 StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
@@ -87,10 +98,14 @@ StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
                                           AvailabilitySet Availabilities,
                                           const DocComment &Comment,
                                           DeclarationFragments Declaration,
-                                          DeclarationFragments SubHeading) {
+                                          DeclarationFragments SubHeading,
+                                          bool IsFromSystemHeader) {
   auto Record = std::make_unique<StructFieldRecord>(
       USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
-      SubHeading);
+      SubHeading, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      Struct->USR, Struct->Name, Struct->getKind(), Struct);
+  USRBasedLookupTable.insert({USR, Record.get()});
   return Struct->Fields.emplace_back(std::move(Record)).get();
 }
 
@@ -98,22 +113,23 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
                                 AvailabilitySet Availabilities,
                                 const DocComment &Comment,
                                 DeclarationFragments Declaration,
-                                DeclarationFragments SubHeading) {
-  return addTopLevelRecord(Structs, USR, Name, Loc, std::move(Availabilities),
-                           Comment, Declaration, SubHeading);
+                                DeclarationFragments SubHeading,
+                                bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Structs, USR, Name, Loc,
+                           std::move(Availabilities), Comment, Declaration,
+                           SubHeading, IsFromSystemHeader);
 }
 
-ObjCCategoryRecord *APISet::addObjCCategory(StringRef Name, StringRef USR,
-                                            PresumedLoc Loc,
-                                            AvailabilitySet Availabilities,
-                                            const DocComment &Comment,
-                                            DeclarationFragments Declaration,
-                                            DeclarationFragments SubHeading,
-                                            SymbolReference Interface) {
+ObjCCategoryRecord *APISet::addObjCCategory(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    AvailabilitySet Availabilities, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    SymbolReference Interface, bool IsFromSystemHeader) {
   // Create the category record.
-  auto *Record = addTopLevelRecord(ObjCCategories, USR, Name, Loc,
-                                   std::move(Availabilities), Comment,
-                                   Declaration, SubHeading, Interface);
+  auto *Record =
+      addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc,
+                        std::move(Availabilities), Comment, Declaration,
+                        SubHeading, Interface, IsFromSystemHeader);
 
   // If this category is extending a known interface, associate it with the
   // ObjCInterfaceRecord.
@@ -124,24 +140,38 @@ ObjCCategoryRecord *APISet::addObjCCategory(StringRef Name, StringRef USR,
   return Record;
 }
 
-ObjCInterfaceRecord *APISet::addObjCInterface(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
-    AvailabilitySet Availabilities, LinkageInfo Linkage,
-    const DocComment &Comment, DeclarationFragments Declaration,
-    DeclarationFragments SubHeading, SymbolReference SuperClass) {
-  return addTopLevelRecord(ObjCInterfaces, USR, Name, Loc,
+ObjCInterfaceRecord *
+APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+                         AvailabilitySet Availabilities, LinkageInfo Linkage,
+                         const DocComment &Comment,
+                         DeclarationFragments Declaration,
+                         DeclarationFragments SubHeading,
+                         SymbolReference SuperClass, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, ObjCInterfaces, USR, Name, Loc,
                            std::move(Availabilities), Linkage, Comment,
-                           Declaration, SubHeading, SuperClass);
+                           Declaration, SubHeading, SuperClass,
+                           IsFromSystemHeader);
 }
 
 ObjCMethodRecord *APISet::addObjCMethod(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
     PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
-    FunctionSignature Signature, bool IsInstanceMethod) {
-  auto Record = std::make_unique<ObjCMethodRecord>(
-      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
-      SubHeading, Signature, IsInstanceMethod);
+    FunctionSignature Signature, bool IsInstanceMethod,
+    bool IsFromSystemHeader) {
+  std::unique_ptr<ObjCMethodRecord> Record;
+  if (IsInstanceMethod)
+    Record = std::make_unique<ObjCInstanceMethodRecord>(
+        USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+        SubHeading, Signature, IsFromSystemHeader);
+  else
+    Record = std::make_unique<ObjCClassMethodRecord>(
+        USR, Name, Loc, std::move(Availabilities), 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();
 }
 
@@ -150,10 +180,22 @@ ObjCPropertyRecord *APISet::addObjCProperty(
     PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
-    StringRef SetterName, bool IsOptional) {
-  auto Record = std::make_unique<ObjCPropertyRecord>(
-      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
-      SubHeading, Attributes, GetterName, SetterName, IsOptional);
+    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(Availabilities), Comment, Declaration,
+        SubHeading, Attributes, GetterName, SetterName, IsOptional,
+        IsFromSystemHeader);
+  else
+    Record = std::make_unique<ObjCClassPropertyRecord>(
+        USR, Name, Loc, std::move(Availabilities), 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();
 }
 
@@ -161,10 +203,13 @@ ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
     PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
-    ObjCInstanceVariableRecord::AccessControl Access) {
+    ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) {
   auto Record = std::make_unique<ObjCInstanceVariableRecord>(
       USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
-      SubHeading, Access);
+      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();
 }
 
@@ -173,28 +218,41 @@ ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
                                             AvailabilitySet Availabilities,
                                             const DocComment &Comment,
                                             DeclarationFragments Declaration,
-                                            DeclarationFragments SubHeading) {
-  return addTopLevelRecord(ObjCProtocols, USR, Name, Loc,
+                                            DeclarationFragments SubHeading,
+                                            bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, ObjCProtocols, USR, Name, Loc,
                            std::move(Availabilities), Comment, Declaration,
-                           SubHeading);
+                           SubHeading, IsFromSystemHeader);
 }
 
 MacroDefinitionRecord *
 APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
                            DeclarationFragments Declaration,
-                           DeclarationFragments SubHeading) {
-  return addTopLevelRecord(Macros, USR, Name, Loc, Declaration, SubHeading);
+                           DeclarationFragments SubHeading,
+                           bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Macros, USR, Name, Loc,
+                           Declaration, SubHeading, IsFromSystemHeader);
 }
 
-TypedefRecord *APISet::addTypedef(StringRef Name, StringRef USR,
-                                  PresumedLoc Loc,
-                                  AvailabilitySet Availabilities,
-                                  const DocComment &Comment,
-                                  DeclarationFragments Declaration,
-                                  DeclarationFragments SubHeading,
-                                  SymbolReference UnderlyingType) {
-  return addTopLevelRecord(Typedefs, USR, Name, Loc, std::move(Availabilities),
-                           Comment, Declaration, SubHeading, UnderlyingType);
+TypedefRecord *
+APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+                   AvailabilitySet Availabilities, const DocComment &Comment,
+                   DeclarationFragments Declaration,
+                   DeclarationFragments SubHeading,
+                   SymbolReference UnderlyingType, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, Typedefs, USR, Name, Loc,
+                           std::move(Availabilities), Comment, Declaration,
+                           SubHeading, UnderlyingType, IsFromSystemHeader);
+}
+
+APIRecord *APISet::findRecordForUSR(StringRef USR) const {
+  if (USR.empty())
+    return nullptr;
+
+  auto It = USRBasedLookupTable.find(USR);
+  if (It != USRBasedLookupTable.end())
+    return It->second;
+  return nullptr;
 }
 
 StringRef APISet::recordUSR(const Decl *D) {
@@ -224,8 +282,9 @@ StringRef APISet::copyString(StringRef String) {
 }
 
 APIRecord::~APIRecord() {}
-
 ObjCContainerRecord::~ObjCContainerRecord() {}
+ObjCMethodRecord::~ObjCMethodRecord() {}
+ObjCPropertyRecord::~ObjCPropertyRecord() {}
 
 void GlobalFunctionRecord::anchor() {}
 void GlobalVariableRecord::anchor() {}
@@ -233,9 +292,11 @@ void EnumConstantRecord::anchor() {}
 void EnumRecord::anchor() {}
 void StructFieldRecord::anchor() {}
 void StructRecord::anchor() {}
-void ObjCPropertyRecord::anchor() {}
+void ObjCInstancePropertyRecord::anchor() {}
+void ObjCClassPropertyRecord::anchor() {}
 void ObjCInstanceVariableRecord::anchor() {}
-void ObjCMethodRecord::anchor() {}
+void ObjCInstanceMethodRecord::anchor() {}
+void ObjCClassMethodRecord::anchor() {}
 void ObjCCategoryRecord::anchor() {}
 void ObjCInterfaceRecord::anchor() {}
 void ObjCProtocolRecord::anchor() {}

diff  --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt
index e7de57d2984cc..d73cfdec98feb 100644
--- a/clang/lib/ExtractAPI/CMakeLists.txt
+++ b/clang/lib/ExtractAPI/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_library(clangExtractAPI
   APIIgnoresList.cpp
   AvailabilityInfo.cpp
   ExtractAPIConsumer.cpp
+  ExtractAPIVisitor.cpp
   DeclarationFragments.cpp
   Serialization/SerializerBase.cpp
   Serialization/SymbolGraphSerializer.cpp

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 71117bc4b68b6..12c91c582aa98 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -109,7 +109,7 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
     SmallString<128> USR;
     index::generateUSRForDecl(NS, USR);
     Fragments.append(NS->getName(),
-                     DeclarationFragments::FragmentKind::Identifier, USR);
+                     DeclarationFragments::FragmentKind::Identifier, USR, NS);
     break;
   }
 
@@ -118,7 +118,8 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
     SmallString<128> USR;
     index::generateUSRForDecl(Alias, USR);
     Fragments.append(Alias->getName(),
-                     DeclarationFragments::FragmentKind::Identifier, USR);
+                     DeclarationFragments::FragmentKind::Identifier, USR,
+                     Alias);
     break;
   }
 
@@ -255,11 +256,11 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
   // direct reference to the typedef instead of the wrapped type.
   if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) {
     const TypedefNameDecl *Decl = TypedefTy->getDecl();
-    std::string USR =
-        TypedefUnderlyingTypeResolver(Context).getUSRForType(QualType(T, 0));
-    return Fragments.append(Decl->getName(),
-                            DeclarationFragments::FragmentKind::TypeIdentifier,
-                            USR);
+    TypedefUnderlyingTypeResolver TypedefResolver(Context);
+    std::string USR = TypedefResolver.getUSRForType(QualType(T, 0));
+    return Fragments.append(
+        Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier,
+        USR, TypedefResolver.getUnderlyingTypeDecl(QualType(T, 0)));
   }
 
   // If the base type is a TagType (struct/interface/union/class/enum), let's
@@ -273,7 +274,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
     clang::index::generateUSRForDecl(Decl, TagUSR);
     return Fragments.append(Decl->getName(),
                             DeclarationFragments::FragmentKind::TypeIdentifier,
-                            TagUSR);
+                            TagUSR, Decl);
   }
 
   // If the base type is an ObjCInterfaceType, use the underlying
@@ -284,7 +285,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
     index::generateUSRForDecl(Decl, USR);
     return Fragments.append(Decl->getName(),
                             DeclarationFragments::FragmentKind::TypeIdentifier,
-                            USR);
+                            USR, Decl);
   }
 
   // Default fragment builder for other kinds of types (BuiltinType etc.)
@@ -530,13 +531,15 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(
     const ObjCCategoryDecl *Category) {
   DeclarationFragments Fragments;
 
+  auto *Interface = Category->getClassInterface();
   SmallString<128> InterfaceUSR;
-  index::generateUSRForDecl(Category->getClassInterface(), InterfaceUSR);
+  index::generateUSRForDecl(Interface, InterfaceUSR);
 
   Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
       .appendSpace()
       .append(Category->getClassInterface()->getName(),
-              DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR)
+              DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR,
+              Interface)
       .append(" (", DeclarationFragments::FragmentKind::Text)
       .append(Category->getName(),
               DeclarationFragments::FragmentKind::Identifier)
@@ -560,7 +563,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface(
     index::generateUSRForDecl(SuperClass, SuperUSR);
     Fragments.append(" : ", DeclarationFragments::FragmentKind::Text)
         .append(SuperClass->getName(),
-                DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR);
+                DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR,
+                SuperClass);
   }
 
   return Fragments;
@@ -719,7 +723,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
       SmallString<128> USR;
       index::generateUSRForDecl(*It, USR);
       Fragments.append((*It)->getName(),
-                       DeclarationFragments::FragmentKind::TypeIdentifier, USR);
+                       DeclarationFragments::FragmentKind::TypeIdentifier, USR,
+                       *It);
     }
     Fragments.append(">", DeclarationFragments::FragmentKind::Text);
   }

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index 5f185d0bd6aa2..a274585cc9ae2 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -7,27 +7,20 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
-/// collect API information.
+/// This file implements the ExtractAPIAction, and ASTConsumer to collect API
+/// information.
 ///
 //===----------------------------------------------------------------------===//
 
-#include "TypedefUnderlyingTypeResolver.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ParentMapContext.h"
-#include "clang/AST/RawCommentList.h"
-#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/ExtractAPI/API.h"
 #include "clang/ExtractAPI/APIIgnoresList.h"
-#include "clang/ExtractAPI/AvailabilityInfo.h"
-#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/ExtractAPI/ExtractAPIVisitor.h"
 #include "clang/ExtractAPI/FrontendActions.h"
 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
 #include "clang/Frontend/ASTConsumers.h"
@@ -55,16 +48,9 @@ using namespace extractapi;
 
 namespace {
 
-StringRef getTypedefName(const TagDecl *Decl) {
-  if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
-    return TypedefDecl->getName();
-
-  return {};
-}
-
-std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
-                                                  StringRef File,
-                                                  bool *IsQuoted = nullptr) {
+Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
+                                             StringRef File,
+                                             bool *IsQuoted = nullptr) {
   assert(CI.hasFileManager() &&
          "CompilerInstance does not have a FileNamager!");
 
@@ -177,7 +163,7 @@ std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
 }
 
 struct LocationFileChecker {
-  bool isLocationInKnownFile(SourceLocation Loc) {
+  bool operator()(SourceLocation Loc) {
     // If the loc refers to a macro expansion we need to first get the file
     // location of the expansion.
     auto &SM = CI.getSourceManager();
@@ -233,516 +219,6 @@ struct LocationFileChecker {
   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
 };
 
-/// The RecursiveASTVisitor to traverse symbol declarations and collect API
-/// information.
-class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
-public:
-  ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
-      : Context(Context), API(API), LCF(LCF) {}
-
-  const APISet &getAPI() const { return API; }
-
-  bool VisitVarDecl(const VarDecl *Decl) {
-    // Skip function parameters.
-    if (isa<ParmVarDecl>(Decl))
-      return true;
-
-    // Skip non-global variables in records (struct/union/class).
-    if (Decl->getDeclContext()->isRecord())
-      return true;
-
-    // Skip local variables inside function or method.
-    if (!Decl->isDefinedOutsideFunctionOrMethod())
-      return true;
-
-    // If this is a template but not specialization or instantiation, skip.
-    if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
-        Decl->getTemplateSpecializationKind() == TSK_Undeclared)
-      return true;
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    // Collect symbol information.
-    StringRef Name = Decl->getName();
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the variable.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-    // Add the global variable record to the API set.
-    API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
-                     Declaration, SubHeading);
-    return true;
-  }
-
-  bool VisitFunctionDecl(const FunctionDecl *Decl) {
-    if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
-      // Skip member function in class templates.
-      if (Method->getParent()->getDescribedClassTemplate() != nullptr)
-        return true;
-
-      // Skip methods in records.
-      for (auto P : Context.getParents(*Method)) {
-        if (P.get<CXXRecordDecl>())
-          return true;
-      }
-
-      // Skip ConstructorDecl and DestructorDecl.
-      if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
-        return true;
-    }
-
-    // Skip templated functions.
-    switch (Decl->getTemplatedKind()) {
-    case FunctionDecl::TK_NonTemplate:
-    case FunctionDecl::TK_DependentNonTemplate:
-      break;
-    case FunctionDecl::TK_MemberSpecialization:
-    case FunctionDecl::TK_FunctionTemplateSpecialization:
-      if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
-        if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
-          return true;
-      }
-      break;
-    case FunctionDecl::TK_FunctionTemplate:
-    case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
-      return true;
-    }
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    // Collect symbol information.
-    StringRef Name = Decl->getName();
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments, sub-heading, and signature of the function.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Decl);
-    FunctionSignature Signature =
-        DeclarationFragmentsBuilder::getFunctionSignature(Decl);
-
-    // Add the function record to the API set.
-    API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
-                          Comment, Declaration, SubHeading, Signature);
-    return true;
-  }
-
-  bool VisitEnumDecl(const EnumDecl *Decl) {
-    if (!Decl->isComplete())
-      return true;
-
-    // Skip forward declaration.
-    if (!Decl->isThisDeclarationADefinition())
-      return true;
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    // Collect symbol information.
-    std::string NameString = Decl->getQualifiedNameAsString();
-    StringRef Name(NameString);
-    if (Name.empty())
-      Name = getTypedefName(Decl);
-
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the enum.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-    EnumRecord *EnumRecord =
-        API.addEnum(API.copyString(Name), USR, Loc, AvailabilitySet(Decl),
-                    Comment, Declaration, SubHeading);
-
-    // Now collect information about the enumerators in this enum.
-    recordEnumConstants(EnumRecord, Decl->enumerators());
-
-    return true;
-  }
-
-  bool VisitRecordDecl(const RecordDecl *Decl) {
-    if (!Decl->isCompleteDefinition())
-      return true;
-
-    // Skip C++ structs/classes/unions
-    // TODO: support C++ records
-    if (isa<CXXRecordDecl>(Decl))
-      return true;
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    // Collect symbol information.
-    StringRef Name = Decl->getName();
-    if (Name.empty())
-      Name = getTypedefName(Decl);
-    if (Name.empty())
-      return true;
-
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the struct.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-    StructRecord *StructRecord =
-        API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment,
-                      Declaration, SubHeading);
-
-    // Now collect information about the fields in this struct.
-    recordStructFields(StructRecord, Decl->fields());
-
-    return true;
-  }
-
-  bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
-    // Skip forward declaration for classes (@class)
-    if (!Decl->isThisDeclarationADefinition())
-      return true;
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    // Collect symbol information.
-    StringRef Name = Decl->getName();
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the interface.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
-    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);
-    }
-
-    ObjCInterfaceRecord *ObjCInterfaceRecord =
-        API.addObjCInterface(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
-                             Comment, Declaration, SubHeading, SuperClass);
-
-    // Record all methods (selectors). This doesn't include automatically
-    // synthesized property methods.
-    recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
-    recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
-    recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
-    recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
-
-    return true;
-  }
-
-  bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
-    // Skip forward declaration for protocols (@protocol).
-    if (!Decl->isThisDeclarationADefinition())
-      return true;
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    // Collect symbol information.
-    StringRef Name = Decl->getName();
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    // Build declaration fragments and sub-heading for the protocol.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-    ObjCProtocolRecord *ObjCProtocolRecord =
-        API.addObjCProtocol(Name, USR, Loc, AvailabilitySet(Decl), Comment,
-                            Declaration, SubHeading);
-
-    recordObjCMethods(ObjCProtocolRecord, Decl->methods());
-    recordObjCProperties(ObjCProtocolRecord, Decl->properties());
-    recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
-
-    return true;
-  }
-
-  bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
-    // Skip ObjC Type Parameter for now.
-    if (isa<ObjCTypeParamDecl>(Decl))
-      return true;
-
-    if (!Decl->isDefinedOutsideFunctionOrMethod())
-      return true;
-
-    if (!LCF.isLocationInKnownFile(Decl->getLocation()))
-      return true;
-
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    StringRef Name = Decl->getName();
-    StringRef USR = API.recordUSR(Decl);
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-
-    QualType Type = Decl->getUnderlyingType();
-    SymbolReference SymRef =
-        TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
-                                                                         API);
-
-    API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
-                   DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
-                   DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
-
-    return true;
-  }
-
-  bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
-    // Collect symbol information.
-    StringRef Name = Decl->getName();
-    StringRef USR = API.recordUSR(Decl);
-    PresumedLoc Loc =
-        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-    DocComment Comment;
-    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
-    // Build declaration fragments and sub-heading for the category.
-    DeclarationFragments Declaration =
-        DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
-    DeclarationFragments SubHeading =
-        DeclarationFragmentsBuilder::getSubHeading(Decl);
-
-    const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
-    SymbolReference Interface(InterfaceDecl->getName(),
-                              API.recordUSR(InterfaceDecl));
-
-    ObjCCategoryRecord *ObjCCategoryRecord =
-        API.addObjCCategory(Name, USR, Loc, AvailabilitySet(Decl), Comment,
-                            Declaration, SubHeading, Interface);
-
-    recordObjCMethods(ObjCCategoryRecord, Decl->methods());
-    recordObjCProperties(ObjCCategoryRecord, Decl->properties());
-    recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
-    recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
-
-    return true;
-  }
-
-private:
-  /// Collect API information for the enum constants and associate with the
-  /// parent enum.
-  void recordEnumConstants(EnumRecord *EnumRecord,
-                           const EnumDecl::enumerator_range Constants) {
-    for (const auto *Constant : Constants) {
-      // Collect symbol information.
-      StringRef Name = Constant->getName();
-      StringRef USR = API.recordUSR(Constant);
-      PresumedLoc Loc =
-          Context.getSourceManager().getPresumedLoc(Constant->getLocation());
-      DocComment Comment;
-      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
-        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                                Context.getDiagnostics());
-
-      // Build declaration fragments and sub-heading for the enum constant.
-      DeclarationFragments Declaration =
-          DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
-      DeclarationFragments SubHeading =
-          DeclarationFragmentsBuilder::getSubHeading(Constant);
-
-      API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
-                          Comment, Declaration, SubHeading);
-    }
-  }
-
-  /// Collect API information for the struct fields and associate with the
-  /// parent struct.
-  void recordStructFields(StructRecord *StructRecord,
-                          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 = Context.getRawCommentForDeclNoCache(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.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
-                         Comment, Declaration, SubHeading);
-    }
-  }
-
-  /// Collect API information for the Objective-C methods and associate with the
-  /// parent container.
-  void recordObjCMethods(ObjCContainerRecord *Container,
-                         const ObjCContainerDecl::method_range Methods) {
-    for (const auto *Method : Methods) {
-      // Don't record selectors for properties.
-      if (Method->isPropertyAccessor())
-        continue;
-
-      StringRef Name = API.copyString(Method->getSelector().getAsString());
-      StringRef USR = API.recordUSR(Method);
-      PresumedLoc Loc =
-          Context.getSourceManager().getPresumedLoc(Method->getLocation());
-      DocComment Comment;
-      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
-        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                                Context.getDiagnostics());
-
-      // Build declaration fragments, sub-heading, and signature for the method.
-      DeclarationFragments Declaration =
-          DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
-      DeclarationFragments SubHeading =
-          DeclarationFragmentsBuilder::getSubHeading(Method);
-      FunctionSignature Signature =
-          DeclarationFragmentsBuilder::getFunctionSignature(Method);
-
-      API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
-                        Comment, Declaration, SubHeading, Signature,
-                        Method->isInstanceMethod());
-    }
-  }
-
-  void recordObjCProperties(ObjCContainerRecord *Container,
-                            const ObjCContainerDecl::prop_range Properties) {
-    for (const auto *Property : Properties) {
-      StringRef Name = Property->getName();
-      StringRef USR = API.recordUSR(Property);
-      PresumedLoc Loc =
-          Context.getSourceManager().getPresumedLoc(Property->getLocation());
-      DocComment Comment;
-      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
-        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                                Context.getDiagnostics());
-
-      // Build declaration fragments and sub-heading for the property.
-      DeclarationFragments Declaration =
-          DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
-      DeclarationFragments SubHeading =
-          DeclarationFragmentsBuilder::getSubHeading(Property);
-
-      StringRef GetterName =
-          API.copyString(Property->getGetterName().getAsString());
-      StringRef SetterName =
-          API.copyString(Property->getSetterName().getAsString());
-
-      // Get the attributes for property.
-      unsigned Attributes = ObjCPropertyRecord::NoAttr;
-      if (Property->getPropertyAttributes() &
-          ObjCPropertyAttribute::kind_readonly)
-        Attributes |= ObjCPropertyRecord::ReadOnly;
-      if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
-        Attributes |= ObjCPropertyRecord::Class;
-
-      API.addObjCProperty(
-          Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
-          Declaration, SubHeading,
-          static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
-          GetterName, SetterName, Property->isOptional());
-    }
-  }
-
-  void recordObjCInstanceVariables(
-      ObjCContainerRecord *Container,
-      const llvm::iterator_range<
-          DeclContext::specific_decl_iterator<ObjCIvarDecl>>
-          Ivars) {
-    for (const auto *Ivar : Ivars) {
-      StringRef Name = Ivar->getName();
-      StringRef USR = API.recordUSR(Ivar);
-      PresumedLoc Loc =
-          Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
-      DocComment Comment;
-      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
-        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                                Context.getDiagnostics());
-
-      // Build declaration fragments and sub-heading for the instance variable.
-      DeclarationFragments Declaration =
-          DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
-      DeclarationFragments SubHeading =
-          DeclarationFragmentsBuilder::getSubHeading(Ivar);
-
-      ObjCInstanceVariableRecord::AccessControl Access =
-          Ivar->getCanonicalAccessControl();
-
-      API.addObjCInstanceVariable(Container, Name, USR, Loc,
-                                  AvailabilitySet(Ivar), Comment, Declaration,
-                                  SubHeading, Access);
-    }
-  }
-
-  void recordObjCProtocols(ObjCContainerRecord *Container,
-                           ObjCInterfaceDecl::protocol_range Protocols) {
-    for (const auto *Protocol : Protocols)
-      Container->Protocols.emplace_back(Protocol->getName(),
-                                        API.recordUSR(Protocol));
-  }
-
-  ASTContext &Context;
-  APISet &API;
-  LocationFileChecker &LCF;
-};
-
 class ExtractAPIConsumer : public ASTConsumer {
 public:
   ExtractAPIConsumer(ASTContext &Context,
@@ -803,7 +279,7 @@ class MacroCallback : public PPCallbacks {
       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
         continue;
 
-      if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
+      if (!LCF(PM.MacroNameToken.getLocation()))
         continue;
 
       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
@@ -814,7 +290,8 @@ class MacroCallback : public PPCallbacks {
       API.addMacroDefinition(
           Name, USR, Loc,
           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
-          DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
+          DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
+          SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
     }
 
     PendingMacros.clear();
@@ -844,13 +321,13 @@ ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
   if (!OS)
     return nullptr;
 
-  ProductName = CI.getFrontendOpts().ProductName;
+  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>(
       CI.getTarget().getTriple(),
-      CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
+      CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
 
   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
 
@@ -942,7 +419,7 @@ void ExtractAPIAction::EndSourceFileAction() {
   // Setup a SymbolGraphSerializer to write out collected API information in
   // the Symbol Graph format.
   // FIXME: Make the kind of APISerializer configurable.
-  SymbolGraphSerializer SGSerializer(*API, ProductName, IgnoresList);
+  SymbolGraphSerializer SGSerializer(*API, IgnoresList);
   SGSerializer.serialize(*OS);
   OS.reset();
 }

diff  --git a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
new file mode 100644
index 0000000000000..38d95aaa6d5eb
--- /dev/null
+++ b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
@@ -0,0 +1,554 @@
+//===- ExtractAPI/ExtractAPIVisitor.cpp -------------------------*- 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 implements the ExtractAPIVisitor an ASTVisitor to collect API
+/// information.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/ExtractAPIVisitor.h"
+
+#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/RawCommentList.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/FrontendOptions.h"
+
+using namespace clang;
+using namespace extractapi;
+
+namespace {
+
+StringRef getTypedefName(const TagDecl *Decl) {
+  if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
+    return TypedefDecl->getName();
+
+  return {};
+}
+
+template <class DeclTy>
+bool isInSystemHeader(const ASTContext &Context, const DeclTy *D) {
+  return Context.getSourceManager().isInSystemHeader(D->getLocation());
+}
+
+} // namespace
+
+bool ExtractAPIVisitor::VisitVarDecl(const VarDecl *Decl) {
+  // skip function parameters.
+  if (isa<ParmVarDecl>(Decl))
+    return true;
+
+  // Skip non-global variables in records (struct/union/class).
+  if (Decl->getDeclContext()->isRecord())
+    return true;
+
+  // Skip local variables inside function or method.
+  if (!Decl->isDefinedOutsideFunctionOrMethod())
+    return true;
+
+  // If this is a template but not specialization or instantiation, skip.
+  if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
+      Decl->getTemplateSpecializationKind() == TSK_Undeclared)
+    return true;
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the variable.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  // Add the global variable record to the API set.
+  API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+                   Declaration, SubHeading, isInSystemHeader(Context, Decl));
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitFunctionDecl(const FunctionDecl *Decl) {
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
+    // Skip member function in class templates.
+    if (Method->getParent()->getDescribedClassTemplate() != nullptr)
+      return true;
+
+    // Skip methods in records.
+    for (auto P : Context.getParents(*Method)) {
+      if (P.get<CXXRecordDecl>())
+        return true;
+    }
+
+    // Skip ConstructorDecl and DestructorDecl.
+    if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
+      return true;
+  }
+
+  // Skip templated functions.
+  switch (Decl->getTemplatedKind()) {
+  case FunctionDecl::TK_NonTemplate:
+  case FunctionDecl::TK_DependentNonTemplate:
+    break;
+  case FunctionDecl::TK_MemberSpecialization:
+  case FunctionDecl::TK_FunctionTemplateSpecialization:
+    if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
+      if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
+        return true;
+    }
+    break;
+  case FunctionDecl::TK_FunctionTemplate:
+  case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
+    return true;
+  }
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments, sub-heading, and signature of the function.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+  FunctionSignature Signature =
+      DeclarationFragmentsBuilder::getFunctionSignature(Decl);
+
+  // Add the function record to the API set.
+  API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+                        Declaration, SubHeading, Signature,
+                        isInSystemHeader(Context, Decl));
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitEnumDecl(const EnumDecl *Decl) {
+  if (!Decl->isComplete())
+    return true;
+
+  // Skip forward declaration.
+  if (!Decl->isThisDeclarationADefinition())
+    return true;
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  // Collect symbol information.
+  std::string NameString = Decl->getQualifiedNameAsString();
+  StringRef Name(NameString);
+  if (Name.empty())
+    Name = getTypedefName(Decl);
+
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the enum.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  EnumRecord *EnumRecord = API.addEnum(
+      API.copyString(Name), USR, Loc, AvailabilitySet(Decl), Comment,
+      Declaration, SubHeading, isInSystemHeader(Context, Decl));
+
+  // Now collect information about the enumerators in this enum.
+  recordEnumConstants(EnumRecord, Decl->enumerators());
+
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitRecordDecl(const RecordDecl *Decl) {
+  if (!Decl->isCompleteDefinition())
+    return true;
+
+  // Skip C++ structs/classes/unions
+  // TODO: support C++ records
+  if (isa<CXXRecordDecl>(Decl))
+    return true;
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  if (Name.empty())
+    Name = getTypedefName(Decl);
+  if (Name.empty())
+    return true;
+
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the struct.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  StructRecord *StructRecord =
+      API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
+                    SubHeading, isInSystemHeader(Context, Decl));
+
+  // Now collect information about the fields in this struct.
+  recordStructFields(StructRecord, Decl->fields());
+
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
+  // Skip forward declaration for classes (@class)
+  if (!Decl->isThisDeclarationADefinition())
+    return true;
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the interface.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
+  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);
+  }
+
+  ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
+      Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
+      SubHeading, SuperClass, isInSystemHeader(Context, Decl));
+
+  // Record all methods (selectors). This doesn't include automatically
+  // synthesized property methods.
+  recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
+  recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
+  recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
+  recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
+
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
+  // Skip forward declaration for protocols (@protocol).
+  if (!Decl->isThisDeclarationADefinition())
+    return true;
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments and sub-heading for the protocol.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
+      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+      isInSystemHeader(Context, Decl));
+
+  recordObjCMethods(ObjCProtocolRecord, Decl->methods());
+  recordObjCProperties(ObjCProtocolRecord, Decl->properties());
+  recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
+
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
+  // Skip ObjC Type Parameter for now.
+  if (isa<ObjCTypeParamDecl>(Decl))
+    return true;
+
+  if (!Decl->isDefinedOutsideFunctionOrMethod())
+    return true;
+
+  if (!LocationChecker(Decl->getLocation()))
+    return true;
+
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  QualType Type = Decl->getUnderlyingType();
+  SymbolReference SymRef =
+      TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
+                                                                       API);
+
+  API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
+                 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
+                 isInSystemHeader(Context, Decl));
+
+  return true;
+}
+
+bool ExtractAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
+  // Collect symbol information.
+  StringRef Name = Decl->getName();
+  StringRef USR = API.recordUSR(Decl);
+  PresumedLoc Loc =
+      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+  DocComment Comment;
+  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+  // Build declaration fragments and sub-heading for the category.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
+  SymbolReference Interface(InterfaceDecl->getName(),
+                            API.recordUSR(InterfaceDecl));
+
+  ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
+      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+      Interface, isInSystemHeader(Context, Decl));
+
+  recordObjCMethods(ObjCCategoryRecord, Decl->methods());
+  recordObjCProperties(ObjCCategoryRecord, Decl->properties());
+  recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
+  recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
+
+  return true;
+}
+
+/// Collect API information for the enum constants and associate with the
+/// parent enum.
+void ExtractAPIVisitor::recordEnumConstants(
+    EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) {
+  for (const auto *Constant : Constants) {
+    // Collect symbol information.
+    StringRef Name = Constant->getName();
+    StringRef USR = API.recordUSR(Constant);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Constant->getLocation());
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the enum constant.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Constant);
+
+    API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
+                        Comment, Declaration, SubHeading,
+                        isInSystemHeader(Context, Constant));
+  }
+}
+
+/// Collect API information for the struct fields and associate with the
+/// parent struct.
+void ExtractAPIVisitor::recordStructFields(
+    StructRecord *StructRecord, 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 = Context.getRawCommentForDeclNoCache(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.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
+                       Comment, Declaration, SubHeading,
+                       isInSystemHeader(Context, Field));
+  }
+}
+
+/// Collect API information for the Objective-C methods and associate with the
+/// parent container.
+void ExtractAPIVisitor::recordObjCMethods(
+    ObjCContainerRecord *Container,
+    const ObjCContainerDecl::method_range Methods) {
+  for (const auto *Method : Methods) {
+    // Don't record selectors for properties.
+    if (Method->isPropertyAccessor())
+      continue;
+
+    StringRef Name = API.copyString(Method->getSelector().getAsString());
+    StringRef USR = API.recordUSR(Method);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Method->getLocation());
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments, sub-heading, and signature for the method.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Method);
+    FunctionSignature Signature =
+        DeclarationFragmentsBuilder::getFunctionSignature(Method);
+
+    API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
+                      Comment, Declaration, SubHeading, Signature,
+                      Method->isInstanceMethod(),
+                      isInSystemHeader(Context, Method));
+  }
+}
+
+void ExtractAPIVisitor::recordObjCProperties(
+    ObjCContainerRecord *Container,
+    const ObjCContainerDecl::prop_range Properties) {
+  for (const auto *Property : Properties) {
+    StringRef Name = Property->getName();
+    StringRef USR = API.recordUSR(Property);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Property->getLocation());
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the property.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Property);
+
+    StringRef GetterName =
+        API.copyString(Property->getGetterName().getAsString());
+    StringRef SetterName =
+        API.copyString(Property->getSetterName().getAsString());
+
+    // Get the attributes for property.
+    unsigned Attributes = ObjCPropertyRecord::NoAttr;
+    if (Property->getPropertyAttributes() &
+        ObjCPropertyAttribute::kind_readonly)
+      Attributes |= ObjCPropertyRecord::ReadOnly;
+
+    API.addObjCProperty(
+        Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
+        Declaration, SubHeading,
+        static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
+        SetterName, Property->isOptional(),
+        !(Property->getPropertyAttributes() &
+          ObjCPropertyAttribute::kind_class),
+        isInSystemHeader(Context, Property));
+  }
+}
+
+void ExtractAPIVisitor::recordObjCInstanceVariables(
+    ObjCContainerRecord *Container,
+    const llvm::iterator_range<
+        DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+        Ivars) {
+  for (const auto *Ivar : Ivars) {
+    StringRef Name = Ivar->getName();
+    StringRef USR = API.recordUSR(Ivar);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the instance variable.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Ivar);
+
+    ObjCInstanceVariableRecord::AccessControl Access =
+        Ivar->getCanonicalAccessControl();
+
+    API.addObjCInstanceVariable(
+        Container, Name, USR, Loc, AvailabilitySet(Ivar), Comment, Declaration,
+        SubHeading, Access, isInSystemHeader(Context, Ivar));
+  }
+}
+
+void ExtractAPIVisitor::recordObjCProtocols(
+    ObjCContainerRecord *Container,
+    ObjCInterfaceDecl::protocol_range Protocols) {
+  for (const auto *Protocol : Protocols)
+    Container->Protocols.emplace_back(Protocol->getName(),
+                                      API.recordUSR(Protocol));
+}

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index cea4d91a84d50..9122d491b873c 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -12,10 +12,17 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Version.h"
 #include "clang/ExtractAPI/API.h"
 #include "clang/ExtractAPI/APIIgnoresList.h"
 #include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/ExtractAPI/Serialization/SerializerBase.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/JSON.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/VersionTuple.h"
@@ -323,18 +330,16 @@ Object serializeNames(const APIRecord &Record) {
   return Names;
 }
 
-/// Serialize the symbol kind information.
-///
-/// The Symbol Graph symbol kind property contains a shorthand \c identifier
-/// which is prefixed by the source language name, useful for tooling to parse
-/// the kind, and a \c displayName for rendering human-readable names.
-Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
   auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
     return (getLanguageName(Lang) + "." + S).str();
   };
 
   Object Kind;
-  switch (Record.getKind()) {
+  switch (RK) {
+  case APIRecord::RK_Unknown:
+    llvm_unreachable("Records should have an explicit kind");
+    break;
   case APIRecord::RK_GlobalFunction:
     Kind["identifier"] = AddLangPrefix("func");
     Kind["displayName"] = "Function";
@@ -363,23 +368,21 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
     Kind["identifier"] = AddLangPrefix("ivar");
     Kind["displayName"] = "Instance Variable";
     break;
-  case APIRecord::RK_ObjCMethod:
-    if (cast<ObjCMethodRecord>(&Record)->IsInstanceMethod) {
-      Kind["identifier"] = AddLangPrefix("method");
-      Kind["displayName"] = "Instance Method";
-    } else {
-      Kind["identifier"] = AddLangPrefix("type.method");
-      Kind["displayName"] = "Type Method";
-    }
+  case APIRecord::RK_ObjCInstanceMethod:
+    Kind["identifier"] = AddLangPrefix("method");
+    Kind["displayName"] = "Instance Method";
     break;
-  case APIRecord::RK_ObjCProperty:
-    if (cast<ObjCPropertyRecord>(&Record)->isClassProperty()) {
-      Kind["identifier"] = AddLangPrefix("type.property");
-      Kind["displayName"] = "Type Property";
-    } else {
-      Kind["identifier"] = AddLangPrefix("property");
-      Kind["displayName"] = "Instance Property";
-    }
+  case APIRecord::RK_ObjCClassMethod:
+    Kind["identifier"] = AddLangPrefix("type.method");
+    Kind["displayName"] = "Type Method";
+    break;
+  case APIRecord::RK_ObjCInstanceProperty:
+    Kind["identifier"] = AddLangPrefix("property");
+    Kind["displayName"] = "Instance Property";
+    break;
+  case APIRecord::RK_ObjCClassProperty:
+    Kind["identifier"] = AddLangPrefix("type.property");
+    Kind["displayName"] = "Type Property";
     break;
   case APIRecord::RK_ObjCInterface:
     Kind["identifier"] = AddLangPrefix("class");
@@ -407,6 +410,15 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
   return Kind;
 }
 
+/// Serialize the symbol kind information.
+///
+/// The Symbol Graph symbol kind property contains a shorthand \c identifier
+/// which is prefixed by the source language name, useful for tooling to parse
+/// the kind, and a \c displayName for rendering human-readable names.
+Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+  return serializeSymbolKind(Record.getKind(), Lang);
+}
+
 template <typename RecordTy>
 Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
                                                      std::true_type) {
@@ -456,6 +468,78 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
                       Record, has_function_signature<RecordTy>()));
 }
 
+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;
+  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 then we need to pretend this belongs to the
+    // associated interface.
+    if (auto *CategoryRecord =
+            dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
+      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)
+      return true;
+
+    ReverseComponenents.push_back(std::move(CurrentParentComponent));
+    CurrentParent = &ParentRecord->ParentInformation;
+  }
+
+  for (const auto &PC : reverse(ReverseComponenents))
+    ComponentTransformer(PC);
+
+  return false;
+}
+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;
+}
+
+template <typename RecordTy>
+Array generateParentContexts(const RecordTy &Record, const APISet &API,
+                             Language Lang) {
+  Array ParentContexts;
+  if (generatePathComponents(
+          Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
+            ParentContexts.push_back(serializeParentContext(PC, Lang));
+          }))
+    ParentContexts.clear();
+  ParentContexts.pop_back();
+
+  return ParentContexts;
+}
+
 } // namespace
 
 void SymbolGraphSerializer::anchor() {}
@@ -475,7 +559,7 @@ Object SymbolGraphSerializer::serializeModule() const {
   Object Module;
   // The user is expected to always pass `--product-name=` on the command line
   // to populate this field.
-  Module["name"] = ProductName;
+  Module["name"] = API.ProductName;
   serializeObject(Module, "platform", serializePlatform(API.getTarget()));
   return Module;
 }
@@ -519,7 +603,16 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
   // TODO: Once we keep track of symbol access information serialize it
   // correctly here.
   Obj["accessLevel"] = "public";
-  serializeArray(Obj, "pathComponents", Array(PathComponents));
+  SmallVector<StringRef, 4> PathComponentsNames;
+  // If this returns true it indicates that we couldn't find a symbol in the
+  // hierarchy.
+  if (generatePathComponents(Record, API,
+                             [&PathComponentsNames](const PathComponent &PC) {
+                               PathComponentsNames.push_back(PC.Name);
+                             }))
+    return {};
+
+  serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
 
   serializeFunctionSignatureMixin(Obj, Record);
 
@@ -530,8 +623,10 @@ 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 MemberPathComponentGuard = makePathComponentGuard(Member->Name);
     auto MemberRecord = serializeAPIRecord(*Member);
     if (!MemberRecord)
       continue;
@@ -567,8 +662,6 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
 
 void SymbolGraphSerializer::serializeGlobalFunctionRecord(
     const GlobalFunctionRecord &Record) {
-  auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
   auto Obj = serializeAPIRecord(Record);
   if (!Obj)
     return;
@@ -578,8 +671,6 @@ void SymbolGraphSerializer::serializeGlobalFunctionRecord(
 
 void SymbolGraphSerializer::serializeGlobalVariableRecord(
     const GlobalVariableRecord &Record) {
-  auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
   auto Obj = serializeAPIRecord(Record);
   if (!Obj)
     return;
@@ -588,7 +679,6 @@ void SymbolGraphSerializer::serializeGlobalVariableRecord(
 }
 
 void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
-  auto EnumPathComponentGuard = makePathComponentGuard(Record.Name);
   auto Enum = serializeAPIRecord(Record);
   if (!Enum)
     return;
@@ -598,7 +688,6 @@ void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
 }
 
 void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
-  auto StructPathComponentGuard = makePathComponentGuard(Record.Name);
   auto Struct = serializeAPIRecord(Record);
   if (!Struct)
     return;
@@ -609,7 +698,6 @@ void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
 
 void SymbolGraphSerializer::serializeObjCContainerRecord(
     const ObjCContainerRecord &Record) {
-  auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name);
   auto ObjCContainer = serializeAPIRecord(Record);
   if (!ObjCContainer)
     return;
@@ -647,7 +735,6 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
 
 void SymbolGraphSerializer::serializeMacroDefinitionRecord(
     const MacroDefinitionRecord &Record) {
-  auto MacroPathComponentGuard = makePathComponentGuard(Record.Name);
   auto Macro = serializeAPIRecord(Record);
 
   if (!Macro)
@@ -656,6 +743,46 @@ void SymbolGraphSerializer::serializeMacroDefinitionRecord(
   Symbols.emplace_back(std::move(*Macro));
 }
 
+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:
+    serializeGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
+    break;
+  case APIRecord::RK_GlobalVariable:
+    serializeGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
+    break;
+  case APIRecord::RK_Enum:
+    serializeEnumRecord(*cast<EnumRecord>(Record));
+    break;
+  case APIRecord::RK_Struct:
+    serializeStructRecord(*cast<StructRecord>(Record));
+    break;
+  case APIRecord::RK_ObjCInterface:
+    serializeObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
+    break;
+  case APIRecord::RK_ObjCProtocol:
+    serializeObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
+    break;
+  case APIRecord::RK_MacroDefinition:
+    serializeMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
+    break;
+  case APIRecord::RK_Typedef:
+    serializeTypedefRecord(*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;
+  }
+}
+
 void SymbolGraphSerializer::serializeTypedefRecord(
     const TypedefRecord &Record) {
   // Typedefs of anonymous types have their entries unified with the underlying
@@ -667,7 +794,6 @@ void SymbolGraphSerializer::serializeTypedefRecord(
   if (ShouldDrop)
     return;
 
-  auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
   auto Typedef = serializeAPIRecord(Record);
   if (!Typedef)
     return;
@@ -677,16 +803,7 @@ void SymbolGraphSerializer::serializeTypedefRecord(
   Symbols.emplace_back(std::move(*Typedef));
 }
 
-SymbolGraphSerializer::PathComponentGuard
-SymbolGraphSerializer::makePathComponentGuard(StringRef Component) {
-  return PathComponentGuard(PathComponents, Component);
-}
-
 Object SymbolGraphSerializer::serialize() {
-  Object Root;
-  serializeObject(Root, "metadata", serializeMetadata());
-  serializeObject(Root, "module", serializeModule());
-
   // Serialize global variables in the API set.
   for (const auto &GlobalVar : API.getGlobalVariables())
     serializeGlobalVariableRecord(*GlobalVar.second);
@@ -716,6 +833,14 @@ Object SymbolGraphSerializer::serialize() {
   for (const auto &Typedef : API.getTypedefs())
     serializeTypedefRecord(*Typedef.second);
 
+  return serializeCurrentGraph();
+}
+
+Object SymbolGraphSerializer::serializeCurrentGraph() {
+  Object Root;
+  serializeObject(Root, "metadata", serializeMetadata());
+  serializeObject(Root, "module", serializeModule());
+
   Root["symbols"] = std::move(Symbols);
   Root["relationships"] = std::move(Relationships);
 
@@ -729,3 +854,53 @@ void SymbolGraphSerializer::serialize(raw_ostream &os) {
   else
     os << formatv("{0:2}", Value(std::move(root))) << "\n";
 }
+
+Optional<Object>
+SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
+                                                const APISet &API) {
+  APIRecord *Record = API.findRecordForUSR(USR);
+  if (!Record)
+    return {};
+
+  Object Root;
+  APIIgnoresList EmptyIgnores;
+  SymbolGraphSerializer Serializer(API, EmptyIgnores,
+                                   /*Options.Compact*/ {true},
+                                   /*ShouldRecurse*/ false);
+  Serializer.serializeSingleRecord(Record);
+  serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
+
+  Language Lang = API.getLanguage();
+  serializeArray(Root, "parentContexts",
+                 generateParentContexts(*Record, API, Lang));
+
+  Array RelatedSymbols;
+
+  for (const auto &Fragment : Record->Declaration.getFragments()) {
+    // If we don't have a USR there isn't much we can do.
+    if (Fragment.PreciseIdentifier.empty())
+      continue;
+
+    APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
+
+    // If we can't find the record let's skip.
+    if (!RelatedRecord)
+      continue;
+
+    Object RelatedSymbol;
+    RelatedSymbol["usr"] = RelatedRecord->USR;
+    RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
+    // TODO: once we record this properly let's serialize it right.
+    RelatedSymbol["accessLevel"] = "public";
+    RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
+    RelatedSymbol["moduleName"] = API.ProductName;
+    RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
+
+    serializeArray(RelatedSymbol, "parentContexts",
+                   generateParentContexts(*RelatedRecord, API, Lang));
+    RelatedSymbols.push_back(std::move(RelatedSymbol));
+  }
+
+  serializeArray(Root, "relatedSymbols", RelatedSymbols);
+  return Root;
+}

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
index 9c165e693e0e7..3da2424ea7263 100644
--- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
@@ -17,9 +17,8 @@
 using namespace clang;
 using namespace extractapi;
 
-namespace {
-
-const NamedDecl *getUnderlyingTypeDecl(QualType Type) {
+const NamedDecl *
+TypedefUnderlyingTypeResolver::getUnderlyingTypeDecl(QualType Type) const {
   const NamedDecl *TypeDecl = nullptr;
 
   const TypedefType *TypedefTy = Type->getAs<TypedefType>();
@@ -44,8 +43,6 @@ const NamedDecl *getUnderlyingTypeDecl(QualType Type) {
   return TypeDecl;
 }
 
-} // namespace
-
 SymbolReference
 TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
                                                          APISet &API) const {

diff  --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
index 0096ff2359148..54aa11c354c0b 100644
--- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
@@ -26,6 +26,8 @@ namespace clang {
 namespace extractapi {
 
 struct TypedefUnderlyingTypeResolver {
+  /// Gets the underlying type declaration.
+  const NamedDecl *getUnderlyingTypeDecl(QualType Type) const;
 
   /// Get a SymbolReference for the given type.
   SymbolReference getSymbolReferenceForType(QualType Type, APISet &API) const;

diff  --git a/clang/test/Index/extract-api-cursor.m b/clang/test/Index/extract-api-cursor.m
new file mode 100644
index 0000000000000..a462c115826c1
--- /dev/null
+++ b/clang/test/Index/extract-api-cursor.m
@@ -0,0 +1,106 @@
+/// Foo docs
+struct Foo {
+    /// Bar docs
+    int bar;
+};
+
+/// Base docs
+ at interface Base
+/// Base property docs
+ at property struct Foo baseProperty;
+
+/// Base method docs
+- (void)baseMethodWithArg:(int)arg;
+ at end
+
+/// Protocol docs
+ at protocol Protocol
+/// Protocol property docs
+ at property struct Foo protocolProperty;
+ at end
+
+/// Derived docs
+ at interface Derived: Base
+/// Derived method docs
+- (void)derivedMethodWithValue:(id<Protocol>)value;
+ at end
+
+// RUN: c-index-test -single-symbol-sgfs local %s | FileCheck %s
+
+// Checking for Foo
+// CHECK: "parentContexts":[]
+// CHECK-SAME: "relatedSymbols":[]
+// CHECK-SAME: "relationships":[]
+// CHECK-SAME: "text":"Foo docs"
+// CHECK-SAME: "kind":{"displayName":"Structure","identifier":"objective-c.struct"}
+// CHECK-SAME: "title":"Foo"
+
+// Checking for bar
+// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"}]
+// CHECK-SAME: "relatedSymbols":[]
+// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:@S at Foo@FI at bar","target":"c:@S at Foo"
+// CHECK-SAME: "text":"Bar docs"
+// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-SAME: "title":"bar"
+
+// Checking for Base
+// CHECK-NEXT: "parentContexts":[]
+// CHECK-SAME: "relatedSymbols":[]
+// CHECK-SAME: "relationships":[]
+// CHECK-SAME: "text":"Base docs"
+// CHECK-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"}
+// CHECK-SAME: "title":"Base"
+
+// Checking for baseProperty
+// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-SAME: "isSystem":false
+// CHECK-SAME: "usr":"c:@S at Foo"}]
+// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base"
+// CHECK-SAME: "text":"Base property docs"
+// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-SAME: "title":"baseProperty"
+
+// Checking for baseMethodWithArg
+// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-SAME:"relatedSymbols":[]
+// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base"
+// CHECK-SAME: "text":"Base method docs"
+// CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-SAME: "title":"baseMethodWithArg:"
+
+// Checking for Protocol
+// CHECK-NEXT: "parentContexts":[]
+// CHECK-SAME: "relatedSymbols":[]
+// CHECK-SAME: "relationships":[]
+// CHECK-SAME: "text":"Protocol docs"
+// CHECK-SAME: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"}
+// CHECK-SAME: "title":"Protocol"
+
+// Checking for protocolProperty
+// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
+// CHECK-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-SAME: "isSystem":false
+// CHECK-SAME: "usr":"c:@S at Foo"}]
+// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol"
+// CHECK-SAME: "text":"Protocol property docs"
+// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-SAME: "title":"protocolProperty"
+
+// Checking for Derived
+// CHECK-NEXT: "parentContexts":[]
+// CHECK-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-SAME: "isSystem":false
+// CHECK-SAME: "usr":"c:objc(cs)Base"}]
+// CHECK-SAME: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base"
+// CHECK-SAME: "text":"Derived docs"
+// CHECK-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"}
+// CHECK-SAME: "title":"Derived"
+
+// Checking for derivedMethodWithValue
+// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
+// CHECK-SAME:"relatedSymbols":[]
+// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
+// CHECK-SAME: "text":"Derived method docs"
+// CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-SAME: "title":"derivedMethodWithValue:"

diff  --git a/clang/test/Index/extract-api-usr.m b/clang/test/Index/extract-api-usr.m
new file mode 100644
index 0000000000000..12bfb0a676e66
--- /dev/null
+++ b/clang/test/Index/extract-api-usr.m
@@ -0,0 +1,115 @@
+/// Foo docs
+struct Foo {
+    /// Bar docs
+    int bar;
+};
+
+/// Base docs
+ at interface Base
+/// Base property docs
+ at property struct Foo baseProperty;
+
+/// Base method docs
+- (void)baseMethodWithArg:(int)arg;
+ at end
+
+/// Protocol docs
+ at protocol Protocol
+/// Protocol property docs
+ at property struct Foo protocolProperty;
+ at end
+
+/// Derived docs
+ at interface Derived: Base
+/// Derived method docs
+- (void)derivedMethodWithValue:(id<Protocol>)value;
+ at end
+
+
+// Checking for Foo
+// RUN: c-index-test "-single-symbol-sgf-for=c:@S at Foo" %s | FileCheck -check-prefix=CHECK-FOO %s
+// CHECK-FOO: "parentContexts":[]
+// CHECK-FOO-SAME: "relatedSymbols":[]
+// CHECK-FOO-SAME: "relationships":[]
+// CHECK-FOO-SAME: "text":"Foo docs"
+// CHECK-FOO-SAME: "kind":{"displayName":"Structure","identifier":"objective-c.struct"}
+// CHECK-FOO-SAME: "title":"Foo"
+
+
+// Checking for bar
+// RUN: c-index-test "-single-symbol-sgf-for=c:@S at Foo@FI at bar" %s | FileCheck -check-prefix=CHECK-BAR %s
+// CHECK-BAR: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S at Foo"}]
+// CHECK-BAR-SAME: "relatedSymbols":[]
+// CHECK-BAR-SAME: "relationships":[{"kind":"memberOf","source":"c:@S at Foo@FI at bar","target":"c:@S at Foo"
+// CHECK-BAR-SAME: "text":"Bar docs"
+// CHECK-BAR-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-BAR-SAME: "title":"bar"
+
+// Checking for Base
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base" %s | FileCheck -check-prefix=CHECK-BASE %s
+// CHECK-BASE: "parentContexts":[]
+// CHECK-BASE-SAME: "relatedSymbols":[]
+// CHECK-BASE-SAME: "relationships":[]
+// CHECK-BASE-SAME: "text":"Base docs"
+// CHECK-BASE-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"}
+// CHECK-BASE-SAME: "title":"Base"
+
+// Checking for baseProperty
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base(py)baseProperty" %s | FileCheck -check-prefix=CHECK-BASEPROP %s
+// CHECK-BASEPROP: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-BASEPROP-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-BASEPROP-SAME: "isSystem":false
+// CHECK-BASEPROP-SAME: "usr":"c:@S at Foo"}]
+// CHECK-BASEPROP-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base"
+// CHECK-BASEPROP-SAME: "text":"Base property docs"
+// CHECK-BASEPROP-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-BASEPROP-SAME: "title":"baseProperty"
+
+// Checking for baseMethodWithArg
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base(im)baseMethodWithArg:" %s | FileCheck -check-prefix=CHECK-BASEMETHOD %s
+// CHECK-BASEMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}]
+// CHECK-BASEMETHOD-SAME:"relatedSymbols":[]
+// CHECK-BASEMETHOD-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base"
+// CHECK-BASEMETHOD-SAME: "text":"Base method docs"
+// CHECK-BASEMETHOD-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-BASEMETHOD-SAME: "title":"baseMethodWithArg:"
+
+// Checking for Protocol
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(pl)Protocol" %s | FileCheck -check-prefix=CHECK-PROT %s
+// CHECK-PROT: "parentContexts":[]
+// CHECK-PROT-SAME: "relatedSymbols":[]
+// CHECK-PROT-SAME: "relationships":[]
+// CHECK-PROT-SAME: "text":"Protocol docs"
+// CHECK-PROT-SAME: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"}
+// CHECK-PROT-SAME: "title":"Protocol"
+
+// Checking for protocolProperty
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(pl)Protocol(py)protocolProperty" %s | FileCheck -check-prefix=CHECK-PROTPROP %s
+// CHECK-PROTPROP: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}]
+// CHECK-PROTPROP-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-PROTPROP-SAME: "isSystem":false
+// CHECK-PROTPROP-SAME: "usr":"c:@S at Foo"}]
+// CHECK-PROTPROP-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol"
+// CHECK-PROTPROP-SAME: "text":"Protocol property docs"
+// CHECK-PROTPROP-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"}
+// CHECK-PROTPROP-SAME: "title":"protocolProperty"
+
+// Checking for Derived
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Derived" %s | FileCheck -check-prefix=CHECK-DERIVED %s
+// CHECK-DERIVED: "parentContexts":[]
+// CHECK-DERIVED-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c"
+// CHECK-DERIVED-SAME: "isSystem":false
+// CHECK-DERIVED-SAME: "usr":"c:objc(cs)Base"}]
+// CHECK-DERIVED-SAME: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base"
+// CHECK-DERIVED-SAME: "text":"Derived docs"
+// CHECK-DERIVED-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"}
+// CHECK-DERIVED-SAME: "title":"Derived"
+
+// Checking for derivedMethodWithValue
+// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Derived(im)derivedMethodWithValue:" %s | FileCheck -check-prefix=CHECK-DERIVEDMETHOD %s
+// CHECK-DERIVEDMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}]
+// CHECK-DERIVEDMETHOD-SAME:"relatedSymbols":[]
+// CHECK-DERIVEDMETHOD-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived"
+// CHECK-DERIVEDMETHOD-SAME: "text":"Derived method docs"
+// CHECK-DERIVEDMETHOD-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"}
+// CHECK-DERIVEDMETHOD-SAME: "title":"derivedMethodWithValue:"

diff  --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c
index ce49b8ed53bc5..cc425fc51efee 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -1,15 +1,17 @@
 /* c-index-test.c */
 
-#include "clang/Config/config.h"
-#include "clang-c/Index.h"
-#include "clang-c/CXCompilationDatabase.h"
 #include "clang-c/BuildSystem.h"
+#include "clang-c/CXCompilationDatabase.h"
+#include "clang-c/CXErrorCode.h"
+#include "clang-c/CXString.h"
 #include "clang-c/Documentation.h"
+#include "clang-c/Index.h"
+#include "clang/Config/config.h"
+#include <assert.h>
 #include <ctype.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <assert.h>
 
 #ifdef CLANG_HAVE_LIBXML
 #include <libxml/parser.h>
@@ -1840,6 +1842,18 @@ static enum CXChildVisitResult PrintManglings(CXCursor cursor, CXCursor p,
   return CXChildVisit_Recurse;
 }
 
+static enum CXChildVisitResult
+PrintSingleSymbolSGFs(CXCursor cursor, CXCursor parent, CXClientData data) {
+  CXString SGFData = clang_getSymbolGraphForCursor(cursor);
+  const char *SGF = clang_getCString(SGFData);
+  if (SGF)
+    printf("%s\n", SGF);
+
+  clang_disposeString(SGFData);
+
+  return CXChildVisit_Recurse;
+}
+
 /******************************************************************************/
 /* Bitwidth testing.                                                          */
 /******************************************************************************/
@@ -4791,6 +4805,64 @@ static int perform_print_build_session_timestamp(void) {
   return 0;
 }
 
+static int perform_test_single_symbol_sgf(const char *input, int argc,
+                                          const char *argv[]) {
+  CXIndex Idx;
+  CXTranslationUnit TU;
+  CXAPISet API;
+  struct CXUnsavedFile *unsaved_files = 0;
+  int num_unsaved_files = 0;
+  enum CXErrorCode Err;
+  int result = 0;
+  const char *InvocationPath;
+  CXString SGF;
+  const char *usr;
+
+  usr = input + strlen("-single-symbol-sgf-for=");
+
+  Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
+                          /* displayDiagnostics=*/0);
+  InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH");
+  if (InvocationPath)
+    clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath);
+
+  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
+    result = -1;
+    goto dispose_index;
+  }
+
+  Err = clang_parseTranslationUnit2(
+      Idx, 0, argv + num_unsaved_files, argc - num_unsaved_files, unsaved_files,
+      num_unsaved_files, getDefaultParsingOptions(), &TU);
+  if (Err != CXError_Success) {
+    fprintf(stderr, "Unable to load translation unit!\n");
+    describeLibclangFailure(Err);
+    result = 1;
+    goto free_remapped_files;
+  }
+
+  Err = clang_createAPISet(TU, &API);
+  if (Err != CXError_Success) {
+    fprintf(stderr,
+            "Unable to create API Set for API information extraction!\n");
+    result = 2;
+    goto dispose_tu;
+  }
+
+  SGF = clang_getSymbolGraphForUSR(usr, API);
+  printf("%s", clang_getCString(SGF));
+
+  clang_disposeString(SGF);
+  clang_disposeAPISet(API);
+dispose_tu:
+  clang_disposeTranslationUnit(TU);
+free_remapped_files:
+  free_remapped_files(unsaved_files, num_unsaved_files);
+dispose_index:
+  clang_disposeIndex(Idx);
+  return result;
+}
+
 /******************************************************************************/
 /* Command line processing.                                                   */
 /******************************************************************************/
@@ -4848,6 +4920,9 @@ static void print_usage(void) {
     "       c-index-test -test-print-type-declaration {<args>}*\n"
     "       c-index-test -print-usr [<CursorKind> {<args>}]*\n"
     "       c-index-test -print-usr-file <file>\n");
+  fprintf(stderr,
+          "       c-index-test -single-symbol-sgfs <symbol filter> {<args>*}\n"
+          "       c-index-test -single-symbol-sgf-for=<usr> {<args>}*\n");
   fprintf(stderr,
     "       c-index-test -write-pch <file> <compiler arguments>\n"
     "       c-index-test -compilation-db [lookup <filename>] database\n");
@@ -4980,6 +5055,11 @@ int cindextest_main(int argc, const char **argv) {
     return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2);
   else if (argc == 2 && strcmp(argv[1], "-print-build-session-timestamp") == 0)
     return perform_print_build_session_timestamp();
+  else if (argc > 3 && strcmp(argv[1], "-single-symbol-sgfs") == 0)
+    return perform_test_load_source(argc - 3, argv + 3, argv[2],
+                                    PrintSingleSymbolSGFs, NULL);
+  else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-for=") == argv[1])
+    return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2);
 
   print_usage();
   return 1;

diff  --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt
index 3bfa78a28856b..a0cc07aa4d7c1 100644
--- a/clang/tools/libclang/CMakeLists.txt
+++ b/clang/tools/libclang/CMakeLists.txt
@@ -32,6 +32,7 @@ set(SOURCES
   CIndexer.cpp
   CXComment.cpp
   CXCursor.cpp
+  CXExtractAPI.cpp
   CXIndexDataConsumer.cpp
   CXCompilationDatabase.cpp
   CXLoadedDiagnostic.cpp
@@ -60,6 +61,7 @@ set(LIBS
   clangAST
   clangBasic
   clangDriver
+  clangExtractAPI
   clangFrontend
   clangIndex
   clangLex

diff  --git a/clang/tools/libclang/CXExtractAPI.cpp b/clang/tools/libclang/CXExtractAPI.cpp
new file mode 100644
index 0000000000000..787334ab1bbb2
--- /dev/null
+++ b/clang/tools/libclang/CXExtractAPI.cpp
@@ -0,0 +1,151 @@
+//===- CXExtractAPI.cpp - libclang APIs for manipulating CXAPISet ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines all libclang APIs related to manipulation CXAPISet
+//
+//===----------------------------------------------------------------------===//
+
+#include "CXCursor.h"
+#include "CXString.h"
+#include "CXTranslationUnit.h"
+#include "clang-c/CXErrorCode.h"
+#include "clang-c/Documentation.h"
+#include "clang-c/Index.h"
+#include "clang-c/Platform.h"
+#include "clang/AST/Decl.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/ExtractAPIVisitor.h"
+#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/CBindingWrapping.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::extractapi;
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet)
+
+static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D);
+
+template <typename DeclTy>
+static bool WalkupParentContext(DeclContext *Parent,
+                                ExtractAPIVisitor &Visitor) {
+  if (auto *D = dyn_cast<DeclTy>(Parent)) {
+    WalkupFromMostDerivedType(Visitor, D);
+    return true;
+  }
+  return false;
+}
+
+static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D) {
+  switch (D->getKind()) {
+#define ABSTRACT_DECL(DECL)
+#define DECL(CLASS, BASE)                                                      \
+  case Decl::CLASS:                                                            \
+    Visitor.WalkUpFrom##CLASS##Decl(static_cast<CLASS##Decl *>(D));            \
+    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 << Value(std::move(Obj));
+  return cxstring::createDup(BackingString.str());
+}
+
+enum CXErrorCode clang_createAPISet(CXTranslationUnit tu, CXAPISet *out_api) {
+  if (cxtu::isNotUsableTU(tu) || !out_api)
+    return CXError_InvalidArguments;
+
+  ASTUnit *Unit = cxtu::getASTUnit(tu);
+
+  auto &Ctx = Unit->getASTContext();
+  auto Lang = Unit->getInputKind().getLanguage();
+  APISet *API = new APISet(Ctx.getTargetInfo().getTriple(), Lang,
+                           Unit->getMainFileName().str());
+  ExtractAPIVisitor Visitor(
+      Ctx, [](SourceLocation Loc) { return true; }, *API);
+
+  for (auto It = Unit->top_level_begin(); It != Unit->top_level_end(); ++It) {
+    Visitor.TraverseDecl(*It);
+  }
+
+  *out_api = wrap(API);
+  return CXError_Success;
+}
+
+void clang_disposeAPISet(CXAPISet api) { delete unwrap(api); }
+
+CXString clang_getSymbolGraphForUSR(const char *usr, CXAPISet api) {
+  auto *API = unwrap(api);
+
+  if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(usr, *API))
+    return GenerateCXStringFromSymbolGraphData(std::move(*SGF));
+
+  return cxstring::createNull();
+}
+
+CXString clang_getSymbolGraphForCursor(CXCursor cursor) {
+  CXCursorKind Kind = clang_getCursorKind(cursor);
+  if (clang_isDeclaration(Kind)) {
+    const Decl *D = cxcursor::getCursorDecl(cursor);
+
+    if (!D)
+      return cxstring::createNull();
+
+    CXTranslationUnit TU = cxcursor::getCursorTU(cursor);
+    if (!TU)
+      return cxstring::createNull();
+
+    ASTUnit *Unit = cxtu::getASTUnit(TU);
+
+    auto &Ctx = Unit->getASTContext();
+    auto Lang = Unit->getInputKind().getLanguage();
+    APISet API(Ctx.getTargetInfo().getTriple(), Lang,
+               Unit->getMainFileName().str());
+    ExtractAPIVisitor Visitor(
+        Ctx, [](SourceLocation Loc) { return true; }, API);
+
+    SmallString<128> USR;
+    if (index::generateUSRForDecl(D, USR))
+      return cxstring::createNull();
+
+    WalkupFromMostDerivedType(Visitor, const_cast<Decl *>(D));
+    auto *Record = API.findRecordForUSR(USR);
+
+    if (!Record)
+      return cxstring::createNull();
+
+    for (const auto &Fragment : Record->Declaration.getFragments()) {
+      if (Fragment.Declaration)
+        WalkupFromMostDerivedType(Visitor,
+                                  const_cast<Decl *>(Fragment.Declaration));
+    }
+
+    if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API))
+      return GenerateCXStringFromSymbolGraphData(std::move(*SGF));
+  }
+
+  return cxstring::createNull();
+}

diff  --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index b4bf700dc1bee..e98187071852b 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -412,6 +412,10 @@ LLVM_16 {
     clang_CXXMethod_isDeleted;
     clang_CXXMethod_isCopyAssignmentOperator;
     clang_CXXMethod_isMoveAssignmentOperator;
+    clang_createAPISet;
+    clang_disposeAPISet;
+    clang_getSymbolGraphForCursor;
+    clang_getSymbolGraphForUSR;
 };
 
 # Example of how to add a new symbol version entry.  If you do add a new symbol


        


More information about the cfe-commits mailing list