[clang] 8b76b44 - [clang][ExtractAPI] Add support for C++ classes

Erick Velez via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 2 10:19:13 PDT 2023


Author: Erick Velez
Date: 2023-08-02T10:19:05-07:00
New Revision: 8b76b44e46ac5b4efbf8cb3702f09d353af2a112

URL: https://github.com/llvm/llvm-project/commit/8b76b44e46ac5b4efbf8cb3702f09d353af2a112
DIFF: https://github.com/llvm/llvm-project/commit/8b76b44e46ac5b4efbf8cb3702f09d353af2a112.diff

LOG: [clang][ExtractAPI] Add support for C++ classes

Add ExtractAPI support C++ classes, fields,  methods, and various qualifiers and specifiers

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

Added: 
    clang/test/ExtractAPI/class.cpp
    clang/test/ExtractAPI/constructor_destructor.cpp
    clang/test/ExtractAPI/conversions.cpp
    clang/test/ExtractAPI/function_noexcepts.cpp
    clang/test/ExtractAPI/methods.cpp
    clang/test/ExtractAPI/multiple_inheritance.cpp
    clang/test/ExtractAPI/operator_overload.cpp
    clang/test/ExtractAPI/simple_inheritance.cpp

Modified: 
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/DeclarationFragments.h
    clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
    clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
    clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index a18879f39645f7..437ac953b7409d 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -22,6 +22,7 @@
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/RawCommentList.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/Specifiers.h"
 #include "clang/ExtractAPI/AvailabilityInfo.h"
 #include "clang/ExtractAPI/DeclarationFragments.h"
 #include "llvm/ADT/MapVector.h"
@@ -64,6 +65,14 @@ struct APIRecord {
     RK_Enum,
     RK_StructField,
     RK_Struct,
+    RK_Union,
+    RK_StaticField,
+    RK_CXXField,
+    RK_CXXClass,
+    RK_CXXStaticMethod,
+    RK_CXXInstanceMethod,
+    RK_CXXConstructorMethod,
+    RK_CXXDestructorMethod,
     RK_ObjCInstanceProperty,
     RK_ObjCClassProperty,
     RK_ObjCIvar,
@@ -266,6 +275,132 @@ struct StructRecord : APIRecord {
   virtual void anchor();
 };
 
+struct CXXFieldRecord : APIRecord {
+  AccessControl Access;
+
+  CXXFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                 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),
+        Access(Access) {}
+
+  CXXFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
+                 PresumedLoc Loc, AvailabilitySet Availabilities,
+                 const DocComment &Comment, DeclarationFragments Declaration,
+                 DeclarationFragments SubHeading, AccessControl Access,
+                 bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
+        Access(Access) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_CXXField;
+  }
+
+private:
+  virtual void anchor();
+};
+
+struct CXXMethodRecord : APIRecord {
+  FunctionSignature Signature;
+  AccessControl Access;
+
+  CXXMethodRecord() = delete;
+
+  CXXMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
+                  PresumedLoc Loc, AvailabilitySet Availabilities,
+                  const DocComment &Comment, DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, FunctionSignature Signature,
+                  AccessControl Access, bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader),
+        Signature(Signature), Access(Access) {}
+
+  virtual ~CXXMethodRecord() = 0;
+};
+
+struct CXXConstructorRecord : CXXMethodRecord {
+  CXXConstructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                       AvailabilitySet Availabilities,
+                       const DocComment &Comment,
+                       DeclarationFragments Declaration,
+                       DeclarationFragments SubHeading,
+                       FunctionSignature Signature, AccessControl Access,
+                       bool IsFromSystemHeader)
+      : CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, Loc,
+                        std::move(Availabilities), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_CXXConstructorMethod;
+  }
+
+private:
+  virtual void anchor();
+};
+
+struct CXXDestructorRecord : CXXMethodRecord {
+  CXXDestructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                      AvailabilitySet Availabilities, const DocComment &Comment,
+                      DeclarationFragments Declaration,
+                      DeclarationFragments SubHeading,
+                      FunctionSignature Signature, AccessControl Access,
+                      bool IsFromSystemHeader)
+      : CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, Loc,
+                        std::move(Availabilities), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_CXXDestructorMethod;
+  }
+
+private:
+  virtual void anchor();
+};
+
+struct CXXStaticMethodRecord : CXXMethodRecord {
+  CXXStaticMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                        AvailabilitySet Availabilities,
+                        const DocComment &Comment,
+                        DeclarationFragments Declaration,
+                        DeclarationFragments SubHeading,
+                        FunctionSignature Signature, AccessControl Access,
+                        bool IsFromSystemHeader)
+      : CXXMethodRecord(RK_CXXStaticMethod, USR, Name, Loc,
+                        std::move(Availabilities), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_CXXStaticMethod;
+  }
+
+private:
+  virtual void anchor();
+};
+
+struct CXXInstanceMethodRecord : CXXMethodRecord {
+  CXXInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                          AvailabilitySet Availabilities,
+                          const DocComment &Comment,
+                          DeclarationFragments Declaration,
+                          DeclarationFragments SubHeading,
+                          FunctionSignature Signature, AccessControl Access,
+                          bool IsFromSystemHeader)
+      : CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, Loc,
+                        std::move(Availabilities), Comment, Declaration,
+                        SubHeading, Signature, Access, IsFromSystemHeader) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_CXXInstanceMethod;
+  }
+
+private:
+  virtual void anchor();
+};
+
 /// This holds information associated with Objective-C properties.
 struct ObjCPropertyRecord : APIRecord {
   /// The attributes associated with an Objective-C property.
@@ -444,6 +579,24 @@ struct SymbolReference {
   bool empty() const { return Name.empty() && USR.empty() && Source.empty(); }
 };
 
+struct StaticFieldRecord : CXXFieldRecord {
+  SymbolReference Context;
+
+  StaticFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                    AvailabilitySet Availabilities, LinkageInfo Linkage,
+                    const DocComment &Comment, DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading, SymbolReference Context,
+                    AccessControl Access, bool IsFromSystemHeader)
+      : CXXFieldRecord(RK_StaticField, USR, Name, Loc,
+                       std::move(Availabilities), Comment, Declaration,
+                       SubHeading, Access, IsFromSystemHeader),
+        Context(Context) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_StaticField;
+  }
+};
+
 /// The base representation of an Objective-C container record. Holds common
 /// information associated with Objective-C containers.
 struct ObjCContainerRecord : APIRecord {
@@ -465,6 +618,28 @@ struct ObjCContainerRecord : APIRecord {
   virtual ~ObjCContainerRecord() = 0;
 };
 
+struct CXXClassRecord : APIRecord {
+  SmallVector<std::unique_ptr<CXXFieldRecord>> Fields;
+  SmallVector<std::unique_ptr<CXXMethodRecord>> Methods;
+  SmallVector<SymbolReference> Bases;
+
+  CXXClassRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+                 AvailabilitySet Availabilities, const DocComment &Comment,
+                 DeclarationFragments Declaration,
+                 DeclarationFragments SubHeading, RecordKind Kind,
+                 bool IsFromSystemHeader)
+      : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
+                  LinkageInfo::none(), Comment, Declaration, SubHeading,
+                  IsFromSystemHeader) {}
+
+  static bool classof(const APIRecord *Record) {
+    return (Record->getKind() == RK_CXXClass);
+  }
+
+private:
+  virtual void anchor();
+};
+
 /// This holds information associated with Objective-C categories.
 struct ObjCCategoryRecord : ObjCContainerRecord {
   SymbolReference Interface;
@@ -591,6 +766,12 @@ struct has_function_signature<ObjCInstanceMethodRecord>
     : public std::true_type {};
 template <>
 struct has_function_signature<ObjCClassMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<CXXMethodRecord> : public std::true_type {};
+
+template <typename RecordTy> struct has_access : public std::false_type {};
+template <> struct has_access<CXXMethodRecord> : public std::true_type {};
+template <> struct has_access<CXXFieldRecord> : public std::true_type {};
 
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
@@ -668,6 +849,41 @@ class APISet {
                           DeclarationFragments SubHeading,
                           bool IsFromSystemHeader);
 
+  StaticFieldRecord *
+  addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
+                 AvailabilitySet Availabilities, LinkageInfo Linkage,
+                 const DocComment &Comment, DeclarationFragments Declaration,
+                 DeclarationFragments SubHeading, SymbolReference Context,
+                 AccessControl Access, bool IsFromSystemHeaderg);
+
+  CXXFieldRecord *addCXXField(CXXClassRecord *CXXClass, StringRef Name,
+                              StringRef USR, PresumedLoc Loc,
+                              AvailabilitySet Availabilities,
+                              const DocComment &Comment,
+                              DeclarationFragments Declaration,
+                              DeclarationFragments SubHeading,
+                              AccessControl Access, bool IsFromSystemHeader);
+
+  CXXClassRecord *
+  addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
+              AvailabilitySet Availability, const DocComment &Comment,
+              DeclarationFragments Declaration, DeclarationFragments SubHeading,
+              APIRecord::RecordKind Kind, bool IsFromSystemHeader);
+
+  CXXMethodRecord *
+  addCXXMethod(CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
+               PresumedLoc Loc, AvailabilitySet Availability,
+               const DocComment &Comment, DeclarationFragments Declaration,
+               DeclarationFragments SubHeading, FunctionSignature Signature,
+               bool IsStatic, AccessControl Access, bool IsFromSystemHeader);
+
+  CXXMethodRecord *addCXXSpecialMethod(
+      CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
+      PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
+      DeclarationFragments Declaration, DeclarationFragments SubHeading,
+      FunctionSignature Signature, bool IsConstructor, AccessControl Access,
+      bool IsFromSystemHeader);
+
   /// Create and add an Objective-C category record into the API set.
   ///
   /// Note: the caller is responsible for keeping the StringRef \p Name and
@@ -790,8 +1006,12 @@ class APISet {
   const RecordMap<GlobalVariableRecord> &getGlobalVariables() const {
     return GlobalVariables;
   }
+  const RecordMap<StaticFieldRecord> &getStaticFields() const {
+    return StaticFields;
+  }
   const RecordMap<EnumRecord> &getEnums() const { return Enums; }
   const RecordMap<StructRecord> &getStructs() const { return Structs; }
+  const RecordMap<CXXClassRecord> &getCXXClasses() const { return CXXClasses; }
   const RecordMap<ObjCCategoryRecord> &getObjCCategories() const {
     return ObjCCategories;
   }
@@ -845,8 +1065,10 @@ class APISet {
   llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
   RecordMap<GlobalFunctionRecord> GlobalFunctions;
   RecordMap<GlobalVariableRecord> GlobalVariables;
+  RecordMap<StaticFieldRecord> StaticFields;
   RecordMap<EnumRecord> Enums;
   RecordMap<StructRecord> Structs;
+  RecordMap<CXXClassRecord> CXXClasses;
   RecordMap<ObjCCategoryRecord> ObjCCategories;
   RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
   RecordMap<ObjCProtocolRecord> ObjCProtocols;

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 82f0c42ab8aa09..dc8a02e8811aed 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -173,10 +173,27 @@ class DeclarationFragments {
   /// Get the corresponding FragmentKind from string \p S.
   static FragmentKind parseFragmentKindFromString(StringRef S);
 
+  static DeclarationFragments
+  getExceptionSpecificationString(ExceptionSpecificationType ExceptionSpec);
+
+  static DeclarationFragments getStructureTypeFragment(const RecordDecl *Decl);
+
 private:
   std::vector<Fragment> Fragments;
 };
 
+class AccessControl {
+public:
+  AccessControl(std::string Access) : Access(Access) {}
+
+  const std::string &getAccess() const { return Access; }
+
+  bool empty() const { return Access.empty(); }
+
+private:
+  std::string Access;
+};
+
 /// Store function signature information with DeclarationFragments of the
 /// return type and parameters.
 class FunctionSignature {
@@ -219,6 +236,29 @@ class FunctionSignature {
 /// A factory class to build DeclarationFragments for 
diff erent kinds of Decl.
 class DeclarationFragmentsBuilder {
 public:
+  /// Build FunctionSignature for a function-like declaration \c FunctionT like
+  /// FunctionDecl, ObjCMethodDecl, or CXXMethodDecl.
+  ///
+  /// The logic and implementation of building a signature for a FunctionDecl,
+  /// CXXMethodDecl, and ObjCMethodDecl are exactly the same, but they do not
+  /// share a common base. This template helps reuse the code.
+  template <typename FunctionT>
+  static FunctionSignature getFunctionSignature(const FunctionT *Function);
+
+  static AccessControl getAccessControl(const Decl *Decl) {
+    switch (Decl->getAccess()) {
+    case AS_public:
+      return AccessControl("public");
+    case AS_private:
+      return AccessControl("private");
+    case AS_protected:
+      return AccessControl("protected");
+    case AS_none:
+      return AccessControl("none");
+    }
+    llvm_unreachable("Unhandled access control");
+  }
+
   /// Build DeclarationFragments for a variable declaration VarDecl.
   static DeclarationFragments getFragmentsForVar(const VarDecl *);
 
@@ -239,6 +279,19 @@ class DeclarationFragmentsBuilder {
   /// Build DeclarationFragments for a struct record declaration RecordDecl.
   static DeclarationFragments getFragmentsForStruct(const RecordDecl *);
 
+  static DeclarationFragments getFragmentsForCXXClass(const CXXRecordDecl *);
+
+  static DeclarationFragments
+  getFragmentsForSpecialCXXMethod(const CXXMethodDecl *);
+
+  static DeclarationFragments getFragmentsForCXXMethod(const CXXMethodDecl *);
+
+  static DeclarationFragments
+  getFragmentsForConversionFunction(const CXXConversionDecl *);
+
+  static DeclarationFragments
+  getFragmentsForOverloadedOperator(const CXXMethodDecl *);
+
   /// Build DeclarationFragments for an Objective-C category declaration
   /// ObjCCategoryDecl.
   static DeclarationFragments
@@ -283,15 +336,6 @@ class DeclarationFragmentsBuilder {
   /// Build a sub-heading for macro \p Name.
   static DeclarationFragments getSubHeadingForMacro(StringRef Name);
 
-  /// Build FunctionSignature for a function-like declaration \c FunctionT like
-  /// FunctionDecl or ObjCMethodDecl.
-  ///
-  /// The logic and implementation of building a signature for a FunctionDecl
-  /// and an ObjCMethodDecl are exactly the same, but they do not share a common
-  /// base. This template helps reuse the code.
-  template <typename FunctionT>
-  static FunctionSignature getFunctionSignature(const FunctionT *);
-
 private:
   DeclarationFragmentsBuilder() = delete;
 
@@ -316,6 +360,24 @@ class DeclarationFragmentsBuilder {
   static DeclarationFragments getFragmentsForParam(const ParmVarDecl *);
 };
 
+template <typename FunctionT>
+FunctionSignature
+DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
+  FunctionSignature Signature;
+
+  DeclarationFragments ReturnType, After;
+  ReturnType
+      .append(getFragmentsForType(Function->getReturnType(),
+                                  Function->getASTContext(), After))
+      .append(std::move(After));
+  Signature.setReturnType(ReturnType);
+
+  for (const auto *Param : Function->parameters())
+    Signature.addParameter(Param->getName(), getFragmentsForParam(Param));
+
+  return Signature;
+}
+
 } // namespace extractapi
 } // namespace clang
 

diff  --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index f0882afb5a61bf..2eed2d119e6ae1 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -14,6 +14,10 @@
 #ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
 #define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
 
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
 #include "llvm/ADT/FunctionExtras.h"
 
 #include "clang/AST/ASTContext.h"
@@ -44,8 +48,14 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
 
   bool VisitEnumDecl(const EnumDecl *Decl);
 
+  bool WalkUpFromRecordDecl(const RecordDecl *Decl);
+
+  bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl);
+
   bool VisitRecordDecl(const RecordDecl *Decl);
 
+  bool VisitCXXRecordDecl(const CXXRecordDecl *Decl);
+
   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl);
 
   bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl);
@@ -69,6 +79,20 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
   void recordStructFields(StructRecord *StructRecord,
                           const RecordDecl::field_range Fields);
 
+  /// Collect API information for the class fields and associate with the parent
+  /// struct
+  void recordCXXFields(CXXClassRecord *CXXClassRecord,
+                       const RecordDecl::field_range Fields);
+
+  void recordCXXMethods(CXXClassRecord *CXXClassRecord,
+                        const CXXRecordDecl::method_range Methods);
+
+  void recordConversionMethod(CXXClassRecord *CXXClassRecord,
+                              const CXXMethodDecl *SpecialCXXMethod);
+
+  void recordSpecialCXXMethod(CXXClassRecord *CXXClassRecord,
+                              const CXXMethodDecl *SpecialCXXMethod);
+
   /// Collect API information for the Objective-C methods and associate with the
   /// parent container.
   void recordObjCMethods(ObjCContainerRecord *Container,
@@ -131,8 +155,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
   if (isa<ParmVarDecl>(Decl))
     return true;
 
-  // Skip non-global variables in records (struct/union/class).
-  if (Decl->getDeclContext()->isRecord())
+  // Skip non-global variables in records (struct/union/class) but not static
+  // members.
+  if (Decl->getDeclContext()->isRecord() && !Decl->isStaticDataMember())
     return true;
 
   // Skip local variables inside function or method.
@@ -165,9 +190,19 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
   DeclarationFragments SubHeading =
       DeclarationFragmentsBuilder::getSubHeading(Decl);
 
-  // Add the global variable record to the API set.
-  API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
-                   Declaration, SubHeading, isInSystemHeader(Decl));
+  if (Decl->isStaticDataMember()) {
+    SymbolReference Context;
+    auto Record = dyn_cast<RecordDecl>(Decl->getDeclContext());
+    Context.Name = Record->getName();
+    Context.USR = API.recordUSR(Record);
+    auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
+    API.addStaticField(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+                       Declaration, SubHeading, Context, Access,
+                       isInSystemHeader(Decl));
+  } else
+    // Add the global variable record to the API set.
+    API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+                     Declaration, SubHeading, isInSystemHeader(Decl));
   return true;
 }
 
