Skip to content

Commit 7dd1c87

Browse files
committed
[SE-0160] Warn about uses of @objc declarations that used deprecated @objc inference.
When in Swift 3 compatibility mode without `-warn-swift3-objc-inference`, warn on the *uses* of declarations that depend on the Objective-C runtime that became `@objc` due to the deprecated inference rule. This far more directly captures important uses of the deprecated Objective-C entrypoints. We diagnose: * `#selector` expressions that refer to one of these `@objc` members * `#keyPath` expressions that refer to one of these `@objc` members * Dynamic lookup (i.e., member access via `AnyObject`) that refers to one of these `@objc` members.
1 parent 2c1981f commit 7dd1c87

15 files changed

+152
-19
lines changed

include/swift/AST/Attr.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,12 @@ class DeclAttribute : public AttributeBase {
197197

198198
/// Whether the name is implicit, produced as the result of caching.
199199
unsigned ImplicitName : 1;
200+
201+
/// Whether the @objc was inferred using Swift 3's deprecated inference
202+
/// rules.
203+
unsigned Swift3Inferred : 1;
200204
};
201-
enum { NumObjCAttrBits = NumDeclAttrBits + 2 };
205+
enum { NumObjCAttrBits = NumDeclAttrBits + 3 };
202206
static_assert(NumObjCAttrBits <= 32, "fits in an unsigned");
203207

