Skip to content

Commit 648d256

Browse files
authored
Merge pull request #38991 from mininny/diagnostic-for-non-objc-open-function-in-extension
[Diagnostics] Add diagnostic for non-objc functions declared as open inside extension
2 parents 37c4198 + f9e93a8 commit 648d256

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,9 @@ ERROR(access_control_extension_open,none,
15141514
ERROR(access_control_open_bad_decl,none,
15151515
"only classes and overridable class members can be declared 'open';"
15161516
" use 'public'", ())
1517+
WARNING(access_control_non_objc_open_member,none,
1518+
"non-'@objc' %0 in extensions cannot be overridden; use 'public' instead",
1519+
(DescriptiveDeclKind))
15171520

15181521
ERROR(invalid_decl_attribute,none,
15191522
"'%0' attribute cannot be applied to this declaration", (DeclAttribute))

lib/Sema/TypeCheckAttr.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,19 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) {
874874
extAttr->getAccess())
875875
.fixItRemove(attr->getRange());
876876
}
877+
} else {
878+
if (auto VD = dyn_cast<ValueDecl>(D)) {
879+
if (!isa<NominalTypeDecl>(VD)) {
880+
// Emit warning when trying to declare non-@objc `open` member inside
881+
// an extension.
882+
if (!VD->isObjC() && attr->getAccess() == AccessLevel::Open) {
883+
diagnose(attr->getLocation(),
884+
diag::access_control_non_objc_open_member,
885+
VD->getDescriptiveKind())
886+
.fixItReplace(attr->getRange(), "public");
887+
}
888+
}
889+
}
877890
}
878891
}
879892

test/attr/open_objc.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,48 @@
44

55
import Foundation
66

7+
open class InvalidOpenExtension {
8+
open func openMethod() { } // No warning: Pure Swift methods can be open inside the class declaration.
9+
open var openVar: Int { 3 } // No warning: Pure Swift properties can be open inside the class declaration.
10+
}
11+
extension InvalidOpenExtension {
12+
open func nonObjcOpenMethod() { } // expected-warning {{non-'@objc' instance method in extensions cannot be overridden; use 'public' instead}} {{3-7=public}}
13+
open class func nonObjcOpenMethod() { } // expected-warning {{non-'@objc' class method in extensions cannot be overridden; use 'public' instead}} {{3-7=public}}
14+
open var nonObjcOpenVar: Int { 3 } // expected-warning {{non-'@objc' property in extensions cannot be overridden; use 'public' instead}} {{3-7=public}}
15+
open subscript(_ index: Int) -> Int { 3 } // expected-warning {{non-'@objc' subscript in extensions cannot be overridden; use 'public' instead}} {{3-7=public}}
16+
open convenience init(index: Int) { self.init() } // expected-error {{only classes and overridable class members can be declared 'open'; use 'public'}} // expected-warning {{non-'@objc' initializer in extensions cannot be overridden; use 'public' instead}}
17+
18+
@objc open func objcOpenMethod() { } // No warning: @objc methods can be open inside extensions.
19+
@objc open var objcOpenVar: Int { 3 } // No warning: @objc methods can be open inside extensions.
20+
21+
open class nonObjcClassDecl { }
22+
open enum nonObjcEnumDecl { case one } // expected-error {{only classes and overridable class members can be declared 'open'; use 'public'}} {{3-7=public}}
23+
}
24+
25+
// For extensions with access level less than open, '`open` modifier conflicts with...' warning will always precede the 'non-@objc member...' warning.
26+
// For extensions with open access level, invalid open extension error will precede the 'non-@objc member...' warning.
27+
public extension InvalidOpenExtension {
28+
open func nonObjcOpenMethod2() { } // expected-warning {{'open' modifier conflicts with extension's default access of 'public'}}
29+
open var nonObjcOpenVar2: Int { 3 } // expected-warning {{'open' modifier conflicts with extension's default access of 'public'}}
30+
@objc open func objcOpenMethod2() { } // expected-warning {{'open' modifier conflicts with extension's default access of 'public'}}
31+
@objc open var objcOpenVar2: Int { 3 } // expected-warning {{'open' modifier conflicts with extension's default access of 'public'}}
32+
}
33+
34+
@objcMembers
35+
open class ValidOpenExtension { }
36+
extension ValidOpenExtension {
37+
open func objcOpenMethod() { } // No warning: This declaration is implicitly @objc
38+
open class func objcOpenMethod() { } // No warning: This declaration is implicitly @objc
39+
open var objcOpenVar: Int { 3 } // No warning: This declaration is implicitly @objc
40+
open subscript(_ index: Int) -> Int { 3 } // No warning: This declaration is implicitly @objc
41+
open convenience init(index: Int) { self.init() } // expected-error {{only classes and overridable class members can be declared 'open'; use 'public'}}
42+
}
43+
44+
public extension ValidOpenExtension {
45+
open func objcOpenMethod2() { } // expected-warning {{'open' modifier conflicts with extension's default access of 'public'}}
46+
open var objcOpenVar2: Int { 3 } // expected-warning {{'open' modifier conflicts with extension's default access of 'public'}}
47+
}
48+
749
class InvalidOpenExtensionClass { }
850

951
open extension InvalidOpenExtensionClass { // expected-error {{extensions cannot be declared 'open'; declare individual members as 'open' instead}} {{1-6=}} {{3-3=public }} {{9-9=open }} {{9-9=open }} {{3-3=public }}

0 commit comments

Comments
 (0)