Skip to content

Commit 20d704a

Browse files
committed
[objc_direct] also go through implementations when looking for clashes
Some methods are sometimes declared in the @implementation blocks which can cause undiagnosed clashes. Just write a checkObjCDirectMethodClashes() for this purpose. Also make sure that "unavailable" selectors do not inherit objc_direct_members. Differential Revision: https://reviews.llvm.org/D76643 Signed-off-by: Pierre Habouzit <[email protected]> Radar-ID: rdar://problem/59332804, rdar://problem/59782963
1 parent dab219e commit 20d704a

File tree

3 files changed

+79
-31
lines changed

3 files changed

+79
-31
lines changed

clang/lib/Sema/SemaDeclObjC.cpp

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4581,6 +4581,62 @@ static void checkObjCMethodX86VectorTypes(Sema &SemaRef,
45814581
<< (Triple.isMacOSX() ? "macOS 10.11" : "iOS 9");
45824582
}
45834583

4584+
static void mergeObjCDirectMembers(Sema &S, Decl *CD, ObjCMethodDecl *Method) {
4585+
if (!Method->isDirectMethod() && !Method->hasAttr<UnavailableAttr>() &&
4586+
CD->hasAttr<ObjCDirectMembersAttr>()) {
4587+
Method->addAttr(
4588+
ObjCDirectAttr::CreateImplicit(S.Context, Method->getLocation()));
4589+
}
4590+
}
4591+
4592+
static void checkObjCDirectMethodClashes(Sema &S, ObjCInterfaceDecl *IDecl,
4593+
ObjCMethodDecl *Method,
4594+
ObjCImplDecl *ImpDecl = nullptr) {
4595+
auto Sel = Method->getSelector();
4596+
bool isInstance = Method->isInstanceMethod();
4597+
bool diagnosed = false;
4598+
4599+
auto diagClash = [&](const ObjCMethodDecl *IMD) {
4600+
if (diagnosed || IMD->isImplicit())
4601+
return;
4602+
if (Method->isDirectMethod() || IMD->isDirectMethod()) {
4603+
S.Diag(Method->getLocation(), diag::err_objc_direct_duplicate_decl)
4604+
<< Method->isDirectMethod() << /* method */ 0 << IMD->isDirectMethod()
4605+
<< Method->getDeclName();
4606+
S.Diag(IMD->getLocation(), diag::note_previous_declaration);
4607+
diagnosed = true;
4608+
}
4609+
};
4610+
4611+
// Look for any other declaration of this method anywhere we can see in this
4612+
// compilation unit.
4613+
//
4614+
// We do not use IDecl->lookupMethod() because we have specific needs:
4615+
//
4616+
// - we absolutely do not need to walk protocols, because
4617+
// diag::err_objc_direct_on_protocol has already been emitted
4618+
// during parsing if there's a conflict,
4619+
//
4620+
// - when we do not find a match in a given @interface container,
4621+
// we need to attempt looking it up in the @implementation block if the
4622+
// translation unit sees it to find more clashes.
4623+
4624+
if (auto *IMD = IDecl->getMethod(Sel, isInstance))
4625+
diagClash(IMD);
4626+
else if (auto *Impl = IDecl->getImplementation())
4627+
if (Impl != ImpDecl)
4628+
if (auto *IMD = IDecl->getImplementation()->getMethod(Sel, isInstance))
4629+
diagClash(IMD);
4630+
4631+
for (const auto *Cat : IDecl->visible_categories())
4632+
if (auto *IMD = Cat->getMethod(Sel, isInstance))
4633+
diagClash(IMD);
4634+
else if (auto CatImpl = Cat->getImplementation())
4635+
if (CatImpl != ImpDecl)
4636+
if (auto *IMD = Cat->getMethod(Sel, isInstance))
4637+
diagClash(IMD);
4638+
}
4639+
45844640
Decl *Sema::ActOnMethodDeclaration(
45854641
Scope *S, SourceLocation MethodLoc, SourceLocation EndLoc,
45864642
tok::TokenKind MethodType, ObjCDeclSpec &ReturnQT, ParsedType ReturnType,
@@ -4809,9 +4865,9 @@ Decl *Sema::ActOnMethodDeclaration(
48094865
Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
48104866
<< ObjCMethod->getDeclName();
48114867
}
4812-
} else if (ImpDecl->hasAttr<ObjCDirectMembersAttr>()) {
4813-
ObjCMethod->addAttr(
4814-
ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
4868+
} else {
4869+
mergeObjCDirectMembers(*this, ClassDecl, ObjCMethod);
4870+
checkObjCDirectMethodClashes(*this, IDecl, ObjCMethod, ImpDecl);
48154871
}
48164872

48174873
// Warn if a method declared in a protocol to which a category or
@@ -4832,39 +4888,16 @@ Decl *Sema::ActOnMethodDeclaration(
48324888
}
48334889
} else {
48344890
if (!isa<ObjCProtocolDecl>(ClassDecl)) {
4835-
if (!ObjCMethod->isDirectMethod() &&
4836-
ClassDecl->hasAttr<ObjCDirectMembersAttr>()) {
4837-
ObjCMethod->addAttr(
4838-
ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
4839-
}
4891+
mergeObjCDirectMembers(*this, ClassDecl, ObjCMethod);
48404892

4841-
// There can be a single declaration in any @interface container
4842-
// for a given direct method, look for clashes as we add them.
4843-
//
4844-
// For valid code, we should always know the primary interface
4845-
// declaration by now, however for invalid code we'll keep parsing
4846-
// but we won't find the primary interface and IDecl will be nil.
48474893
ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(ClassDecl);
48484894
if (!IDecl)
48494895
IDecl = cast<ObjCCategoryDecl>(ClassDecl)->getClassInterface();
4850-
4896+
// For valid code, we should always know the primary interface
4897+
// declaration by now, however for invalid code we'll keep parsing
4898+
// but we won't find the primary interface and IDecl will be nil.
48514899
if (IDecl)
4852-
if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
4853-
ObjCMethod->isInstanceMethod(),
4854-
/*shallowCategoryLookup=*/false,
4855-
/*followSuper=*/false)) {
4856-
if (isa<ObjCProtocolDecl>(IMD->getDeclContext())) {
4857-
// Do not emit a diagnostic for the Protocol case:
4858-
// diag::err_objc_direct_on_protocol has already been emitted
4859-
// during parsing for these with a nicer diagnostic.
4860-
} else if (ObjCMethod->isDirectMethod() || IMD->isDirectMethod()) {
4861-
Diag(ObjCMethod->getLocation(),
4862-
diag::err_objc_direct_duplicate_decl)
4863-
<< ObjCMethod->isDirectMethod() << /* method */ 0
4864-
<< IMD->isDirectMethod() << ObjCMethod->getDeclName();
4865-
Diag(IMD->getLocation(), diag::note_previous_declaration);
4866-
}
4867-
}
4900+
checkObjCDirectMethodClashes(*this, IDecl, ObjCMethod);
48684901
}
48694902

