zixuw created this revision.
Herald added a reviewer: dang.
Herald added a project: All.
zixuw requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- Add support for Objective-C interface declarations in ExtractAPI.
- Use `clang::Language` for language information correctly in `APISet`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D122446

Files:
  clang/include/clang/ExtractAPI/API.h
  clang/include/clang/ExtractAPI/DeclarationFragments.h
  clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
  clang/lib/ExtractAPI/API.cpp
  clang/lib/ExtractAPI/DeclarationFragments.cpp
  clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
  clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
  clang/test/ExtractAPI/objc_interface.m

Index: clang/test/ExtractAPI/objc_interface.m
===================================================================
--- /dev/null
+++ clang/test/ExtractAPI/objc_interface.m
@@ -0,0 +1,403 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: diff %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+@protocol Protocol
+@end
+
+@interface Super <Protocol>
+@property(readonly, getter=getProperty) unsigned Property;
++ (id)getWithProperty:(unsigned) Property;
+@end
+
+@interface Derived : Super {
+  char Ivar;
+}
+- (char)getIvar;
+@end
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationhips": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Super(cm)getWithProperty:",
+      "target": "c:objc(cs)Super"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Super(py)Property",
+      "target": "c:objc(cs)Super"
+    },
+    {
+      "kind": "conformsTo",
+      "source": "c:objc(cs)Super",
+      "target": "c:objc(pl)Protocol"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Derived@Ivar",
+      "target": "c:objc(cs)Derived"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Derived(im)getIvar",
+      "target": "c:objc(cs)Derived"
+    },
+    {
+      "kind": "inheritsFrom",
+      "source": "c:objc(cs)Derived",
+      "target": "c:objc(cs)Super"
+    }
+  ],
+  "symbols": [
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Super"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "character": 12,
+        "line": 4,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Super"
+          }
+        ],
+        "title": "Super"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "+ ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "id"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getWithProperty"
+        },
+        {
+          "kind": "text",
+          "spelling": ":"
+        },
+        {
+          "kind": "text",
+          "spelling": "("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "internalParam",
+          "spelling": "Property"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super(cm)getWithProperty:"
+      },
+      "kind": {
+        "displayName": "Type Method",
+        "identifier": "objective-c.type.method"
+      },
+      "location": {
+        "character": 1,
+        "line": 6,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "+ "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "getWithProperty:"
+          }
+        ],
+        "title": "getWithProperty:"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "atomic"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "readonly"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "getter"
+        },
+        {
+          "kind": "text",
+          "spelling": "="
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getProperty"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Property"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Super(py)Property"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "character": 50,
+        "line": 5,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Property"
+          }
+        ],
+        "title": "Property"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Derived"
+        },
+        {
+          "kind": "text",
+          "spelling": " : "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:objc(cs)Super",
+          "spelling": "Super"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Derived"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "character": 12,
+        "line": 9,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Derived"
+          }
+        ],
+        "title": "Derived"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:C",
+          "spelling": "char"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Ivar"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Derived@Ivar"
+      },
+      "kind": {
+        "displayName": "Instance Variable",
+        "identifier": "objective-c.ivar"
+      },
+      "location": {
+        "character": 8,
+        "line": 10,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Ivar"
+          }
+        ],
+        "title": "Ivar"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:C",
+          "spelling": "char"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "getIvar"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Derived(im)getIvar"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "character": 1,
+        "line": 12,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "getIvar"
+          }
+        ],
+        "title": "getIvar"
+      }
+    }
+  ]
+}
Index: clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
===================================================================
--- clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -152,10 +152,8 @@
 }
 
 /// Get the short language name string for interface language references.