@@ -280,15 +315,23 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
 }
 
 template <typename Derived>
-bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
-  // Skip C++ structs/classes/unions
-  // TODO: support C++ records
-  if (isa<CXXRecordDecl>(Decl))
-    return true;
+bool ExtractAPIVisitorBase<Derived>::WalkUpFromRecordDecl(
+    const RecordDecl *Decl) {
+  getDerivedExtractAPIVisitor().VisitRecordDecl(Decl);
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::WalkUpFromCXXRecordDecl(
+    const CXXRecordDecl *Decl) {
+  getDerivedExtractAPIVisitor().VisitCXXRecordDecl(Decl);
+  return true;
+}
 
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
   if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
     return true;
-
   // Collect symbol information.
   StringRef Name = Decl->getName();
   if (Name.empty())
@@ -322,6 +365,57 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
   return true;
 }
 
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
+    const CXXRecordDecl *Decl) {
+  if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+    return true;
+
+  StringRef Name = Decl->getName();
+  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());
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForCXXClass(Decl);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+  APIRecord::RecordKind Kind;
+  if (Decl->isUnion())
+    Kind = APIRecord::RecordKind::RK_Union;
+  else if (Decl->isStruct())
+    Kind = APIRecord::RecordKind::RK_Struct;
+  else
+    Kind = APIRecord::RecordKind::RK_CXXClass;
+
+  CXXClassRecord *CXXClassRecord =
+      API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+                      Declaration, SubHeading, Kind, isInSystemHeader(Decl));
+
+  // FIXME: store AccessSpecifier given by inheritance
+  for (const auto BaseSpecifier : Decl->bases()) {
+    // skip classes not inherited as public
+    if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public)
+      continue;
+    SymbolReference BaseClass;
+    CXXRecordDecl *BaseClassDecl =
+        BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl();
+    BaseClass.Name = BaseClassDecl->getName();
+    BaseClass.USR = API.recordUSR(BaseClassDecl);
+    CXXClassRecord->Bases.emplace_back(BaseClass);
+  }
+
+  getDerivedExtractAPIVisitor().recordCXXFields(CXXClassRecord, Decl->fields());
+  getDerivedExtractAPIVisitor().recordCXXMethods(CXXClassRecord,
+                                                 Decl->methods());
+  return true;
+}
+
 template <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
     const ObjCInterfaceDecl *Decl) {
@@ -558,6 +652,154 @@ void ExtractAPIVisitorBase<Derived>::recordStructFields(
   }
 }
 
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordCXXFields(
+    CXXClassRecord *CXXClassRecord, const RecordDecl::field_range Fields) {
+  for (const auto *Field : Fields) {
+    // Collect symbol information.
+    StringRef Name = Field->getName();
+    StringRef USR = API.recordUSR(Field);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Field->getLocation());
+    Context.getSourceManager().getPresumedLoc(Field->getLocation());
+    DocComment Comment;
+    if (auto *RawComment =
+            getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Field))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the struct field.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForField(Field);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Field);
+    AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Field);
+
+    API.addCXXField(CXXClassRecord, Name, USR, Loc, AvailabilitySet(Field),
+                    Comment, Declaration, SubHeading, Access,
+                    isInSystemHeader(Field));
+  }
+}
+
+/// Collect API information for constructors and destructors and associate with
+/// the parent class.
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordSpecialCXXMethod(
+    CXXClassRecord *CXXClassRecord, const CXXMethodDecl *CXXSpecialMethod) {
+  StringRef Name;
+  bool isConstructor = false;
+  if (isa<CXXConstructorDecl>(CXXSpecialMethod)) {
+    isConstructor = true;
+    Name = CXXClassRecord->Name;
+  } else if (isa<CXXDestructorDecl>(CXXSpecialMethod)) {
+    Name = API.copyString(CXXSpecialMethod->getNameAsString());
+  }
+
+  StringRef USR = API.recordUSR(CXXSpecialMethod);
+  PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(
+      CXXSpecialMethod->getLocation());
+  DocComment Comment;
+  if (auto *RawComment = getDerivedExtractAPIVisitor().fetchRawCommentForDecl(
+          CXXSpecialMethod))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments, sub-heading, and signature for the method.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
+          CXXSpecialMethod);
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(CXXSpecialMethod);
+  FunctionSignature Signature =
+      DeclarationFragmentsBuilder::getFunctionSignature(CXXSpecialMethod);
+  AccessControl Access =
+      DeclarationFragmentsBuilder::getAccessControl(CXXSpecialMethod);
+
+  API.addCXXSpecialMethod(CXXClassRecord, Name, USR, Loc,
+                          AvailabilitySet(CXXSpecialMethod), Comment,
+                          Declaration, SubHeading, Signature, isConstructor,
+                          Access, isInSystemHeader(CXXSpecialMethod));
+}
+
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordConversionMethod(
+    CXXClassRecord *CXXClassRecord, const CXXMethodDecl *SpecialCXXMethod) {
+  StringRef Name = API.copyString(SpecialCXXMethod->getNameAsString());
+  StringRef USR = API.recordUSR(SpecialCXXMethod);
+  PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(
+      SpecialCXXMethod->getLocation());
+  DocComment Comment;
+  if (auto *RawComment = getDerivedExtractAPIVisitor().fetchRawCommentForDecl(
+          SpecialCXXMethod))
+    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                            Context.getDiagnostics());
+
+  // Build declaration fragments, sub-heading, and signature for the method.
+  DeclarationFragments Declaration =
+      DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
+          cast<CXXConversionDecl>(SpecialCXXMethod));
+  DeclarationFragments SubHeading =
+      DeclarationFragmentsBuilder::getSubHeading(SpecialCXXMethod);
+  FunctionSignature Signature =
+      DeclarationFragmentsBuilder::getFunctionSignature(SpecialCXXMethod);
+  AccessControl Access =
+      DeclarationFragmentsBuilder::getAccessControl(SpecialCXXMethod);
+
+  API.addCXXMethod(CXXClassRecord, Name, USR, Loc,
+                   AvailabilitySet(SpecialCXXMethod), Comment, Declaration,
+                   SubHeading, Signature, SpecialCXXMethod->isStatic(), Access,
+                   isInSystemHeader(SpecialCXXMethod));
+}
+
+template <typename Derived>
+void ExtractAPIVisitorBase<Derived>::recordCXXMethods(
+    CXXClassRecord *CXXClassRecord, const CXXRecordDecl::method_range Methods) {
+  for (const auto *Method : Methods) {
+    if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) {
+      recordSpecialCXXMethod(CXXClassRecord, Method);
+      continue;
+    }
+
+    if (isa<CXXConversionDecl>(Method)) {
+      recordConversionMethod(CXXClassRecord, Method);
+      continue;
+    }
+
+    StringRef Name;
+    DeclarationFragments Declaration;
+    if (Method->isOverloadedOperator()) {
+      Name = API.copyString(Method->getNameAsString());
+      Declaration =
+          DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
+              Method);
+    } else {
+      Name = API.copyString(Method->getNameAsString());
+      Declaration =
+          DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Method);
+    }
+    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());
+
+    // Build declaration fragments, sub-heading, and signature for the method.
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Method);
+    FunctionSignature Signature =
+        DeclarationFragmentsBuilder::getFunctionSignature(Method);
+    AccessControl Access =
+        DeclarationFragmentsBuilder::getAccessControl(Method);
+
+    API.addCXXMethod(CXXClassRecord, Name, USR, Loc, AvailabilitySet(Method),
+                     Comment, Declaration, SubHeading, Signature,
+                     Method->isStatic(), Access, isInSystemHeader(Method));
+  }
+}
+
 /// Collect API information for the Objective-C methods and associate with the
 /// parent container.
 template <typename Derived>

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
index 006e92be29555c..afb84c20ddefea 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -29,6 +29,10 @@ template <typename Derived> class APISetVisitor {
 
     getDerived()->traverseEnumRecords();
 
+    getDerived()->traverseStaticFieldRecords();
+
+    getDerived()->traverseCXXClassRecords();
+
     getDerived()->traverseStructRecords();
 
     getDerived()->traverseObjCInterfaces();
@@ -60,6 +64,16 @@ template <typename Derived> class APISetVisitor {
       getDerived()->visitStructRecord(*Struct.second);
   }
 
+  void traverseStaticFieldRecords() {
+    for (const auto &StaticField : API.getStaticFields())
+      getDerived()->visitStaticFieldRecord(*StaticField.second);
+  }
+
+  void traverseCXXClassRecords() {
+    for (const auto &Class : API.getCXXClasses())
+      getDerived()->visitCXXClassRecord(*Class.second);
+  }
+
   void traverseObjCInterfaces() {
     for (const auto &Interface : API.getObjCInterfaces())
       getDerived()->visitObjCContainerRecord(*Interface.second);
@@ -92,6 +106,10 @@ template <typename Derived> class APISetVisitor {
   /// Visit a struct record.
   void visitStructRecord(const StructRecord &Record){};
 
+  void visitStaticFieldRecord(const StaticFieldRecord &Record){};
+
+  void visitCXXClassRecord(const CXXClassRecord &Record){};
+
   /// Visit an Objective-C container record.
   void visitObjCContainerRecord(const ObjCContainerRecord &Record){};
 

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index e77903f8ba08ff..d975e7a61345da 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -160,6 +160,10 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
   /// Visit a struct record.
   void visitStructRecord(const StructRecord &Record);
 
+  void visitStaticFieldRecord(const StaticFieldRecord &Record);
+
+  void visitCXXClassRecord(const CXXClassRecord &Record);
+
   /// Visit an Objective-C container record.
   void visitObjCContainerRecord(const ObjCContainerRecord &Record);
 

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 10e79b37de7395..02875325099360 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -120,6 +120,91 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
                            SubHeading, IsFromSystemHeader);
 }
 
+StaticFieldRecord *
+APISet::addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
+                       AvailabilitySet Availabilities, LinkageInfo Linkage,
+                       const DocComment &Comment,
+                       DeclarationFragments Declaration,
+                       DeclarationFragments SubHeading, SymbolReference Context,
+                       AccessControl Access, bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, StaticFields, USR, Name, Loc,
+                           std::move(Availabilities), Linkage, Comment,
+                           Declaration, SubHeading, Context, Access,
+                           IsFromSystemHeader);
+}
+
+CXXFieldRecord *
+APISet::addCXXField(CXXClassRecord *CXXClass, StringRef Name, StringRef USR,
+                    PresumedLoc Loc, AvailabilitySet Availabilities,
+                    const DocComment &Comment, DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading, AccessControl Access,
+                    bool IsFromSystemHeader) {
+  auto Record = std::make_unique<CXXFieldRecord>(
+      USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+      SubHeading, Access, IsFromSystemHeader);
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      CXXClass->USR, CXXClass->Name, CXXClass->getKind(), CXXClass);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return CXXClass->Fields.emplace_back(std::move(Record)).get();
+}
+
+CXXClassRecord *
+APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
+                    AvailabilitySet Availabilities, const DocComment &Comment,
+                    DeclarationFragments Declaration,
+                    DeclarationFragments SubHeading, APIRecord::RecordKind Kind,
+                    bool IsFromSystemHeader) {
+  return addTopLevelRecord(USRBasedLookupTable, CXXClasses, USR, Name, Loc,
+                           std::move(Availabilities), Comment, Declaration,
+                           SubHeading, Kind, IsFromSystemHeader);
+}
+
+CXXMethodRecord *APISet::addCXXMethod(
+    CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
+    PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, bool IsStatic, AccessControl Access,
+    bool IsFromSystemHeader) {
+  std::unique_ptr<CXXMethodRecord> Record;
+  if (IsStatic)
+    Record = std::make_unique<CXXStaticMethodRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Signature, Access, IsFromSystemHeader);
+  else
+    Record = std::make_unique<CXXInstanceMethodRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Signature, Access, IsFromSystemHeader);
+
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(),
+      CXXClassRecord);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return CXXClassRecord->Methods.emplace_back(std::move(Record)).get();
+}
+
+CXXMethodRecord *APISet::addCXXSpecialMethod(
+    CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
+    PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    FunctionSignature Signature, bool IsConstructor, AccessControl Access,
+    bool IsFromSystemHeader) {
+  std::unique_ptr<CXXMethodRecord> Record;
+  if (IsConstructor)
+    Record = std::make_unique<CXXConstructorRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Signature, Access, IsFromSystemHeader);
+  else
+    Record = std::make_unique<CXXDestructorRecord>(
+        USR, Name, Loc, std::move(Availability), Comment, Declaration,
+        SubHeading, Signature, Access, IsFromSystemHeader);
+
+  Record->ParentInformation = APIRecord::HierarchyInformation(
+      CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(),
+      CXXClassRecord);
+  USRBasedLookupTable.insert({USR, Record.get()});
+  return CXXClassRecord->Methods.emplace_back(std::move(Record)).get();
+}
+
 ObjCCategoryRecord *APISet::addObjCCategory(
     StringRef Name, StringRef USR, PresumedLoc Loc,
     AvailabilitySet Availabilities, const DocComment &Comment,
@@ -282,6 +367,7 @@ APIRecord::~APIRecord() {}
 ObjCContainerRecord::~ObjCContainerRecord() {}
 ObjCMethodRecord::~ObjCMethodRecord() {}
 ObjCPropertyRecord::~ObjCPropertyRecord() {}
+CXXMethodRecord::~CXXMethodRecord() {}
 
 void GlobalFunctionRecord::anchor() {}
 void GlobalVariableRecord::anchor() {}
@@ -289,6 +375,12 @@ void EnumConstantRecord::anchor() {}
 void EnumRecord::anchor() {}
 void StructFieldRecord::anchor() {}
 void StructRecord::anchor() {}
+void CXXFieldRecord::anchor() {}
+void CXXClassRecord::anchor() {}
+void CXXConstructorRecord::anchor() {}
+void CXXDestructorRecord::anchor() {}
+void CXXInstanceMethodRecord::anchor() {}
+void CXXStaticMethodRecord::anchor() {}
 void ObjCInstancePropertyRecord::anchor() {}
 void ObjCClassPropertyRecord::anchor() {}
 void ObjCInstanceVariableRecord::anchor() {}

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 20335768ac30b0..ea72835c995313 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -12,8 +12,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/OperatorKinds.h"
 #include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
 #include "clang/Index/USRGeneration.h"
+#include "clang/Parse/Parser.h"
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace clang::extractapi;
@@ -84,6 +88,59 @@ DeclarationFragments::parseFragmentKindFromString(StringRef S) {
       .Default(DeclarationFragments::FragmentKind::None);
 }
 
+DeclarationFragments DeclarationFragments::getExceptionSpecificationString(
+    ExceptionSpecificationType ExceptionSpec) {
+  DeclarationFragments Fragments;
+  switch (ExceptionSpec) {
+  case ExceptionSpecificationType::EST_None:
+    return Fragments;
+  case ExceptionSpecificationType::EST_DynamicNone:
+    return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
+        .append("throw", DeclarationFragments::FragmentKind::Keyword)
+        .append("(", DeclarationFragments::FragmentKind::Text)
+        .append(")", DeclarationFragments::FragmentKind::Text);
+  case ExceptionSpecificationType::EST_Dynamic:
+    // FIXME: throw(int), get types of inner expression
+    return Fragments;
+  case ExceptionSpecificationType::EST_BasicNoexcept:
+
+    return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
+        .append("noexcept", DeclarationFragments::FragmentKind::Keyword);
+  case ExceptionSpecificationType::EST_DependentNoexcept:
+    // FIXME: throw(conditional-expression), get expression
+    break;
+  case ExceptionSpecificationType::EST_NoexceptFalse:
+    return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
+        .append("noexcept", DeclarationFragments::FragmentKind::Keyword)
+        .append("(", DeclarationFragments::FragmentKind::Text)
+        .append("false", DeclarationFragments::FragmentKind::Keyword)
+        .append(")", DeclarationFragments::FragmentKind::Text);
+  case ExceptionSpecificationType::EST_NoexceptTrue:
+    return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
+        .append("noexcept", DeclarationFragments::FragmentKind::Keyword)
+        .append("(", DeclarationFragments::FragmentKind::Text)
+        .append("true", DeclarationFragments::FragmentKind::Keyword)
+        .append(")", DeclarationFragments::FragmentKind::Text);
+  default:
+    return Fragments;
+  }
+
+  llvm_unreachable("Unhandled exception specification");
+}
+
+DeclarationFragments
+DeclarationFragments::getStructureTypeFragment(const RecordDecl *Record) {
+  DeclarationFragments Fragments;
+  if (Record->isStruct())
+    Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
+  else if (Record->isUnion())
+    Fragments.append("union", DeclarationFragments::FragmentKind::Keyword);
+  else
+    Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);
+
+  return Fragments;
+}
+
 // NNS stores C++ nested name specifiers, which are prefixes to qualified names.
 // Build declaration fragments for NNS recursively so that we have the USR for
 // every part in a qualified name, and also leaves the actual underlying type
