Skip to content

Commit d1d34ba

Browse files
committed
[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
1 parent 122638d commit d1d34ba

File tree

7 files changed

+277
-0
lines changed

7 files changed

+277
-0
lines changed

clang/include/clang/ExtractAPI/API.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct APIRecord {
8282
RK_ObjCIvar,
8383
RK_ObjCMethod,
8484
RK_ObjCInterface,
85+
RK_ObjCProtocol,
8586
};
8687

8788
private:
@@ -354,6 +355,25 @@ struct ObjCInterfaceRecord : ObjCContainerRecord {
354355
virtual void anchor();
355356
};
356357

358+
/// This holds information associated with Objective-C protocols.
359+
struct ObjCProtocolRecord : ObjCContainerRecord {
360+
ObjCProtocolRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
361+
const AvailabilityInfo &Availability,
362+
const DocComment &Comment,
363+
DeclarationFragments Declaration,
364+
DeclarationFragments SubHeading)
365+
: ObjCContainerRecord(RK_ObjCProtocol, Name, USR, Loc, Availability,
366+
LinkageInfo::none(), Comment, Declaration,
367+
SubHeading) {}
368+
369+
static bool classof(const APIRecord *Record) {
370+
return Record->getKind() == RK_ObjCProtocol;
371+
}
372+
373+
private:
374+
virtual void anchor();
375+
};
376+
357377
/// APISet holds the set of API records collected from given inputs.
358378
class APISet {
359379
public:
@@ -497,6 +517,19 @@ class APISet {
497517
DeclarationFragments SubHeading,
498518
ObjCInstanceVariableRecord::AccessControl Access);
499519

520+
/// Create and add an Objective-C protocol record into the API set.
521+
///
522+
/// Note: the caller is responsible for keeping the StringRef \p Name and
523+
/// \p USR alive. APISet::copyString provides a way to copy strings into
524+
/// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
525+
/// to generate the USR for \c D and keep it alive in APISet.
526+
ObjCProtocolRecord *addObjCProtocol(StringRef Name, StringRef USR,
527+
PresumedLoc Loc,
528+
const AvailabilityInfo &Availability,
529+
const DocComment &Comment,
530+
DeclarationFragments Declaration,
531+
DeclarationFragments SubHeading);
532+
500533
/// A map to store the set of GlobalRecord%s with the declaration name as the
501534
/// key.
502535
using GlobalRecordMap =
@@ -516,6 +549,11 @@ class APISet {
516549
using ObjCInterfaceRecordMap =
517550
llvm::MapVector<StringRef, std::unique_ptr<ObjCInterfaceRecord>>;
518551

552+
/// A map to store the set of ObjCProtocolRecord%s with the declaration name
553+
/// as the key.
554+
using ObjCProtocolRecordMap =
555+
llvm::MapVector<StringRef, std::unique_ptr<ObjCProtocolRecord>>;
556+
519557
/// Get the target triple for the ExtractAPI invocation.
520558
const llvm::Triple &getTarget() const { return Target; }
521559

@@ -528,6 +566,9 @@ class APISet {
528566
const ObjCInterfaceRecordMap &getObjCInterfaces() const {
529567
return ObjCInterfaces;
530568
}
569+
const ObjCProtocolRecordMap &getObjCProtocols() const {
570+
return ObjCProtocols;
571+
}
531572

532573
/// Generate and store the USR of declaration \p D.
533574
///
@@ -557,6 +598,7 @@ class APISet {
557598
EnumRecordMap Enums;
558599
StructRecordMap Structs;
559600
ObjCInterfaceRecordMap ObjCInterfaces;
601+
ObjCProtocolRecordMap ObjCProtocols;
560602
};
561603

562604
} // namespace extractapi

clang/include/clang/ExtractAPI/DeclarationFragments.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ class DeclarationFragmentsBuilder {
217217
static DeclarationFragments
218218
getFragmentsForObjCProperty(const ObjCPropertyDecl *);
219219

220+
/// Build DeclarationFragments for an Objective-C protocol declaration
221+
/// ObjCProtocolDecl.
222+
static DeclarationFragments
223+
getFragmentsForObjCProtocol(const ObjCProtocolDecl *);
224+
220225
/// Build sub-heading fragments for a NamedDecl.
221226
static DeclarationFragments getSubHeading(const NamedDecl *);
222227

clang/lib/ExtractAPI/API.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,20 @@ ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
161161
return Container->Ivars.emplace_back(std::move(Record)).get();
162162
}
163163

164+
ObjCProtocolRecord *APISet::addObjCProtocol(
165+
StringRef Name, StringRef USR, PresumedLoc Loc,
166+
const AvailabilityInfo &Availability, const DocComment &Comment,
167+
DeclarationFragments Declaration, DeclarationFragments SubHeading) {
168+
auto Result = ObjCProtocols.insert({Name, nullptr});
169+
if (Result.second) {
170+
// Create the record if it does not already exist.
171+
auto Record = std::make_unique<ObjCProtocolRecord>(
172+
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
173+
Result.first->second = std::move(Record);
174+
}
175+
return Result.first->second.get();
176+
}
177+
164178
StringRef APISet::recordUSR(const Decl *D) {
165179
SmallString<128> USR;
166180
index::generateUSRForDecl(D, USR);
@@ -193,3 +207,4 @@ void ObjCPropertyRecord::anchor() {}
193207
void ObjCInstanceVariableRecord::anchor() {}
194208
void ObjCMethodRecord::anchor() {}
195209
void ObjCInterfaceRecord::anchor() {}
210+
void ObjCProtocolRecord::anchor() {}

clang/lib/ExtractAPI/DeclarationFragments.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,35 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
621621
.append(std::move(After));
622622
}
623623

624+
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
625+
const ObjCProtocolDecl *Protocol) {
626+
DeclarationFragments Fragments;
627+
// Build basic protocol declaration.
628+
Fragments.append("@protocol", DeclarationFragments::FragmentKind::Keyword)
629+
.appendSpace()
630+
.append(Protocol->getName(),
631+
DeclarationFragments::FragmentKind::Identifier);
632+
633+
// If this protocol conforms to other protocols, build the conformance list.
634+
if (!Protocol->protocols().empty()) {
635+
Fragments.append(" <", DeclarationFragments::FragmentKind::Text);
636+
for (ObjCProtocolDecl::protocol_iterator It = Protocol->protocol_begin();
637+
It != Protocol->protocol_end(); It++) {
638+
// Add a leading comma if this is not the first protocol rendered.
639+
if (It != Protocol->protocol_begin())
640+
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
641+
642+
SmallString<128> USR;
643+
index::generateUSRForDecl(*It, USR);
644+
Fragments.append((*It)->getName(),
645+
DeclarationFragments::FragmentKind::TypeIdentifier, USR);
646+
}
647+
Fragments.append(">", DeclarationFragments::FragmentKind::Text);
648+
}
649+
650+
return Fragments;
651+
}
652+
624653
template <typename FunctionT>
625654
FunctionSignature
626655
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {

clang/lib/ExtractAPI/ExtractAPIConsumer.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,38 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
260260
return true;
261261
}
262262

263+
bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
264+
// Skip forward declaration for protocols (@protocol).
265+
if (!Decl->isThisDeclarationADefinition())
266+
return true;
267+
268+
// Collect symbol information.
269+
StringRef Name = Decl->getName();
270+
StringRef USR = API.recordUSR(Decl);
271+
PresumedLoc Loc =
272+
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
273+
AvailabilityInfo Availability = getAvailability(Decl);
274+
DocComment Comment;
275+
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
276+
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
277+
Context.getDiagnostics());
278+
279+
// Build declaration fragments and sub-heading for the protocol.
280+
DeclarationFragments Declaration =
281+
DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
282+
DeclarationFragments SubHeading =
283+
DeclarationFragmentsBuilder::getSubHeading(Decl);
284+
285+
ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
286+
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
287+
288+
recordObjCMethods(ObjCProtocolRecord, Decl->methods());
289+
recordObjCProperties(ObjCProtocolRecord, Decl->properties());
290+
recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
291+
292+
return true;
293+
}
294+
263295
private:
264296
/// Get availability information of the declaration \p D.
265297
AvailabilityInfo getAvailability(const Decl *D) const {

clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
393393
Kind["identifier"] = AddLangPrefix("class");
394394
Kind["displayName"] = "Class";
395395
break;
396+
case APIRecord::RK_ObjCProtocol:
397+
Kind["identifier"] = AddLangPrefix("protocol");
398+
Kind["displayName"] = "Protocol";
399+
break;
396400
}
397401

