Author: Zixu Wang
Date: 2022-04-06T12:00:12-07:00
New Revision: 178aad9b946e3c5abe9df162e5c482fb4acae99c

URL: 
https://github.com/llvm/llvm-project/commit/178aad9b946e3c5abe9df162e5c482fb4acae99c
DIFF: 
https://github.com/llvm/llvm-project/commit/178aad9b946e3c5abe9df162e5c482fb4acae99c.diff

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

Add (partial) support for Objective-C category records in ExtractAPI.
The current ExtractAPI collects everything for an Objective-C category,
but not fully serialized in the SymbolGraphSerializer. Categories
extending external interfaces are disgarded during serialization, and
categories extending known interfaces are merged (all members surfaced)
into the interfaces.

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

Added: 
    clang/test/ExtractAPI/objc_category.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 827cde953c31c..57b6a2ee5a43c 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -86,6 +86,7 @@ struct APIRecord {
     RK_ObjCIvar,
     RK_ObjCMethod,
     RK_ObjCInterface,
+    RK_ObjCCategory,
     RK_ObjCProtocol,
     RK_MacroDefinition,
     RK_Typedef,
@@ -340,9 +341,33 @@ struct ObjCContainerRecord : APIRecord {
   virtual ~ObjCContainerRecord() = 0;
 };
 
+/// This holds information associated with Objective-C categories.
+struct ObjCCategoryRecord : ObjCContainerRecord {
+  SymbolReference Interface;
+
+  ObjCCategoryRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                     const AvailabilityInfo &Availability,
+                     const DocComment &Comment,
+                     DeclarationFragments Declaration,
+                     DeclarationFragments SubHeading, SymbolReference 
Interface)
+      : ObjCContainerRecord(RK_ObjCCategory, Name, USR, Loc, Availability,
+                            LinkageInfo::none(), Comment, Declaration,
+                            SubHeading),
+        Interface(Interface) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_ObjCCategory;
+  }
+
+private:
+  virtual void anchor();
+};
+
 /// This holds information associated with Objective-C interfaces/classes.
 struct ObjCInterfaceRecord : ObjCContainerRecord {
   SymbolReference SuperClass;
+  // ObjCCategoryRecord%s are stored in and owned by APISet.
+  SmallVector<ObjCCategoryRecord *> Categories;
 
   ObjCInterfaceRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
                       const AvailabilityInfo &Availability, LinkageInfo 
Linkage,
@@ -512,6 +537,18 @@ class APISet {
                           DeclarationFragments Declaration,
                           DeclarationFragments SubHeading);
 
+  /// Create and add an Objective-C category 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.
+  ObjCCategoryRecord *
+  addObjCCategory(StringRef Name, StringRef USR, PresumedLoc Loc,
+                  const AvailabilityInfo &Availability,
+                  const DocComment &Comment, DeclarationFragments Declaration,
+                  DeclarationFragments SubHeading, SymbolReference Interface);
+
   /// Create and add an Objective-C interface record into the API set.
   ///
   /// Note: the caller is responsible for keeping the StringRef \p Name and
@@ -618,6 +655,9 @@ class APISet {
   const RecordMap<GlobalRecord> &getGlobals() const { return Globals; }
   const RecordMap<EnumRecord> &getEnums() const { return Enums; }
   const RecordMap<StructRecord> &getStructs() const { return Structs; }
+  const RecordMap<ObjCCategoryRecord> &getObjCCategories() const {
+    return ObjCCategories;
+  }
   const RecordMap<ObjCInterfaceRecord> &getObjCInterfaces() const {
     return ObjCInterfaces;
   }
@@ -662,6 +702,7 @@ class APISet {
   RecordMap<GlobalRecord> Globals;
   RecordMap<EnumRecord> Enums;
   RecordMap<StructRecord> Structs;
+  RecordMap<ObjCCategoryRecord> ObjCCategories;
   RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
   RecordMap<ObjCProtocolRecord> ObjCProtocols;
   RecordMap<MacroDefinitionRecord> Macros;

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h 
b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 80fb5faf68783..55a6768eed9b9 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -204,6 +204,11 @@ class DeclarationFragmentsBuilder {
   /// Build DeclarationFragments for a struct record declaration RecordDecl.
   static DeclarationFragments getFragmentsForStruct(const RecordDecl *);
 
+  /// Build DeclarationFragments for an Objective-C category declaration
+  /// ObjCCategoryDecl.
+  static DeclarationFragments
+  getFragmentsForObjCCategory(const ObjCCategoryDecl *);
+
   /// Build DeclarationFragments for an Objective-C interface declaration
   /// ObjCInterfaceDecl.
   static DeclarationFragments

diff  --git 
a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h 
b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index 6c9611acd132a..93942ff05a0ae 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -124,6 +124,12 @@ class SymbolGraphSerializer : public APISerializer {
   /// containing common symbol information of \p Record.
   Optional<Object> serializeAPIRecord(const APIRecord &Record) const;
 
+  /// Helper method to serialize second-level member records of \p Record and
+  /// the member-of relationships.
+  template <typename MemberTy>
+  void serializeMembers(const APIRecord &Record,
+                        const SmallVector<std::unique_ptr<MemberTy>> &Members);
+
   /// Serialize the \p Kind relationship between \p Source and \p Target.
   ///
   /// Record the relationship between the two symbols in

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 6d5ac8a1b40c9..1c33bbf4328cf 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -109,6 +109,24 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef 
USR, PresumedLoc Loc,
                            Declaration, SubHeading);
 }
 
+ObjCCategoryRecord *APISet::addObjCCategory(
+    StringRef Name, StringRef USR, PresumedLoc Loc,
+    const AvailabilityInfo &Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading,
+    SymbolReference Interface) {
+  // Create the category record.
+  auto *Record = addTopLevelRecord(ObjCCategories, Name, USR, Loc, 
Availability,
+                                   Comment, Declaration, SubHeading, 
Interface);
+
+  // If this category is extending a known interface, associate it with the
+  // ObjCInterfaceRecord.
+  auto It = ObjCInterfaces.find(Interface.Name);
+  if (It != ObjCInterfaces.end())
+    It->second->Categories.push_back(Record);
+
+  return Record;
+}
+
 ObjCInterfaceRecord *APISet::addObjCInterface(
     StringRef Name, StringRef USR, PresumedLoc Loc,
     const AvailabilityInfo &Availability, LinkageInfo Linkage,
@@ -219,6 +237,7 @@ void StructRecord::anchor() {}
 void ObjCPropertyRecord::anchor() {}
 void ObjCInstanceVariableRecord::anchor() {}
 void ObjCMethodRecord::anchor() {}
+void ObjCCategoryRecord::anchor() {}
 void ObjCInterfaceRecord::anchor() {}
 void ObjCProtocolRecord::anchor() {}
 void MacroDefinitionRecord::anchor() {}

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp 
b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 4e0e8da99972c..fa28fad358db9 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -526,6 +526,25 @@ 
DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
   return Fragments;
 }
 
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(
+    const ObjCCategoryDecl *Category) {
+  DeclarationFragments Fragments;
+
+  SmallString<128> InterfaceUSR;
+  index::generateUSRForDecl(Category->getClassInterface(), InterfaceUSR);
+
+  Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
+      .appendSpace()
+      .append(Category->getClassInterface()->getName(),
+              DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR)
+      .append(" (", DeclarationFragments::FragmentKind::Text)
+      .append(Category->getName(),
+              DeclarationFragments::FragmentKind::Identifier)
+      .append(")", DeclarationFragments::FragmentKind::Text);
+
+  return Fragments;
+}
+
 DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface(
     const ObjCInterfaceDecl *Interface) {
   DeclarationFragments Fragments;

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp 
b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index 493d490674b1c..7c2914da7ea0c 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -338,6 +338,39 @@ class ExtractAPIVisitor : public 
RecursiveASTVisitor<ExtractAPIVisitor> {
     return true;
   }
 
+  bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
+    // Collect symbol information.
+    StringRef Name = Decl->getName();
+    StringRef USR = API.recordUSR(Decl);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    AvailabilityInfo Availability = getAvailability(Decl);
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+    // Build declaration fragments and sub-heading for the category.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+    const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
+    SymbolReference Interface(InterfaceDecl->getName(),
+                              API.recordUSR(InterfaceDecl));
+
+    ObjCCategoryRecord *ObjCCategoryRecord =
+        API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
+                            SubHeading, Interface);
+
+    recordObjCMethods(ObjCCategoryRecord, Decl->methods());
+    recordObjCProperties(ObjCCategoryRecord, Decl->properties());
+    recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
+    recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
+
+    return true;
+  }
+
 private:
   /// Get availability information of the declaration \p D.
   AvailabilityInfo getAvailability(const Decl *D) const {

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp 
b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index f38a5bbae7d00..efe3c276671da 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -401,6 +401,11 @@ Object serializeSymbolKind(const APIRecord &Record, 
Language Lang) {
     Kind["identifier"] = AddLangPrefix("class");
     Kind["displayName"] = "Class";
     break;
+  case APIRecord::RK_ObjCCategory:
+    // We don't serialize out standalone Objective-C category symbols yet.
+    llvm_unreachable("Serializing standalone Objective-C category symbols is "
+                     "not supported.");
+    break;
   case APIRecord::RK_ObjCProtocol:
     Kind["identifier"] = AddLangPrefix("protocol");
     Kind["displayName"] = "Protocol";
@@ -476,6 +481,21 @@ SymbolGraphSerializer::serializeAPIRecord(const APIRecord 
&Record) const {
   return Obj;
 }
 
+template <typename MemberTy>
+void SymbolGraphSerializer::serializeMembers(
+    const APIRecord &Record,
+    const SmallVector<std::unique_ptr<MemberTy>> &Members) {
+  for (const auto &Member : Members) {
+    auto MemberPathComponentGuard = makePathComponentGuard(Member->Name);
+    auto MemberRecord = serializeAPIRecord(*Member);
+    if (!MemberRecord)
+      continue;
+
+    Symbols.emplace_back(std::move(*MemberRecord));
+    serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
+  }
+}
+
 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
   switch (Kind) {
   case RelationshipKind::MemberOf:
@@ -520,18 +540,7 @@ void SymbolGraphSerializer::serializeEnumRecord(const 
EnumRecord &Record) {
     return;
 
   Symbols.emplace_back(std::move(*Enum));
-
-  for (const auto &Constant : Record.Constants) {
-    auto EnumConstantPathComponentGuard =
-        makePathComponentGuard(Constant->Name);
-    auto EnumConstant = serializeAPIRecord(*Constant);
-
-    if (!EnumConstant)
-      continue;
-
-    Symbols.emplace_back(std::move(*EnumConstant));
-    serializeRelationship(RelationshipKind::MemberOf, *Constant, Record);
-  }
+  serializeMembers(Record, Record.Constants);
 }
 
 void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
@@ -541,17 +550,7 @@ void SymbolGraphSerializer::serializeStructRecord(const 
StructRecord &Record) {
     return;
 
   Symbols.emplace_back(std::move(*Struct));
-
-  for (const auto &Field : Record.Fields) {
-    auto StructFieldPathComponentGuard = makePathComponentGuard(Field->Name);
-    auto StructField = serializeAPIRecord(*Field);
-
-    if (!StructField)
-      continue;
-
-    Symbols.emplace_back(std::move(*StructField));
-    serializeRelationship(RelationshipKind::MemberOf, *Field, Record);
-  }
+  serializeMembers(Record, Record.Fields);
 }
 
 void SymbolGraphSerializer::serializeObjCContainerRecord(
@@ -563,53 +562,33 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
 
   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 IvarPathComponentGuard = makePathComponentGuard(Ivar->Name);
-    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 MethodPathComponentGuard = makePathComponentGuard(Method->Name);
-    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 PropertyPathComponentGuard = makePathComponentGuard(Property->Name);
-    auto ObjCProperty = serializeAPIRecord(*Property);
-
-    if (!ObjCProperty)
-      continue;
-
-    Symbols.emplace_back(std::move(*ObjCProperty));
-    serializeRelationship(RelationshipKind::MemberOf, *Property, Record);
-  }
+  serializeMembers(Record, Record.Ivars);
+  serializeMembers(Record, Record.Methods);
+  serializeMembers(Record, Record.Properties);
 
   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 (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);
+
+    // Members of categories extending an interface are serialized as members 
of
+    // the interface.
+    for (const auto *Category : ObjCInterface->Categories) {
+      serializeMembers(Record, Category->Ivars);
+      serializeMembers(Record, Category->Methods);
+      serializeMembers(Record, Category->Properties);
+
+      // Surface the protocols of the the category to the interface.
+      for (const auto &Protocol : Category->Protocols)
+        serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
+    }
+  }
 }
 
 void SymbolGraphSerializer::serializeMacroDefinitionRecord(

diff  --git a/clang/test/ExtractAPI/objc_category.m 
b/clang/test/ExtractAPI/objc_category.m
new file mode 100644
index 0000000000000..bc572adfcd161
--- /dev/null
+++ b/clang/test/ExtractAPI/objc_category.m
@@ -0,0 +1,311 @@
+// 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;
+
+@interface Interface
+@end
+
+@interface Interface (Category) <Protocol>
+@property int Property;
+- (void)InstanceMethod;
++ (void)ClassMethod;
+@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"
+    }
+  },
+  "relationships": [
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(im)InstanceMethod",
+      "target": "c:objc(cs)Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(cm)ClassMethod",
+      "target": "c:objc(cs)Interface"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:objc(cs)Interface(py)Property",
+      "target": "c:objc(cs)Interface"
+    },
+    {
+      "kind": "conformsTo",
+      "source": "c:objc(cs)Interface",
+      "target": "c:objc(pl)Protocol"
+    }
+  ],
+  "symbols": [
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@interface"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Interface"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface"
+      },
+      "kind": {
+        "displayName": "Class",
+        "identifier": "objective-c.class"
+      },
+      "location": {
+        "position": {
+          "character": 12,
+          "line": 3
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Interface"
+          }
+        ],
+        "title": "Interface"
+      },
+      "pathComponents": [
+        "Interface"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "- ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "InstanceMethod"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(im)InstanceMethod"
+      },
+      "kind": {
+        "displayName": "Instance Method",
+        "identifier": "objective-c.method"
+      },
+      "location": {
+        "position": {
+          "character": 1,
+          "line": 8
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "- "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "InstanceMethod"
+          }
+        ],
+        "title": "InstanceMethod"
+      },
+      "pathComponents": [
+        "Interface",
+        "InstanceMethod"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "text",
+          "spelling": "+ ("
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:v",
+          "spelling": "void"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "ClassMethod"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(cm)ClassMethod"
+      },
+      "kind": {
+        "displayName": "Type Method",
+        "identifier": "objective-c.type.method"
+      },
+      "location": {
+        "position": {
+          "character": 1,
+          "line": 9
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "text",
+            "spelling": "+ "
+          },
+          {
+            "kind": "identifier",
+            "spelling": "ClassMethod"
+          }
+        ],
+        "title": "ClassMethod"
+      },
+      "pathComponents": [
+        "Interface",
+        "ClassMethod"
+      ]
+    },
+    {
+      "accessLevel": "public",
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "@property"
+        },
+        {
+          "kind": "text",
+          "spelling": " ("
+        },
+        {
+          "kind": "keyword",
+          "spelling": "atomic"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "assign"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "unsafe_unretained"
+        },
+        {
+          "kind": "text",
+          "spelling": ", "
+        },
+        {
+          "kind": "keyword",
+          "spelling": "readwrite"
+        },
+        {
+          "kind": "text",
+          "spelling": ")"
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:I",
+          "spelling": "int"
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Property"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "objective-c",
+        "precise": "c:objc(cs)Interface(py)Property"
+      },
+      "kind": {
+        "displayName": "Instance Property",
+        "identifier": "objective-c.property"
+      },
+      "location": {
+        "position": {
+          "character": 15,
+          "line": 7
+        },
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Property"
+          }
+        ],
+        "title": "Property"
+      },
+      "pathComponents": [
+        "Interface",
+        "Property"
+      ]
+    }
+  ]
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to