@@ -369,6 +426,9 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
 DeclarationFragments
 DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
   DeclarationFragments Fragments;
+  if (Var->isConstexpr())
+    Fragments.append("constexpr ", DeclarationFragments::FragmentKind::Keyword);
+
   StorageClass SC = Var->getStorageClass();
   if (SC != SC_None)
     Fragments
@@ -438,7 +498,10 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
   case SC_Register:
     llvm_unreachable("invalid for functions");
   }
-  // FIXME: Handle C++ function specifiers: constexpr, consteval, explicit, etc.
+  if (Func->isConsteval()) // if consteval, it is also constexpr
+    Fragments.append("consteval ", DeclarationFragments::FragmentKind::Keyword);
+  else if (Func->isConstexpr())
+    Fragments.append("constexpr ", DeclarationFragments::FragmentKind::Keyword);
 
   // FIXME: Is `after` actually needed here?
   DeclarationFragments After;
@@ -457,6 +520,9 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
   }
   Fragments.append(")", DeclarationFragments::FragmentKind::Text);
 
+  Fragments.append(DeclarationFragments::getExceptionSpecificationString(
+      Func->getExceptionSpecType()));
+
   // FIXME: Handle exception specifiers: throw, noexcept
   return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
@@ -493,7 +559,13 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
 DeclarationFragments
 DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
   DeclarationFragments After;