-StringRef getLanguageName(const LangOptions &LangOpts) {
-  auto LanguageKind =
-      LangStandard::getLangStandardForKind(LangOpts.LangStd).getLanguage();
-  switch (LanguageKind) {
+StringRef getLanguageName(Language Lang) {
+  switch (Lang) {
   case Language::C:
     return "c";
   case Language::ObjC:
@@ -184,11 +182,10 @@
 ///
 /// The identifier property of a symbol contains the USR for precise and unique
 /// references, and the interface language name.
-Object serializeIdentifier(const APIRecord &Record,
-                           const LangOptions &LangOpts) {
+Object serializeIdentifier(const APIRecord &Record, Language Lang) {
   Object Identifier;
   Identifier["precise"] = Record.USR;
-  Identifier["interfaceLanguage"] = getLanguageName(LangOpts);
+  Identifier["interfaceLanguage"] = getLanguageName(Lang);
 
   return Identifier;
 }
@@ -334,10 +331,9 @@
 /// The Symbol Graph symbol kind property contains a shorthand \c identifier
 /// which is prefixed by the source language name, useful for tooling to parse
 /// the kind, and a \c displayName for rendering human-readable names.
-Object serializeSymbolKind(const APIRecord &Record,
-                           const LangOptions &LangOpts) {
-  auto AddLangPrefix = [&LangOpts](StringRef S) -> std::string {
-    return (getLanguageName(LangOpts) + "." + S).str();
+Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+  auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
+    return (getLanguageName(Lang) + "." + S).str();
   };
 
   Object Kind;
@@ -375,6 +371,27 @@
     Kind["identifier"] = AddLangPrefix("struct");
     Kind["displayName"] = "Structure";
     break;
+  case APIRecord::RK_ObjCIvar:
+    Kind["identifier"] = AddLangPrefix("ivar");
+    Kind["displayName"] = "Instance Variable";
+    break;
+  case APIRecord::RK_ObjCMethod:
+    if (dyn_cast<ObjCMethodRecord>(&Record)->IsInstanceMethod) {
+      Kind["identifier"] = AddLangPrefix("method");
+      Kind["displayName"] = "Instance Method";
+    } else {
+      Kind["identifier"] = AddLangPrefix("type.method");
+      Kind["displayName"] = "Type Method";
+    }
+    break;
+  case APIRecord::RK_ObjCProperty:
+    Kind["identifier"] = AddLangPrefix("property");
+    Kind["displayName"] = "Instance Property";
+    break;
+  case APIRecord::RK_ObjCInterface:
+    Kind["identifier"] = AddLangPrefix("class");
+    Kind["displayName"] = "Class";
+    break;
   }
 
   return Kind;
@@ -419,8 +436,8 @@
 
   Object Obj;
   serializeObject(Obj, "identifier",
-                  serializeIdentifier(Record, API.getLangOpts()));
-  serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLangOpts()));
+                  serializeIdentifier(Record, API.getLanguage()));
+  serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
   serializeObject(Obj, "names", serializeNames(Record));
   serializeObject(
       Obj, "location",
@@ -438,13 +455,17 @@
   switch (Kind) {
   case RelationshipKind::MemberOf:
     return "memberOf";
+  case RelationshipKind::InheritsFrom:
+    return "inheritsFrom";
+  case RelationshipKind::ConformsTo:
+    return "conformsTo";
   }
   llvm_unreachable("Unhandled relationship kind");
 }
 
 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
-                                                  const APIRecord &Source,
-                                                  const APIRecord &Target) {
+                                                  SymbolReference Source,
+                                                  SymbolReference Target) {
   Object Relationship;
   Relationship["source"] = Source.USR;
   Relationship["target"] = Target.USR;
@@ -499,6 +520,57 @@
   }
 }
 
+void SymbolGraphSerializer::serializeObjCContainerRecord(
+    const ObjCContainerRecord &Record) {
+  auto ObjCContainer = serializeAPIRecord(Record);
+  if (!ObjCContainer)
+    return;
+
+  Symbols.emplace_back(std::move(*ObjCContainer));
+
+  // Record instance variables and that the instance variables are members of
+  // the container.
+  for (const auto &Ivar : Record.Ivars) {
+    auto ObjCIvar = serializeAPIRecord(*Ivar);
+    if (!ObjCIvar)
+      continue;
+
+    Symbols.emplace_back(std::move(*ObjCIvar));
+    serializeRelationship(RelationshipKind::MemberOf, *Ivar, Record);
+  }
+
+  // Record methods and that the methods are members of the container.
+  for (const auto &Method : Record.Methods) {
+    auto ObjCMethod = serializeAPIRecord(*Method);
+    if (!ObjCMethod)
+      continue;
+
+    Symbols.emplace_back(std::move(*ObjCMethod));
+    serializeRelationship(RelationshipKind::MemberOf, *Method, Record);
+  }
+
+  // Record properties and that the properties are members of the container.
+  for (const auto &Property : Record.Properties) {
+    auto ObjCProperty = serializeAPIRecord(*Property);
+    if (!ObjCProperty)
+      continue;
+
+    Symbols.emplace_back(std::move(*ObjCProperty));
+    serializeRelationship(RelationshipKind::MemberOf, *Property, Record);
+  }
+
+  for (const auto &Protocol : Record.Protocols)
+    // Record that Record conforms to Protocol.
+    serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
+
+  if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record))
+    if (!ObjCInterface->SuperClass.empty())
+      // If Record is an Objective-C interface record and it has a super class,
+      // record that Record is inherited from SuperClass.
+      serializeRelationship(RelationshipKind::InheritsFrom, Record,
+                            ObjCInterface->SuperClass);
+}
+
 Object SymbolGraphSerializer::serialize() {
   Object Root;
   serializeObject(Root, "metadata", serializeMetadata());
@@ -516,6 +588,10 @@
   for (const auto &Struct : API.getStructs())
     serializeStructRecord(*Struct.second);
 
+  // Serialize Objective-C interface records in the API set.
+  for (const auto &ObjCInterface : API.getObjCInterfaces())
+    serializeObjCContainerRecord(*ObjCInterface.second);
+
   Root["symbols"] = std::move(Symbols);
   Root["relationhips"] = std::move(Relationships);
 
Index: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
===================================================================
--- clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -41,9 +41,8 @@
 /// information.
 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
 public:
-  explicit ExtractAPIVisitor(ASTContext &Context)
-      : Context(Context),
-        API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {}
+  ExtractAPIVisitor(ASTContext &Context, Language Lang)
+      : Context(Context), API(Context.getTargetInfo().getTriple(), Lang) {}
 
   const APISet &getAPI() const { return API; }
 
@@ -217,6 +216,50 @@
     return true;
   }
 
