[clang] 178aad9 - [clang][extract-api] Add Objective-C Category support
Zixu Wang via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 6 12:03:33 PDT 2022
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 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 Interface
+ at end
+
+ at interface Interface (Category) <Protocol>
+ at property int Property;
+- (void)InstanceMethod;
++ (void)ClassMethod;
+ 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"
+ }
+ },
+ "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"
+ ]
+ }
+ ]
+}
More information about the cfe-commits
mailing list