-  return getFragmentsForType(Field->getType(), Field->getASTContext(), After)
+  DeclarationFragments Fragments;
+  if (Field->isMutable())
+    Fragments.append("mutable", DeclarationFragments::FragmentKind::Keyword)
+        .appendSpace();
+  return Fragments
+      .append(
+          getFragmentsForType(Field->getType(), Field->getASTContext(), After))
       .appendSpace()
       .append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
       .append(std::move(After))
@@ -515,6 +587,156 @@ DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
   return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
+    const CXXRecordDecl *Record) {
+  if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
+    return getFragmentsForTypedef(TypedefNameDecl);
+
+  DeclarationFragments Fragments;
+  Fragments.append(DeclarationFragments::getStructureTypeFragment(Record));
+
+  if (!Record->getName().empty())
+    Fragments.appendSpace().append(
+        Record->getName(), DeclarationFragments::FragmentKind::Identifier);
+
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
+    const CXXMethodDecl *Method) {
+  DeclarationFragments Fragments;
+  StringRef Name;
+  if (isa<CXXConstructorDecl>(Method)) {
+    auto *Constructor = dyn_cast<CXXConstructorDecl>(Method);
+    Name = cast<CXXRecordDecl>(Constructor->getDeclContext())->getName();
+    if (Constructor->isExplicit())
+      Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
+          .appendSpace();
+  } else if (isa<CXXDestructorDecl>(Method))
+    Name = StringRef(Method->getNameAsString());
+
+  DeclarationFragments After;
+  Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier)
+      .append(std::move(After));
+  Fragments.append("(", DeclarationFragments::FragmentKind::Text);
+  for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
+    if (i)
+      Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+    Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
+  }
+  Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+
+  Fragments.append(DeclarationFragments::getExceptionSpecificationString(
+      Method->getExceptionSpecType()));
+
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
+    const CXXMethodDecl *Method) {
+  DeclarationFragments Fragments;
+  StringRef Name;
+  Name = Method->getName();
+  if (Method->isStatic())
+    Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
+        .appendSpace();
+  if (Method->isConstexpr())
+    Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
+        .appendSpace();
+  if (Method->isVolatile())
+    Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword)
+        .appendSpace();
+
+  // Build return type
+  DeclarationFragments After;
+  Fragments
+      .append(getFragmentsForType(Method->getReturnType(),
+                                  Method->getASTContext(), After))
+      .appendSpace()
+      .append(Name, DeclarationFragments::FragmentKind::Identifier)
+      .append(std::move(After));
+  Fragments.append("(", DeclarationFragments::FragmentKind::Text);
+  for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
+    if (i)
+      Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+    Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
+  }
+  Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+
+  if (Method->isConst())
+    Fragments.appendSpace().append("const",
+                                   DeclarationFragments::FragmentKind::Keyword);
+
+  Fragments.append(DeclarationFragments::getExceptionSpecificationString(
+      Method->getExceptionSpecType()));
+
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
+    const CXXConversionDecl *ConversionFunction) {
+  DeclarationFragments Fragments;
+
+  if (ConversionFunction->isExplicit())
+    Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
+        .appendSpace();
+
+  Fragments.append("operator", DeclarationFragments::FragmentKind::Keyword)
+      .appendSpace();
+
+  Fragments
+      .append(ConversionFunction->getConversionType().getAsString(),
+              DeclarationFragments::FragmentKind::TypeIdentifier)
+      .append("(", DeclarationFragments::FragmentKind::Text);
+  for (unsigned i = 0, end = ConversionFunction->getNumParams(); i != end;
+       ++i) {
+    if (i)
+      Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+    Fragments.append(getFragmentsForParam(ConversionFunction->getParamDecl(i)));
+  }
+  Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+
+  if (ConversionFunction->isConst())
+    Fragments.appendSpace().append("const",
+                                   DeclarationFragments::FragmentKind::Keyword);
+
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
+    const CXXMethodDecl *Method) {
+  DeclarationFragments Fragments;
+
+  // Build return type
+  DeclarationFragments After;
+  Fragments
+      .append(getFragmentsForType(Method->getReturnType(),
+                                  Method->getASTContext(), After))
+      .appendSpace()
+      .append(Method->getNameAsString(),
+              DeclarationFragments::FragmentKind::Identifier)
+      .append(std::move(After));
+  Fragments.append("(", DeclarationFragments::FragmentKind::Text);
+  for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
+    if (i)
+      Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+    Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
+  }
+  Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+
+  if (Method->isConst())
+    Fragments.appendSpace().append("const",
+                                   DeclarationFragments::FragmentKind::Keyword);
+
+  Fragments.append(DeclarationFragments::getExceptionSpecificationString(
+      Method->getExceptionSpecType()));
+
+  return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
+}
+
 DeclarationFragments
 DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
                                                   const MacroDirective *MD) {
@@ -765,24 +987,6 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
   return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
 }
 