+  bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
+    // Skip forward declaration for classes (@class)
+    if (!Decl->isThisDeclarationADefinition())
+      return true;
+
+    // Collect symbol information.
+    StringRef Name = Decl->getName();
+    StringRef USR = API.recordUSR(Decl);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    AvailabilityInfo Availability = getAvailability(Decl);
+    LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the interface.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+    // Collect super class information.
+    SymbolReference SuperClass;
+    if (const auto *SuperClassDecl = Decl->getSuperClass()) {
+      SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
+      SuperClass.USR = API.recordUSR(SuperClassDecl);
+    }
+
+    ObjCInterfaceRecord *ObjCInterfaceRecord =
+        API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
+                             Declaration, SubHeading, SuperClass);
+
+    // Record all methods (selectors). This doesn't include automatically
+    // synthesized property methods.
+    recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
+    recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
+    recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
+    recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
+
+    return true;
+  }
+
 private:
   /// Get availability information of the declaration \p D.
   AvailabilityInfo getAvailability(const Decl *D) const {
@@ -303,15 +346,125 @@
     }
   }
 
+  /// Collect API information for the Objective-C methods and associate with the
+  /// parent container.
+  void recordObjCMethods(ObjCContainerRecord *Container,
+                         const ObjCContainerDecl::method_range Methods) {
+    for (const auto *Method : Methods) {
+      // Don't record selectors for properties.
+      if (Method->isPropertyAccessor())
+        continue;
+
+      StringRef Name = API.copyString(Method->getSelector().getAsString());
+      StringRef USR = API.recordUSR(Method);
+      PresumedLoc Loc =
+          Context.getSourceManager().getPresumedLoc(Method->getLocation());
+      AvailabilityInfo Availability = getAvailability(Method);
+      DocComment Comment;
+      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
+        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                                Context.getDiagnostics());
+
+      // Build declaration fragments, sub-heading, and signature for the method.
+      DeclarationFragments Declaration =
+          DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
+      DeclarationFragments SubHeading =
+          DeclarationFragmentsBuilder::getSubHeading(Method);
+      FunctionSignature Signature =
+          DeclarationFragmentsBuilder::getFunctionSignature(Method);
+
+      API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
+                        Declaration, SubHeading, Signature,
+                        Method->isInstanceMethod());
+    }
+  }
+
+  void recordObjCProperties(ObjCContainerRecord *Container,
+                            const ObjCContainerDecl::prop_range Properties) {
+    for (const auto *Property : Properties) {
+      StringRef Name = Property->getName();
+      StringRef USR = API.recordUSR(Property);
+      PresumedLoc Loc =
+          Context.getSourceManager().getPresumedLoc(Property->getLocation());
+      AvailabilityInfo Availability = getAvailability(Property);
+      DocComment Comment;
+      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
+        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                                Context.getDiagnostics());
+
+      // Build declaration fragments and sub-heading for the property.
+      DeclarationFragments Declaration =
+          DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
+      DeclarationFragments SubHeading =
+          DeclarationFragmentsBuilder::getSubHeading(Property);
+
+      StringRef GetterName =
+          API.copyString(Property->getGetterName().getAsString());
+      StringRef SetterName =
+          API.copyString(Property->getSetterName().getAsString());
+
+      // Get the attributes for property.
+      unsigned Attributes = ObjCPropertyRecord::NoAttr;
+      if (Property->getPropertyAttributes() &
+          ObjCPropertyAttribute::kind_readonly)
+        Attributes |= ObjCPropertyRecord::ReadOnly;
+      if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
+        Attributes |= ObjCPropertyRecord::Class;
+
+      API.addObjCProperty(
+          Container, Name, USR, Loc, Availability, Comment, Declaration,
+          SubHeading,
+          static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
+          GetterName, SetterName, Property->isOptional());
+    }
+  }
+
+  void recordObjCInstanceVariables(
+      ObjCContainerRecord *Container,
+      const llvm::iterator_range<
+          DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+          Ivars) {
+    for (const auto *Ivar : Ivars) {
+      StringRef Name = Ivar->getName();
+      StringRef USR = API.recordUSR(Ivar);
+      PresumedLoc Loc =
+          Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
+      AvailabilityInfo Availability = getAvailability(Ivar);
+      DocComment Comment;
+      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
+        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                                Context.getDiagnostics());
+
+      // Build declaration fragments and sub-heading for the instance variable.
+      DeclarationFragments Declaration =
+          DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
+      DeclarationFragments SubHeading =
+          DeclarationFragmentsBuilder::getSubHeading(Ivar);
+
+      ObjCInstanceVariableRecord::AccessControl Access =
+          Ivar->getCanonicalAccessControl();
+
+      API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
+                                  Comment, Declaration, SubHeading, Access);
+    }
+  }
+
+  void recordObjCProtocols(ObjCContainerRecord *Container,
+                           ObjCInterfaceDecl::protocol_range Protocols) {
+    for (const auto *Protocol : Protocols)
+      Container->Protocols.emplace_back(Protocol->getName(),
+                                        API.recordUSR(Protocol));
+  }
+
   ASTContext &Context;
   APISet API;
 };
 
 class ExtractAPIConsumer : public ASTConsumer {
 public:
-  ExtractAPIConsumer(ASTContext &Context, StringRef ProductName,
+  ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, Language Lang,
                      std::unique_ptr<raw_pwrite_stream> OS)
-      : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {}
+      : Visitor(Context, Lang), ProductName(ProductName), OS(std::move(OS)) {}
 
   void HandleTranslationUnit(ASTContext &Context) override {
     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
@@ -339,6 +492,7 @@
     return nullptr;
   return std::make_unique<ExtractAPIConsumer>(
       CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName,
+      CI.getFrontendOpts().Inputs.back().getKind().getLanguage(),
       std::move(OS));
 }
 
Index: clang/lib/ExtractAPI/DeclarationFragments.cpp
===================================================================
--- clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -245,6 +245,22 @@
   // unqualified base type.
   QualType Base = T->getCanonicalTypeUnqualified();
 
+  // Render Objective-C `id`/`instancetype` as keywords.
+  if (T->isObjCIdType())
+    return Fragments.append(Base.getAsString(),
+                            DeclarationFragments::FragmentKind::Keyword);
+
+  // If the base type is an ObjCInterfaceType, use the underlying
+  // ObjCInterfaceDecl for the true USR.
+  if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(Base)) {
+    const auto *Decl = ObjCIT->getDecl();
+    SmallString<128> USR;
+    index::generateUSRForDecl(Decl, USR);
+    return Fragments.append(Decl->getName(),
+                            DeclarationFragments::FragmentKind::TypeIdentifier,
+                            USR);
+  }
+
   // Default fragment builder for other kinds of types (BuiltinType etc.)
   SmallString<128> USR;
   clang::index::generateUSRForType(Base, Context, USR);
