[clang] 9b36e12 - [clang][extract-api] Add Objective-C interface support

Zixu Wang via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 29 14:29:50 PDT 2022


Author: Zixu Wang
Date: 2022-03-29T14:29:39-07:00
New Revision: 9b36e126fdb1da4d7e255e089ef225dfb130ef63

URL: https://github.com/llvm/llvm-project/commit/9b36e126fdb1da4d7e255e089ef225dfb130ef63
DIFF: https://github.com/llvm/llvm-project/commit/9b36e126fdb1da4d7e255e089ef225dfb130ef63.diff

LOG: [clang][extract-api] Add Objective-C interface support

Add support for Objective-C interface declarations in ExtractAPI.

Depends on D122495

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

Added: 
    clang/test/ExtractAPI/objc_interface.m

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index b22d110475908..a2a462be42cd3 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/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 @@ struct APIRecord {
     RK_Enum,
     RK_StructField,
     RK_Struct,
+    RK_ObjCProperty,
+    RK_ObjCIvar,
+    RK_ObjCMethod,
+    RK_ObjCInterface,
   };
 
 private:
@@ -201,6 +206,154 @@ struct StructRecord : APIRecord {
   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 @@ class APISet {
                           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,6 +511,11 @@ class APISet {
   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; }
 
@@ -315,6 +525,9 @@ class APISet {
   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.
   ///
@@ -343,6 +556,7 @@ class APISet {
   GlobalRecordMap Globals;
   EnumRecordMap Enums;
   StructRecordMap Structs;
+  ObjCInterfaceRecordMap ObjCInterfaces;
 };
 
 } // namespace extractapi

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index f8ef431b8525d..f147abb4f425c 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/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,11 +203,34 @@ class DeclarationFragmentsBuilder {
   /// 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 FunctionSignature for a function declaration FunctionDecl.
-  static FunctionSignature getFunctionSignature(const FunctionDecl *);
+  /// Build sub-heading fragments for an Objective-C method.
+  static DeclarationFragments getSubHeading(const ObjCMethodDecl *);
+
+  /// 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;

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index 109833f474948..b5002cabfbbe1 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -62,6 +62,13 @@ class SymbolGraphSerializer : public APISerializer {
     /// 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 @@ class SymbolGraphSerializer : public APISerializer {
   ///
   /// 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 @@ class SymbolGraphSerializer : public APISerializer {
   /// 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 = {})

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 92c4e0ea48383..2456022523adb 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -109,6 +109,58 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
   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 @@ StringRef APISet::copyString(StringRef String) {
 
 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() {}

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 1980e8e6dcc69..b35388ec52ad8 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -245,6 +245,22 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
   // 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,27 +470,183 @@ DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
   return Fragments;
 }
 
-FunctionSignature
-DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *Func) {
-  FunctionSignature Signature;
+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);
+  }
 
-  for (const auto *Param : Func->parameters()) {
-    StringRef Name = Param->getName();
-    DeclarationFragments Fragments = getFragmentsForParam(Param);
+  return Fragments;
+}
 
-    Signature.addParameter(Name, 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));
   }
 
-  DeclarationFragments After;
-  DeclarationFragments Returns =
-      getFragmentsForType(Func->getReturnType(), Func->getASTContext(), After)
-          .append(std::move(After));
+  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));
+}
 
-  Signature.setReturnType(Returns);
+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 *);
+
+// Instantiate template for ObjCMethodDecl.
+template FunctionSignature
+DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *);
+
 // Subheading of a symbol defaults to its name.
 DeclarationFragments
 DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
@@ -484,3 +656,17 @@ DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
                      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);
+}

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index dc3e067a837e2..f8b1c9b20fb1e 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -216,6 +216,50 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     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 {
@@ -302,6 +346,116 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     }
   }
 
+  /// 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;
 };

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index cde81ecf0abd4..3faf9cd751035 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -372,6 +372,27 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
     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;
@@ -435,13 +456,17 @@ StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
   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;
@@ -496,6 +521,57 @@ void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
   }
 }
 
+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());
@@ -513,6 +589,10 @@ Object SymbolGraphSerializer::serialize() {
   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);
 

diff  --git a/clang/test/ExtractAPI/objc_interface.m b/clang/test/ExtractAPI/objc_interface.m
new file mode 100644
index 0000000000000..1e903cfc359e0
--- /dev/null
+++ b/clang/test/ExtractAPI/objc_interface.m
@@ -0,0 +1,402 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at 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
+ at protocol Protocol;
+
+ at interface Super <Protocol>
+ at property(readonly, getter=getProperty) unsigned Property;
++ (id)getWithProperty:(unsigned) Property;
+ at end
+
+ at interface Derived : Super {
+  char Ivar;
+}
+- (char)getIvar;
+ at end
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "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 at 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": 3,
+        "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": 5,
+        "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": 4,
+        "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": 8,
+        "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 at Ivar"
+      },
+      "kind": {
+        "displayName": "Instance Variable",
+        "identifier": "objective-c.ivar"
+      },
+      "location": {
+        "character": 8,
+        "line": 9,
+        "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": 11,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "getIvar"
+          }
+        ],
+        "title": "getIvar"
+      }
+    }
+  ]
+}


        


More information about the cfe-commits mailing list