[clang-tools-extra] [clang-tools-extra][ExtractAPI] create clang-symbolgraph-merger (PR #65894)

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 10 04:54:09 PDT 2023


https://github.com/Arsenic-ATG created https://github.com/llvm/llvm-project/pull/65894:

Create a clang tool to merge all the JSON symbolgraph emited by --emit-symbol-graph or -extract-api options into one unified JSON symbolgraph file.

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

>From 5061e131a542c1a9bdf613fda66037040f0dc702 Mon Sep 17 00:00:00 2001
From: Ankur <arsenic.secondary at gmail.com>
Date: Sun, 10 Sep 2023 17:16:11 +0530
Subject: [PATCH 1/2] [clang][ExtractAPI] Create extractapi::RecordLocation

Create and use extractapi::RecordLocation instead of conventional
clang::PresumedLoc to track the location of an APIRecord, this reduces
the dependency of APISet on SourceManager and would help if someone
wants to create APISet from JSON Serialized SymbolGraph.

These changes also add extractapi::CommentLine which is similar to
RawComment::CommentLine but use RecordLocation instead of PresumedLoc.

Differential Revision: https://reviews.llvm.org/D157810
---
 clang/include/clang/ExtractAPI/API.h          | 363 ++++++++++--------
 .../clang/ExtractAPI/ExtractAPIVisitor.h      | 178 +++------
 clang/lib/ExtractAPI/API.cpp                  |  90 ++---
 .../Serialization/SymbolGraphSerializer.cpp   |  10 +-
 clang/tools/libclang/CXExtractAPI.cpp         |   9 +-
 5 files changed, 312 insertions(+), 338 deletions(-)

diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index b4c0e0ad39cdf2a..d4367caf647550f 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -134,7 +134,48 @@ class Template {
   bool empty() const { return Parameters.empty() && Constraints.empty(); }
 };
 
-/// DocComment is a vector of RawComment::CommentLine.
+/// Slightly cut down version of PresumedLoc to suit the needs of
+/// ExtractAPI.
+class RecordLocation {
+  unsigned Line, Col;
+  std::string Filename;
+
+public:
+  RecordLocation(const unsigned Line, const unsigned Col,
+                 std::string Filename = "")
+      : Line(Line), Col(Col), Filename(Filename) {}
+  RecordLocation(const PresumedLoc &Location)
+      : Line(Location.getLine()), Col(Location.getColumn()),
+        Filename(Location.getFilename()) {}
+  RecordLocation() = default;
+
+  bool isInvalid() const { return Filename.empty(); }
+  bool isValid() const { return !isInvalid(); }
+
+  const char *getFilename() const { return Filename.c_str(); }
+
+  unsigned getLine() const { return Line; }
+
+  unsigned getColumn() const { return Col; }
+};
+
+/// Store a documentation comment line of an APIRecord
+///
+/// Similar to RawComment::CommentLine but use RecordLocation instead
+/// of PresumedLoc.
+struct CommentLine {
+
+  std::string Text;
+  RecordLocation Begin;
+  RecordLocation End;
+
+  CommentLine(llvm::StringRef Text, RecordLocation Begin, RecordLocation End)
+      : Text(Text), Begin(Begin), End(End) {}
+  CommentLine(const RawComment::CommentLine RComment)
+      : Text(RComment.Text), Begin(RComment.Begin), End(RComment.End) {}
+};
+
+/// DocComment is a vector of extractapi::CommentLine.
 ///
 /// Each line represents one line of striped documentation comment,
 /// with source range information. This simplifies calculating the source
@@ -147,7 +188,7 @@ class Template {
 ///   ///     with multiple lines.
 ///       ^~~~~~~~~~~~~~~~~~~~~~~'         Second line.
 /// \endcode
-using DocComment = std::vector<RawComment::CommentLine>;
+using DocComment = std::vector<extractapi::CommentLine>;
 
 // Classes deriving from APIRecord need to have USR be the first constructor
 // argument. This is so that they are compatible with `addTopLevelRecord`
@@ -223,7 +264,7 @@ struct APIRecord {
 
   StringRef USR;
   StringRef Name;
-  PresumedLoc Location;
+  RecordLocation Location;
   AvailabilitySet Availabilities;
   LinkageInfo Linkage;
 
@@ -256,7 +297,7 @@ struct APIRecord {
   APIRecord() = delete;
 
   APIRecord(RecordKind Kind, StringRef USR, StringRef Name,
-            PresumedLoc Location, AvailabilitySet Availabilities,
+            RecordLocation Location, AvailabilitySet Availabilities,
             LinkageInfo Linkage, const DocComment &Comment,
             DeclarationFragments Declaration, DeclarationFragments SubHeading,
             bool IsFromSystemHeader)
@@ -273,7 +314,7 @@ struct APIRecord {
 };
 
 struct NamespaceRecord : APIRecord {
-  NamespaceRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  NamespaceRecord(StringRef USR, StringRef Name, RecordLocation Loc,
                   AvailabilitySet Availabilities, LinkageInfo Linkage,
                   const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, bool IsFromSystemHeader)
@@ -290,19 +331,20 @@ struct NamespaceRecord : APIRecord {
 struct GlobalFunctionRecord : APIRecord {
   FunctionSignature Signature;
 
-  GlobalFunctionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  GlobalFunctionRecord(StringRef USR, StringRef Name,
+                       RecordLocation RecordLocation,
                        AvailabilitySet Availabilities, LinkageInfo Linkage,
                        const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
                        FunctionSignature Signature, bool IsFromSystemHeader)
-      : APIRecord(RK_GlobalFunction, USR, Name, Loc, std::move(Availabilities),
-                  Linkage, Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
+      : APIRecord(RK_GlobalFunction, USR, Name, RecordLocation,
+                  std::move(Availabilities), Linkage, Comment, Declaration,
+                  SubHeading, IsFromSystemHeader),
         Signature(Signature) {}
 
   GlobalFunctionRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                       PresumedLoc Loc, AvailabilitySet Availabilities,
+                       RecordLocation Loc, AvailabilitySet Availabilities,
                        LinkageInfo Linkage, const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
@@ -322,7 +364,8 @@ struct GlobalFunctionRecord : APIRecord {
 struct GlobalFunctionTemplateRecord : GlobalFunctionRecord {
   Template Templ;
 
-  GlobalFunctionTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  GlobalFunctionTemplateRecord(StringRef USR, StringRef Name,
+                               RecordLocation Loc,
                                AvailabilitySet Availabilities,
                                LinkageInfo Linkage, const DocComment &Comment,
                                DeclarationFragments Declaration,
@@ -342,7 +385,7 @@ struct GlobalFunctionTemplateRecord : GlobalFunctionRecord {
 
 struct GlobalFunctionTemplateSpecializationRecord : GlobalFunctionRecord {
   GlobalFunctionTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, PresumedLoc Loc,
+      StringRef USR, StringRef Name, RecordLocation Loc,
       AvailabilitySet Availabilities, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, FunctionSignature Signature,
@@ -359,17 +402,18 @@ struct GlobalFunctionTemplateSpecializationRecord : GlobalFunctionRecord {
 
 /// This holds information associated with global functions.
 struct GlobalVariableRecord : APIRecord {
-  GlobalVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  GlobalVariableRecord(StringRef USR, StringRef Name,
+                       RecordLocation RecordLocation,
                        AvailabilitySet Availabilities, LinkageInfo Linkage,
                        const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_GlobalVariable, USR, Name, Loc, std::move(Availabilities),
-                  Linkage, Comment, Declaration, SubHeading,
-                  IsFromSystemHeader) {}
+      : APIRecord(RK_GlobalVariable, USR, Name, RecordLocation,
+                  std::move(Availabilities), Linkage, Comment, Declaration,
+                  SubHeading, IsFromSystemHeader) {}
 
   GlobalVariableRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                       PresumedLoc Loc, AvailabilitySet Availabilities,
+                       RecordLocation Loc, AvailabilitySet Availabilities,
                        LinkageInfo Linkage, const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading, bool IsFromSystemHeader)
@@ -387,7 +431,8 @@ struct GlobalVariableRecord : APIRecord {
 struct GlobalVariableTemplateRecord : GlobalVariableRecord {
   Template Templ;
 
-  GlobalVariableTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  GlobalVariableTemplateRecord(StringRef USR, StringRef Name,
+                               RecordLocation Loc,
                                AvailabilitySet Availabilities,
                                LinkageInfo Linkage, const DocComment &Comment,
                                DeclarationFragments Declaration,
@@ -405,7 +450,7 @@ struct GlobalVariableTemplateRecord : GlobalVariableRecord {
 
 struct GlobalVariableTemplateSpecializationRecord : GlobalVariableRecord {
   GlobalVariableTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, PresumedLoc Loc,
+      StringRef USR, StringRef Name, RecordLocation Loc,
       AvailabilitySet Availabilities, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, bool IsFromSystemHeader)
@@ -423,7 +468,7 @@ struct GlobalVariableTemplatePartialSpecializationRecord
   Template Templ;
 
   GlobalVariableTemplatePartialSpecializationRecord(
-      StringRef USR, StringRef Name, PresumedLoc Loc,
+      StringRef USR, StringRef Name, RecordLocation Loc,
       AvailabilitySet Availabilities, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, class Template Template,
@@ -441,13 +486,14 @@ struct GlobalVariableTemplatePartialSpecializationRecord
 
 /// This holds information associated with enum constants.
 struct EnumConstantRecord : APIRecord {
-  EnumConstantRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  EnumConstantRecord(StringRef USR, StringRef Name,
+                     RecordLocation RecordLocation,
                      AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_EnumConstant, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader) {}
+      : APIRecord(RK_EnumConstant, USR, Name, RecordLocation,
+                  std::move(Availabilities), LinkageInfo::none(), Comment,
+                  Declaration, SubHeading, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_EnumConstant;
@@ -461,11 +507,11 @@ struct EnumConstantRecord : APIRecord {
 struct EnumRecord : APIRecord {
   SmallVector<std::unique_ptr<EnumConstantRecord>> Constants;
 
-  EnumRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  EnumRecord(StringRef USR, StringRef Name, RecordLocation RecordLocation,
              AvailabilitySet Availabilities, const DocComment &Comment,
              DeclarationFragments Declaration, DeclarationFragments SubHeading,
              bool IsFromSystemHeader)
-      : APIRecord(RK_Enum, USR, Name, Loc, std::move(Availabilities),
+      : APIRecord(RK_Enum, USR, Name, RecordLocation, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader) {}
 
@@ -479,13 +525,14 @@ struct EnumRecord : APIRecord {
 
 /// This holds information associated with struct fields.
 struct StructFieldRecord : APIRecord {
-  StructFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  StructFieldRecord(StringRef USR, StringRef Name,
+                    RecordLocation RecordLocation,
                     AvailabilitySet Availabilities, const DocComment &Comment,
                     DeclarationFragments Declaration,
                     DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_StructField, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader) {}
+      : APIRecord(RK_StructField, USR, Name, RecordLocation,
+                  std::move(Availabilities), LinkageInfo::none(), Comment,
+                  Declaration, SubHeading, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_StructField;
@@ -499,13 +546,13 @@ struct StructFieldRecord : APIRecord {
 struct StructRecord : APIRecord {
   SmallVector<std::unique_ptr<StructFieldRecord>> Fields;
 
-  StructRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  StructRecord(StringRef USR, StringRef Name, RecordLocation RecordLocation,
                AvailabilitySet Availabilities, const DocComment &Comment,
                DeclarationFragments Declaration,
                DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(RK_Struct, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader) {}
+      : APIRecord(RK_Struct, USR, Name, RecordLocation,
+                  std::move(Availabilities), LinkageInfo::none(), Comment,
+                  Declaration, SubHeading, IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
     return Record->getKind() == RK_Struct;
@@ -518,22 +565,22 @@ struct StructRecord : APIRecord {
 struct CXXFieldRecord : APIRecord {
   AccessControl Access;
 
-  CXXFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXFieldRecord(StringRef USR, StringRef Name, RecordLocation RecordLocation,
                  AvailabilitySet Availabilities, const DocComment &Comment,
                  DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, AccessControl Access,
                  bool IsFromSystemHeader)
-      : APIRecord(RK_CXXField, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
+      : APIRecord(RK_CXXField, USR, Name, RecordLocation,
+                  std::move(Availabilities), LinkageInfo::none(), Comment,
+                  Declaration, SubHeading, IsFromSystemHeader),
         Access(Access) {}
 
   CXXFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                 PresumedLoc Loc, AvailabilitySet Availabilities,
+                 RecordLocation RecordLocation, AvailabilitySet Availabilities,
                  const DocComment &Comment, DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, AccessControl Access,
                  bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+      : APIRecord(Kind, USR, Name, RecordLocation, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Access(Access) {}
@@ -549,7 +596,7 @@ struct CXXFieldRecord : APIRecord {
 struct CXXFieldTemplateRecord : CXXFieldRecord {
   Template Templ;
 
-  CXXFieldTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXFieldTemplateRecord(StringRef USR, StringRef Name, RecordLocation Loc,
                          AvailabilitySet Availabilities,
                          const DocComment &Comment,
                          DeclarationFragments Declaration,
@@ -572,11 +619,11 @@ struct CXXMethodRecord : APIRecord {
   CXXMethodRecord() = delete;
 
   CXXMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                  PresumedLoc Loc, AvailabilitySet Availabilities,
+                  RecordLocation RecordLocation, AvailabilitySet Availabilities,
                   const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, FunctionSignature Signature,
                   AccessControl Access, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+      : APIRecord(Kind, USR, Name, RecordLocation, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Signature(Signature), Access(Access) {}
@@ -585,14 +632,15 @@ struct CXXMethodRecord : APIRecord {
 };
 
 struct CXXConstructorRecord : CXXMethodRecord {
-  CXXConstructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXConstructorRecord(StringRef USR, StringRef Name,
+                       RecordLocation RecordLocation,
                        AvailabilitySet Availabilities,
                        const DocComment &Comment,
                        DeclarationFragments Declaration,
                        DeclarationFragments SubHeading,
                        FunctionSignature Signature, AccessControl Access,
                        bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, Loc,
+      : CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, RecordLocation,
                         std::move(Availabilities), Comment, Declaration,
                         SubHeading, Signature, Access, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
@@ -604,13 +652,14 @@ struct CXXConstructorRecord : CXXMethodRecord {
 };
 
 struct CXXDestructorRecord : CXXMethodRecord {
-  CXXDestructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXDestructorRecord(StringRef USR, StringRef Name,
+                      RecordLocation RecordLocation,
                       AvailabilitySet Availabilities, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading,
                       FunctionSignature Signature, AccessControl Access,
                       bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, Loc,
+      : CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, RecordLocation,
                         std::move(Availabilities), Comment, Declaration,
                         SubHeading, Signature, Access, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
@@ -622,14 +671,15 @@ struct CXXDestructorRecord : CXXMethodRecord {
 };
 
 struct CXXStaticMethodRecord : CXXMethodRecord {
-  CXXStaticMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXStaticMethodRecord(StringRef USR, StringRef Name,
+                        RecordLocation RecordLocation,
                         AvailabilitySet Availabilities,
                         const DocComment &Comment,
                         DeclarationFragments Declaration,
                         DeclarationFragments SubHeading,
                         FunctionSignature Signature, AccessControl Access,
                         bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXStaticMethod, USR, Name, Loc,
+      : CXXMethodRecord(RK_CXXStaticMethod, USR, Name, RecordLocation,
                         std::move(Availabilities), Comment, Declaration,
                         SubHeading, Signature, Access, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
@@ -641,14 +691,15 @@ struct CXXStaticMethodRecord : CXXMethodRecord {
 };
 
 struct CXXInstanceMethodRecord : CXXMethodRecord {
-  CXXInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXInstanceMethodRecord(StringRef USR, StringRef Name,
+                          RecordLocation RecordLocation,
                           AvailabilitySet Availabilities,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading,
                           FunctionSignature Signature, AccessControl Access,
                           bool IsFromSystemHeader)
-      : CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, Loc,
+      : CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, RecordLocation,
                         std::move(Availabilities), Comment, Declaration,
                         SubHeading, Signature, Access, IsFromSystemHeader) {}
 
@@ -663,7 +714,7 @@ struct CXXInstanceMethodRecord : CXXMethodRecord {
 struct CXXMethodTemplateRecord : CXXMethodRecord {
   Template Templ;
 
-  CXXMethodTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXMethodTemplateRecord(StringRef USR, StringRef Name, RecordLocation Loc,
                           AvailabilitySet Availabilities,
                           const DocComment &Comment,
                           DeclarationFragments Declaration,
@@ -682,7 +733,7 @@ struct CXXMethodTemplateRecord : CXXMethodRecord {
 
 struct CXXMethodTemplateSpecializationRecord : CXXMethodRecord {
   CXXMethodTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, PresumedLoc Loc,
+      StringRef USR, StringRef Name, RecordLocation Loc,
       AvailabilitySet Availabilities, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access,
@@ -711,13 +762,13 @@ struct ObjCPropertyRecord : APIRecord {
   bool IsOptional;
 
   ObjCPropertyRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                     PresumedLoc Loc, AvailabilitySet Availabilities,
-                     const DocComment &Comment,
+                     RecordLocation RecordLocation,
+                     AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, AttributeKind Attributes,
                      StringRef GetterName, StringRef SetterName,
                      bool IsOptional, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+      : APIRecord(Kind, USR, Name, RecordLocation, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Attributes(Attributes), GetterName(GetterName), SetterName(SetterName),
@@ -730,15 +781,13 @@ struct ObjCPropertyRecord : APIRecord {
 };
 
 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,
+  ObjCInstancePropertyRecord(
+      StringRef USR, StringRef Name, RecordLocation RecordLocation,
+      AvailabilitySet Availabilities, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      AttributeKind Attributes, StringRef GetterName, StringRef SetterName,
+      bool IsOptional, bool IsFromSystemHeader)
+      : ObjCPropertyRecord(RK_ObjCInstanceProperty, USR, Name, RecordLocation,
                            std::move(Availabilities), Comment, Declaration,
                            SubHeading, Attributes, GetterName, SetterName,
                            IsOptional, IsFromSystemHeader) {}
@@ -752,15 +801,13 @@ struct ObjCInstancePropertyRecord : ObjCPropertyRecord {
 };
 
 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,
+  ObjCClassPropertyRecord(
+      StringRef USR, StringRef Name, RecordLocation RecordLocation,
+      AvailabilitySet Availabilities, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      AttributeKind Attributes, StringRef GetterName, StringRef SetterName,
+      bool IsOptional, bool IsFromSystemHeader)
+      : ObjCPropertyRecord(RK_ObjCClassProperty, USR, Name, RecordLocation,
                            std::move(Availabilities), Comment, Declaration,
                            SubHeading, Attributes, GetterName, SetterName,
                            IsOptional, IsFromSystemHeader) {}
@@ -778,15 +825,16 @@ struct ObjCInstanceVariableRecord : APIRecord {
   using AccessControl = ObjCIvarDecl::AccessControl;
   AccessControl Access;
 
-  ObjCInstanceVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ObjCInstanceVariableRecord(StringRef USR, StringRef Name,
+                             RecordLocation RecordLocation,
                              AvailabilitySet Availabilities,
                              const DocComment &Comment,
                              DeclarationFragments Declaration,
                              DeclarationFragments SubHeading,
                              AccessControl Access, bool IsFromSystemHeader)
-      : APIRecord(RK_ObjCIvar, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo::none(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
+      : APIRecord(RK_ObjCIvar, USR, Name, RecordLocation,
+                  std::move(Availabilities), LinkageInfo::none(), Comment,
+                  Declaration, SubHeading, IsFromSystemHeader),
         Access(Access) {}
 
   static bool classof(const APIRecord *Record) {
@@ -804,11 +852,12 @@ struct ObjCMethodRecord : APIRecord {
   ObjCMethodRecord() = delete;
 
   ObjCMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                   PresumedLoc Loc, AvailabilitySet Availabilities,
-                   const DocComment &Comment, DeclarationFragments Declaration,
+                   RecordLocation RecordLocation,
+                   AvailabilitySet Availabilities, const DocComment &Comment,
+                   DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, FunctionSignature Signature,
                    bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+      : APIRecord(Kind, USR, Name, RecordLocation, std::move(Availabilities),
                   LinkageInfo::none(), Comment, Declaration, SubHeading,
                   IsFromSystemHeader),
         Signature(Signature) {}
@@ -817,13 +866,14 @@ struct ObjCMethodRecord : APIRecord {
 };
 
 struct ObjCInstanceMethodRecord : ObjCMethodRecord {
-  ObjCInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ObjCInstanceMethodRecord(StringRef USR, StringRef Name,
+                           RecordLocation RecordLocation,
                            AvailabilitySet Availabilities,
                            const DocComment &Comment,
                            DeclarationFragments Declaration,
                            DeclarationFragments SubHeading,
                            FunctionSignature Signature, bool IsFromSystemHeader)
-      : ObjCMethodRecord(RK_ObjCInstanceMethod, USR, Name, Loc,
+      : ObjCMethodRecord(RK_ObjCInstanceMethod, USR, Name, RecordLocation,
                          std::move(Availabilities), Comment, Declaration,
                          SubHeading, Signature, IsFromSystemHeader) {}
   static bool classof(const APIRecord *Record) {
@@ -835,13 +885,14 @@ struct ObjCInstanceMethodRecord : ObjCMethodRecord {
 };
 
 struct ObjCClassMethodRecord : ObjCMethodRecord {
-  ObjCClassMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ObjCClassMethodRecord(StringRef USR, StringRef Name,
+                        RecordLocation RecordLocation,
                         AvailabilitySet Availabilities,
                         const DocComment &Comment,
                         DeclarationFragments Declaration,
                         DeclarationFragments SubHeading,
                         FunctionSignature Signature, bool IsFromSystemHeader)
-      : ObjCMethodRecord(RK_ObjCClassMethod, USR, Name, Loc,
+      : ObjCMethodRecord(RK_ObjCClassMethod, USR, Name, RecordLocation,
                          std::move(Availabilities), Comment, Declaration,
                          SubHeading, Signature, IsFromSystemHeader) {}
 
@@ -879,12 +930,13 @@ struct SymbolReference {
 struct StaticFieldRecord : CXXFieldRecord {
   SymbolReference Context;
 
-  StaticFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  StaticFieldRecord(StringRef USR, StringRef Name,
+                    RecordLocation RecordLocation,
                     AvailabilitySet Availabilities, LinkageInfo Linkage,
                     const DocComment &Comment, DeclarationFragments Declaration,
                     DeclarationFragments SubHeading, SymbolReference Context,
                     AccessControl Access, bool IsFromSystemHeader)
-      : CXXFieldRecord(RK_StaticField, USR, Name, Loc,
+      : CXXFieldRecord(RK_StaticField, USR, Name, RecordLocation,
                        std::move(Availabilities), Comment, Declaration,
                        SubHeading, Access, IsFromSystemHeader),
         Context(Context) {}
@@ -905,12 +957,14 @@ struct ObjCContainerRecord : APIRecord {
   ObjCContainerRecord() = delete;
 
   ObjCContainerRecord(RecordKind Kind, StringRef USR, StringRef Name,
-                      PresumedLoc Loc, AvailabilitySet Availabilities,
-                      LinkageInfo Linkage, const DocComment &Comment,
+                      RecordLocation RecordLocation,
+                      AvailabilitySet Availabilities, LinkageInfo Linkage,
+                      const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), Linkage,
-                  Comment, Declaration, SubHeading, IsFromSystemHeader) {}
+      : APIRecord(Kind, USR, Name, RecordLocation, std::move(Availabilities),
+                  Linkage, Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
 
   virtual ~ObjCContainerRecord() = 0;
 };
@@ -921,7 +975,7 @@ struct CXXClassRecord : APIRecord {
   SmallVector<SymbolReference> Bases;
   AccessControl Access;
 
-  CXXClassRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  CXXClassRecord(StringRef USR, StringRef Name, RecordLocation Loc,
                  AvailabilitySet Availabilities, const DocComment &Comment,
                  DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, RecordKind Kind,
@@ -942,7 +996,7 @@ struct CXXClassRecord : APIRecord {
 struct ClassTemplateRecord : CXXClassRecord {
   Template Templ;
 
-  ClassTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ClassTemplateRecord(StringRef USR, StringRef Name, RecordLocation Loc,
                       AvailabilitySet Availabilities, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading, Template Template,
@@ -959,7 +1013,7 @@ struct ClassTemplateRecord : CXXClassRecord {
 
 struct ClassTemplateSpecializationRecord : CXXClassRecord {
   ClassTemplateSpecializationRecord(
-      StringRef USR, StringRef Name, PresumedLoc Loc,
+      StringRef USR, StringRef Name, RecordLocation Loc,
       AvailabilitySet Availabilities, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       AccessControl Access, bool IsFromSystemHeader)
@@ -975,7 +1029,7 @@ struct ClassTemplateSpecializationRecord : CXXClassRecord {
 struct ClassTemplatePartialSpecializationRecord : CXXClassRecord {
   Template Templ;
   ClassTemplatePartialSpecializationRecord(
-      StringRef USR, StringRef Name, PresumedLoc Loc,
+      StringRef USR, StringRef Name, RecordLocation Loc,
       AvailabilitySet Availabilities, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       Template Template, AccessControl Access, bool IsFromSystemHeader)
@@ -992,7 +1046,7 @@ struct ClassTemplatePartialSpecializationRecord : CXXClassRecord {
 struct ConceptRecord : APIRecord {
   Template Templ;
 
-  ConceptRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ConceptRecord(StringRef USR, StringRef Name, RecordLocation Loc,
                 AvailabilitySet Availabilities, const DocComment &Comment,
                 DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, Template Template,
@@ -1009,12 +1063,13 @@ struct ObjCCategoryRecord : ObjCContainerRecord {
   /// Determine whether the Category is derived from external class interface.
   bool IsFromExternalModule = false;
 
-  ObjCCategoryRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ObjCCategoryRecord(StringRef USR, StringRef Name,
+                     RecordLocation RecordLocation,
                      AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, SymbolReference Interface,
                      bool IsFromSystemHeader)
-      : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Loc,
+      : ObjCContainerRecord(RK_ObjCCategory, USR, Name, RecordLocation,
                             std::move(Availabilities), LinkageInfo::none(),
                             Comment, Declaration, SubHeading,
                             IsFromSystemHeader),
@@ -1034,13 +1089,14 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
   // ObjCCategoryRecord%s are stored in and owned by APISet.
   SmallVector<ObjCCategoryRecord *> Categories;
 
-  ObjCInterfaceRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ObjCInterfaceRecord(StringRef USR, StringRef Name,
+                      RecordLocation RecordLocation,
                       AvailabilitySet Availabilities, LinkageInfo Linkage,
                       const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading,
                       SymbolReference SuperClass, bool IsFromSystemHeader)
-      : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Loc,
+      : ObjCContainerRecord(RK_ObjCInterface, USR, Name, RecordLocation,
                             std::move(Availabilities), Linkage, Comment,
                             Declaration, SubHeading, IsFromSystemHeader),
         SuperClass(SuperClass) {}
@@ -1055,11 +1111,12 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
 
 /// This holds information associated with Objective-C protocols.
 struct ObjCProtocolRecord : ObjCContainerRecord {
-  ObjCProtocolRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  ObjCProtocolRecord(StringRef USR, StringRef Name,
+                     RecordLocation RecordLocation,
                      AvailabilitySet Availabilities, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, bool IsFromSystemHeader)
-      : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Loc,
+      : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, RecordLocation,
                             std::move(Availabilities), LinkageInfo::none(),
                             Comment, Declaration, SubHeading,
                             IsFromSystemHeader) {}
@@ -1074,12 +1131,13 @@ struct ObjCProtocolRecord : ObjCContainerRecord {
 
 /// This holds information associated with macro definitions.
 struct MacroDefinitionRecord : APIRecord {
-  MacroDefinitionRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  MacroDefinitionRecord(StringRef USR, StringRef Name,
+                        RecordLocation RecordLocation,
                         DeclarationFragments Declaration,
                         DeclarationFragments SubHeading,
                         bool IsFromSystemHeader)
-      : APIRecord(RK_MacroDefinition, USR, Name, Loc, AvailabilitySet(),
-                  LinkageInfo(), {}, Declaration, SubHeading,
+      : APIRecord(RK_MacroDefinition, USR, Name, RecordLocation,
+                  AvailabilitySet(), LinkageInfo(), {}, Declaration, SubHeading,
                   IsFromSystemHeader) {}
 
   static bool classof(const APIRecord *Record) {
@@ -1098,14 +1156,14 @@ struct MacroDefinitionRecord : APIRecord {
 struct TypedefRecord : APIRecord {
   SymbolReference UnderlyingType;
 
-  TypedefRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+  TypedefRecord(StringRef USR, StringRef Name, RecordLocation RecordLocation,
                 AvailabilitySet Availabilities, const DocComment &Comment,
                 DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, SymbolReference UnderlyingType,
                 bool IsFromSystemHeader)
-      : APIRecord(RK_Typedef, USR, Name, Loc, std::move(Availabilities),
-                  LinkageInfo(), Comment, Declaration, SubHeading,
-                  IsFromSystemHeader),
+      : APIRecord(RK_Typedef, USR, Name, RecordLocation,
+                  std::move(Availabilities), LinkageInfo(), Comment,
+                  Declaration, SubHeading, IsFromSystemHeader),
         UnderlyingType(UnderlyingType) {}
 
   static bool classof(const APIRecord *Record) {
@@ -1132,7 +1190,8 @@ struct has_function_signature<ObjCInstanceMethodRecord>
 template <>
 struct has_function_signature<ObjCClassMethodRecord> : public std::true_type {};
 template <>
-struct has_function_signature<CXXInstanceMethodRecord> : public std::true_type {};
+struct has_function_signature<CXXInstanceMethodRecord> : public std::true_type {
+};
 template <>
 struct has_function_signature<CXXStaticMethodRecord> : public std::true_type {};
 template <>
@@ -1190,7 +1249,7 @@ struct has_function_signature<GlobalFunctionTemplateSpecializationRecord>
 class APISet {
 public:
   NamespaceRecord *addNamespace(APIRecord *Parent, StringRef Name,
-                                StringRef USR, PresumedLoc Loc,
+                                StringRef USR, RecordLocation Loc,
                                 AvailabilitySet Availability,
                                 LinkageInfo Linkage, const DocComment &Comment,
                                 DeclarationFragments Declaration,
@@ -1203,13 +1262,13 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   GlobalVariableRecord *
-  addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
+  addGlobalVar(StringRef Name, StringRef USR, RecordLocation RecordLocation,
                AvailabilitySet Availability, LinkageInfo Linkage,
                const DocComment &Comment, DeclarationFragments Declaration,
                DeclarationFragments SubHeadin, bool IsFromSystemHeaderg);
 
   GlobalVariableTemplateRecord *
-  addGlobalVariableTemplate(StringRef Name, StringRef USR, PresumedLoc Loc,
+  addGlobalVariableTemplate(StringRef Name, StringRef USR, RecordLocation Loc,
                             AvailabilitySet Availability, LinkageInfo Linkage,
                             const DocComment &Comment,
                             DeclarationFragments Declaration,
@@ -1223,14 +1282,15 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   GlobalFunctionRecord *
-  addGlobalFunction(StringRef Name, StringRef USR, PresumedLoc Loc,
-                    AvailabilitySet Availability, LinkageInfo Linkage,
-                    const DocComment &Comment, DeclarationFragments Declaration,
+  addGlobalFunction(StringRef Name, StringRef USR,
+                    RecordLocation RecordLocation, AvailabilitySet Availability,
+                    LinkageInfo Linkage, const DocComment &Comment,
+                    DeclarationFragments Declaration,
                     DeclarationFragments SubHeading,
                     FunctionSignature Signature, bool IsFromSystemHeader);
 
   GlobalFunctionTemplateRecord *addGlobalFunctionTemplate(
-      StringRef Name, StringRef USR, PresumedLoc Loc,
+      StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, FunctionSignature Signature,
@@ -1238,7 +1298,7 @@ class APISet {
 
   GlobalFunctionTemplateSpecializationRecord *
   addGlobalFunctionTemplateSpecialization(
-      StringRef Name, StringRef USR, PresumedLoc Loc,
+      StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, FunctionSignature Signature,
@@ -1252,7 +1312,7 @@ class APISet {
   /// 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,
+                  RecordLocation RecordLocation, AvailabilitySet Availability,
                   const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
@@ -1262,7 +1322,8 @@ 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.
-  EnumRecord *addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+  EnumRecord *addEnum(StringRef Name, StringRef USR,
+                      RecordLocation RecordLocation,
                       AvailabilitySet Availability, const DocComment &Comment,
                       DeclarationFragments Declaration,
                       DeclarationFragments SubHeading, bool IsFromSystemHeader);
@@ -1275,7 +1336,7 @@ class APISet {
   /// 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,
+                 RecordLocation RecordLocation, AvailabilitySet Availability,
                  const DocComment &Comment, DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
@@ -1285,22 +1346,21 @@ 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.
-  StructRecord *addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
-                          AvailabilitySet Availability,
-                          const DocComment &Comment,
-                          DeclarationFragments Declaration,
-                          DeclarationFragments SubHeading,
-                          bool IsFromSystemHeader);
+  StructRecord *
+  addStruct(StringRef Name, StringRef USR, RecordLocation RecordLocation,
+            AvailabilitySet Availability, const DocComment &Comment,
+            DeclarationFragments Declaration, DeclarationFragments SubHeading,
+            bool IsFromSystemHeader);
 
   StaticFieldRecord *
-  addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
+  addStaticField(StringRef Name, StringRef USR, RecordLocation Loc,
                  AvailabilitySet Availabilities, LinkageInfo Linkage,
                  const DocComment &Comment, DeclarationFragments Declaration,
                  DeclarationFragments SubHeading, SymbolReference Context,
                  AccessControl Access, bool IsFromSystemHeaderg);
 
   CXXFieldRecord *addCXXField(APIRecord *CXXClass, StringRef Name,
-                              StringRef USR, PresumedLoc Loc,
+                              StringRef USR, RecordLocation Loc,
                               AvailabilitySet Availabilities,
                               const DocComment &Comment,
                               DeclarationFragments Declaration,
@@ -1308,13 +1368,13 @@ class APISet {
                               AccessControl Access, bool IsFromSystemHeader);
 
   CXXFieldTemplateRecord *addCXXFieldTemplate(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       AccessControl Access, Template Template, bool IsFromSystemHeader);
 
   CXXClassRecord *addCXXClass(APIRecord *Parent, StringRef Name, StringRef USR,
-                              PresumedLoc Loc, AvailabilitySet Availability,
+                              RecordLocation Loc, AvailabilitySet Availability,
                               const DocComment &Comment,
                               DeclarationFragments Declaration,
                               DeclarationFragments SubHeading,
@@ -1323,75 +1383,75 @@ class APISet {
 
   ClassTemplateRecord *
   addClassTemplate(APIRecord *Parent, StringRef Name, StringRef USR,
-                   PresumedLoc Loc, AvailabilitySet Availability,
+                   RecordLocation Loc, AvailabilitySet Availability,
                    const DocComment &Comment, DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, Template Template,
                    AccessControl Access, bool IsFromSystemHeader);
 
   ClassTemplateSpecializationRecord *addClassTemplateSpecialization(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       AccessControl Access, bool IsFromSystemHeader);
 
   ClassTemplatePartialSpecializationRecord *
   addClassTemplatePartialSpecialization(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       Template Template, AccessControl Access, bool IsFromSystemHeader);
 
   GlobalVariableTemplateSpecializationRecord *
   addGlobalVariableTemplateSpecialization(
-      StringRef Name, StringRef USR, PresumedLoc Loc,
+      StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, bool IsFromSystemHeader);
 
   GlobalVariableTemplatePartialSpecializationRecord *
   addGlobalVariableTemplatePartialSpecialization(
-      StringRef Name, StringRef USR, PresumedLoc Loc,
+      StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, LinkageInfo Linkage,
       const DocComment &Comment, DeclarationFragments Declaration,
       DeclarationFragments SubHeading, Template Template,
       bool IsFromSystemHeader);
 
   CXXMethodRecord *addCXXInstanceMethod(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access,
       bool IsFromSystemHeader);
 
   CXXMethodRecord *addCXXStaticMethod(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access,
       bool IsFromSystemHeader);
 
   CXXMethodRecord *addCXXSpecialMethod(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access,
       bool IsFromSystemHeader);
 
   CXXMethodTemplateRecord *addCXXMethodTemplate(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access, Template Template,
       bool IsFromSystemHeader);
 
   CXXMethodTemplateSpecializationRecord *addCXXMethodTemplateSpec(
-      APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+      APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
       AvailabilitySet Availability, const DocComment &Comment,
       DeclarationFragments Declaration, DeclarationFragments SubHeading,
       FunctionSignature Signature, AccessControl Access,
       bool IsFromSystemHeader);
 
-  ConceptRecord *addConcept(StringRef Name, StringRef USR, PresumedLoc Loc,
+  ConceptRecord *addConcept(StringRef Name, StringRef USR, RecordLocation Loc,
                             AvailabilitySet Availability,
                             const DocComment &Comment,
                             DeclarationFragments Declaration,
@@ -1405,7 +1465,7 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCCategoryRecord *
-  addObjCCategory(StringRef Name, StringRef USR, PresumedLoc Loc,
+  addObjCCategory(StringRef Name, StringRef USR, RecordLocation RecordLocation,
                   AvailabilitySet Availability, const DocComment &Comment,
                   DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, SymbolReference Interface,
@@ -1418,7 +1478,7 @@ class APISet {
   /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCInterfaceRecord *
-  addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+  addObjCInterface(StringRef Name, StringRef USR, RecordLocation RecordLocation,
                    AvailabilitySet Availability, LinkageInfo Linkage,
                    const DocComment &Comment, DeclarationFragments Declaration,
                    DeclarationFragments SubHeading, SymbolReference SuperClass,
@@ -1432,7 +1492,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCMethodRecord *
   addObjCMethod(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-                PresumedLoc Loc, AvailabilitySet Availability,
+                RecordLocation RecordLocation, AvailabilitySet Availability,
                 const DocComment &Comment, DeclarationFragments Declaration,
                 DeclarationFragments SubHeading, FunctionSignature Signature,
                 bool IsInstanceMethod, bool IsFromSystemHeader);
@@ -1445,7 +1505,7 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCPropertyRecord *
   addObjCProperty(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-                  PresumedLoc Loc, AvailabilitySet Availability,
+                  RecordLocation RecordLocation, AvailabilitySet Availability,
                   const DocComment &Comment, DeclarationFragments Declaration,
                   DeclarationFragments SubHeading,
                   ObjCPropertyRecord::AttributeKind Attributes,
@@ -1460,8 +1520,9 @@ class APISet {
   /// to generate the USR for \c D and keep it alive in APISet.
   ObjCInstanceVariableRecord *addObjCInstanceVariable(
       ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-      PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
-      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      RecordLocation RecordLocation, AvailabilitySet Availability,
+      const DocComment &Comment, DeclarationFragments Declaration,
+      DeclarationFragments SubHeading,
       ObjCInstanceVariableRecord::AccessControl Access,
       bool IsFromSystemHeader);
 
@@ -1472,7 +1533,7 @@ class APISet {
   /// 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,
+  addObjCProtocol(StringRef Name, StringRef USR, RecordLocation RecordLocation,
                   AvailabilitySet Availability, const DocComment &Comment,
                   DeclarationFragments Declaration,
                   DeclarationFragments SubHeading, bool IsFromSystemHeader);
@@ -1485,7 +1546,7 @@ class APISet {
   /// SourceLocation SL, const SourceManager &SM) is a helper method to generate
   /// the USR for the macro and keep it alive in APISet.
   MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR,
-                                            PresumedLoc Loc,
+                                            RecordLocation RecordLocation,
                                             DeclarationFragments Declaration,
                                             DeclarationFragments SubHeading,
                                             bool IsFromSystemHeader);
@@ -1497,7 +1558,7 @@ class APISet {
   /// 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,
+  addTypedef(StringRef Name, StringRef USR, RecordLocation RecordLocation,
              AvailabilitySet Availability, const DocComment &Comment,
              DeclarationFragments Declaration, DeclarationFragments SubHeading,
              SymbolReference UnderlyingType, bool IsFromSystemHeader);
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 84ea8e2f7ce3851..110023564eac3d0 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -29,6 +29,7 @@
 #include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/ADT/StringRef.h"
+#include <algorithm>
 #include <type_traits>
 
 namespace clang {
@@ -123,6 +124,8 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
   const RawComment *fetchRawCommentForDecl(const Decl *Decl) const;
 
 protected:
+  DocComment FetchDocCommentFromDecl(const Decl *Decl);
+
   /// Collect API information for the enum constants and associate with the
   /// parent enum.
   void recordEnumConstants(EnumRecord *EnumRecord,
@@ -223,6 +226,19 @@ static void modifyRecords(const T &Records, const StringRef &Name) {
   }
 }
 
+template <typename Derived>
+DocComment ExtractAPIVisitorBase<Derived>::FetchDocCommentFromDecl(const Decl *Decl) {
+  DocComment Comment;
+  if (auto *RawComment =
+          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl)) {
+    auto RawCommentVec = RawComment->getFormattedLines(
+        Context.getSourceManager(), Context.getDiagnostics());
+    std::copy(RawCommentVec.begin(), RawCommentVec.end(),
+              std::back_inserter(Comment));
+  }
+  return Comment;
+}
+
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
   // skip function parameters.
@@ -252,11 +268,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the variable.
   DeclarationFragments Declaration =
@@ -322,11 +334,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments, sub-heading, and signature of the function.
   DeclarationFragments SubHeading =
@@ -368,11 +376,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the enum.
   DeclarationFragments Declaration =
@@ -484,11 +488,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitNamespaceDecl(
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the struct.
   DeclarationFragments Declaration =
@@ -516,11 +516,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the struct.
   DeclarationFragments Declaration =
@@ -550,11 +546,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
   DeclarationFragments Declaration =
       DeclarationFragmentsBuilder::getFragmentsForCXXClass(Decl);
   DeclarationFragments SubHeading =
@@ -606,11 +598,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
   auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
@@ -664,11 +652,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXConstructorDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments, sub-heading, and signature for the method.
   DeclarationFragments Declaration =
@@ -697,11 +681,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXDestructorDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments, sub-heading, and signature for the method.
   DeclarationFragments Declaration =
@@ -731,11 +711,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitConceptDecl(const ConceptDecl *Decl) {
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
   DeclarationFragments Declaration =
       DeclarationFragmentsBuilder::getFragmentsForConcept(Decl);
   DeclarationFragments SubHeading =
@@ -755,11 +731,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitClassTemplateSpecializationDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
   DeclarationFragments Declaration =
       DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
           Decl);
@@ -788,11 +760,7 @@ bool ExtractAPIVisitorBase<Derived>::
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
   DeclarationFragments Declaration = DeclarationFragmentsBuilder::
       getFragmentsForClassTemplatePartialSpecialization(Decl);
   DeclarationFragments SubHeading =
@@ -823,11 +791,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateDecl(
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the variable.
   DeclarationFragments Declaration;
@@ -868,11 +832,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateSpecializationDecl(
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the variable.
   DeclarationFragments Declaration =
@@ -899,11 +859,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplatePartialSpecializationDecl(
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the variable.
   DeclarationFragments Declaration = DeclarationFragmentsBuilder::
@@ -931,11 +887,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionTemplateDecl(
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
@@ -963,11 +915,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the interface.
   DeclarationFragments Declaration =
@@ -1011,11 +959,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCProtocolDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the protocol.
   DeclarationFragments Declaration =
@@ -1069,11 +1013,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl(
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
   StringRef Name = Decl->getName();
   StringRef USR = API.recordUSR(Decl);
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   QualType Type = Decl->getUnderlyingType();
   SymbolReference SymRef =
@@ -1098,11 +1038,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitObjCCategoryDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
   // Build declaration fragments and sub-heading for the category.
   DeclarationFragments Declaration =
       DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
@@ -1148,11 +1084,7 @@ void ExtractAPIVisitorBase<Derived>::recordEnumConstants(
     StringRef USR = API.recordUSR(Constant);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Constant->getLocation());
-    DocComment Comment;
-    if (auto *RawComment =
-            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Constant))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
+    DocComment Comment = FetchDocCommentFromDecl(Constant);
 
     // Build declaration fragments and sub-heading for the enum constant.
     DeclarationFragments Declaration =
@@ -1177,11 +1109,7 @@ void ExtractAPIVisitorBase<Derived>::recordStructFields(
     StringRef USR = API.recordUSR(Field);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Field->getLocation());
-    DocComment Comment;
-    if (auto *RawComment =
-            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Field))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
+    DocComment Comment = FetchDocCommentFromDecl(Field);
 
     // Build declaration fragments and sub-heading for the struct field.
     DeclarationFragments Declaration =
@@ -1206,11 +1134,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFieldDecl(const FieldDecl *Decl) {
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments and sub-heading for the struct field.
   DeclarationFragments Declaration =
@@ -1235,11 +1159,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXConversionDecl(
   StringRef USR = API.recordUSR(Decl);
   PresumedLoc Loc =
       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
-  DocComment Comment;
-  if (auto *RawComment =
-          getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
-    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                            Context.getDiagnostics());
+  DocComment Comment = FetchDocCommentFromDecl(Decl);
 
   // Build declaration fragments, sub-heading, and signature for the method.
   DeclarationFragments Declaration =
@@ -1281,11 +1201,7 @@ void ExtractAPIVisitorBase<Derived>::recordObjCMethods(
     StringRef USR = API.recordUSR(Method);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Method->getLocation());
-    DocComment Comment;
-    if (auto *RawComment =
-            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Method))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
+    DocComment Comment = FetchDocCommentFromDecl(Method);
 
     // Build declaration fragments, sub-heading, and signature for the method.
     DeclarationFragments Declaration =
@@ -1310,11 +1226,7 @@ void ExtractAPIVisitorBase<Derived>::recordObjCProperties(
     StringRef USR = API.recordUSR(Property);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Property->getLocation());
-    DocComment Comment;
-    if (auto *RawComment =
-            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Property))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
+    DocComment Comment = FetchDocCommentFromDecl(Property);
 
     // Build declaration fragments and sub-heading for the property.
     DeclarationFragments Declaration =
@@ -1355,11 +1267,7 @@ void ExtractAPIVisitorBase<Derived>::recordObjCInstanceVariables(
     StringRef USR = API.recordUSR(Ivar);
     PresumedLoc Loc =
         Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
-    DocComment Comment;
-    if (auto *RawComment =
-            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Ivar))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
+    DocComment Comment = FetchDocCommentFromDecl(Ivar);
 
     // Build declaration fragments and sub-heading for the instance variable.
     DeclarationFragments Declaration =
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 2973a31345c9b2d..7fb2f96fa4e9b6e 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -46,7 +46,7 @@ RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
 
 NamespaceRecord *
 APISet::addNamespace(APIRecord *Parent, StringRef Name, StringRef USR,
-                     PresumedLoc Loc, AvailabilitySet Availability,
+                     RecordLocation Loc, AvailabilitySet Availability,
                      LinkageInfo Linkage, const DocComment &Comment,
                      DeclarationFragments Declaration,
                      DeclarationFragments SubHeading, bool IsFromSystemHeader) {
@@ -61,7 +61,7 @@ APISet::addNamespace(APIRecord *Parent, StringRef Name, StringRef USR,
 }
 
 GlobalVariableRecord *
-APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
+APISet::addGlobalVar(StringRef Name, StringRef USR, RecordLocation Loc,
                      AvailabilitySet Availabilities, LinkageInfo Linkage,
                      const DocComment &Comment, DeclarationFragments Fragments,
                      DeclarationFragments SubHeading, bool IsFromSystemHeader) {
@@ -71,7 +71,7 @@ APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
 }
 
 GlobalVariableTemplateRecord *APISet::addGlobalVariableTemplate(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Declaration,
     DeclarationFragments SubHeading, Template Template,
@@ -83,7 +83,7 @@ GlobalVariableTemplateRecord *APISet::addGlobalVariableTemplate(
 }
 
 GlobalFunctionRecord *APISet::addGlobalFunction(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availabilities, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Fragments,
     DeclarationFragments SubHeading, FunctionSignature Signature,
@@ -95,7 +95,7 @@ GlobalFunctionRecord *APISet::addGlobalFunction(
 }
 
 GlobalFunctionTemplateRecord *APISet::addGlobalFunctionTemplate(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Declaration,
     DeclarationFragments SubHeading, FunctionSignature Signature,
@@ -108,7 +108,7 @@ GlobalFunctionTemplateRecord *APISet::addGlobalFunctionTemplate(
 
 GlobalFunctionTemplateSpecializationRecord *
 APISet::addGlobalFunctionTemplateSpecialization(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Declaration,
     DeclarationFragments SubHeading, FunctionSignature Signature,
@@ -120,7 +120,7 @@ APISet::addGlobalFunctionTemplateSpecialization(
 }
 
 EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
-                                            StringRef USR, PresumedLoc Loc,
+                                            StringRef USR, RecordLocation Loc,
                                             AvailabilitySet Availabilities,
                                             const DocComment &Comment,
                                             DeclarationFragments Declaration,
@@ -135,7 +135,7 @@ EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
   return Enum->Constants.emplace_back(std::move(Record)).get();
 }
 
-EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, RecordLocation Loc,
                             AvailabilitySet Availabilities,
                             const DocComment &Comment,
                             DeclarationFragments Declaration,
@@ -147,7 +147,7 @@ EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
 }
 
 StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
-                                          StringRef USR, PresumedLoc Loc,
+                                          StringRef USR, RecordLocation Loc,
                                           AvailabilitySet Availabilities,
                                           const DocComment &Comment,
                                           DeclarationFragments Declaration,
@@ -162,19 +162,18 @@ StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
   return Struct->Fields.emplace_back(std::move(Record)).get();
 }
 
-StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
-                                AvailabilitySet Availabilities,
-                                const DocComment &Comment,
-                                DeclarationFragments Declaration,
-                                DeclarationFragments SubHeading,
-                                bool IsFromSystemHeader) {
+StructRecord *
+APISet::addStruct(StringRef Name, StringRef USR, RecordLocation Loc,
+                  AvailabilitySet Availabilities, const DocComment &Comment,
+                  DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, bool IsFromSystemHeader) {
   return addTopLevelRecord(USRBasedLookupTable, Structs, USR, Name, Loc,
                            std::move(Availabilities), Comment, Declaration,
                            SubHeading, IsFromSystemHeader);
 }
 
 StaticFieldRecord *
-APISet::addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
+APISet::addStaticField(StringRef Name, StringRef USR, RecordLocation Loc,
                        AvailabilitySet Availabilities, LinkageInfo Linkage,
                        const DocComment &Comment,
                        DeclarationFragments Declaration,
@@ -188,7 +187,7 @@ APISet::addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
 
 CXXFieldRecord *
 APISet::addCXXField(APIRecord *CXXClass, StringRef Name, StringRef USR,
-                    PresumedLoc Loc, AvailabilitySet Availabilities,
+                    RecordLocation Loc, AvailabilitySet Availabilities,
                     const DocComment &Comment, DeclarationFragments Declaration,
                     DeclarationFragments SubHeading, AccessControl Access,
                     bool IsFromSystemHeader) {
@@ -201,7 +200,7 @@ APISet::addCXXField(APIRecord *CXXClass, StringRef Name, StringRef USR,
 }
 
 CXXFieldTemplateRecord *APISet::addCXXFieldTemplate(
-    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     AccessControl Access, Template Template, bool IsFromSystemHeader) {
@@ -217,7 +216,7 @@ CXXFieldTemplateRecord *APISet::addCXXFieldTemplate(
 
 CXXClassRecord *
 APISet::addCXXClass(APIRecord *Parent, StringRef Name, StringRef USR,
-                    PresumedLoc Loc, AvailabilitySet Availabilities,
+                    RecordLocation Loc, AvailabilitySet Availabilities,
                     const DocComment &Comment, DeclarationFragments Declaration,
                     DeclarationFragments SubHeading, APIRecord::RecordKind Kind,
                     AccessControl Access, bool IsFromSystemHeader) {
@@ -232,7 +231,7 @@ APISet::addCXXClass(APIRecord *Parent, StringRef Name, StringRef USR,
 }
 
 ClassTemplateRecord *APISet::addClassTemplate(
-    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     Template Template, AccessControl Access, bool IsFromSystemHeader) {
@@ -247,7 +246,7 @@ ClassTemplateRecord *APISet::addClassTemplate(
 }
 
 ClassTemplateSpecializationRecord *APISet::addClassTemplateSpecialization(
-    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     AccessControl Access, bool IsFromSystemHeader) {
@@ -263,7 +262,7 @@ ClassTemplateSpecializationRecord *APISet::addClassTemplateSpecialization(
 
 ClassTemplatePartialSpecializationRecord *
 APISet::addClassTemplatePartialSpecialization(
-    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     Template Template, AccessControl Access, bool IsFromSystemHeader) {
@@ -279,7 +278,7 @@ APISet::addClassTemplatePartialSpecialization(
 
 GlobalVariableTemplateSpecializationRecord *
 APISet::addGlobalVariableTemplateSpecialization(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Declaration,
     DeclarationFragments SubHeading, bool IsFromSystemHeader) {
@@ -291,7 +290,7 @@ APISet::addGlobalVariableTemplateSpecialization(
 
 GlobalVariableTemplatePartialSpecializationRecord *
 APISet::addGlobalVariableTemplatePartialSpecialization(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, LinkageInfo Linkage,
     const DocComment &Comment, DeclarationFragments Declaration,
     DeclarationFragments SubHeading, Template Template,
@@ -303,7 +302,8 @@ APISet::addGlobalVariableTemplatePartialSpecialization(
 }
 
 ConceptRecord *APISet::addConcept(StringRef Name, StringRef USR,
-                                  PresumedLoc Loc, AvailabilitySet Availability,
+                                  RecordLocation Loc,
+                                  AvailabilitySet Availability,
                                   const DocComment &Comment,
                                   DeclarationFragments Declaration,
                                   DeclarationFragments SubHeading,
@@ -314,8 +314,8 @@ ConceptRecord *APISet::addConcept(StringRef Name, StringRef USR,
 }
 
 CXXMethodRecord *APISet::addCXXInstanceMethod(
-    APIRecord *CXXClassRecord, StringRef Name, StringRef USR, PresumedLoc Loc,
-    AvailabilitySet Availability, const DocComment &Comment,
+    APIRecord *CXXClassRecord, StringRef Name, StringRef USR,
+    RecordLocation Loc, AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     FunctionSignature Signature, AccessControl Access,
     bool IsFromSystemHeader) {
@@ -331,8 +331,8 @@ CXXMethodRecord *APISet::addCXXInstanceMethod(
 }
 
 CXXMethodRecord *APISet::addCXXStaticMethod(
-    APIRecord *CXXClassRecord, StringRef Name, StringRef USR, PresumedLoc Loc,
-    AvailabilitySet Availability, const DocComment &Comment,
+    APIRecord *CXXClassRecord, StringRef Name, StringRef USR,
+    RecordLocation Loc, AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     FunctionSignature Signature, AccessControl Access,
     bool IsFromSystemHeader) {
@@ -348,7 +348,7 @@ CXXMethodRecord *APISet::addCXXStaticMethod(
 }
 
 CXXMethodTemplateRecord *APISet::addCXXMethodTemplate(
-    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     FunctionSignature Signature, AccessControl Access, Template Template,
@@ -364,7 +364,7 @@ CXXMethodTemplateRecord *APISet::addCXXMethodTemplate(
 }
 
 CXXMethodTemplateSpecializationRecord *APISet::addCXXMethodTemplateSpec(
-    APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+    APIRecord *Parent, StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availability, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     FunctionSignature Signature, AccessControl Access,
@@ -381,7 +381,7 @@ CXXMethodTemplateSpecializationRecord *APISet::addCXXMethodTemplateSpec(
 }
 
 ObjCCategoryRecord *APISet::addObjCCategory(
-    StringRef Name, StringRef USR, PresumedLoc Loc,
+    StringRef Name, StringRef USR, RecordLocation Loc,
     AvailabilitySet Availabilities, const DocComment &Comment,
     DeclarationFragments Declaration, DeclarationFragments SubHeading,
     SymbolReference Interface, bool IsFromSystemHeader,
@@ -402,7 +402,7 @@ ObjCCategoryRecord *APISet::addObjCCategory(
 }
 
 ObjCInterfaceRecord *
-APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+APISet::addObjCInterface(StringRef Name, StringRef USR, RecordLocation Loc,
                          AvailabilitySet Availabilities, LinkageInfo Linkage,
                          const DocComment &Comment,
                          DeclarationFragments Declaration,
@@ -416,10 +416,10 @@ APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
 
 ObjCMethodRecord *APISet::addObjCMethod(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-    PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
-    DeclarationFragments Declaration, DeclarationFragments SubHeading,
-    FunctionSignature Signature, bool IsInstanceMethod,
-    bool IsFromSystemHeader) {
+    RecordLocation Loc, AvailabilitySet Availabilities,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, FunctionSignature Signature,
+    bool IsInstanceMethod, bool IsFromSystemHeader) {
   std::unique_ptr<ObjCMethodRecord> Record;
   if (IsInstanceMethod)
     Record = std::make_unique<ObjCInstanceMethodRecord>(
@@ -438,8 +438,9 @@ ObjCMethodRecord *APISet::addObjCMethod(
 
 ObjCPropertyRecord *APISet::addObjCProperty(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-    PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
-    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    RecordLocation Loc, AvailabilitySet Availabilities,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading,
     ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
     StringRef SetterName, bool IsOptional, bool IsInstanceProperty,
     bool IsFromSystemHeader) {
@@ -462,8 +463,9 @@ ObjCPropertyRecord *APISet::addObjCProperty(
 
 ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
     ObjCContainerRecord *Container, StringRef Name, StringRef USR,
-    PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
-    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    RecordLocation Loc, AvailabilitySet Availabilities,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading,
     ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) {
   auto Record = std::make_unique<ObjCInstanceVariableRecord>(
       USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
@@ -475,7 +477,7 @@ ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
 }
 
 ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
-                                            PresumedLoc Loc,
+                                            RecordLocation Loc,
                                             AvailabilitySet Availabilities,
                                             const DocComment &Comment,
                                             DeclarationFragments Declaration,
@@ -487,7 +489,7 @@ ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
 }
 
 MacroDefinitionRecord *
-APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
+APISet::addMacroDefinition(StringRef Name, StringRef USR, RecordLocation Loc,
                            DeclarationFragments Declaration,
                            DeclarationFragments SubHeading,
                            bool IsFromSystemHeader) {
@@ -496,7 +498,7 @@ APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
 }
 
 TypedefRecord *
-APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+APISet::addTypedef(StringRef Name, StringRef USR, RecordLocation Loc,
                    AvailabilitySet Availabilities, const DocComment &Comment,
                    DeclarationFragments Declaration,
                    DeclarationFragments SubHeading,
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 6ee6e72d99ec57b..08e6aad09e4698b 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -14,6 +14,7 @@
 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Version.h"
+#include "clang/ExtractAPI/API.h"
 #include "clang/ExtractAPI/DeclarationFragments.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
@@ -105,8 +106,7 @@ Object serializePlatform(const Triple &T) {
 }
 
 /// Serialize a source position.
-Object serializeSourcePosition(const PresumedLoc &Loc) {
-  assert(Loc.isValid() && "invalid source position");
+Object serializeSourcePosition(const RecordLocation &Loc) {
 
   Object SourcePosition;
   SourcePosition["line"] = Loc.getLine();
@@ -120,7 +120,7 @@ Object serializeSourcePosition(const PresumedLoc &Loc) {
 /// \param Loc The presumed location to serialize.
 /// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
 /// Defaults to false.
-Object serializeSourceLocation(const PresumedLoc &Loc,
+Object serializeSourceLocation(const RecordLocation &Loc,
                                bool IncludeFileURI = false) {
   Object SourceLocation;
   serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));
@@ -136,8 +136,8 @@ Object serializeSourceLocation(const PresumedLoc &Loc,
 }
 
 /// Serialize a source range with begin and end locations.
-Object serializeSourceRange(const PresumedLoc &BeginLoc,
-                            const PresumedLoc &EndLoc) {
+Object serializeSourceRange(const RecordLocation &BeginLoc,
+                            const RecordLocation &EndLoc) {
   Object SourceRange;
   serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
   serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
diff --git a/clang/tools/libclang/CXExtractAPI.cpp b/clang/tools/libclang/CXExtractAPI.cpp
index 9128e891538a807..efb1e8098c9ddf4 100644
--- a/clang/tools/libclang/CXExtractAPI.cpp
+++ b/clang/tools/libclang/CXExtractAPI.cpp
@@ -61,9 +61,12 @@ struct LibClangExtractAPIVisitor
         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
     DocComment Comment;
-    if (auto *RawComment = fetchRawCommentForDecl(Interface))
-      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
-                                              Context.getDiagnostics());
+    if (auto *RawComment = fetchRawCommentForDecl(Interface)) {
+      auto RawCommentVec = RawComment->getFormattedLines(
+          Context.getSourceManager(), Context.getDiagnostics());
+      std::copy(RawCommentVec.begin(), RawCommentVec.end(),
+                std::back_inserter(Comment));
+    }
 
     // Build declaration fragments and sub-heading by generating them for the
     // interface.

>From a24e832827a376d803c03405eff474fa996d8228 Mon Sep 17 00:00:00 2001
From: Ankur <arsenic.secondary at gmail.com>
Date: Sun, 10 Sep 2023 17:16:20 +0530
Subject: [PATCH 2/2] [clang-tools-extra][ExtractAPI] create
 clang-symbolgraph-merger

Create a clang tool to merge all the JSON symbolgraph emited by --emit-symbol-graph or -extract-api options into one unified JSON symbolgraph file.

Differential Revision: https://reviews.llvm.org/D158646
---
 clang-tools-extra/CMakeLists.txt              |   1 +
 .../clang-symbolgraph-merger/CMakeLists.txt   |   3 +
 .../clang-symbolgraph-merger/SymbolGraph.h    |  48 +++
 .../SymbolGraphMerger.h                       |  45 +++
 .../SymbolGraphVisitor.h                      |  68 ++++
 .../lib/CMakeLists.txt                        |  14 +
 .../lib/SymbolGraph.cpp                       | 243 +++++++++++++++
 .../lib/SymbolGraphMerger.cpp                 | 290 ++++++++++++++++++
 .../tool/CMakeLists.txt                       |  13 +
 .../tool/SymbolGraphMergerMain.cpp            | 125 ++++++++
 .../clang/ExtractAPI/AvailabilityInfo.h       |   6 +
 11 files changed, 856 insertions(+)
 create mode 100644 clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt
 create mode 100755 clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp

diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt
index 6a3f741721ee6c7..a4052e0894076ef 100644
--- a/clang-tools-extra/CMakeLists.txt
+++ b/clang-tools-extra/CMakeLists.txt
@@ -13,6 +13,7 @@ if(CLANG_INCLUDE_TESTS)
   endif()
 endif()
 
+add_subdirectory(clang-symbolgraph-merger)
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-reorder-fields)
 add_subdirectory(modularize)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt b/clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
new file mode 100644
index 000000000000000..a071a8a11693337
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
@@ -0,0 +1,3 @@
+include_directories(include)
+add_subdirectory(lib)
+add_subdirectory(tool)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
new file mode 100755
index 000000000000000..a613f833ffad73b
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
@@ -0,0 +1,48 @@
+#ifndef SYMBOLGRAPH_H
+#define SYMBOLGRAPH_H
+
+#include "clang/Basic/LangStandard.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+#include <vector>
+
+namespace sgmerger {
+
+// see https://github.com/apple/swift-docc-symbolkit/bdob/main/openapi.yaml
+struct SymbolGraph {
+
+  struct Symbol {
+    Symbol(const llvm::json::Object &SymbolObj);
+
+    llvm::json::Object SymbolObj;
+    std::string AccessLevel;
+    clang::extractapi::APIRecord::RecordKind Kind;
+    clang::extractapi::DeclarationFragments DeclFragments;
+    clang::extractapi::FunctionSignature FunctionSign;
+    std::string Name;
+    std::string USR;
+    clang::extractapi::AvailabilitySet Availabilities;
+    clang::extractapi::DocComment Comments;
+    clang::extractapi::RecordLocation Location;
+    clang::extractapi::DeclarationFragments SubHeadings;
+
+    // underlying type in case of Typedef
+    clang::extractapi::SymbolReference UnderLyingType;
+  };
+
+  SymbolGraph(const llvm::StringRef JSON);
+  llvm::json::Object SymbolGraphObject;
+  llvm::json::Object Metadata;
+  llvm::json::Object Module;
+  std::vector<Symbol> Symbols;
+  llvm::json::Array Relationships;
+};
+
+} // namespace sgmerger
+
+#endif /* SYMBOLGRAPH_H */
diff --git a/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
new file mode 100755
index 000000000000000..179cadafd877825
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
@@ -0,0 +1,45 @@
+#ifndef SYMBOLGRAPHMERGER_H
+#define SYMBOLGRAPHMERGER_H
+
+#include "clang-symbolgraph-merger/SymbolGraph.h"
+#include "clang-symbolgraph-merger/SymbolGraphVisitor.h"
+#include "clang/Basic/LangStandard.h"
+#include "clang/ExtractAPI/API.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/TargetParser/Triple.h"
+#include <memory>
+
+namespace sgmerger {
+
+using SymbolMap = llvm::DenseMap<llvm::StringRef, const SymbolGraph::Symbol *>;
+
+class SymbolGraphMerger : public SymbolGraphVisitor<SymbolGraphMerger> {
+public:
+  SymbolGraphMerger(const clang::SmallVector<SymbolGraph> &SymbolGraphs,
+                    const std::string &ProductName = "")
+      : ProductName(ProductName), Lang(clang::Language::Unknown),
+        SymbolGraphs(SymbolGraphs) {}
+  bool merge();
+  bool visitMetadata(const llvm::json::Object &Metadata);
+  bool visitModule(const llvm::json::Object &Module);
+  bool visitSymbol(const SymbolGraph::Symbol &Symbol);
+  bool visitRelationship(const llvm::json::Object &Relationship);
+
+private:
+  std::string Generator;
+
+  // stuff required to construct the APISet
+  std::string ProductName;
+  llvm::Triple Target;
+  clang::Language Lang;
+
+  SymbolMap PendingSymbols;
+  SymbolMap VisitedSymbols;
+
+  const clang::SmallVector<SymbolGraph> &SymbolGraphs;
+};
+
+} // namespace sgmerger
+
+#endif /* SYMBOLGRAPHMERGER_H */
diff --git a/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
new file mode 100755
index 000000000000000..6e2042784147a62
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
@@ -0,0 +1,68 @@
+#ifndef SYMBOLGRAPHVISITOR_H
+#define SYMBOLGRAPHVISITOR_H
+
+#include "clang-symbolgraph-merger/SymbolGraph.h"
+#include "clang/ExtractAPI/API.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+
+namespace sgmerger {
+
+// Visits a symbol graph obbect and record the extracted info to API
+template <typename Derived> class SymbolGraphVisitor {
+public:
+  bool traverseSymbolGraph(const SymbolGraph &SG) {
+    bool Success = true;
+    Success = (getDerived()->visitMetadata(SG.Metadata) &&
+               getDerived()->visitModule(SG.Module) &&
+               getDerived()->traverseSymbols(SG.Symbols) &&
+               getDerived()->traverseRelationships(SG.Relationships));
+
+    return Success;
+  }
+
+  bool traverseSymbols(const std::vector<SymbolGraph::Symbol> &Symbols) {
+    bool Success = true;
+    for (const auto &Symbol : Symbols)
+      Success = getDerived()->visitSymbol(Symbol);
+    return Success;
+  }
+
+  bool traverseRelationships(const llvm::json::Array &Relationships) {
+    bool Success = true;
+    for (const auto &RelValue : Relationships) {
+      if (const auto *RelObj = RelValue.getAsObject())
+        Success = getDerived()->visitRelationship(*RelObj);
+    }
+    return Success;
+  }
+
+  bool visitMetadata(const llvm::json::Object &Metadata);
+  bool visitModule(const llvm::json::Object &Module);
+  bool visitSymbol(const SymbolGraph::Symbol &Symbol);
+  bool visitRelationship(const llvm::json::Object &Relationship);
+
+  std::unique_ptr<clang::extractapi::APISet> getAPISet() {
+    return std::move(API);
+  }
+
+protected:
+  std::unique_ptr<clang::extractapi::APISet> API;
+
+public:
+  SymbolGraphVisitor(const SymbolGraphVisitor &) = delete;
+  SymbolGraphVisitor(SymbolGraphVisitor &&) = delete;
+  SymbolGraphVisitor &operator=(const SymbolGraphVisitor &) = delete;
+  SymbolGraphVisitor &operator=(SymbolGraphVisitor &&) = delete;
+
+protected:
+  SymbolGraphVisitor() : API(nullptr) {}
+  ~SymbolGraphVisitor() = default;
+
+  Derived *getDerived() { return static_cast<Derived *>(this); };
+};
+
+} // namespace sgmerger
+
+#endif /* SYMBOLGRAPHVISITOR_H */
diff --git a/clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt b/clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
new file mode 100755
index 000000000000000..5f0bcc65c4762e2
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_clang_library(clangSymbolGraphMerger
+  SymbolGraphMerger.cpp
+  SymbolGraph.cpp
+  )
+
+clang_target_link_libraries(clangSymbolGraphMerger
+  PRIVATE
+  clangBasic
+  clangToolingCore
+  clangToolingInclusions
+  clangExtractAPI
+)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
new file mode 100755
index 000000000000000..030a9bda99db08e
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
@@ -0,0 +1,243 @@
+#include "clang-symbolgraph-merger/SymbolGraph.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/VersionTuple.h"
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <vector>
+
+using namespace sgmerger;
+using namespace llvm;
+using namespace llvm::json;
+using namespace clang::extractapi;
+
+namespace {
+
+APIRecord::RecordKind getSymbolKind(const Object &Kind) {
+
+  if (auto Identifier = Kind.getString("identifier")) {
+    // Remove danguage prefix
+    auto Id = Identifier->split('.').second;
+    if (Id.equals("func"))
+      return APIRecord::RK_GlobalFunction;
+    if (Id.equals("var"))
+      return APIRecord::RK_GlobalVariable;
+    if (Id.equals("enum.case"))
+      return APIRecord::RK_EnumConstant;
+    if (Id.equals("enum"))
+      return APIRecord::RK_Enum;
+    if (Id.equals("property"))
+      return APIRecord::RK_StructField;
+    if (Id.equals("struct"))
+      return APIRecord::RK_Struct;
+    if (Id.equals("ivar"))
+      return APIRecord::RK_ObjCIvar;
+    if (Id.equals("method"))
+      return APIRecord::RK_ObjCInstanceMethod;
+    if (Id.equals("type.method"))
+      return APIRecord::RK_ObjCClassMethod;
+    if (Id.equals("property"))
+      return APIRecord::RK_ObjCInstanceProperty;
+    if (Id.equals("type.property"))
+      return APIRecord::RK_ObjCClassProperty;
+    if (Id.equals("class"))
+      return APIRecord::RK_ObjCInterface;
+    if (Id.equals("protocod"))
+      return APIRecord::RK_ObjCProtocol;
+    if (Id.equals("macro"))
+      return APIRecord::RK_MacroDefinition;
+    if (Id.equals("typealias"))
+      return APIRecord::RK_Typedef;
+  }
+  return APIRecord::RK_Unknown;
+}
+
+VersionTuple parseVersionTupleFromJSON(const Object *VTObj) {
+  auto Major = VTObj->getInteger("major").value_or(0);
+  auto Minor = VTObj->getInteger("minor").value_or(0);
+  auto Patch = VTObj->getInteger("patch").value_or(0);
+  return VersionTuple(Major, Minor, Patch);
+}
+
+RecordLocation parseSourcePositionFromJSON(const Object *PosObj,
+                                           std::string Filename = "") {
+  assert(PosObj);
+  unsigned Line = PosObj->getInteger("line").value_or(0);
+  unsigned Col = PosObj->getInteger("character").value_or(0);
+  return RecordLocation(Line, Col, Filename);
+}
+
+RecordLocation parseRecordLocationFromJSON(const Object *LocObj) {
+  assert(LocObj);
+
+  std::string Filename(LocObj->getString("uri").value_or(""));
+  // extract file name from URI
+  std::string URIScheme = "file://";
+  if (Filename.find(URIScheme) == 0)
+    Filename.erase(0, URIScheme.length());
+
+  const auto *PosObj = LocObj->getObject("position");
+
+  return parseSourcePositionFromJSON(PosObj, Filename);
+}
+
+DocComment parseCommentsFromJSON(const Object *CommentsObj) {
+  assert(CommentsObj);
+  const auto *LinesArray = CommentsObj->getArray("lines");
+  DocComment Comments;
+  if (LinesArray) {
+    for (auto &LineValue : *LinesArray) {
+      const auto *LineObj = LineValue.getAsObject();
+      auto Text = LineObj->getString("text").value_or("");
+
+      // parse range
+      const auto *BeginLocObj = LineObj->getObject("start");
+      RecordLocation BeginLoc = parseSourcePositionFromJSON(BeginLocObj);
+      const auto *EndLocObj = LineObj->getObject("end");
+      RecordLocation EndLoc = parseSourcePositionFromJSON(EndLocObj);
+      Comments.push_back(CommentLine(Text, BeginLoc, EndLoc));
+    }
+  }
+  return Comments;
+}
+
+AvailabilitySet parseAvailabilitiesFromJSON(const Array *AvailablityArray) {
+  if (AvailablityArray) {
+    SmallVector<AvailabilityInfo, 4> AList;
+    for (auto &AvailablityValue : *AvailablityArray) {
+      const auto *AvailablityObj = AvailablityValue.getAsObject();
+      auto Domain = AvailablityObj->getString("domain").value_or("");
+      auto IntroducedVersion = parseVersionTupleFromJSON(
+          AvailablityObj->getObject("introducedVersion"));
+      auto ObsoletedVersion = parseVersionTupleFromJSON(
+          AvailablityObj->getObject("obsoletedVersion"));
+      auto DeprecatedVersion = parseVersionTupleFromJSON(
+          AvailablityObj->getObject("deprecatedVersion"));
+      AList.emplace_back(AvailabilityInfo(Domain, IntroducedVersion,
+                                          DeprecatedVersion, ObsoletedVersion,
+                                          false));
+    }
+    return AvailabilitySet(AList);
+  }
+  return nullptr;
+}
+
+DeclarationFragments parseDeclFragmentsFromJSON(const Array *FragmentsArray) {
+  DeclarationFragments Fragments;
+  if (FragmentsArray) {
+    for (auto &FragmentValue : *FragmentsArray) {
+      Object FragmentObj = *(FragmentValue.getAsObject());
+      auto Spelling = FragmentObj.getString("spelling").value_or("");
+      auto FragmentKind = DeclarationFragments::parseFragmentKindFromString(
+          FragmentObj.getString("kind").value_or(""));
+      StringRef PreciseIdentifier =
+          FragmentObj.getString("preciseIdentifier").value_or("");
+      Fragments.append(Spelling, FragmentKind, PreciseIdentifier);
+    }
+  }
+  return Fragments;
+}
+
+FunctionSignature parseFunctionSignaturesFromJSON(const Object *SignaturesObj) {
+  FunctionSignature ParsedSignatures;
+  if (SignaturesObj) {
+    // parse return type
+    const auto *RT = SignaturesObj->getArray("returns");
+    ParsedSignatures.setReturnType(parseDeclFragmentsFromJSON(RT));
+
+    // parse function parameters
+    if (const auto *ParamArray = SignaturesObj->getArray("parameters")) {
+      for (auto &Param : *ParamArray) {
+        auto ParamObj = *(Param.getAsObject());
+        auto Name = ParamObj.getString("name").value_or("");
+        auto Fragments = parseDeclFragmentsFromJSON(
+            ParamObj.getArray("declarationFragments"));
+        ParsedSignatures.addParameter(Name, Fragments);
+      }
+    }
+  }
+  return ParsedSignatures;
+}
+
+std::vector<SymbolGraph::Symbol>
+parseSymbolsFromJSON(const Array *SymbolsArray) {
+  std::vector<SymbolGraph::Symbol> SymbolsVector;
+  if (SymbolsArray) {
+    for (const auto &S : *SymbolsArray)
+      if (const auto *Symbol = S.getAsObject())
+        SymbolsVector.push_back(SymbolGraph::Symbol(*Symbol));
+  }
+  return SymbolsVector;
+}
+
+} // namespace
+
+SymbolGraph::Symbol::Symbol(const Object &SymbolObject)
+    : SymbolObj(SymbolObject) {
+
+  AccessLevel = SymbolObj.getString("accessLevel").value_or("unknown");
+  Kind = getSymbolKind(*(SymbolObject.getObject("kind")));
+
+  // parse Doc comments
+  if (const auto *CommentsArray = SymbolObject.getObject("docComment"))
+    Comments = parseCommentsFromJSON(CommentsArray);
+
+  // parse Availabilityinfo
+  if (const auto *AvailabilityArray = SymbolObj.getArray("availability"))
+    Availabilities = parseAvailabilitiesFromJSON(AvailabilityArray);
+
+  // parse declaration fragments
+  if (const auto *FragmentsArray = SymbolObj.getArray("declarationFragments"))
+    DeclFragments = parseDeclFragmentsFromJSON(FragmentsArray);
+
+  // parse function signatures if any
+  if (const auto *FunctionSignObj = SymbolObj.getObject("functionSignature"))
+    FunctionSign = parseFunctionSignaturesFromJSON(FunctionSignObj);
+
+  // parse identifier
+  if (const auto *IDObj = SymbolObj.getObject("identifier"))
+    USR = IDObj->getString("precise").value_or("");
+
+  // parse Location
+  if (const auto *LocObj = SymbolObject.getObject("location"))
+    Location = parseRecordLocationFromJSON(LocObj);
+
+  // parse name and subheadings.
+  if (const auto *NamesObj = SymbolObj.getObject("names")) {
+    Name = NamesObj->getString("title").value_or("");
+    if (const auto *SubHObj = NamesObj->getArray("subHeading"))
+      SubHeadings = parseDeclFragmentsFromJSON(SubHObj);
+  }
+
+  // parse underlying type in case of Typedef
+  auto UType = SymbolObject.getString("type");
+  if (UType.has_value()) {
+    auto UTypeUSR = UType.value();
+    // FIXME: this is a hacky way for Underlying type to be
+    // serialized into the final graph. Get someway to extract the
+    // actual name of the underlying type from USR
+    UnderLyingType = SymbolReference(" ", UTypeUSR);
+  }
+}
+
+SymbolGraph::SymbolGraph(const llvm::StringRef JSON) {
+  Expected<llvm::json::Value> SGValue = llvm::json::parse(JSON);
+  if (SGValue) {
+    assert(SGValue && SGValue->kind() == llvm::json::Value::Object);
+    if (const auto *SGObject = SGValue->getAsObject()) {
+      SymbolGraphObject = *SGObject;
+      if (const auto *MetadataObj = SGObject->getObject("metadata"))
+        Metadata = *MetadataObj;
+      if (const auto *ModuleObj = SGObject->getObject("module"))
+        Module = *ModuleObj;
+      if (const auto *RelArray = SGObject->getArray("relationships"))
+        Relationships = *RelArray;
+
+      Symbols = parseSymbolsFromJSON(SGObject->getArray("symbols"));
+    }
+  }
+}
diff --git a/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
new file mode 100755
index 000000000000000..71facea3e6ba8bc
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
@@ -0,0 +1,290 @@
+#include "clang-symbolgraph-merger/SymbolGraphMerger.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <memory>
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace clang;
+using namespace clang::extractapi;
+using namespace sgmerger;
+
+namespace {
+ObjCInstanceVariableRecord::AccessControl
+getAccessFromString(const StringRef AccessLevel) {
+  if (AccessLevel.equals("Private"))
+    return ObjCInstanceVariableRecord::AccessControl::Private;
+  if (AccessLevel.equals("Protected"))
+    return ObjCInstanceVariableRecord::AccessControl::Protected;
+  if (AccessLevel.equals("Public"))
+    return ObjCInstanceVariableRecord::AccessControl::Public;
+  if (AccessLevel.equals("Package"))
+    return ObjCInstanceVariableRecord::AccessControl::Package;
+  return ObjCInstanceVariableRecord::AccessControl::None;
+}
+
+Language getLanguageFromString(const StringRef LangName) {
+  if (LangName.equals("c"))
+    return Language::C;
+  if (LangName.equals("objective-c"))
+    return Language::ObjC;
+  if (LangName.equals("C++"))
+    return Language::CXX;
+
+  return Language::Unknown;
+}
+
+template <typename Lambda>
+bool addWithContainerRecord(APIRecord::RecordKind Kind, APIRecord *TargetRecord,
+                            Lambda Inserter) {
+  switch (Kind) {
+  case APIRecord::RK_ObjCInterface: {
+    if (ObjCInterfaceRecord *Container =
+            dyn_cast_or_null<ObjCInterfaceRecord>(TargetRecord))
+      Inserter(Container);
+  } break;
+  case APIRecord::RK_ObjCProtocol: {
+    if (ObjCProtocolRecord *Container =
+            dyn_cast_or_null<ObjCProtocolRecord>(TargetRecord))
+      Inserter(Container);
+  } break;
+  case APIRecord::RK_ObjCCategory: {
+    if (ObjCCategoryRecord *Container =
+            dyn_cast_or_null<ObjCCategoryRecord>(TargetRecord))
+      Inserter(Container);
+  } break;
+  default:
+    return false;
+  }
+  return true;
+}
+} // namespace
+
+bool SymbolGraphMerger::merge() {
+  for (const auto &SG : SymbolGraphs)
+    traverseSymbolGraph(SG);
+  return true;
+}
+
+bool SymbolGraphMerger::visitMetadata(const Object &Metadata) {
+  // TODO: check if all the symbol graphs are generated form same
+  // generator or not. Info from metadata is currently not needed to
+  // construct the APISet,
+  return true;
+}
+
+bool SymbolGraphMerger::visitModule(const Object &Module) {
+  if (!API) {
+    // If the product name is not provided via command line then extract product
+    // name from SymbolGraph
+    if (ProductName.empty())
+      if (auto NameStr = Module.getString("name"))
+        ProductName = NameStr->str();
+
+    // extract target triple info
+    if (const auto *Platform = Module.getObject("platform")) {
+      auto Arch = Platform->getString("architecture");
+      const auto *OSObj = Platform->getObject("operatingSystem");
+      auto Vendor = Platform->getString("vendor");
+      if (!(Arch && Vendor && OSObj))
+        return false;
+      if (auto OSStr = OSObj->getString("name")) {
+        Target = llvm::Triple(*Arch, *Vendor, *OSStr);
+        return true;
+      }
+    }
+    return false;
+  }
+  return true;
+}
+
+bool SymbolGraphMerger::visitRelationship(const Object &Relationship) {
+  std::string SourceUSR(Relationship.getString("source").value_or(""));
+  std::string TargetUSR(Relationship.getString("target").value_or(""));
+
+  auto *SourceSymbol = PendingSymbols.lookup(SourceUSR);
+  auto *TargetSymbol = VisitedSymbols.lookup(TargetUSR);
+  auto *TargetRecord = API->findRecordForUSR(TargetUSR);
+
+  switch (SourceSymbol->Kind) {
+  case APIRecord::RK_StructField: {
+    if (StructRecord *ParentStruct =
+            dyn_cast_or_null<StructRecord>(TargetRecord))
+      API->addStructField(ParentStruct, SourceSymbol->Name, SourceSymbol->USR,
+                          SourceSymbol->Location, SourceSymbol->Availabilities,
+                          SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                          SourceSymbol->SubHeadings,
+                          false /*IsFromSystemHeader*/);
+  } break;
+  case APIRecord::RK_ObjCIvar: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      ObjCInstanceVariableRecord::AccessControl Access =
+          getAccessFromString(SourceSymbol->AccessLevel);
+
+      API->addObjCInstanceVariable(
+          Container, SourceSymbol->Name, SourceSymbol->USR,
+          SourceSymbol->Location, SourceSymbol->Availabilities,
+          SourceSymbol->Comments, SourceSymbol->DeclFragments,
+          SourceSymbol->SubHeadings, Access, false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCInstanceMethod: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCMethod(Container, SourceSymbol->Name, SourceSymbol->USR,
+                         SourceSymbol->Location, SourceSymbol->Availabilities,
+                         SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                         SourceSymbol->SubHeadings, SourceSymbol->FunctionSign,
+                         true
+                         /*IsInstanceMethod*/,
+                         false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_EnumConstant: {
+    if (EnumRecord *Enum = dyn_cast_or_null<EnumRecord>(TargetRecord))
+      API->addEnumConstant(Enum, SourceSymbol->Name, SourceSymbol->USR,
+                           SourceSymbol->Location, SourceSymbol->Availabilities,
+                           SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                           SourceSymbol->SubHeadings,
+                           false /*IsFromSystemHeader*/);
+  } break;
+  case APIRecord::RK_ObjCClassMethod: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCMethod(Container, SourceSymbol->Name, SourceSymbol->USR,
+                         SourceSymbol->Location, SourceSymbol->Availabilities,
+                         SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                         SourceSymbol->SubHeadings, SourceSymbol->FunctionSign,
+                         false
+                         /*IsInstanceMethod*/,
+                         false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCInstanceProperty: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCProperty(Container, SourceSymbol->Name, SourceSymbol->USR,
+                           SourceSymbol->Location, SourceSymbol->Availabilities,
+                           SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                           SourceSymbol->SubHeadings,
+                           ObjCPropertyRecord::AttributeKind::NoAttr, "", "",
+                           false /*IsOptional*/, true /*IsInstanceProperty*/,
+                           false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCClassProperty: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCProperty(Container, SourceSymbol->Name, SourceSymbol->USR,
+                           SourceSymbol->Location, SourceSymbol->Availabilities,
+                           SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                           SourceSymbol->SubHeadings,
+                           ObjCPropertyRecord::AttributeKind::NoAttr, "", "",
+                           false /*IsOptional*/, false /*IsInstanceProperty*/,
+                           false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCInterface: {
+    if (TargetRecord) {
+      SymbolReference SuperClass(TargetRecord);
+
+      API->addObjCInterface(
+          SourceSymbol->Name, SourceSymbol->USR, SourceSymbol->Location,
+          SourceSymbol->Availabilities, LinkageInfo(), SourceSymbol->Comments,
+          SourceSymbol->DeclFragments, SourceSymbol->SubHeadings, SuperClass,
+          false /*IsFromSystemHeader*/);
+    }
+  } break;
+  case APIRecord::RK_ObjCCategory: {
+    if (TargetRecord) {
+      SymbolReference Interface(TargetRecord);
+
+      API->addObjCCategory(
+          SourceSymbol->Name, SourceSymbol->USR, SourceSymbol->Location,
+          SourceSymbol->Availabilities, SourceSymbol->Comments,
+          SourceSymbol->DeclFragments, SourceSymbol->SubHeadings, Interface,
+          false /*IsFromSystemHeader*/, true /*IsFromExternalModule*/);
+    }
+  } break;
+  default:
+    return false;
+  }
+  return true;
+}
+
+bool SymbolGraphMerger::visitSymbol(const SymbolGraph::Symbol &Symbol) {
+  // If the APISet is not yet created, then create it ( it's generally
+  // the Language information that is pending uptill this point )
+  if (!API) {
+    if (const auto *Id = Symbol.SymbolObj.getObject("identifier"))
+      if (const auto LangName = Id->getString("interfaceLanguage"))
+        Lang = getLanguageFromString(LangName.value_or(""));
+    API = std::make_unique<extractapi::APISet>(Target, Lang, ProductName);
+  }
+
+  switch (Symbol.Kind) {
+  // TODO: Handle unknown symbols properly
+  case APIRecord::RK_Unknown:
+    break;
+  case APIRecord::RK_GlobalVariable: {
+    API->addGlobalVar(Symbol.Name, Symbol.USR, Symbol.Location,
+                      Symbol.Availabilities, LinkageInfo(), Symbol.Comments,
+                      Symbol.DeclFragments, Symbol.SubHeadings,
+                      false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_GlobalFunction: {
+    API->addGlobalFunction(
+        Symbol.Name, Symbol.USR, Symbol.Location, Symbol.Availabilities,
+        LinkageInfo(), Symbol.Comments, Symbol.DeclFragments,
+        Symbol.SubHeadings, Symbol.FunctionSign, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+
+  case APIRecord::RK_Enum: {
+    API->addEnum(Symbol.Name, Symbol.USR, Symbol.Location,
+                 Symbol.Availabilities, Symbol.Comments, Symbol.DeclFragments,
+                 Symbol.SubHeadings, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_Struct: {
+    API->addStruct(Symbol.Name, Symbol.USR, Symbol.Location,
+                   Symbol.Availabilities, Symbol.Comments, Symbol.DeclFragments,
+                   Symbol.SubHeadings, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_ObjCProtocol: {
+    API->addObjCProtocol(Symbol.Name, Symbol.USR, Symbol.Location,
+                         Symbol.Availabilities, Symbol.Comments,
+                         Symbol.DeclFragments, Symbol.SubHeadings,
+                         false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_MacroDefinition: {
+    API->addMacroDefinition(Symbol.Name, Symbol.USR, Symbol.Location,
+                            Symbol.DeclFragments, Symbol.SubHeadings,
+                            false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_Typedef: {
+    API->addTypedef(Symbol.Name, Symbol.USR, Symbol.Location,
+                    Symbol.Availabilities, Symbol.Comments,
+                    Symbol.DeclFragments, Symbol.SubHeadings,
+                    Symbol.UnderLyingType, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  default:
+    // Try again when visiting Relationships
+    PendingSymbols[Symbol.USR] = &Symbol;
+  }
+  return true;
+}
diff --git a/clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt b/clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt
new file mode 100755
index 000000000000000..635a056cf1821d2
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_clang_tool(clang-symbolgraph-merger SymbolGraphMergerMain.cpp)
+
+target_link_libraries(clang-symbolgraph-merger PRIVATE
+  clangBasic
+  clangFrontend
+  clangSerialization
+  clangTooling
+)
+
+target_link_libraries(clang-symbolgraph-merger PRIVATE
+  clangSymbolGraphMerger)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp b/clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp
new file mode 100755
index 000000000000000..9a2ce97810cdd25
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp
@@ -0,0 +1,125 @@
+#include "clang-symbolgraph-merger/SymbolGraphMerger.h"
+#include "clang-symbolgraph-merger/SymbolGraphVisitor.h"
+#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <system_error>
+
+using namespace clang::tooling;
+using namespace llvm;
+using namespace sgmerger;
+
+namespace {
+
+bool collectSymbolGraphs(StringRef SGDirectory,
+                         SmallVector<SymbolGraph> &SymbolGraphs) {
+  std::error_code Error;
+  for (sys::fs::directory_iterator I(SGDirectory, Error), End; I != End;
+       I.increment(Error)) {
+    if (Error)
+      return false;
+    std::string File(I->path());
+    llvm::ErrorOr<sys::fs::basic_file_status> Status = I->status();
+    if (!Status)
+      return false;
+    sys::fs::file_type Type = Status->type();
+    // If the file is a directory, ignore the name and recurse.
+    if (Type == sys::fs::file_type::directory_file) {
+      if (!collectSymbolGraphs(File, SymbolGraphs))
+        return false;
+      continue;
+    }
+
+    // Ignore all the non json files
+    if (!sys::path::extension(File).equals(".json"))
+      continue;
+
+    // Read the Symbol Graph from the file
+    int FileFD;
+    if (auto OpenError = sys::fs::openFileForRead(File, FileFD))
+      return false;
+
+    llvm::SmallString<256> Payload;
+    if (auto ReadError = sys::fs::readNativeFileToEOF(FileFD, Payload))
+      return false;
+
+    SymbolGraphs.emplace_back(SymbolGraph(Payload));
+    llvm::sys::fs::closeFile(FileFD);
+  }
+  return true;
+}
+
+} // namespace
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+// TODO: add more help text
+// static cl::extrahelp MoreHelp("\nMore help text...\n");
+
+static cl::OptionCategory
+    SymbolGraphMergerCategory("clang-symbolgraph-merger options");
+
+static cl::opt<std::string> ProjectName("project-name",
+                                        cl::desc("Name of project."),
+                                        cl::cat(SymbolGraphMergerCategory),
+                                        cl::init(""));
+
+static cl::opt<std::string>
+    OutFile("o", cl::desc("File for outputing generated Symbol Graph."),
+            cl::cat(SymbolGraphMergerCategory), cl::value_desc("filepath"),
+            cl::init("output"));
+
+static cl::opt<std::string>
+    InputDir(cl::Positional, cl::Required, cl::cat(SymbolGraphMergerCategory),
+             cl::value_desc("filepath"),
+             cl::desc("Input directory containing all the SymbolGraphs"));
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+  cl::ParseCommandLineOptions(argc, argv);
+
+  // collect symbol graphs from input dir
+  SmallVector<SymbolGraph> SymbolGraphs;
+  if (collectSymbolGraphs(InputDir, SymbolGraphs)) {
+    llvm::outs() << "found " << SymbolGraphs.size() << " Symbol-graphs in "
+                 << InputDir << "\n";
+
+    // merge them together to form unified APIset
+    llvm::outs() << "merging ...\n";
+    SymbolGraphMerger Merger(SymbolGraphs);
+    if (Merger.merge()) {
+      // serialize the unified symbol graph
+      std::error_code Error;
+      if (!sys::path::extension(OutFile).equals(".json"))
+        OutFile.append(".json");
+
+      llvm::outs() << "serializing...\n";
+      auto OS = std::make_unique<raw_fd_ostream>(OutFile, Error);
+      const auto APISet = Merger.getAPISet();
+      if (APISet) {
+        clang::extractapi::SymbolGraphSerializer SGSerializer(
+            *APISet, clang::extractapi::APIIgnoresList());
+        SGSerializer.serialize(*OS);
+        OS.reset();
+        llvm::outs() << "successfully serialized resultant SymbolGraph to "
+                     << OutFile << "\n";
+      }
+    } else {
+      llvm::errs() << "merge faliure\n";
+      return 1;
+    }
+  } else {
+    llvm::errs() << "some error occured while accessing " << InputDir << "\n";
+    return 1;
+  }
+  return 0;
+}
diff --git a/clang/include/clang/ExtractAPI/AvailabilityInfo.h b/clang/include/clang/ExtractAPI/AvailabilityInfo.h
index 0af373135b6674a..da7dffbd780b6ab 100644
--- a/clang/include/clang/ExtractAPI/AvailabilityInfo.h
+++ b/clang/include/clang/ExtractAPI/AvailabilityInfo.h
@@ -53,6 +53,12 @@ class AvailabilitySet {
 
 public:
   AvailabilitySet(const Decl *Decl);
+  AvailabilitySet(AvailabilityList &List,
+                  bool UnconditionallyDeprecated = false,
+                  bool UnconditionallyUnavailable = false)
+      : Availabilities(List),
+        UnconditionallyDeprecated(UnconditionallyDeprecated),
+        UnconditionallyUnavailable(UnconditionallyUnavailable) {}
   AvailabilitySet() = default;
 
   AvailabilityList::const_iterator begin() const {



More information about the cfe-commits mailing list