-template <typename FunctionT>
-FunctionSignature
-DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
-  FunctionSignature Signature;
-
-  DeclarationFragments ReturnType, After;
-  ReturnType
-      .append(getFragmentsForType(Function->getReturnType(),
-                                  Function->getASTContext(), After))
-      .append(std::move(After));
-  Signature.setReturnType(ReturnType);
-
-  for (const auto *Param : Function->parameters())
-    Signature.addParameter(Param->getName(), getFragmentsForParam(Param));
-
-  return Signature;
-}
-
 // Instantiate template for FunctionDecl.
 template FunctionSignature
 DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *);
@@ -795,7 +999,18 @@ DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *);
 DeclarationFragments
 DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
   DeclarationFragments Fragments;
-  if (!Decl->getName().empty())
+  if (isa<CXXConstructorDecl>(Decl) || isa<CXXDestructorDecl>(Decl))
+    Fragments.append(cast<CXXRecordDecl>(Decl->getDeclContext())->getName(),
+                     DeclarationFragments::FragmentKind::Identifier);
+  else if (isa<CXXConversionDecl>(Decl)) {
+    Fragments.append(
+        cast<CXXConversionDecl>(Decl)->getConversionType().getAsString(),
+        DeclarationFragments::FragmentKind::Identifier);
+  } else if (isa<CXXMethodDecl>(Decl) &&
+             cast<CXXMethodDecl>(Decl)->isOverloadedOperator()) {
+    Fragments.append(Decl->getNameAsString(),
+                     DeclarationFragments::FragmentKind::Identifier);
+  } else if (!Decl->getName().empty())
     Fragments.append(Decl->getName(),
                      DeclarationFragments::FragmentKind::Identifier);
   return Fragments;

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 534e9288cc713b..c9fbf0d87ac218 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -38,6 +38,14 @@ void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) {
     Paren[Key] = std::move(*Obj);
 }
 
+/// Helper function to inject a StringRef \p String into an object \p Paren at
+/// position \p Key
+void serializeString(Object &Paren, StringRef Key,
+                     std::optional<std::string> String) {
+  if (String)
+    Paren[Key] = std::move(*String);
+}
+
 /// Helper function to inject a JSON array \p Array into object \p Paren at
 /// position \p Key.
 void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