@@ -454,6 +470,157 @@
   return Fragments;
 }
 
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface(
+    const ObjCInterfaceDecl *Interface) {
+  DeclarationFragments Fragments;
+  // Build the base of the Objective-C interface declaration.
+  Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
+      .appendSpace()
+      .append(Interface->getName(),
+              DeclarationFragments::FragmentKind::Identifier);
+
+  // Build the inheritance part of the declaration.
+  if (const ObjCInterfaceDecl *SuperClass = Interface->getSuperClass()) {
+    SmallString<128> SuperUSR;
+    index::generateUSRForDecl(SuperClass, SuperUSR);
+    Fragments.append(" : ", DeclarationFragments::FragmentKind::Text)
+        .append(SuperClass->getName(),
+                DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR);
+  }
+
+  return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCMethod(
+    const ObjCMethodDecl *Method) {
+  DeclarationFragments Fragments, After;
+  // Build the instance/class method indicator.
+  if (Method->isClassMethod())
+    Fragments.append("+ ", DeclarationFragments::FragmentKind::Text);
+  else if (Method->isInstanceMethod())
+    Fragments.append("- ", DeclarationFragments::FragmentKind::Text);
+
+  // Build the return type.
+  Fragments.append("(", DeclarationFragments::FragmentKind::Text)
+      .append(getFragmentsForType(Method->getReturnType(),
+                                  Method->getASTContext(), After))
+      .append(std::move(After))
+      .append(")", DeclarationFragments::FragmentKind::Text);
+
+  // Build the selector part.
+  Selector Selector = Method->getSelector();
+  if (Selector.getNumArgs() == 0)
+    // For Objective-C methods that don't take arguments, the first (and only)
+    // slot of the selector is the method name.
+    Fragments.appendSpace().append(
+        Selector.getNameForSlot(0),
+        DeclarationFragments::FragmentKind::Identifier);
+
+  // For Objective-C methods that take arguments, build the selector slots.
+  for (unsigned i = 0, end = Method->param_size(); i != end; ++i) {
+    Fragments.appendSpace()
+        .append(Selector.getNameForSlot(i),
+                // The first slot is the name of the method, record as an
+                // identifier, otherwise as exteranl parameters.
+                i == 0 ? DeclarationFragments::FragmentKind::Identifier
+                       : DeclarationFragments::FragmentKind::ExternalParam)
+        .append(":", DeclarationFragments::FragmentKind::Text);
+
+    // Build the internal parameter.
+    const ParmVarDecl *Param = Method->getParamDecl(i);
+    Fragments.append(getFragmentsForParam(Param));
+  }
+
+  return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
+    const ObjCPropertyDecl *Property) {
+  DeclarationFragments Fragments, After;
+
+  // Build the Objective-C property keyword.
+  Fragments.append("@property", DeclarationFragments::FragmentKind::Keyword);
+
+  const auto Attributes = Property->getPropertyAttributes();
+  // Build the attributes if there is any associated with the property.
+  if (Attributes != ObjCPropertyAttribute::kind_noattr) {
+    // No leading comma for the first attribute.
+    bool First = true;
+    Fragments.append(" (", DeclarationFragments::FragmentKind::Text);
+    // Helper function to render the attribute.
+    auto RenderAttribute =
+        [&](ObjCPropertyAttribute::Kind Kind, StringRef Spelling,
+            StringRef Arg = "",
+            DeclarationFragments::FragmentKind ArgKind =
+                DeclarationFragments::FragmentKind::Identifier) {
+          // Check if the `Kind` attribute is set for this property.
+          if ((Attributes & Kind) && !Spelling.empty()) {
+            // Add a leading comma if this is not the first attribute rendered.
+            if (!First)
+              Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+            // Render the spelling of this attribute `Kind` as a keyword.
+            Fragments.append(Spelling,
+                             DeclarationFragments::FragmentKind::Keyword);
+            // If this attribute takes in arguments (e.g. `getter=getterName`),
+            // render the arguments.
+            if (!Arg.empty())
+              Fragments.append("=", DeclarationFragments::FragmentKind::Text)
+                  .append(Arg, ArgKind);
+            First = false;
+          }
+        };
+
+    // Go through all possible Objective-C property attributes and render set
+    // ones.
+    RenderAttribute(ObjCPropertyAttribute::kind_class, "class");
+    RenderAttribute(ObjCPropertyAttribute::kind_direct, "direct");
+    RenderAttribute(ObjCPropertyAttribute::kind_nonatomic, "nonatomic");
+    RenderAttribute(ObjCPropertyAttribute::kind_atomic, "atomic");
+    RenderAttribute(ObjCPropertyAttribute::kind_assign, "assign");
+    RenderAttribute(ObjCPropertyAttribute::kind_retain, "retain");
+    RenderAttribute(ObjCPropertyAttribute::kind_strong, "strong");
+    RenderAttribute(ObjCPropertyAttribute::kind_copy, "copy");
+    RenderAttribute(ObjCPropertyAttribute::kind_weak, "weak");
+    RenderAttribute(ObjCPropertyAttribute::kind_unsafe_unretained,
+                    "unsafe_unretained");
+    RenderAttribute(ObjCPropertyAttribute::kind_readwrite, "readwrite");
+    RenderAttribute(ObjCPropertyAttribute::kind_readonly, "readonly");
+    RenderAttribute(ObjCPropertyAttribute::kind_getter, "getter",
+                    Property->getGetterName().getAsString());
+    RenderAttribute(ObjCPropertyAttribute::kind_setter, "setter",
+                    Property->getSetterName().getAsString());
+
+    // Render nullability attributes.
+    if (Attributes & ObjCPropertyAttribute::kind_nullability) {
+      QualType Type = Property->getType();
+      if (const auto Nullability =
+              AttributedType::stripOuterNullability(Type)) {
+        if (!First)
+          Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+        if (*Nullability == NullabilityKind::Unspecified &&
+            (Attributes & ObjCPropertyAttribute::kind_null_resettable))
+          Fragments.append("null_resettable",
+                           DeclarationFragments::FragmentKind::Keyword);
+        else
+          Fragments.append(
+              getNullabilitySpelling(*Nullability, /*isContextSensitive=*/true),
+              DeclarationFragments::FragmentKind::Keyword);
+        First = false;
+      }
+    }
+
+    Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+  }
+
+  // Build the property type and name, and return the completed fragments.
+  return Fragments.appendSpace()
+      .append(getFragmentsForType(Property->getType(),
+                                  Property->getASTContext(), After))
+      .append(Property->getName(),
+              DeclarationFragments::FragmentKind::Identifier)
+      .append(std::move(After));
+}
+
 FunctionSignature
 DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *Func) {
   FunctionSignature Signature;
@@ -475,6 +642,23 @@
   return Signature;
 }
 
