Skip to content

Commit e7c91b2

Browse files
committed
Diagnose objcImpl members with wrong name
1 parent 3212d2e commit e7c91b2

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
@@ -1601,6 +1601,15 @@ ERROR(objc_implementation_wrong_category,none,
16011601
"%select{main class interface|category %3}3",
16021602
(DescriptiveDeclKind, ValueDecl *, Identifier, Identifier))
16031603

1604+
ERROR(objc_implementation_wrong_objc_name,none,
1605+
"selector %0 for %1 %2 not found in header; did you mean %3?",
1606+
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))
1607+
1608+
ERROR(objc_implementation_wrong_swift_name,none,
1609+
"selector %0 used in header by an %1 with a different name; did you "
1610+
"mean %2?",
1611+
(ObjCSelector, DescriptiveDeclKind, DeclName))
1612+
16041613
ERROR(cdecl_not_at_top_level,none,
16051614
"@_cdecl can only be applied to global functions", ())
16061615
ERROR(cdecl_empty_name,none,

lib/Sema/TypeCheckDeclPrimary.cpp

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

12461246
auto isNotRelevantInterfaceMember = [&](ValueDecl *member) -> bool {
12471247
return !member->hasClangNode()
1248+
|| member->getAttrs().isUnavailable(VD->getASTContext())
12481249
|| VD->isInstanceMember() != member->isInstanceMember();
12491250
};
12501251
assert(!VD->hasClangNode() &&
@@ -1330,6 +1331,32 @@ static void checkObjCImplementationMemberAvoidsVTable(ValueDecl *VD) {
13301331
return;
13311332
}
13321333

1334+
auto selectedMethod = *selectedMethodIter;
1335+
// If the Swift names don't match, suggest correcting the implementation
1336+
if (selectedMethod->getName() != VD->getName()) {
1337+
auto diag = diags.diagnose(VD, diag::objc_implementation_wrong_swift_name,
1338+
*selectedMethod->getObjCRuntimeName(),
1339+
selectedMethod->getDescriptiveKind(),
1340+
selectedMethod->getName());
1341+
fixDeclarationName(diag, VD, selectedMethod->getName());
1342+
}
1343+
// If there was no @objc, add one with the implementation's @objc name.
1344+
else if (!objcAttr) {
1345+
objcAttr = ObjCAttr::create(VD->getASTContext(),
1346+
selectedMethod->getObjCRuntimeName(),
1347+
true);
1348+
VD->getAttrs().add(objcAttr);
1349+
}
1350+
// If there was an @objc with the wrong name, diagnose.
1351+
else if (objcAttr->hasName()
1352+
&& objcAttr->getName() != selectedMethod->getObjCRuntimeName()) {
1353+
auto diag = diags.diagnose(VD, diag::objc_implementation_wrong_objc_name,
1354+
*objcAttr->getName(), VD->getDescriptiveKind(),
1355+
VD, *selectedMethod->getObjCRuntimeName());
1356+
fixDeclarationObjCName(diag, VD, objcAttr->getName(),
1357+
selectedMethod->getObjCRuntimeName());
1358+
}
1359+
13331360
assert(VD->isObjC());
13341361
assert(isa<DestructorDecl>(VD) || VD->isDynamic() &&
13351362
"@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)