@@ -189,9 +197,10 @@ StringRef getLanguageName(Language Lang) {
     return "c";
   case Language::ObjC:
     return "objective-c";
+  case Language::CXX:
+    return "c++";
 
   // Unsupported language currently
-  case Language::CXX:
   case Language::ObjCXX:
   case Language::OpenCL:
   case Language::OpenCLCXX:
@@ -366,6 +375,38 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
     Kind["identifier"] = AddLangPrefix("struct");
     Kind["displayName"] = "Structure";
     break;
+  case APIRecord::RK_CXXField:
+    Kind["identifier"] = AddLangPrefix("property");
+    Kind["displayName"] = "Instance Property";
+    break;
+  case APIRecord::RK_Union:
+    Kind["identifier"] = AddLangPrefix("union");
+    Kind["displayName"] = "Union";
+    break;
+  case APIRecord::RK_StaticField:
+    Kind["identifier"] = AddLangPrefix("type.property");
+    Kind["displayName"] = "Type Property";
+    break;
+  case APIRecord::RK_CXXClass:
+    Kind["identifier"] = AddLangPrefix("class");
+    Kind["displayName"] = "Class";
+    break;
+  case APIRecord::RK_CXXStaticMethod:
+    Kind["identifier"] = AddLangPrefix("type.method");
+    Kind["displayName"] = "Static Method";
+    break;
+  case APIRecord::RK_CXXInstanceMethod:
+    Kind["identifier"] = AddLangPrefix("method");
+    Kind["displayName"] = "Instance Method";
+    break;
+  case APIRecord::RK_CXXConstructorMethod:
+    Kind["identifier"] = AddLangPrefix("method");
+    Kind["displayName"] = "Constructor";
+    break;
+  case APIRecord::RK_CXXDestructorMethod:
+    Kind["identifier"] = AddLangPrefix("method");
+    Kind["displayName"] = "Destructor";
+    break;
   case APIRecord::RK_ObjCIvar:
     Kind["identifier"] = AddLangPrefix("ivar");
     Kind["displayName"] = "Instance Variable";
@@ -470,6 +511,31 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
                       Record, has_function_signature<RecordTy>()));
 }
 
+template <typename RecordTy>
+std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
+                                                    std::true_type) {
+  const auto &AccessControl = Record.Access;
+  std::string Access;
+  if (AccessControl.empty())
+    return std::nullopt;
+  Access = AccessControl.getAccess();
+  return Access;
+}
+
+template <typename RecordTy>
+std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
+                                                    std::false_type) {
+  return std::nullopt;
+}
+
+template <typename RecordTy>
+void serializeAccessMixin(Object &Paren, const RecordTy &Record) {
+  auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>());
+  if (!accessLevel.has_value())
+    accessLevel = "public";
+  serializeString(Paren, "accessLevel", accessLevel);
+}
+
 struct PathComponent {
   StringRef USR;
   StringRef Name;
@@ -543,7 +609,6 @@ Array generateParentContexts(const RecordTy &Record, const APISet &API,
 
   return ParentContexts;
 }
-
 } // namespace
 
 /// Defines the format version emitted by SymbolGraphSerializer.
@@ -602,9 +667,6 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
   serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
   serializeArray(Obj, "declarationFragments",
                  serializeDeclarationFragments(Record.Declaration));
-  // TODO: Once we keep track of symbol access information serialize it
-  // correctly here.
-  Obj["accessLevel"] = "public";
   SmallVector<StringRef, 4> PathComponentsNames;
   // If this returns true it indicates that we couldn't find a symbol in the
   // hierarchy.
@@ -617,6 +679,7 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
   serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
 
   serializeFunctionSignatureMixin(Obj, Record);
+  serializeAccessMixin(Obj, Record);
 
   return Obj;
 }
@@ -698,6 +761,28 @@ void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) {
   serializeMembers(Record, Record.Fields);
 }
 