204208
class AccessibilityAttrBitFields {
@@ -716,6 +720,7 @@ class ObjCAttr final : public DeclAttribute,
716720
{
717721
ObjCAttrBits.HasTrailingLocationInfo = false;
718722
ObjCAttrBits.ImplicitName = implicitName;
723+
ObjCAttrBits.Swift3Inferred = false;
719724

720725
if (name) {
721726
NameData = name->getOpaqueValue();
@@ -823,6 +828,18 @@ class ObjCAttr final : public DeclAttribute,
823828
ObjCAttrBits.ImplicitName = implicit;
824829
}
825830

831+
/// Determine whether this attribute was inferred based on Swift 3's
832+
/// deprecated @objc inference rules.
833+
bool isSwift3Inferred() const {
834+
return ObjCAttrBits.Swift3Inferred;
835+
}
836+
837+
/// Set whether this attribute was inferred based on Swift 3's deprecated
838+
/// @objc inference rules.
839+
void setSwift3Inferred(bool inferred = true) {
840+
ObjCAttrBits.Swift3Inferred = inferred;
841+
}
842+
826843
/// Clear the name of this entity.
827844
void clearName() {
828845
NameData = nullptr;

include/swift/AST/DiagnosticsSema.def

+13
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,10 @@ ERROR(expression_unused_keypath_result,none,
415415
ERROR(expr_keypath_non_objc_property,none,
416416
"argument of '#keyPath' refers to non-'@objc' property %0",
417417
(DeclName))
418+
WARNING(expr_keypath_swift3_objc_inference,none,
419+
"argument of '#keyPath' refers to property %0 in %1 that depends on "
420+
"'@objc' attribute inference deprecated in Swift 4",
421+
(DeclName, Identifier))
418422
ERROR(stdlib_anyobject_not_found,none,
419423
"broken standard library: cannot find 'AnyObject' protocol", ())
420424
ERROR(expr_keypath_type_of_property,none,
@@ -463,6 +467,10 @@ ERROR(expr_selector_not_objc,none,
463467
NOTE(make_decl_objc,none,
464468
"add '@objc' to expose this %0 to Objective-C",
465469
(DescriptiveDeclKind))
470+
WARNING(expr_selector_swift3_objc_inference,none,
471+
"argument of '#selector' refers to %0 %1 in %2 that depends on "
472+
"'@objc' attribute inference deprecated in Swift 4",
473+
(DescriptiveDeclKind, DeclName, Identifier))
466474

467475
// Selectors-as-string-literals.
468476
WARNING(selector_literal_invalid,none,
@@ -1111,6 +1119,11 @@ WARNING(unsupported_synthesize_init_variadic,none,
11111119
NOTE(variadic_superclass_init_here,none,
11121120
"variadic superclass initializer defined here", ())
11131121

1122+
WARNING(expr_dynamic_lookup_swift3_objc_inference,none,
1123+
"reference to %0 %1 of %2 depends on '@objc' attribute inference "
1124+
"deprecated in Swift 4",
1125+
(DescriptiveDeclKind, DeclName, Identifier))
1126+
11141127
// Alignment attribute
11151128
ERROR(alignment_not_power_of_two,none,
11161129
"alignment value must be a power of two", ())

lib/AST/Attr.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ ObjCAttr::ObjCAttr(SourceLoc atLoc, SourceRange baseRange,
624624
}
625625

626626
ObjCAttrBits.ImplicitName = false;
627+
ObjCAttrBits.Swift3Inferred = false;
627628
}
628629

629630
ObjCAttr *ObjCAttr::create(ASTContext &Ctx, Optional<ObjCSelector> name,
@@ -700,7 +701,9 @@ SourceLoc ObjCAttr::getRParenLoc() const {
700701
}
701702

702703
ObjCAttr *ObjCAttr::clone(ASTContext &context) const {
703-
return new (context) ObjCAttr(getName(), isNameImplicit());
704+
auto attr = new (context) ObjCAttr(getName(), isNameImplicit());
705+
attr->setSwift3Inferred(isSwift3Inferred());
706+
return attr;
704707
}
705708

706709
AvailableAttr *

lib/Sema/CSApply.cpp

+40
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,25 @@ namespace {
910910

911911
closeExistential(ref, locator, /*force=*/openedExistential);
912912

913+
// If this attribute was inferred based on deprecated Swift 3 rules,
914+
// complain.
915+
if (auto attr = member->getAttrs().getAttribute<ObjCAttr>()) {
916+
if (attr->isSwift3Inferred() &&
917+
!tc.Context.LangOpts.WarnSwift3ObjCInference) {
918+
tc.diagnose(memberLoc,
919+
diag::expr_dynamic_lookup_swift3_objc_inference,
920+
member->getDescriptiveKind(),
921+
member->getFullName(),
922+
member->getDeclContext()
923+
->getAsNominalTypeOrNominalTypeExtensionContext()
924+
->getName());
925+
tc.diagnose(member, diag::make_decl_objc,
926+
member->getDescriptiveKind())
927+
.fixItInsert(member->getAttributeInsertionLoc(false),
928+
"@objc ");
929+
}
930+
}
931+
913932
return ref;
914933
}
915934

@@ -3772,6 +3791,11 @@ namespace {
37723791
return E;
37733792
}
37743793

3794+
if (cs.getType(subExpr)->isLValueType()) {
3795+
// Treat this like a read of the property.
3796+
cs.propagateLValueAccessKind(subExpr, AccessKind::Read);
3797+
}
3798+
37753799
// Check that we requested a property getter or setter.
37763800
switch (E->getSelectorKind()) {
37773801
case ObjCSelectorExpr::Method: {
@@ -3856,6 +3880,22 @@ namespace {
38563880
.fixItInsert(foundDecl->getAttributeInsertionLoc(false),
38573881
"@objc ");
38583882
return E;
3883+
} else if (auto attr = foundDecl->getAttrs().getAttribute<ObjCAttr>()) {
3884+
// If this attribute was inferred based on deprecated Swift 3 rules,
3885+
// complain.
3886+
if (attr->isSwift3Inferred() &&
3887+
!tc.Context.LangOpts.WarnSwift3ObjCInference) {
3888+
tc.diagnose(E->getLoc(), diag::expr_selector_swift3_objc_inference,
3889+
foundDecl->getDescriptiveKind(), foundDecl->getFullName(),
3890+
foundDecl->getDeclContext()
3891+
->getAsNominalTypeOrNominalTypeExtensionContext()
3892+
->getName())
3893+
.highlight(subExpr->getSourceRange());
3894+
tc.diagnose(foundDecl, diag::make_decl_objc,
3895+
foundDecl->getDescriptiveKind())
3896+
.fixItInsert(foundDecl->getAttributeInsertionLoc(false),
3897+
"@objc ");
3898+
}
38593899
}
38603900

38613901
// Note which method we're referencing.

lib/Sema/TypeCheckDecl.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -2771,7 +2771,17 @@ void swift::markAsObjC(TypeChecker &TC, ValueDecl *D,
27712771
TC.diagnose(D, diag::objc_inference_swift3, *deprecatedReason)
27722772
.highlight(dynamicLoc)
27732773
.fixItInsert(D->getAttributeInsertionLoc(false), "@objc ");
2774-
};
2774+
}
2775+
2776+
// Mark the attribute as having used Swift 3 inference, or create an
2777+
// implicit @objc for that purpose.
2778+
auto attr = D->getAttrs().getAttribute<ObjCAttr>();
2779+
if (!attr) {
2780+
attr = ObjCAttr::createUnnamedImplicit(TC.Context);
2781+
D->getAttrs().add(attr);
2782+
}
2783+
2784+
attr->setSwift3Inferred();
27752785
}
27762786
}
27772787

lib/Sema/TypeCheckExprObjC.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,20 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc,
302302
.fixItInsert(var->getAttributeInsertionLoc(false),
303303
"@objc ");
304304
}
305+
} else if (auto attr = var->getAttrs().getAttribute<ObjCAttr>()) {
306+
// If this attribute was inferred based on deprecated Swift 3 rules,
307+
// complain.
308+
if (attr->isSwift3Inferred() &&
309+
!Context.LangOpts.WarnSwift3ObjCInference) {
310+
diagnose(componentNameLoc, diag::expr_keypath_swift3_objc_inference,
311+
var->getFullName(),
312+
var->getDeclContext()
313+
->getAsNominalTypeOrNominalTypeExtensionContext()
314+
->getName());
315+
diagnose(var, diag::make_decl_objc, var->getDescriptiveKind())
316+
.fixItInsert(var->getAttributeInsertionLoc(false),
317+
"@objc ");
318+
}
305319
} else {
306320
// FIXME: Warn about non-KVC-compliant getter/setter names?
307321
}

test/ClangImporter/objc_bridging_generics.swift

+9-9
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ extension GenericClass {
108108
}
109109
// Doesn't use 'T', since its metadata isn't necessary to erase to AnyObject
110110
// or to existential metatype
111-
func doesntUseGenericParam5(_ x: T, _ y: T.Type) -> T {
111+
@objc func doesntUseGenericParam5(_ x: T, _ y: T.Type) -> T {
112112
_ = y as AnyObject.Type
113113
_ = y as Any.Type
114114
_ = y as AnyObject
@@ -143,10 +143,10 @@ extension GenericClass {
143143
// expected-error@+1{{extension of a generic Objective-C class cannot access the class's generic parameters}}
144144
func usesGenericParamJ() -> [(T, T)]? {} // expected-note{{used here}}
145145

146-
static func doesntUseGenericParam() {}
147-
static func doesntUseGenericParam2() -> Self {}
146+
@objc static func doesntUseGenericParam() {}
147+
@objc static func doesntUseGenericParam2() -> Self {}
148148
// Doesn't technically use 'T', since it's type-erased at runtime
149-
static func doesntUseGenericParam3() -> GenericClass<T> {}
149+
@objc static func doesntUseGenericParam3() -> GenericClass<T> {}
150150

151151
// expected-error@+1{{extension of a generic Objective-C class cannot access the class's generic parameters}}
152152
static func usesGenericParamC(_ x: [(T, T)]?) {} // expected-note{{used here}}
@@ -175,12 +175,12 @@ extension GenericClass {
175175
func swiftFunction<T: Animal>(x: T) {}
176176

177177
extension AnimalContainer {
178-
func doesntUseGenericParam1(_ x: T, _ y: T.Type) {
178+
@objc func doesntUseGenericParam1(_ x: T, _ y: T.Type) {
179179
_ = #selector(x.another)
180180
_ = #selector(y.create)
181181
}
182182

183-
func doesntUseGenericParam2(_ x: T, _ y: T.Type) {
183+
@objc func doesntUseGenericParam2(_ x: T, _ y: T.Type) {
184184
let a = x.another()
185185
_ = a.another()
186186
_ = x.another().another()
@@ -193,15 +193,15 @@ extension AnimalContainer {
193193
x.eat(a)
194194
}
195195

196-
func doesntUseGenericParam3(_ x: T, _ y: T.Type) {
196+
@objc func doesntUseGenericParam3(_ x: T, _ y: T.Type) {
197197
let sup: Animal = x
198198
sup.eat(x)
199199
_ = x.buddy
200200
_ = x[0]
201201
x[0] = x
202202
}
203203

204-
func doesntUseGenericParam4(_ x: T, _ y: T.Type) {
204+
@objc func doesntUseGenericParam4(_ x: T, _ y: T.Type) {
205205
_ = type(of: x).apexPredator.another()
206206
type(of: x).apexPredator = x
207207

@@ -290,7 +290,7 @@ extension AnimalContainer {
290290
}
291291

292292
extension PettableContainer {
293-
func doesntUseGenericParam(_ x: T, _ y: T.Type) {
293+
@objc func doesntUseGenericParam(_ x: T, _ y: T.Type) {
294294
// TODO: rdar://problem/27796375--allocating entry points are emitted as
295295
// true generics.
296296
// _ = type(of: x).init(fur: x).other()

test/Constraints/dynamic_lookup.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ _ = anyDict["test"]!.bar() // expected-error {{value of type 'Any' has no member
235235
// looked-up dynamically through AnyObject are treated as conforming
236236
// to the protocols they are supposed to conform to.
237237
class NSObjDerived1 : NSObject {
238-
var everything: [Any] = []
238+
@objc var everything: [Any] = []
239239
}
240240

241241
class NSObjDerived2 : NSObject {

test/Sema/availability_versions.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,7 @@ class Y {
13101310

13111311
@objc
13121312
class X {
1313-
var y = Y()
1313+
@objc var y = Y()
13141314
}
13151315

13161316
func testForFixitWithNestedMemberRefExpr() {

test/attr/attr_objc_swift3_deprecated.swift

+7
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ class DynamicMembers {
1414
dynamic var bar: NSObject? = nil // expected-warning{{inference of '@objc' for 'dynamic' members is deprecated in Swift 4}}{{3-3=@objc }}
1515
}
1616

17+
// Suppress diagnostices about references to inferred @objc declarations
18+
// in this mode.
19+
func test(sc: ObjCSubclass, dm: DynamicMembers) {
20+
_ = #selector(sc.foo)
21+
_ = #selector(getter: dm.bar)
22+
_ = #keyPath(DynamicMembers.bar)
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify %s -swift-version 3
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
class ObjCSubclass : NSObject {
7+
func foo() { } // expected-note 2{{add '@objc' to expose this instance method to Objective-C}}{{3-3=@objc }}
8+
var bar: NSObject? = nil // expected-note{{add '@objc' to expose this var to Objective-C}}{{3-3=@objc }}
9+
}
10+
11+
class DynamicMembers {
12+
dynamic func foo() { }
13+
14+
dynamic var bar: NSObject? = nil // expected-note 2{{add '@objc' to expose this var to Objective-C}}{{3-3=@objc }}
15+
}
16+
17+
func testSelector(sc: ObjCSubclass, dm: DynamicMembers) {
18+
_ = #selector(sc.foo) // expected-warning{{argument of '#selector' refers to instance method 'foo()' in 'ObjCSubclass' that depends on '@objc' attribute inference deprecated in Swift 4}}
19+
_ = #selector(getter: dm.bar) // expected-warning{{argument of '#selector' refers to var 'bar' in 'DynamicMembers' that depends on '@objc' attribute inference deprecated in Swift 4}}
20+
}
21+
22+
func testKeypath(dm: DynamicMembers) {
23+
_ = #keyPath(DynamicMembers.bar) // expected-warning{{argument of '#keyPath' refers to property 'bar' in 'DynamicMembers' that depends on '@objc' attribute inference deprecated in Swift 4}}
24+
}
25+
26+
func testDynamicCalls(ao: AnyObject) {
27+
ao.foo?() // expected-warning{{reference to instance method 'foo()' of 'ObjCSubclass' depends on '@objc' attribute inference deprecated in Swift 4}}
28+
_ = ao.bar! // expected-warning{{reference to var 'bar' of 'ObjCSubclass' depends on '@objc' attribute inference deprecated in Swift 4}}
29+
}

test/expr/dynamic_lookup.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-typecheck-verify-swift
22

33
@objc class HasStaticProperties {
4-
class var staticVar1: Int { return 4 }
4+
@objc class var staticVar1: Int { return 4 }
55
}
66

77
func testStaticProperty(classObj: AnyObject.Type) {

test/expr/unary/selector/Inputs/property_helper.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class OtherObjCClass: NSObject {
55
@objc private(set) var privateSetVar = 2 // expected-note 2{{'privateSetVar' declared here}}
66
@objc internal var internalVar = 2
77

8-
internal func internalFunc() {}
8+
@objc internal func internalFunc() {}
99

1010
private func privateFunc() {} // expected-note 2{{'privateFunc' declared here}}
1111
}

test/expr/unary/selector/property.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ class ObjCClassWithGetterSetter: NSObject {
143143
// Looking up inherited members
144144

145145
class BaseClass: NSObject {
146-
var myVar = 1
146+
@objc var myVar = 1
147147

148-
func myFunc() {
148+
@objc func myFunc() {
149149
}
150150
}
151151

test/expr/unary/selector/selector.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ case #selector(C1.method1)?:
129129
}
130130

131131
@objc class SR1827 {
132-
func bar() {}
132+
@objc func bar() {}
133133
}
134134

135135
switch optionalSel {

0 commit comments

Comments
 (0)