Skip to content

Commit 0a34ab2

Browse files
committed
Diagnose objcImpl members with wrong name
1 parent 369f77b commit 0a34ab2

File tree

4 files changed

+79
-4
lines changed

4 files changed

+79
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,15 @@ ERROR(objc_implementation_wrong_category,none,
16161616
"%select{main class interface|category %3}3",
16171617
(DescriptiveDeclKind, ValueDecl *, Identifier, Identifier))
16181618

1619+
ERROR(objc_implementation_wrong_objc_name,none,
1620+
"selector %0 for %1 %2 not found in header; did you mean %3?",
1621+
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))
1622+
1623+
ERROR(objc_implementation_wrong_swift_name,none,
1624+
"selector %0 used in header by an %1 with a different name; did you "
1625+
"mean %2?",
1626+
(ObjCSelector, DescriptiveDeclKind, DeclName))
1627+
16191628
ERROR(cdecl_not_at_top_level,none,
16201629
"@_cdecl can only be applied to global functions", ())
16211630
ERROR(cdecl_empty_name,none,

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,7 @@ static void checkObjCImplementationMemberAvoidsVTable(ValueDecl *VD) {
12531253

12541254
auto isNotRelevantInterfaceMember = [&](ValueDecl *member) -> bool {
12551255
return !member->hasClangNode()
1256+
|| member->getAttrs().isUnavailable(VD->getASTContext())
12561257
|| VD->isInstanceMember() != member->isInstanceMember();
12571258
};
12581259
assert(!VD->hasClangNode() &&
@@ -1338,6 +1339,32 @@ static void checkObjCImplementationMemberAvoidsVTable(ValueDecl *VD) {
13381339
return;
13391340
}
13401341

1342+
auto selectedMethod = *selectedMethodIter;
1343+
// If the Swift names don't match, suggest correcting the implementation
1344+
if (selectedMethod->getName() != VD->getName()) {
1345+
auto diag = diags.diagnose(VD, diag::objc_implementation_wrong_swift_name,
1346+
*selectedMethod->getObjCRuntimeName(),
1347+
selectedMethod->getDescriptiveKind(),
1348+
selectedMethod->getName());
1349+
fixDeclarationName(diag, VD, selectedMethod->getName());
1350+
}
1351+
// If there was no @objc, add one with the implementation's @objc name.
1352+
else if (!objcAttr) {
1353+
objcAttr = ObjCAttr::create(VD->getASTContext(),
1354+
selectedMethod->getObjCRuntimeName(),
1355+
true);
1356+
VD->getAttrs().add(objcAttr);
1357+
}
1358+
// If there was an @objc with the wrong name, diagnose.
1359+
else if (objcAttr->hasName()
1360+
&& objcAttr->getName() != selectedMethod->getObjCRuntimeName()) {
1361+
auto diag = diags.diagnose(VD, diag::objc_implementation_wrong_objc_name,
1362+
*objcAttr->getName(), VD->getDescriptiveKind(),
1363+
VD, *selectedMethod->getObjCRuntimeName());
1364+
fixDeclarationObjCName(diag, VD, objcAttr->getName(),
1365+
selectedMethod->getObjCRuntimeName());
1366+
}
1367+
13411368
assert(VD->isObjC());
13421369
assert(isa<DestructorDecl>(VD) || VD->isDynamic() &&
13431370
"@objc decls in @_objcImplementations should be dynamic!");

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@
1111
@end
1212

1313
@interface ObjCClass : ObjCBaseClass
14+
1415
- (void)methodFromHeader1:(int)param;
1516
- (void)methodFromHeader2:(int)param;
1617
- (void)methodFromHeader3:(int)param;
1718
- (void)methodFromHeader4:(int)param;
1819

19-
// FIXME: test case involving swift_name
20-
2120
@property int propertyFromHeader1;
2221
@property int propertyFromHeader2;
2322
@property int propertyFromHeader3;
@@ -38,20 +37,31 @@
3837
@end
3938

4039
@interface ObjCClass (PresentAdditions)
40+
4141
- (void)categoryMethodFromHeader1:(int)param;
4242
- (void)categoryMethodFromHeader2:(int)param;
4343
- (void)categoryMethodFromHeader3:(int)param;
4444
- (void)categoryMethodFromHeader4:(int)param;
4545

46-
// FIXME: test case involving swift_name
47-
4846
@property int categoryPropertyFromHeader1;
4947
@property int categoryPropertyFromHeader2;
5048
@property int categoryPropertyFromHeader3;
5149
@property int categoryPropertyFromHeader4;
5250

5351
@end
5452

53+
@interface ObjCClass (SwiftNameTests)
54+
55+
- (void)methodObjCName1 __attribute__((swift_name("methodSwiftName1()")));
56+
- (void)methodObjCName2 __attribute__((swift_name("methodSwiftName2()")));
57+
- (void)methodObjCName3 __attribute__((swift_name("methodSwiftName3()")));
58+
- (void)methodObjCName4 __attribute__((swift_name("methodSwiftName4()")));
59+
- (void)methodObjCName5 __attribute__((swift_name("methodSwiftName5()")));
60+
- (void)methodObjCName6A __attribute__((swift_name("methodSwiftName6A()")));
61+
- (void)methodObjCName6B __attribute__((swift_name("methodSwiftName6B()")));
62+
63+
@end
64+
5565
@interface ObjCSubclass : ObjCClass
5666

5767
- (void)subclassMethodFromHeader1:(int)param;

test/decl/ext/objc_implementation.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,35 @@
178178
}
179179
}
180180

181+
@_objcImplementation(SwiftNameTests) extension ObjCClass {
182+
func methodSwiftName1() {
183+
// OK, infers `@objc(methodObjCName1)`
184+
}
185+
186+
@objc(methodObjCName2) func methodSwiftName2() {
187+
// OK
188+
}
189+
190+
func methodObjCName3() {
191+
// expected-error@-1 {{instance method 'methodObjCName3()' does not match any instance method declared in the headers for 'ObjCClass'; did you use the instance method's Swift name?}}
192+
// expected-note@-2 {{add 'private' or 'fileprivate' to define an Objective-C-compatible instance method not declared in the header}}
193+
// expected-note@-3 {{add 'final' to define a Swift instance method that cannot be overridden}}
194+
// FIXME: provide a specialized fix-it for this situation
195+
}
196+
197+
@objc(methodWrongObjCName4) func methodSwiftName4() {
198+
// expected-error@-1 {{selector 'methodWrongObjCName4' for instance method 'methodSwiftName4()' not found in header; did you mean 'methodObjCName4'?}} {{9-29=methodObjCName4}}
199+
}
200+
201+
@objc(methodObjCName5) func methodWrongSwiftName5() {
202+
// expected-error@-1 {{selector 'methodObjCName5' used in header by an instance method with a different name; did you mean 'methodSwiftName5()'?}} {{31-52=methodSwiftName5}}
203+
}
204+
205+
@objc(methodObjCName6A) func methodSwiftName6B() {
206+
// expected-error@-1 {{selector 'methodObjCName6A' used in header by an instance method with a different name; did you mean 'methodSwiftName6A()'?}} {{32-49=methodSwiftName6A}}
207+
}
208+
}
209+
181210
@_objcImplementation extension ObjCClass {}
182211
// expected-error@-1 {{duplicate implementation of Objective-C class 'ObjCClass'}}
183212

0 commit comments

Comments
 (0)