+void SymbolGraphSerializer::visitStaticFieldRecord(
+    const StaticFieldRecord &Record) {
+  auto StaticField = serializeAPIRecord(Record);
+  if (!StaticField)
+    return;
+  Symbols.emplace_back(std::move(*StaticField));
+  serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context);
+}
+
+void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
+  auto Class = serializeAPIRecord(Record);
+  if (!Class)
+    return;
+
+  Symbols.emplace_back(std::move(*Class));
+  serializeMembers(Record, Record.Fields);
+  serializeMembers(Record, Record.Methods);
+
+  for (const auto Base : Record.Bases)
+    serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
+}
+
 void SymbolGraphSerializer::visitObjCContainerRecord(
     const ObjCContainerRecord &Record) {
   auto ObjCContainer = serializeAPIRecord(Record);
@@ -761,6 +846,12 @@ void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
   case APIRecord::RK_Struct:
     visitStructRecord(*cast<StructRecord>(Record));
     break;
+  case APIRecord::RK_StaticField:
+    visitStaticFieldRecord(*cast<StaticFieldRecord>(Record));
+    break;
+  case APIRecord::RK_CXXClass:
+    visitCXXClassRecord(*cast<CXXClassRecord>(Record));
+    break;
   case APIRecord::RK_ObjCInterface:
     visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
     break;

diff  --git a/clang/test/ExtractAPI/class.cpp b/clang/test/ExtractAPI/class.cpp
new file mode 100644
index 00000000000000..17250cc3b401da
--- /dev/null
+++ b/clang/test/ExtractAPI/class.cpp
@@ -0,0 +1,366 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Foo {
+private:
+  int a;
+  mutable int b;
+
+protected:
+  int c;
+
+public:
+  int d;
+};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@FI at a",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@FI at b",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@FI at c",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@FI at d",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "a"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@FI at a"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "c++.property"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "a"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "a"
+          }
+        ],
+        "title": "a"
+      },
+      "pathComponents": [
+        "Foo",
+        "a"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "mutable"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "b"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@FI at b"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "c++.property"
+      },
+      "location": {
+        "position": {
+          "character": 15,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "b"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "b"
+          }
+        ],
+        "title": "b"
+      },
+      "pathComponents": [
+        "Foo",
+        "b"
+      ]
+    },
+    {
+      "accessLevel": "protected",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "c"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@FI at c"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "c++.property"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "c"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "c"
+          }
+        ],
+        "title": "c"
+      },
+      "pathComponents": [
+        "Foo",
+        "c"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "d"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@FI at d"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "c++.property"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 10
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "d"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "d"
+          }
+        ],
+        "title": "d"
+      },
+      "pathComponents": [
+        "Foo",
+        "d"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/constructor_destructor.cpp b/clang/test/ExtractAPI/constructor_destructor.cpp
new file mode 100644
index 00000000000000..3c8e9821f08080
--- /dev/null
+++ b/clang/test/ExtractAPI/constructor_destructor.cpp
@@ -0,0 +1,227 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Foo {
+  Foo();
+  ~Foo();
+};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at Foo#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F@~Foo#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at Foo#"
+      },
+      "kind": {
+        "displayName": "Constructor",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 3,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo",
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "~Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F@~Foo#"
+      },
+      "kind": {
+        "displayName": "Destructor",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 3,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "~Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "~Foo"
+      },
+      "pathComponents": [
+        "Foo",
+        "~Foo"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/conversions.cpp b/clang/test/ExtractAPI/conversions.cpp
new file mode 100644
index 00000000000000..2da1639f447e97
--- /dev/null
+++ b/clang/test/ExtractAPI/conversions.cpp
@@ -0,0 +1,251 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Foo {
+  operator int();
+  explicit operator double();
+};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at operator int#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at operator double#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "operator"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at operator int#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 3,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "operator int"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "int"
+          }
+        ],
+        "title": "operator int"
+      },
+      "pathComponents": [
+        "Foo",
+        "operator int"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "explicit"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "operator"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "spelling": "double"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:d",
+            "spelling": "double"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at operator double#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 12,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "operator double"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "double"
+          }
+        ],
+        "title": "operator double"
+      },
+      "pathComponents": [
+        "Foo",
+        "operator double"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/function_noexcepts.cpp b/clang/test/ExtractAPI/function_noexcepts.cpp
new file mode 100644
index 00000000000000..7d5cc1741b0560
--- /dev/null
+++ b/clang/test/ExtractAPI/function_noexcepts.cpp
@@ -0,0 +1,293 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+void getFoo() noexcept;
+
+void getBar() noexcept(true);
+
+void getFooBar() noexcept(false);
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getFoo"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "noexcept"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@F at getFoo#"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c++.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getFoo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getFoo"
+          }
+        ],
+        "title": "getFoo"
+      },
+      "pathComponents": [
+        "getFoo"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getBar"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "noexcept"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "true"
+        },
+        {
+          "kind": "text",
+          "spelling": ");"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@F at getBar#"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c++.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getBar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getBar"
+          }
+        ],
+        "title": "getBar"
+      },
+      "pathComponents": [
+        "getBar"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getFooBar"
+        },
+        {
+          "kind": "text",
+          "spelling": "()"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "noexcept"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "false"
+        },
+        {
+          "kind": "text",
+          "spelling": ");"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@F at getFooBar#"
+      },
+      "kind": {
+        "displayName": "Function",
+        "identifier": "c++.func"
+      },
+      "location": {
+        "position": {
+          "character": 6,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getFooBar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getFooBar"
+          }
+        ],
+        "title": "getFooBar"
+      },
+      "pathComponents": [
+        "getFooBar"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/methods.cpp b/clang/test/ExtractAPI/methods.cpp
new file mode 100644
index 00000000000000..0dea2a39a17ee9
--- /dev/null
+++ b/clang/test/ExtractAPI/methods.cpp
@@ -0,0 +1,467 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Foo {
+  int getCount();
+
+  void setLength(int length) noexcept;
+
+public:
+  static double getFoo();
+
+protected:
+  constexpr int getBar() const;
+};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at getCount#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at setLength#I#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at getFoo#S",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at getBar#1",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getCount"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at getCount#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getCount"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getCount"
+          }
+        ],
+        "title": "getCount"
+      },
+      "pathComponents": [
+        "Foo",
+        "getCount"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "setLength"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "length"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "noexcept"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "length"
+              }
+            ],
+            "name": "length"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:v",
+            "spelling": "void"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at setLength#I#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 8,
+          "line": 4
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "setLength"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "setLength"
+          }
+        ],
+        "title": "setLength"
+      },
+      "pathComponents": [
+        "Foo",
+        "setLength"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "static"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:d",
+          "spelling": "double"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getFoo"
+        },
+        {
+          "kind": "text",
+          "spelling": "();"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:d",
+            "spelling": "double"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at getFoo#S"
+      },
+      "kind": {
+        "displayName": "Static Method",
+        "identifier": "c++.type.method"
+      },
+      "location": {
+        "position": {
+          "character": 17,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getFoo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getFoo"
+          }
+        ],
+        "title": "getFoo"
+      },
+      "pathComponents": [
+        "Foo",
+        "getFoo"
+      ]
+    },
+    {
+      "accessLevel": "protected",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "constexpr"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getBar"
+        },
+        {
+          "kind": "text",
+          "spelling": "() "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "const"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "functionSignature": {
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at getBar#1"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 17,
+          "line": 10
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "getBar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "getBar"
+          }
+        ],
+        "title": "getBar"
+      },
+      "pathComponents": [
+        "Foo",
+        "getBar"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/multiple_inheritance.cpp b/clang/test/ExtractAPI/multiple_inheritance.cpp
new file mode 100644
index 00000000000000..b9869cd8b9b752
--- /dev/null
+++ b/clang/test/ExtractAPI/multiple_inheritance.cpp
@@ -0,0 +1,293 @@
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Fizz {};
+
+class Foo : public Fizz {};
+
+class Bar : public Fizz {};
+
+class FooBar : public Foo, public Bar{};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "inheritsFrom",
+      "source": "c:@S at Foo",
+      "target": "c:@S at Fizz",
+      "targetFallback": "Fizz"
+    },
+    {
+      "kind": "inheritsFrom",
+      "source": "c:@S at Bar",
+      "target": "c:@S at Fizz",
+      "targetFallback": "Fizz"
+    },
+    {
+      "kind": "inheritsFrom",
+      "source": "c:@S at FooBar",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    },
+    {
+      "kind": "inheritsFrom",
+      "source": "c:@S at FooBar",
+      "target": "c:@S at Bar",
+      "targetFallback": "Bar"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Fizz"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Fizz"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Fizz"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Fizz"
+          }
+        ],
+        "title": "Fizz"
+      },
+      "pathComponents": [
+        "Fizz"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Bar"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Bar"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 5
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Bar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Bar"
+          }
+        ],
+        "title": "Bar"
+      },
+      "pathComponents": [
+        "Bar"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "FooBar"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at FooBar"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "FooBar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "FooBar"
+          }
+        ],
+        "title": "FooBar"
+      },
+      "pathComponents": [
+        "FooBar"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/operator_overload.cpp b/clang/test/ExtractAPI/operator_overload.cpp
new file mode 100644
index 00000000000000..1547a9c2c68f5d
--- /dev/null
+++ b/clang/test/ExtractAPI/operator_overload.cpp
@@ -0,0 +1,210 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Foo {
+  int operator+(int x);
+};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:@S at Foo@F at operator+#I#",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "private",
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "operator+"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "x"
+        },
+        {
+          "kind": "text",
+          "spelling": ");"
+        }
+      ],
+      "functionSignature": {
+        "parameters": [
+          {
+            "declarationFragments": [
+              {
+                "kind": "typeIdentifier",
+                "preciseIdentifier": "c:I",
+                "spelling": "int"
+              },
+              {
+                "kind": "text",
+                "spelling": " "
+              },
+              {
+                "kind": "internalParam",
+                "spelling": "x"
+              }
+            ],
+            "name": "x"
+          }
+        ],
+        "returns": [
+          {
+            "kind": "typeIdentifier",
+            "preciseIdentifier": "c:I",
+            "spelling": "int"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo@F at operator+#I#"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "c++.method"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 2
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "operator+"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "operator+"
+          }
+        ],
+        "title": "operator+"
+      },
+      "pathComponents": [
+        "Foo",
+        "operator+"
+      ]
+    }
+  ]
+}

diff  --git a/clang/test/ExtractAPI/simple_inheritance.cpp b/clang/test/ExtractAPI/simple_inheritance.cpp
new file mode 100644
index 00000000000000..94465f0333af3a
--- /dev/null
+++ b/clang/test/ExtractAPI/simple_inheritance.cpp
@@ -0,0 +1,162 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
+// RUN: %t/input.h -o %t/output.json -Xclang -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Foo {};
+
+class Bar : public Foo {};
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "inheritsFrom",
+      "source": "c:@S at Bar",
+      "target": "c:@S at Foo",
+      "targetFallback": "Foo"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Foo"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Foo"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 1
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Foo"
+          }
+        ],
+        "title": "Foo"
+      },
+      "pathComponents": [
+        "Foo"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "class"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Bar"
+        },
+        {
+          "kind": "text",
+          "spelling": ";"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c++",
+        "precise": "c:@S at Bar"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "c++.class"
+      },
+      "location": {
+        "position": {
+          "character": 7,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "navigator": [
+          {
+            "kind": "identifier",
+            "spelling": "Bar"
+          }
+        ],
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Bar"
+          }
+        ],
+        "title": "Bar"
+      },
+      "pathComponents": [
+        "Bar"
+      ]
+    }
+  ]
+}


        


More information about the cfe-commits mailing list