398402
return Kind;
@@ -593,6 +597,10 @@ Object SymbolGraphSerializer::serialize() {
593597
for (const auto &ObjCInterface : API.getObjCInterfaces())
594598
serializeObjCContainerRecord(*ObjCInterface.second);
595599

600+
// Serialize Objective-C protocol records in the API set.
601+
for (const auto &ObjCProtocol : API.getObjCProtocols())
602+
serializeObjCContainerRecord(*ObjCProtocol.second);
603+
596604
Root["symbols"] = std::move(Symbols);
597605
Root["relationhips"] = std::move(Relationships);
598606

clang/test/ExtractAPI/objc_protocol.m

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \
4+
// RUN: %t/reference.output.json
5+
// RUN: %clang -extract-api -x objective-c-header -target arm64-apple-macosx \
6+
// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
7+
8+
// Generator version is not consistent across test runs, normalize it.
9+
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
10+
// RUN: %t/output.json >> %t/output-normalized.json
11+
// RUN: diff %t/reference.output.json %t/output-normalized.json
12+
13+
// CHECK-NOT: error:
14+
// CHECK-NOT: warning:
15+
16+
//--- input.h
17+
@protocol Protocol
18+
@end
19+
20+
@protocol AnotherProtocol <Protocol>
21+
@end
22+
23+
//--- reference.output.json.in
24+
{
25+
"metadata": {
26+
"formatVersion": {
27+
"major": 0,
28+
"minor": 5,
29+
"patch": 3
30+
},
31+
"generator": "?"
32+
},
33+
"module": {
34+
"name": "",
35+
"platform": {
36+
"architecture": "arm64",
37+
"operatingSystem": {
38+
"minimumVersion": {
39+
"major": 11,
40+
"minor": 0,
41+
"patch": 0
42+
},
43+
"name": "macosx"
44+
},
45+
"vendor": "apple"
46+
}
47+
},
48+
"relationhips": [
49+
{
50+
"kind": "conformsTo",
51+
"source": "c:objc(pl)AnotherProtocol",
52+
"target": "c:objc(pl)Protocol"
53+
}
54+
],
55+
"symbols": [
56+
{
57+
"declarationFragments": [
58+
{
59+
"kind": "keyword",
60+
"spelling": "@protocol"
61+
},
62+
{
63+
"kind": "text",
64+
"spelling": " "
65+
},
66+
{
67+
"kind": "identifier",
68+
"spelling": "Protocol"
69+
}
70+
],
71+
"identifier": {
72+
"interfaceLanguage": "objective-c",
73+
"precise": "c:objc(pl)Protocol"
74+
},
75+
"kind": {
76+
"displayName": "Protocol",
77+
"identifier": "objective-c.protocol"
78+
},
79+
"location": {
80+
"character": 11,
81+
"line": 1,
82+
"uri": "file://INPUT_DIR/input.h"
83+
},
84+
"names": {
85+
"subHeading": [
86+
{
87+
"kind": "identifier",
88+
"spelling": "Protocol"
89+
}
90+
],
91+
"title": "Protocol"
92+
}
93+
},
94+
{
95+
"declarationFragments": [
96+
{
97+
"kind": "keyword",
98+
"spelling": "@protocol"
99+
},
100+
{
101+
"kind": "text",
102+
"spelling": " "
103+
},
104+
{
105+
"kind": "identifier",
106+
"spelling": "AnotherProtocol"
107+
},
108+
{
109+
"kind": "text",
110+
"spelling": " <"
111+
},
112+
{
113+
"kind": "typeIdentifier",
114+
"preciseIdentifier": "c:objc(pl)Protocol",
115+
"spelling": "Protocol"
116+
},
117+
{
118+
"kind": "text",
119+
"spelling": ">"
120+
}
121+
],
122+
"identifier": {
123+
"interfaceLanguage": "objective-c",
124+
"precise": "c:objc(pl)AnotherProtocol"
125+
},
126+
"kind": {
127+
"displayName": "Protocol",
128+
"identifier": "objective-c.protocol"
129+
},
130+
"location": {
131+
"character": 11,
132+
"line": 4,
133+
"uri": "file://INPUT_DIR/input.h"
134+
},
135+
"names": {
136+
"subHeading": [
137+
{
138+
"kind": "identifier",
139+
"spelling": "AnotherProtocol"
140+
}
141+
],
142+
"title": "AnotherProtocol"
143+
}
144+
}
145+
]
146+
}

0 commit comments

Comments
 (0)