+FunctionSignature DeclarationFragmentsBuilder::getFunctionSignature(
+    const ObjCMethodDecl *Method) {
+  FunctionSignature Signature;
+
+  DeclarationFragments ReturnType, After;
+  ReturnType
+      .append(getFragmentsForType(Method->getReturnType(),
+                                  Method->getASTContext(), After))
+      .append(std::move(After));
+  Signature.setReturnType(ReturnType);
+
+  for (const auto *Param : Method->parameters())
+    Signature.addParameter(Param->getName(), getFragmentsForParam(Param));
+
+  return Signature;
+}
+
 // Subheading of a symbol defaults to its name.
 DeclarationFragments
 DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
@@ -484,3 +668,17 @@
                      DeclarationFragments::FragmentKind::Identifier);
   return Fragments;
 }
+
+// Subheading of an Objective-C method is a `+` or `-` sign indicating whether
+// it's a class method or an instance method, followed by the selector name.
+DeclarationFragments
+DeclarationFragmentsBuilder::getSubHeading(const ObjCMethodDecl *Method) {
+  DeclarationFragments Fragments;
+  if (Method->isClassMethod())
+    Fragments.append("+ ", DeclarationFragments::FragmentKind::Text);
+  else if (Method->isInstanceMethod())
+    Fragments.append("- ", DeclarationFragments::FragmentKind::Text);
+
+  return Fragments.append(Method->getNameAsString(),
+                          DeclarationFragments::FragmentKind::Identifier);
+}
Index: clang/lib/ExtractAPI/API.cpp
===================================================================
--- clang/lib/ExtractAPI/API.cpp
+++ clang/lib/ExtractAPI/API.cpp
@@ -109,6 +109,58 @@
   return Result.first->second.get();
 }
 
