Skip to content

Commit 06949a4

Browse files
committed
Check protocols for selector conflicts
Although we have always checked classes to see if their @objc members had the same selectors, it turns out we never did this for protocols. Oops. Keep a table of ObjC selector names for protocols, just as we do for classes, and diagnose any conflicts between them. Fixes rdar://80990066.
1 parent 974fbc1 commit 06949a4

File tree

7 files changed

+22
-8
lines changed

7 files changed

+22
-8
lines changed

lib/AST/NameLookup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1487,7 +1487,7 @@ bool NominalTypeDecl::createObjCMethodLookup() {
14871487
assert(!ObjCMethodLookup && "Already have an Objective-C member table");
14881488

14891489
// Most types cannot have ObjC methods.
1490-
if (!(isa<ClassDecl>(this)))
1490+
if (!(isa<ClassDecl>(this) || isa<ProtocolDecl>(this)))
14911491
return false;
14921492

14931493
auto &ctx = getASTContext();

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,6 +2447,7 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
24472447
// Diagnose each conflict.
24482448
bool anyConflicts = false;
24492449
for (const auto &conflict : localConflicts) {
2450+
NominalTypeDecl *tyDecl = std::get<0>(conflict);
24502451
ObjCSelector selector = std::get<1>(conflict);
24512452

24522453
auto methods = getObjCMethodConflictDecls(conflict);
@@ -2530,6 +2531,9 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
25302531
origDiagInfo.first, origDiagInfo.second,
25312532
selector);
25322533

2534+
// Protocols weren't checked for selector conflicts in 5.0.
2535+
diag.warnUntilSwiftVersionIf(!isa<ClassDecl>(tyDecl), 6);
2536+
25332537
auto objcAttr = getObjCAttrIfFromAccessNote(conflictingDecl);
25342538
swift::softenIfAccessNote(conflictingDecl, objcAttr, diag);
25352539
if (objcAttr)

test/ClangImporter/objc_bridging_custom.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,9 @@ protocol TestProto {
159159
@objc optional func testUnmigrated(a: NSRuncingMode, b: Refrigerator, c: NSCoding) // expected-note {{here}} {{none}}
160160
@objc optional func testPartialMigrated(a: NSRuncingMode, b: Refrigerator) // expected-note {{here}} {{none}}
161161

162-
@objc optional subscript(a a: Refrigerator) -> Refrigerator? { get } // expected-note {{here}} {{none}}
162+
@objc optional subscript(a a: Refrigerator) -> Refrigerator? { get } // expected-note 2 {{here}} {{none}}
163163
@objc optional subscript(generic a: ManufacturerInfo<NSString>) -> ManufacturerInfo<NSString>? { get } // expected-note {{here}} {{none}}
164+
// expected-warning@-1 {{subscript getter with Objective-C selector 'objectForKeyedSubscript:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}}
164165

165166
@objc optional var prop: Refrigerator? { get } // expected-note {{here}} {{none}}
166167
@objc optional var propGeneric: ManufacturerInfo<NSString>? { get } // expected-note {{here}} {{none}}

test/Constraints/common_type_objc.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import Foundation
77

88
@objc protocol P {
9-
func foo(_ i: Int) -> Double
10-
func foo(_ d: Double) -> Double
9+
func foo(_ i: Int) -> Double // expected-note {{'foo' previously declared here}}
10+
func foo(_ d: Double) -> Double // expected-warning {{method 'foo' with Objective-C selector 'foo:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}}
1111

12-
@objc optional func opt(_ i: Int) -> Int
13-
@objc optional func opt(_ d: Double) -> Int
12+
@objc optional func opt(_ i: Int) -> Int // expected-note {{'opt' previously declared here}}
13+
@objc optional func opt(_ d: Double) -> Int // expected-warning {{method 'opt' with Objective-C selector 'opt:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}}
1414
}
1515

1616
func testOptional(obj: P) {

test/Constraints/overload_filtering_objc.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import Foundation
1010

1111
@objc protocol P {
12-
func foo(_ i: Int) -> Int
13-
func foo(_ d: Double) -> Int
12+
func foo(_ i: Int) -> Int // expected-note {{'foo' previously declared here}}
13+
func foo(_ d: Double) -> Int // expected-warning {{method 'foo' with Objective-C selector 'foo:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}}
1414

1515
@objc optional func opt(_ i: Int) -> Int
1616
@objc optional func opt(double: Double) -> Int

test/attr/attr_objc.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,11 @@ protocol subject_containerObjCProtocol1 {
364364
@objc // access-note-move{{subject_containerObjCProtocol2}}
365365
protocol subject_containerObjCProtocol2 {
366366
init(a: Int)
367+
// expected-note@-1 {{'init' previously declared here}}
367368

368369
@objc // FIXME: Access notes can't distinguish between init(a:) overloads
369370
init(a: Double)
371+
// expected-warning@-1 {{initializer 'init(a:)' with Objective-C selector 'initWithA:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}}
370372

371373
func func1() -> Int
372374
@objc // access-note-move{{subject_containerObjCProtocol2.func1_()}}

test/decl/protocol/objc.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,10 @@ class C8SubRW: C8Base {
258258
class C8SubRW2: C8Base {
259259
var prop: Int = 0
260260
}
261+
262+
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
263+
@objc protocol P9 {
264+
@objc(custom:) func f(_: Any) // expected-note 2 {{method 'f' declared here}}
265+
@objc(custom:) func g(_: Any) // expected-warning {{method 'g' with Objective-C selector 'custom:' conflicts with method 'f' with the same Objective-C selector; this is an error in Swift 6}}
266+
@objc(custom:) func h() async // expected-warning {{method 'h()' with Objective-C selector 'custom:' conflicts with method 'f' with the same Objective-C selector; this is an error in Swift 6}}
267+
}

0 commit comments

Comments
 (0)