forked from OSchip/llvm-project
[clang][extract-api] Add Objective-C protocol support
Add support for Objective-C protocol declarations in ExtractAPI. Depends on D122446 Differential Revision: https://reviews.llvm.org/D122511
This commit is contained in:
parent
122638d97d
commit
d1d34bafef
clang
include/clang/ExtractAPI
lib/ExtractAPI
test/ExtractAPI
|
@ -82,6 +82,7 @@ struct APIRecord {
|
|||
RK_ObjCIvar,
|
||||
RK_ObjCMethod,
|
||||
RK_ObjCInterface,
|
||||
RK_ObjCProtocol,
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -354,6 +355,25 @@ private:
|
|||
virtual void anchor();
|
||||
};
|
||||
|
||||
/// This holds information associated with Objective-C protocols.
|
||||
struct ObjCProtocolRecord : ObjCContainerRecord {
|
||||
ObjCProtocolRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
|
||||
const AvailabilityInfo &Availability,
|
||||
const DocComment &Comment,
|
||||
DeclarationFragments Declaration,
|
||||
DeclarationFragments SubHeading)
|
||||
: ObjCContainerRecord(RK_ObjCProtocol, Name, USR, Loc, Availability,
|
||||
LinkageInfo::none(), Comment, Declaration,
|
||||
SubHeading) {}
|
||||
|
||||
static bool classof(const APIRecord *Record) {
|
||||
return Record->getKind() == RK_ObjCProtocol;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void anchor();
|
||||
};
|
||||
|
||||
/// APISet holds the set of API records collected from given inputs.
|
||||
class APISet {
|
||||
public:
|
||||
|
@ -497,6 +517,19 @@ public:
|
|||
DeclarationFragments SubHeading,
|
||||
ObjCInstanceVariableRecord::AccessControl Access);
|
||||
|
||||
/// Create and add an Objective-C protocol 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.
|
||||
ObjCProtocolRecord *addObjCProtocol(StringRef Name, StringRef USR,
|
||||
PresumedLoc Loc,
|
||||
const AvailabilityInfo &Availability,
|
||||
const DocComment &Comment,
|
||||
DeclarationFragments Declaration,
|
||||
DeclarationFragments SubHeading);
|
||||
|
||||
/// A map to store the set of GlobalRecord%s with the declaration name as the
|
||||
/// key.
|
||||
using GlobalRecordMap =
|
||||
|
@ -516,6 +549,11 @@ public:
|
|||
using ObjCInterfaceRecordMap =
|
||||
llvm::MapVector<StringRef, std::unique_ptr<ObjCInterfaceRecord>>;
|
||||
|
||||
/// A map to store the set of ObjCProtocolRecord%s with the declaration name
|
||||
/// as the key.
|
||||
using ObjCProtocolRecordMap =
|
||||
llvm::MapVector<StringRef, std::unique_ptr<ObjCProtocolRecord>>;
|
||||
|
||||
/// Get the target triple for the ExtractAPI invocation.
|
||||
const llvm::Triple &getTarget() const { return Target; }
|
||||
|
||||
|
@ -528,6 +566,9 @@ public:
|
|||
const ObjCInterfaceRecordMap &getObjCInterfaces() const {
|
||||
return ObjCInterfaces;
|
||||
}
|
||||
const ObjCProtocolRecordMap &getObjCProtocols() const {
|
||||
return ObjCProtocols;
|
||||
}
|
||||
|
||||
/// Generate and store the USR of declaration \p D.
|
||||
///
|
||||
|
@ -557,6 +598,7 @@ private:
|
|||
EnumRecordMap Enums;
|
||||
StructRecordMap Structs;
|
||||
ObjCInterfaceRecordMap ObjCInterfaces;
|
||||
ObjCProtocolRecordMap ObjCProtocols;
|
||||
};
|
||||
|
||||
} // namespace extractapi
|
||||
|
|
|
@ -217,6 +217,11 @@ public:
|
|||
static DeclarationFragments
|
||||
getFragmentsForObjCProperty(const ObjCPropertyDecl *);
|
||||
|
||||
/// Build DeclarationFragments for an Objective-C protocol declaration
|
||||
/// ObjCProtocolDecl.
|
||||
static DeclarationFragments
|
||||
getFragmentsForObjCProtocol(const ObjCProtocolDecl *);
|
||||
|
||||
/// Build sub-heading fragments for a NamedDecl.
|
||||
static DeclarationFragments getSubHeading(const NamedDecl *);
|
||||
|
||||
|
|
|
@ -161,6 +161,20 @@ ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
|
|||
return Container->Ivars.emplace_back(std::move(Record)).get();
|
||||
}
|
||||
|
||||
ObjCProtocolRecord *APISet::addObjCProtocol(
|
||||
StringRef Name, StringRef USR, PresumedLoc Loc,
|
||||
const AvailabilityInfo &Availability, const DocComment &Comment,
|
||||
DeclarationFragments Declaration, DeclarationFragments SubHeading) {
|
||||
auto Result = ObjCProtocols.insert({Name, nullptr});
|
||||
if (Result.second) {
|
||||
// Create the record if it does not already exist.
|
||||
auto Record = std::make_unique<ObjCProtocolRecord>(
|
||||
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
|
||||
Result.first->second = std::move(Record);
|
||||
}
|
||||
return Result.first->second.get();
|
||||
}
|
||||
|
||||
StringRef APISet::recordUSR(const Decl *D) {
|
||||
SmallString<128> USR;
|
||||
index::generateUSRForDecl(D, USR);
|
||||
|
@ -193,3 +207,4 @@ void ObjCPropertyRecord::anchor() {}
|
|||
void ObjCInstanceVariableRecord::anchor() {}
|
||||
void ObjCMethodRecord::anchor() {}
|
||||
void ObjCInterfaceRecord::anchor() {}
|
||||
void ObjCProtocolRecord::anchor() {}
|
||||
|
|
|
@ -621,6 +621,35 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
|
|||
.append(std::move(After));
|
||||
}
|
||||
|
||||
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
|
||||
const ObjCProtocolDecl *Protocol) {
|
||||
DeclarationFragments Fragments;
|
||||
// Build basic protocol declaration.
|
||||
Fragments.append("@protocol", DeclarationFragments::FragmentKind::Keyword)
|
||||
.appendSpace()
|
||||
.append(Protocol->getName(),
|
||||
DeclarationFragments::FragmentKind::Identifier);
|
||||
|
||||
// If this protocol conforms to other protocols, build the conformance list.
|
||||
if (!Protocol->protocols().empty()) {
|
||||
Fragments.append(" <", DeclarationFragments::FragmentKind::Text);
|
||||
for (ObjCProtocolDecl::protocol_iterator It = Protocol->protocol_begin();
|
||||
It != Protocol->protocol_end(); It++) {
|
||||
// Add a leading comma if this is not the first protocol rendered.
|
||||
if (It != Protocol->protocol_begin())
|
||||
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
|
||||
|
||||
SmallString<128> USR;
|
||||
index::generateUSRForDecl(*It, USR);
|
||||
Fragments.append((*It)->getName(),
|
||||
DeclarationFragments::FragmentKind::TypeIdentifier, USR);
|
||||
}
|
||||
Fragments.append(">", DeclarationFragments::FragmentKind::Text);
|
||||
}
|
||||
|
||||
return Fragments;
|
||||
}
|
||||
|
||||
template <typename FunctionT>
|
||||
FunctionSignature
|
||||
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
|
||||
|
|
|
@ -260,6 +260,38 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
|
||||
// Skip forward declaration for protocols (@protocol).
|
||||
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);
|
||||
DocComment Comment;
|
||||
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
|
||||
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
|
||||
Context.getDiagnostics());
|
||||
|
||||
// Build declaration fragments and sub-heading for the protocol.
|
||||
DeclarationFragments Declaration =
|
||||
DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
|
||||
DeclarationFragments SubHeading =
|
||||
DeclarationFragmentsBuilder::getSubHeading(Decl);
|
||||
|
||||
ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
|
||||
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
|
||||
|
||||
recordObjCMethods(ObjCProtocolRecord, Decl->methods());
|
||||
recordObjCProperties(ObjCProtocolRecord, Decl->properties());
|
||||
recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Get availability information of the declaration \p D.
|
||||
AvailabilityInfo getAvailability(const Decl *D) const {
|
||||
|
|
|
@ -393,6 +393,10 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
|
|||
Kind["identifier"] = AddLangPrefix("class");
|
||||
Kind["displayName"] = "Class";
|
||||
break;
|
||||
case APIRecord::RK_ObjCProtocol:
|
||||
Kind["identifier"] = AddLangPrefix("protocol");
|
||||
Kind["displayName"] = "Protocol";
|
||||
break;
|
||||
}
|
||||
|
||||
return Kind;
|
||||
|
@ -593,6 +597,10 @@ Object SymbolGraphSerializer::serialize() {
|
|||
for (const auto &ObjCInterface : API.getObjCInterfaces())
|
||||
serializeObjCContainerRecord(*ObjCInterface.second);
|
||||
|
||||
// Serialize Objective-C protocol records in the API set.
|
||||
for (const auto &ObjCProtocol : API.getObjCProtocols())
|
||||
serializeObjCContainerRecord(*ObjCProtocol.second);
|
||||
|
||||
Root["symbols"] = std::move(Symbols);
|
||||
Root["relationhips"] = std::move(Relationships);
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
// RUN: rm -rf %t
|
||||
// RUN: split-file %s %t
|
||||
// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \
|
||||
// RUN: %t/reference.output.json
|
||||
// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
|
||||
// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
|
||||
|
||||
// Generator version is not consistent across test runs, normalize it.
|
||||
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
|
||||
// RUN: %t/output.json >> %t/output-normalized.json
|
||||
// RUN: diff %t/reference.output.json %t/output-normalized.json
|
||||
|
||||
// CHECK-NOT: error:
|
||||
// CHECK-NOT: warning:
|
||||
|
||||
//--- input.h
|
||||
@protocol Protocol
|
||||
@end
|
||||
|
||||
@protocol AnotherProtocol <Protocol>
|
||||
@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": "conformsTo",
|
||||
"source": "c:objc(pl)AnotherProtocol",
|
||||
"target": "c:objc(pl)Protocol"
|
||||
}
|
||||
],
|
||||
"symbols": [
|
||||
{
|
||||
"declarationFragments": [
|
||||
{
|
||||
"kind": "keyword",
|
||||
"spelling": "@protocol"
|
||||
},
|
||||
{
|
||||
"kind": "text",
|
||||
"spelling": " "
|
||||
},
|
||||
{
|
||||
"kind": "identifier",
|
||||
"spelling": "Protocol"
|
||||
}
|
||||
],
|
||||
"identifier": {
|
||||
"interfaceLanguage": "objective-c",
|
||||
"precise": "c:objc(pl)Protocol"
|
||||
},
|
||||
"kind": {
|
||||
"displayName": "Protocol",
|
||||
"identifier": "objective-c.protocol"
|
||||
},
|
||||
"location": {
|
||||
"character": 11,
|
||||
"line": 1,
|
||||
"uri": "file://INPUT_DIR/input.h"
|
||||
},
|
||||
"names": {
|
||||
"subHeading": [
|
||||
{
|
||||
"kind": "identifier",
|
||||
"spelling": "Protocol"
|
||||
}
|
||||
],
|
||||
"title": "Protocol"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declarationFragments": [
|
||||
{
|
||||
"kind": "keyword",
|
||||
"spelling": "@protocol"
|
||||
},
|
||||
{
|
||||
"kind": "text",
|
||||
"spelling": " "
|
||||
},
|
||||
{
|
||||
"kind": "identifier",
|
||||
"spelling": "AnotherProtocol"
|
||||
},
|
||||
{
|
||||
"kind": "text",
|
||||
"spelling": " <"
|
||||
},
|
||||
{
|
||||
"kind": "typeIdentifier",
|
||||
"preciseIdentifier": "c:objc(pl)Protocol",
|
||||
"spelling": "Protocol"
|
||||
},
|
||||
{
|
||||
"kind": "text",
|
||||
"spelling": ">"
|
||||
}
|
||||
],
|
||||
"identifier": {
|
||||
"interfaceLanguage": "objective-c",
|
||||
"precise": "c:objc(pl)AnotherProtocol"
|
||||
},
|
||||
"kind": {
|
||||
"displayName": "Protocol",
|
||||
"identifier": "objective-c.protocol"
|
||||
},
|
||||
"location": {
|
||||
"character": 11,
|
||||
"line": 4,
|
||||
"uri": "file://INPUT_DIR/input.h"
|
||||
},
|
||||
"names": {
|
||||
"subHeading": [
|
||||
{
|
||||
"kind": "identifier",
|
||||
"spelling": "AnotherProtocol"
|
||||
}
|
||||
],
|
||||
"title": "AnotherProtocol"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue