Skip to content

Commit db226cd

Browse files
committed
[objc] diagnose protocol conformance in categories with direct members
in their corresponding class interfaces Categories that add protocol conformances to classes with direct members should prohibit protocol conformances when the methods/properties that the protocol expects are actually declared as 'direct' in the class. Differential Revision: https://reviews.llvm.org/D92602
1 parent eddd1d1 commit db226cd

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,10 @@ def warn_objc_direct_property_ignored : Warning<
10621062
InGroup<IgnoredAttributes>;
10631063
def err_objc_direct_dynamic_property : Error<
10641064
"direct property cannot be @dynamic">;
1065+
def err_objc_direct_protocol_conformance : Error<
1066+
"%select{category %1|class extension}0 cannot conform to protocol %2 because "
1067+
"of direct members declared in interface %3">;
1068+
def note_direct_member_here : Note<"direct member declared here">;
10651069

10661070
def warn_conflicting_overriding_ret_types : Warning<
10671071
"conflicting return type in "

clang/lib/Sema/SemaDeclObjC.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3912,6 +3912,55 @@ static void DiagnoseVariableSizedIvars(Sema &S, ObjCContainerDecl *OCD) {
39123912
}
39133913
}
39143914

3915+
static void DiagnoseCategoryDirectMembersProtocolConformance(
3916+
Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl);
3917+
3918+
static void DiagnoseCategoryDirectMembersProtocolConformance(
3919+
Sema &S, ObjCCategoryDecl *CDecl,
3920+
const llvm::iterator_range<ObjCProtocolList::iterator> &Protocols) {
3921+
for (auto *PI : Protocols)
3922+
DiagnoseCategoryDirectMembersProtocolConformance(S, PI, CDecl);
3923+
}
3924+
3925+
static void DiagnoseCategoryDirectMembersProtocolConformance(
3926+
Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl) {
3927+
if (!PDecl->isThisDeclarationADefinition() && PDecl->getDefinition())
3928+
PDecl = PDecl->getDefinition();
3929+
3930+
llvm::SmallVector<const Decl *, 4> DirectMembers;
3931+
const auto *IDecl = CDecl->getClassInterface();
3932+
for (auto *MD : PDecl->methods()) {
3933+
if (!MD->isPropertyAccessor()) {
3934+
if (const auto *CMD =
3935+
IDecl->getMethod(MD->getSelector(), MD->isInstanceMethod())) {
3936+
if (CMD->isDirectMethod())
3937+
DirectMembers.push_back(CMD);
3938+
}
3939+
}
3940+
}
3941+
for (auto *PD : PDecl->properties()) {
3942+
if (const auto *CPD = IDecl->FindPropertyVisibleInPrimaryClass(
3943+
PD->getIdentifier(),
3944+
PD->isClassProperty()
3945+
? ObjCPropertyQueryKind::OBJC_PR_query_class
3946+
: ObjCPropertyQueryKind::OBJC_PR_query_instance)) {
3947+
if (CPD->isDirectProperty())
3948+
DirectMembers.push_back(CPD);
3949+
}
3950+
}
3951+
if (!DirectMembers.empty()) {
3952+
S.Diag(CDecl->getLocation(), diag::err_objc_direct_protocol_conformance)
3953+
<< CDecl->IsClassExtension() << CDecl << PDecl << IDecl;
3954+
for (const auto *MD : DirectMembers)
3955+
S.Diag(MD->getLocation(), diag::note_direct_member_here);
3956+
return;
3957+
}
3958+
3959+
// Check on this protocols's referenced protocols, recursively.
3960+
DiagnoseCategoryDirectMembersProtocolConformance(S, CDecl,
3961+
PDecl->protocols());
3962+
}
3963+
39153964
// Note: For class/category implementations, allMethods is always null.
39163965
Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
39173966
ArrayRef<DeclGroupPtrTy> allTUVars) {
@@ -4012,6 +4061,8 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
40124061
ObjCInterfaceDecl *CCPrimary = C->getClassInterface();
40134062
DiagnoseClassExtensionDupMethods(C, CCPrimary);
40144063
}
4064+
4065+
DiagnoseCategoryDirectMembersProtocolConformance(*this, C, C->protocols());
40154066
}
40164067
if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(ClassDecl)) {
40174068
if (CDecl->getIdentifier())
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
__attribute__((objc_root_class))
4+
@interface RootClass
5+
6+
- (void)baseMethod;
7+
8+
@end
9+
10+
__attribute__((objc_direct_members))
11+
@interface I : RootClass
12+
13+
- (void)direct; // expected-note {{direct member declared here}}
14+
15+
@end
16+
17+
@protocol P
18+
- (void)direct;
19+
@end
20+
21+
@interface I (Cat1) <P> // expected-error {{category 'Cat1' cannot conform to protocol 'P' because of direct members declared in interface 'I'}}
22+
@end
23+
24+
@protocol BaseP
25+
- (void)baseMethod;
26+
@end
27+
28+
@interface I (CatBase) <BaseP> // OK
29+
@end
30+
31+
@protocol P2
32+
- (void)indirect;
33+
@end
34+
35+
@interface I (Cat2) <P2> // OK
36+
- (void)indirect;
37+
@end
38+
39+
@protocol P3
40+
- (void)indirect3;
41+
@end
42+
43+
@interface I (Cat3) <P3> // OK
44+
@end
45+
46+
@interface ExpDirect : RootClass
47+
48+
- (void)direct __attribute__((objc_direct)); // expected-note {{direct member declared here}}
49+
50+
- (void)directRecursive __attribute__((objc_direct)); // expected-note {{direct member declared here}}
51+
52+
@end
53+
54+
@interface ExpDirect (CatExpDirect) <P> // expected-error {{category 'CatExpDirect' cannot conform to protocol 'P' because of direct members declared in interface 'ExpDirect'}}
55+
@end
56+
57+
@protocol PRecursive1
58+
- (void)directRecursive;
59+
@end
60+
61+
@protocol PRecursiveTop <PRecursive1>
62+
@end
63+
64+
@interface ExpDirect () <PRecursiveTop> // expected-error {{class extension cannot conform to protocol 'PRecursive1' because of direct members declared in interface 'ExpDirect'}}
65+
@end
66+
67+
68+
@protocol PProp
69+
70+
@property (nonatomic, readonly) I *name;
71+
72+
@end
73+
74+
__attribute__((objc_direct_members))
75+
@interface IProp1 : RootClass
76+
77+
@property (nonatomic, readonly) I *name; // expected-note {{direct member declared here}}
78+
79+
@end
80+
81+
@interface IProp1 () <PProp> // expected-error {{class extension cannot conform to protocol 'PProp' because of direct members declared in interface 'IProp1'}}
82+
@end
83+
84+
85+
@protocol PProp2
86+
87+
@property (nonatomic, readonly, class) I *name;
88+
89+
@end
90+
91+
@interface IProp2 : RootClass
92+
93+
@property (nonatomic, readonly, class, direct) I *name; // expected-note {{direct member declared here}}
94+
95+
@end
96+
97+
@interface IProp2 () <PProp2> // expected-error {{class extension cannot conform to protocol 'PProp2' because of direct members declared in interface 'IProp2'}}
98+
@end

0 commit comments

Comments
 (0)