48704903
cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);

clang/test/SemaObjC/method-direct-one-definition.m

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ @interface B (OtherCat)
3030
- (void)B_OtherCat __attribute__((objc_direct)); // expected-note {{previous declaration is here}}
3131
@end
3232

33+
@implementation B
34+
- (void)B_primary {
35+
}
36+
- (void)B_extension {
37+
}
38+
- (void)B_implOnly __attribute__((objc_direct)) { // expected-note {{previous declaration is here}}
39+
}
40+
@end
41+
3342
@implementation B (Cat)
3443
- (void)B_primary { // expected-error {{direct method was declared in the primary interface but is implemented in a category}}
3544
}
@@ -39,6 +48,8 @@ - (void)B_Cat {
3948
}
4049
- (void)B_OtherCat { // expected-error {{direct method was declared in a category but is implemented in a different category}}
4150
}
51+
- (void)B_implOnly __attribute__((objc_direct)) { // expected-error {{direct method declaration conflicts with previous direct declaration of method 'B_implOnly'}}
52+
}
4253
@end
4354

4455
__attribute__((objc_root_class))

clang/test/SemaObjC/method-direct.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ + (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{'objc
1212

1313
__attribute__((objc_root_class))
1414
@interface Root
15+
- (void)unavailableInChild;
1516
- (void)rootRegular; // expected-note {{previous declaration is here}}
1617
+ (void)classRootRegular; // expected-note {{previous declaration is here}}
1718
- (void)rootDirect __attribute__((objc_direct)); // expected-note {{previous declaration is here}};
@@ -52,6 +53,7 @@ + (void)classRootCategoryDirect2 __attribute__((objc_direct)); // expected-note
5253
__attribute__((objc_direct_members))
5354
@interface SubDirectMembers : Root
5455
@property int foo; // expected-note {{previous declaration is here}}
56+
- (void)unavailableInChild __attribute__((unavailable)); // should not warn
5557
- (instancetype)init;
5658
@end
5759

@@ -81,6 +83,8 @@ + (void)classRootCategoryDirect2; // expected-error {{cannot override a method
8183

8284
__attribute__((objc_direct_members))
8385
@implementation Root
86+
- (void)unavailableInChild {
87+
}
8488
- (void)rootRegular {
8589
}
8690
+ (void)classRootRegular {

0 commit comments

Comments
 (0)