[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