+ObjCInterfaceRecord *APISet::addObjCInterface(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    const AvailabilityInfo &Availability, LinkageInfo Linkage,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, SymbolReference SuperClass) {
+  auto Result = ObjCInterfaces.insert({Name, nullptr});
+  if (Result.second) {
+    // Create the record if it does not already exist.
+    auto Record = std::make_unique<ObjCInterfaceRecord>(
+        Name, USR, Loc, Availability, Linkage, Comment, Declaration, SubHeading,
+        SuperClass);
+    Result.first->second = std::move(Record);
+  }
+  return Result.first->second.get();
+}
+
+ObjCMethodRecord *APISet::addObjCMethod(
+    ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+    PresumedLoc Loc, const AvailabilityInfo &Availability,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading, FunctionSignature Signature,
+    bool IsInstanceMethod) {
+  auto Record = std::make_unique<ObjCMethodRecord>(
+      Name, USR, Loc, Availability, Comment, Declaration, SubHeading, Signature,
+      IsInstanceMethod);
+  return Container->Methods.emplace_back(std::move(Record)).get();
+}
+
+ObjCPropertyRecord *APISet::addObjCProperty(
+    ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+    PresumedLoc Loc, const AvailabilityInfo &Availability,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading,
+    ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
+    StringRef SetterName, bool IsOptional) {
+  auto Record = std::make_unique<ObjCPropertyRecord>(
+      Name, USR, Loc, Availability, Comment, Declaration, SubHeading,
+      Attributes, GetterName, SetterName, IsOptional);
+  return Container->Properties.emplace_back(std::move(Record)).get();
+}
+
+ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
+    ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+    PresumedLoc Loc, const AvailabilityInfo &Availability,
+    const DocComment &Comment, DeclarationFragments Declaration,
+    DeclarationFragments SubHeading,
+    ObjCInstanceVariableRecord::AccessControl Access) {
+  auto Record = std::make_unique<ObjCInstanceVariableRecord>(
+      Name, USR, Loc, Availability, Comment, Declaration, SubHeading, Access);
+  return Container->Ivars.emplace_back(std::move(Record)).get();
+}
+
 StringRef APISet::recordUSR(const Decl *D) {
   SmallString<128> USR;
   index::generateUSRForDecl(D, USR);
@@ -130,8 +182,14 @@
 
 APIRecord::~APIRecord() {}
 
+ObjCContainerRecord::~ObjCContainerRecord() {}
+
 void GlobalRecord::anchor() {}
 void EnumConstantRecord::anchor() {}
 void EnumRecord::anchor() {}
 void StructFieldRecord::anchor() {}
 void StructRecord::anchor() {}
+void ObjCPropertyRecord::anchor() {}
+void ObjCInstanceVariableRecord::anchor() {}
+void ObjCMethodRecord::anchor() {}
+void ObjCInterfaceRecord::anchor() {}
Index: clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
===================================================================
--- clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -62,6 +62,13 @@
     /// For example enum constants are members of the enum, class/instance
     /// methods are members of the class, etc.
     MemberOf,
+
+    /// The source symbol is inherited from the target symbol.
+    InheritsFrom,
+
+    /// The source symbol conforms to the target symbol.
+    /// For example Objective-C protocol conformances.
+    ConformsTo,
   };
 
   /// Get the string representation of the relationship kind.
@@ -101,8 +108,8 @@
   ///
   /// Record the relationship between the two symbols in
   /// SymbolGraphSerializer::Relationships.
-  void serializeRelationship(RelationshipKind Kind, const APIRecord &Source,
-                             const APIRecord &Target);
+  void serializeRelationship(RelationshipKind Kind, SymbolReference Source,
+                             SymbolReference Target);
 
   /// Serialize a global record.
   void serializeGlobalRecord(const GlobalRecord &Record);
@@ -113,6 +120,9 @@
   /// Serialize a struct record.
   void serializeStructRecord(const StructRecord &Record);
 
+  /// Serialize an Objective-C container record.
+  void serializeObjCContainerRecord(const ObjCContainerRecord &Record);
+
 public:
   SymbolGraphSerializer(const APISet &API, StringRef ProductName,
                         APISerializerOption Options = {})
Index: clang/include/clang/ExtractAPI/DeclarationFragments.h
===================================================================
--- clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -21,6 +21,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
 #include "llvm/ADT/StringRef.h"
 #include <vector>
 
@@ -202,12 +203,32 @@
   /// Build DeclarationFragments for a struct record declaration RecordDecl.
   static DeclarationFragments getFragmentsForStruct(const RecordDecl *);
 
+  /// Build DeclarationFragments for an Objective-C interface declaration
+  /// ObjCInterfaceDecl.
+  static DeclarationFragments
+  getFragmentsForObjCInterface(const ObjCInterfaceDecl *);
+
+  /// Build DeclarationFragments for an Objective-C method declaration
+  /// ObjCMethodDecl.
+  static DeclarationFragments getFragmentsForObjCMethod(const ObjCMethodDecl *);
+
+  /// Build DeclarationFragments for an Objective-C property declaration
+  /// ObjCPropertyDecl.
+  static DeclarationFragments
+  getFragmentsForObjCProperty(const ObjCPropertyDecl *);
+
   /// Build sub-heading fragments for a NamedDecl.
   static DeclarationFragments getSubHeading(const NamedDecl *);
 
+  /// Build sub-heading fragments for an Objective-C method.
+  static DeclarationFragments getSubHeading(const ObjCMethodDecl *);
+
   /// Build FunctionSignature for a function declaration FunctionDecl.
   static FunctionSignature getFunctionSignature(const FunctionDecl *);
 
+  /// Build FunctionSignature for an Objective-C method.
+  static FunctionSignature getFunctionSignature(const ObjCMethodDecl *);
+
 private:
   DeclarationFragmentsBuilder() = delete;
 
Index: clang/include/clang/ExtractAPI/API.h
===================================================================
--- clang/include/clang/ExtractAPI/API.h
+++ clang/include/clang/ExtractAPI/API.h
@@ -19,6 +19,7 @@
 #define LLVM_CLANG_EXTRACTAPI_API_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/AST/RawCommentList.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/ExtractAPI/AvailabilityInfo.h"
@@ -77,6 +78,10 @@
     RK_Enum,
     RK_StructField,
     RK_Struct,
+    RK_ObjCProperty,
+    RK_ObjCIvar,
+    RK_ObjCMethod,
+    RK_ObjCInterface,
   };
 
 private:
@@ -201,6 +206,154 @@
   virtual void anchor();
 };
 
+/// This holds information associated with Objective-C properties.
+struct ObjCPropertyRecord : APIRecord {
+  /// The attributes associated with an Objective-C property.
+  enum AttributeKind : unsigned {
+    NoAttr = 0,
+    ReadOnly = 1,
+    Class = 1 << 1,
+    Dynamic = 1 << 2,
+  };
+
+  AttributeKind Attributes;
+  StringRef GetterName;
+  StringRef SetterName;
+  bool IsOptional;
+
+  ObjCPropertyRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                     const AvailabilityInfo &Availability,
+                     const DocComment &Comment,
+                     DeclarationFragments Declaration,
+                     DeclarationFragments SubHeading, AttributeKind Attributes,
+                     StringRef GetterName, StringRef SetterName,
+                     bool IsOptional)
+      : APIRecord(RK_ObjCProperty, Name, USR, Loc, Availability,
+                  LinkageInfo::none(), Comment, Declaration, SubHeading),
+        Attributes(Attributes), GetterName(GetterName), SetterName(SetterName),
+        IsOptional(IsOptional) {}
+
+  bool isReadOnly() const { return Attributes & ReadOnly; }
+  bool isDynamic() const { return Attributes & Dynamic; }
+  bool isClassProperty() const { return Attributes & Class; }
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCProperty;
+  }
+
+private:
+  virtual void anchor();
+};
+
+/// This holds information associated with Objective-C instance variables.
+struct ObjCInstanceVariableRecord : APIRecord {
+  using AccessControl = ObjCIvarDecl::AccessControl;
+  AccessControl Access;
+
+  ObjCInstanceVariableRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                             const AvailabilityInfo &Availability,
+                             const DocComment &Comment,
+                             DeclarationFragments Declaration,
+                             DeclarationFragments SubHeading,
+                             AccessControl Access)
+      : APIRecord(RK_ObjCIvar, Name, USR, Loc, Availability,
+                  LinkageInfo::none(), Comment, Declaration, SubHeading),
+        Access(Access) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCIvar;
+  }
+
+private:
+  virtual void anchor();
+};
+
+/// This holds information associated with Objective-C methods.
+struct ObjCMethodRecord : APIRecord {
+  FunctionSignature Signature;
+  bool IsInstanceMethod;
+
+  ObjCMethodRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                   const AvailabilityInfo &Availability,
+                   const DocComment &Comment, DeclarationFragments Declaration,
+                   DeclarationFragments SubHeading, FunctionSignature Signature,
+                   bool IsInstanceMethod)
+      : APIRecord(RK_ObjCMethod, Name, USR, Loc, Availability,
+                  LinkageInfo::none(), Comment, Declaration, SubHeading),
+        Signature(Signature), IsInstanceMethod(IsInstanceMethod) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCMethod;
+  }
+
+private:
+  virtual void anchor();
+};
+
+/// This represents a reference to another symbol that might come from external
+/// sources.
+struct SymbolReference {
+  StringRef Name;
+  StringRef USR;
+
+  /// The source project/module/product of the referred symbol.
+  StringRef Source;
+
+  SymbolReference() = default;
+  SymbolReference(StringRef Name, StringRef USR = "", StringRef Source = "")
+      : Name(Name), USR(USR), Source(Source) {}
+  SymbolReference(const APIRecord &Record)
+      : Name(Record.Name), USR(Record.USR) {}
+
+  /// Determine if this SymbolReference is empty.
+  ///
+  /// \returns true if and only if all \c Name, \c USR, and \c Source is empty.
+  bool empty() const { return Name.empty() && USR.empty() && Source.empty(); }
+};
+
+/// The base representation of an Objective-C container record. Holds common
+/// information associated with Objective-C containers.
+struct ObjCContainerRecord : APIRecord {
+  SmallVector<std::unique_ptr<ObjCMethodRecord>> Methods;
+  SmallVector<std::unique_ptr<ObjCPropertyRecord>> Properties;
+  SmallVector<std::unique_ptr<ObjCInstanceVariableRecord>> Ivars;
+  SmallVector<SymbolReference> Protocols;
+
+  ObjCContainerRecord() = delete;
+
+  ObjCContainerRecord(RecordKind Kind, StringRef Name, StringRef USR,
+                      PresumedLoc Loc, const AvailabilityInfo &Availability,
+                      LinkageInfo Linkage, const DocComment &Comment,
+                      DeclarationFragments Declaration,
+                      DeclarationFragments SubHeading)
+      : APIRecord(Kind, Name, USR, Loc, Availability, Linkage, Comment,
+                  Declaration, SubHeading) {}
+
+  virtual ~ObjCContainerRecord() = 0;
+};
+
+/// This holds information associated with Objective-C interfaces/classes.
+struct ObjCInterfaceRecord : ObjCContainerRecord {
+  SymbolReference SuperClass;
+
+  ObjCInterfaceRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                      const AvailabilityInfo &Availability, LinkageInfo Linkage,
+                      const DocComment &Comment,
+                      DeclarationFragments Declaration,
+                      DeclarationFragments SubHeading,
+                      SymbolReference SuperClass)
+      : ObjCContainerRecord(RK_ObjCInterface, Name, USR, Loc, Availability,
+                            Linkage, Comment, Declaration, SubHeading),
+        SuperClass(SuperClass) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCInterface;
+  }
+
+private:
+  virtual void anchor();
+};
+
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
 public:
@@ -292,6 +445,58 @@
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading);
 
+  /// Create and add an Objective-C interface record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCInterfaceRecord *
+  addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+                   const AvailabilityInfo &Availability, LinkageInfo Linkage,
+                   const DocComment &Comment, DeclarationFragments Declaration,
+                   DeclarationFragments SubHeading, SymbolReference SuperClass);
+
+  /// Create and add an Objective-C method record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCMethodRecord *
+  addObjCMethod(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+                PresumedLoc Loc, const AvailabilityInfo &Availability,
+                const DocComment &Comment, DeclarationFragments Declaration,
+                DeclarationFragments SubHeading, FunctionSignature Signature,
+                bool IsInstanceMethod);
+
+  /// Create and add an Objective-C property record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCPropertyRecord *
+  addObjCProperty(ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+                  PresumedLoc Loc, const AvailabilityInfo &Availability,
+                  const DocComment &Comment, DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading,
+                  ObjCPropertyRecord::AttributeKind Attributes,
+                  StringRef GetterName, StringRef SetterName, bool IsOptional);
+
+  /// Create and add an Objective-C instance variable record into the API set.
+  ///
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  ObjCInstanceVariableRecord *addObjCInstanceVariable(
+      ObjCContainerRecord *Container, StringRef Name, StringRef USR,
+      PresumedLoc Loc, const AvailabilityInfo &Availability,
+      const DocComment &Comment, DeclarationFragments Declaration,
+      DeclarationFragments SubHeading,
+      ObjCInstanceVariableRecord::AccessControl Access);
+
   /// A map to store the set of GlobalRecord%s with the declaration name as the
   /// key.
   using GlobalRecordMap =
@@ -306,15 +511,23 @@
   using StructRecordMap =
       llvm::MapVector<StringRef, std::unique_ptr<StructRecord>>;
 
+  /// A map to store the set of ObjCInterfaceRecord%s with the declaration name
+  /// as the key.
+  using ObjCInterfaceRecordMap =
+      llvm::MapVector<StringRef, std::unique_ptr<ObjCInterfaceRecord>>;
+
   /// Get the target triple for the ExtractAPI invocation.
   const llvm::Triple &getTarget() const { return Target; }
 
-  /// Get the language options used to parse the APIs.
-  const LangOptions &getLangOpts() const { return LangOpts; }
+  /// Get the language used by the APIs.
+  Language getLanguage() const { return Lang; }
 
   const GlobalRecordMap &getGlobals() const { return Globals; }
   const EnumRecordMap &getEnums() const { return Enums; }
   const StructRecordMap &getStructs() const { return Structs; }
+  const ObjCInterfaceRecordMap &getObjCInterfaces() const {
+    return ObjCInterfaces;
+  }
 
   /// Generate and store the USR of declaration \p D.
   ///
@@ -328,8 +541,8 @@
   /// \returns a StringRef of the copied string in APISet::Allocator.
   StringRef copyString(StringRef String);
 
-  APISet(const llvm::Triple &Target, const LangOptions &LangOpts)
-      : Target(Target), LangOpts(LangOpts) {}
+  APISet(const llvm::Triple &Target, Language Lang)
+      : Target(Target), Lang(Lang) {}
 
 private:
   /// BumpPtrAllocator to store generated/copied strings.
@@ -338,11 +551,12 @@
   llvm::BumpPtrAllocator StringAllocator;
 
   const llvm::Triple Target;
-  const LangOptions LangOpts;
+  Language Lang;
 
   GlobalRecordMap Globals;
   EnumRecordMap Enums;
   StructRecordMap Structs;
+  ObjCInterfaceRecordMap ObjCInterfaces;
 };
 
 } // namespace extractapi
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to