Skip to content

Commit a270c87

Browse files
authored
Merge pull request #960 from apple/eng/PR-59332804-59782963
[objc_direct] also go through implementations when looking for clashes
2 parents bfdb4dc + 1f4c8d4 commit a270c87

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
@@ -4636,6 +4636,62 @@ static void checkObjCMethodX86VectorTypes(Sema &SemaRef,
46364636
<< (Triple.isMacOSX() ? "macOS 10.11" : "iOS 9");
46374637
}
46384638

4639+
static void mergeObjCDirectMembers(Sema &S, Decl *CD, ObjCMethodDecl *Method) {
4640+
if (!Method->isDirectMethod() && !Method->hasAttr<UnavailableAttr>() &&
4641+
CD->hasAttr<ObjCDirectMembersAttr>()) {
4642+
Method->addAttr(
4643+
ObjCDirectAttr::CreateImplicit(S.Context, Method->getLocation()));
4644+
}
4645+
}
4646+
4647+
static void checkObjCDirectMethodClashes(Sema &S, ObjCInterfaceDecl *IDecl,
4648+
ObjCMethodDecl *Method,
4649+
ObjCImplDecl *ImpDecl = nullptr) {
4650+
auto Sel = Method->getSelector();
4651+
bool isInstance = Method->isInstanceMethod();
4652+
bool diagnosed = false;
4653+
4654+
auto diagClash = [&](const ObjCMethodDecl *IMD) {
4655+
if (diagnosed || IMD->isImplicit())
4656+
return;
4657+
if (Method->isDirectMethod() || IMD->isDirectMethod()) {
4658+
S.Diag(Method->getLocation(), diag::err_objc_direct_duplicate_decl)
4659+
<< Method->isDirectMethod() << /* method */ 0 << IMD->isDirectMethod()
4660+
<< Method->getDeclName();
4661+
S.Diag(IMD->getLocation(), diag::note_previous_declaration);
4662+
diagnosed = true;
4663+
}
4664+
};
4665+
4666+
// Look for any other declaration of this method anywhere we can see in this
4667+
// compilation unit.
4668+
//
4669+
// We do not use IDecl->lookupMethod() because we have specific needs:
4670+
//
4671+
// - we absolutely do not need to walk protocols, because
4672+
// diag::err_objc_direct_on_protocol has already been emitted
4673+
// during parsing if there's a conflict,
4674+
//
4675+
// - when we do not find a match in a given @interface container,
4676+
// we need to attempt looking it up in the @implementation block if the
4677+
// translation unit sees it to find more clashes.
4678+
4679+
if (auto *IMD = IDecl->getMethod(Sel, isInstance))
4680+
diagClash(IMD);
4681+
else if (auto *Impl = IDecl->getImplementation())
4682+
if (Impl != ImpDecl)
4683+
if (auto *IMD = IDecl->getImplementation()->getMethod(Sel, isInstance))
4684+
diagClash(IMD);
4685+
4686+
for (const auto *Cat : IDecl->visible_categories())
4687+
if (auto *IMD = Cat->getMethod(Sel, isInstance))
4688+
diagClash(IMD);
4689+
else if (auto CatImpl = Cat->getImplementation())
4690+
if (CatImpl != ImpDecl)
4691+
if (auto *IMD = Cat->getMethod(Sel, isInstance))
4692+
diagClash(IMD);
4693+
}
4694+
46394695
Decl *Sema::ActOnMethodDeclaration(
46404696
Scope *S, SourceLocation MethodLoc, SourceLocation EndLoc,
46414697
tok::TokenKind MethodType, ObjCDeclSpec &ReturnQT, ParsedType ReturnType,
@@ -4866,9 +4922,9 @@ Decl *Sema::ActOnMethodDeclaration(
48664922
Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
48674923
<< ObjCMethod->getDeclName();
48684924
}
4869-
} else if (ImpDecl->hasAttr<ObjCDirectMembersAttr>()) {
4870-
ObjCMethod->addAttr(
4871-
ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
4925+
} else {
4926+
mergeObjCDirectMembers(*this, ClassDecl, ObjCMethod);
4927+
checkObjCDirectMethodClashes(*this, IDecl, ObjCMethod, ImpDecl);
48724928
}
48734929

48744930
// Warn if a method declared in a protocol to which a category or
@@ -4889,39 +4945,16 @@ Decl *Sema::ActOnMethodDeclaration(
48894945
}
48904946
} else {
48914947
if (!isa<ObjCProtocolDecl>(ClassDecl)) {
4892-
if (!ObjCMethod->isDirectMethod() &&
4893-
ClassDecl->hasAttr<ObjCDirectMembersAttr>()) {
4894-
ObjCMethod->addAttr(
4895-
ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
4896-
}
4948+
mergeObjCDirectMembers(*this, ClassDecl, ObjCMethod);
48974949

4898-
// There can be a single declaration in any @interface container
4899-
// for a given direct method, look for clashes as we add them.
4900-
//
4901-
// For valid code, we should always know the primary interface
4902-
// declaration by now, however for invalid code we'll keep parsing
4903-
// but we won't find the primary interface and IDecl will be nil.
49044950
ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(ClassDecl);
49054951
if (!IDecl)
49064952
IDecl = cast<ObjCCategoryDecl>(ClassDecl)->getClassInterface();
4907-
4953+
// For valid code, we should always know the primary interface
4954+
// declaration by now, however for invalid code we'll keep parsing
4955+
// but we won't find the primary interface and IDecl will be nil.
49084956
if (IDecl)
4909-
if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
4910-
ObjCMethod->isInstanceMethod(),
4911-
/*shallowCategoryLookup=*/false,
4912-
/*followSuper=*/false)) {
4913-
if (isa<ObjCProtocolDecl>(IMD->getDeclContext())) {
4914-
// Do not emit a diagnostic for the Protocol case:
4915-
// diag::err_objc_direct_on_protocol has already been emitted
4916-
// during parsing for these with a nicer diagnostic.
4917-
} else if (ObjCMethod->isDirectMethod() || IMD->isDirectMethod()) {
4918-
Diag(ObjCMethod->getLocation(),
4919-
diag::err_objc_direct_duplicate_decl)
4920-
<< ObjCMethod->isDirectMethod() << /* method */ 0
4921-
<< IMD->isDirectMethod() << ObjCMethod->getDeclName();
4922-
Diag(IMD->getLocation(), diag::note_previous_declaration);
4923-
}
4924-
}
4957+
checkObjCDirectMethodClashes(*this, IDecl, ObjCMethod);
49254958
}
49264959

49274960
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)