Skip to content

Commit 0e1b5ab

Browse files
committed
Conditionalize warnings for IUO-to-Any coercion on Swift version.
These warnings are turning out to be pretty noisy for code that declares IUOs (e.g. for @IBOutlets) and then passes them to Objective-C APIs with parameters declared as _Nonnull id. Since we bridge non-nil values successfully in most cases, and previuosly written and correctly executing code is either not seeing nil values passed in or are handling the nil (which is bridged as NSNull), it seems like a nuisance to warn about these for existing Swift versions. We'll conditionalize the warning, and then users can deal with these when moving to the new language version. Fixes: rdar://problem/39886178 (cherry picked from commit a61a6d5)
1 parent 0e6d867 commit 0e1b5ab

File tree

8 files changed

+138
-52
lines changed

8 files changed

+138
-52
lines changed

lib/AST/Expr.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -1559,8 +1559,17 @@ static ValueDecl *getCalledValue(Expr *E) {
15591559
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
15601560
return DRE->getDecl();
15611561

1562+
if (auto *OCRE = dyn_cast<OtherConstructorDeclRefExpr>(E))
1563+
return OCRE->getDecl();
1564+
1565+
// Look through SelfApplyExpr.
1566+
if (auto *SAE = dyn_cast<SelfApplyExpr>(E))
1567+
return SAE->getCalledValue();
1568+
15621569
Expr *E2 = E->getValueProvidingExpr();
1563-
if (E != E2) return getCalledValue(E2);
1570+
if (E != E2)
1571+
return getCalledValue(E2);
1572+
15641573
return nullptr;
15651574
}
15661575

lib/Sema/MiscDiagnostics.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -3723,6 +3723,32 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E,
37233723
.fixItInsertAfter(E->getEndLoc(), coercionString);
37243724
}
37253725

3726+
static bool hasImplicitlyUnwrappedResult(Expr *E) {
3727+
auto getDeclForExpr = [&](Expr *E) -> ValueDecl * {
3728+
if (auto *call = dyn_cast<CallExpr>(E))
3729+
E = call->getDirectCallee();
3730+
3731+
if (auto *memberRef = dyn_cast<MemberRefExpr>(E))
3732+
return memberRef->getMember().getDecl();
3733+
if (auto *declRef = dyn_cast<DeclRefExpr>(E))
3734+
return declRef->getDecl();
3735+
if (auto *apply = dyn_cast<ApplyExpr>(E))
3736+
return apply->getCalledValue();
3737+
3738+
return nullptr;
3739+
};
3740+
3741+
// Look through implicit conversions like loads, derived-to-base
3742+
// conversion, etc.
3743+
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(E))
3744+
E = ICE->getSubExpr();
3745+
3746+
auto *decl = getDeclForExpr(E);
3747+
3748+
return decl
3749+
&& decl->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
3750+
}
3751+
37263752
void visitErasureExpr(ErasureExpr *E, OptionalToAnyCoercion coercion) {
37273753
if (coercion.shouldSuppressDiagnostic())
37283754
return;
@@ -3734,6 +3760,12 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E,
37343760
while (auto *bindExpr = dyn_cast<BindOptionalExpr>(subExpr))
37353761
subExpr = bindExpr->getSubExpr();
37363762

3763+
// Do not warn on coercions from implicitly unwrapped optionals
3764+
// for Swift versions less than 5.
3765+
if (!TC.Context.isSwiftVersionAtLeast(5) &&
3766+
hasImplicitlyUnwrappedResult(subExpr))
3767+
return;
3768+
37373769
// We're taking the source type from the child of any BindOptionalExprs,
37383770
// and the destination from the parent of any
37393771
// (InjectIntoOptional/OptionalEvaluation)Exprs in order to take into

test/ClangImporter/objc_parse.swift

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -407,22 +407,10 @@ func testPropertyAndMethodCollision(_ obj: PropertyAndMethodCollision,
407407
type(of: rev).classRef(rev, doSomething:#selector(getter: NSMenuItem.action))
408408

409409
var value: Any
410-
value = obj.protoProp() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}}
411-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
412-
// expected-note@-2 {{provide a default value to avoid this warning}}
413-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
414-
value = obj.protoPropRO() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}}
415-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
416-
// expected-note@-2 {{provide a default value to avoid this warning}}
417-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
418-
value = type(of: obj).protoClassProp() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}}
419-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
420-
// expected-note@-2 {{provide a default value to avoid this warning}}
421-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
422-
value = type(of: obj).protoClassPropRO() // expected-warning {{expression implicitly coerced from 'Any?' to 'Any'}}
423-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
424-
// expected-note@-2 {{provide a default value to avoid this warning}}
425-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
410+
value = obj.protoProp()
411+
value = obj.protoPropRO()
412+
value = type(of: obj).protoClassProp()
413+
value = type(of: obj).protoClassPropRO()
426414
_ = value
427415
}
428416

test/Constraints/casts_objc.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ func nsobject_as_class_cast<T>(_ x: NSObject, _: T) {
3434
func test(_ a : CFString!, b : CFString) {
3535
let dict = NSMutableDictionary()
3636
let object = NSObject()
37-
dict[a] = object // expected-warning {{expression implicitly coerced from 'CFString?' to 'Any'}}
38-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
39-
// expected-note@-2 {{provide a default value to avoid this warning}}
40-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
41-
42-
37+
dict[a] = object
4338
dict[b] = object
4439
}
4540

test/Constraints/diag_ambiguities.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ struct SR3715 {
5151
func take(_ a: [Any]) {}
5252

5353
func test() {
54-
take([overloaded]) // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}}
55-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
56-
// expected-note@-2 {{provide a default value to avoid this warning}}
57-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
54+
take([overloaded])
5855
}
5956
}
6057

test/Constraints/existential_metatypes.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,5 @@ func testP3(_ p: P3, something: Something) {
8787
}
8888

8989
func testIUOToAny(_ t: AnyObject.Type!) {
90-
let _: Any = t // expected-warning {{expression implicitly coerced from 'AnyObject.Type?' to 'Any'}}
91-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}
92-
// expected-note@-2 {{provide a default value to avoid this warning}}
93-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}
90+
let _: Any = t
9491
}

test/Sema/diag_unintended_optional_behavior.swift

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,34 +82,33 @@ func warnNestedOptionalToOptionalAnyCoercion(_ a: Int?, _ b: Any??, _ c: Int???,
8282
takesOptionalAny(c as Any?, d as Any?)
8383
}
8484

85-
func warnIUOToAnyCoercion(_ a: Int!, _ b: Any?!) {
86-
_ = takeAny(a, b) // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}}
87-
// expected-note@-1 {{provide a default value to avoid this warning}}{{16-16= ?? <#default value#>}}
88-
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{16-16=!}}
89-
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{16-16= as Any}}
90-
// expected-warning@-4 {{expression implicitly coerced from 'Any??' to 'Any'}}
91-
// expected-note@-5 {{force-unwrap the value to avoid this warning}}{{19-19=!!}}
92-
// expected-note@-6 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{19-19= as Any}}
85+
class C {
86+
var a: Int!
87+
var b: Any?!
88+
func returningIUO() -> Int! { return a }
89+
func returningAny() -> Any { return a }
90+
}
91+
92+
func returningIUO() -> Int! { return 1 }
93+
94+
// No warnings in Swift 3/4 for IUO-to-Any coercion.
95+
func nowarnIUOToAnyCoercion(_ a: Int!, _ b: Any?!) {
96+
_ = takeAny(a, b)
97+
_ = takeAny(returningIUO(), C().returningIUO())
98+
_ = takeAny(C().a, C().b)
9399

94100
_ = takeAny(a as Any, b as Any)
95101
}
96102

97-
func warnIUOToOptionalAnyCoercion(_ a: Int!, _ b: Any?!, _ c: Int??!, _ d: Any???!) {
98-
takesOptionalAny(a, b) // expected-warning {{expression implicitly coerced from 'Any??' to 'Any?'}}
99-
// expected-note@-1 {{provide a default value to avoid this warning}}{{24-24= ?? <#default value#>}}
100-
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{24-24=!}}
101-
// expected-note@-3 {{explicitly cast to 'Any?' with 'as Any?' to silence this warning}}{{24-24= as Any?}}
103+
// No warnings in Swift 3/4 for IUO-to-Any coercion.
104+
func nowarnIUOToOptionalAnyCoercion(_ a: Int!, _ b: Any?!, _ c: Int??!, _ d: Any???!) {
105+
takesOptionalAny(a, b)
102106

103107
takesOptionalAny(a, b ?? "")
104108
takesOptionalAny(a, b!)
105109
takesOptionalAny(a, b as Any?)
106110

107-
takesOptionalAny(c, d) // expected-warning {{expression implicitly coerced from 'Int???' to 'Any?'}}
108-
// expected-note@-1 {{force-unwrap the value to avoid this warning}}{{21-21=!!}}
109-
// expected-note@-2 {{explicitly cast to 'Any?' with 'as Any?' to silence this warning}}{{21-21= as Any?}}
110-
// expected-warning@-3 {{expression implicitly coerced from 'Any????' to 'Any?'}}
111-
// expected-note@-4 {{force-unwrap the value to avoid this warning}}{{24-24=!!!}}
112-
// expected-note@-5 {{explicitly cast to 'Any?' with 'as Any?' to silence this warning}}{{24-24= as Any?}}
111+
takesOptionalAny(c, d)
113112

114113
takesOptionalAny(c!!, d!!!)
115114
takesOptionalAny(c as Any?, d as Any?)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
2+
3+
// Additional warnings produced in Swift 5+ mode.
4+
5+
func takeAny(_ left: Any, _ right: Any) -> Int? {
6+
return left as? Int
7+
}
8+
9+
func takesOptionalAny(_: Any?, _: Any?) {}
10+
11+
class C {
12+
var a: Int!
13+
var b: Any?!
14+
func returningIUO() -> Int! { return a }
15+
func returningAny() -> Any { return a } // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}}
16+
// expected-note@-1 {{provide a default value to avoid this warning}}{{40-40= ?? <#default value#>}}
17+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{40-40=!}}
18+
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{40-40= as Any}}
19+
}
20+
21+
func returningIUO() -> Int! { return 1 }
22+
23+
func warnIUOToAnyCoercion(_ a: Int!, _ b: Any?!) {
24+
_ = takeAny(a, b) // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}}
25+
// expected-note@-1 {{provide a default value to avoid this warning}}{{16-16= ?? <#default value#>}}
26+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{16-16=!}}
27+
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{16-16= as Any}}
28+
// expected-warning@-4 {{expression implicitly coerced from 'Any??' to 'Any'}}
29+
// expected-note@-5 {{force-unwrap the value to avoid this warning}}{{19-19=!!}}
30+
// expected-note@-6 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{19-19= as Any}}
31+
_ = takeAny(returningIUO(), C().returningIUO()) // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}}
32+
// expected-note@-1 {{provide a default value to avoid this warning}}{{29-29= ?? <#default value#>}}
33+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{29-29=!}}
34+
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{29-29= as Any}}
35+
// expected-warning@-4 {{expression implicitly coerced from 'Int?' to 'Any'}}
36+
// expected-note@-5 {{provide a default value to avoid this warning}}{{49-49= ?? <#default value#>}}
37+
// expected-note@-6 {{force-unwrap the value to avoid this warning}}{{49-49=!}}
38+
// expected-note@-7 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{49-49= as Any}}
39+
_ = takeAny(C().a, C().b) // expected-warning {{expression implicitly coerced from 'Int?' to 'Any'}}
40+
// expected-note@-1 {{provide a default value to avoid this warning}}{{20-20= ?? <#default value#>}}
41+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{20-20=!}}
42+
// expected-note@-3 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{20-20= as Any}}
43+
// expected-warning@-4 {{expression implicitly coerced from 'Any??' to 'Any'}}
44+
// expected-note@-5 {{force-unwrap the value to avoid this warning}}{{27-27=!!}}
45+
// expected-note@-6 {{explicitly cast to 'Any' with 'as Any' to silence this warning}}{{27-27= as Any}}
46+
47+
_ = takeAny(a as Any, b as Any)
48+
}
49+
50+
func warnIUOToOptionalAnyCoercion(_ a: Int!, _ b: Any?!, _ c: Int??!, _ d: Any???!) {
51+
takesOptionalAny(a, b) // expected-warning {{expression implicitly coerced from 'Any??' to 'Any?'}}
52+
// expected-note@-1 {{provide a default value to avoid this warning}}{{24-24= ?? <#default value#>}}
53+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}{{24-24=!}}
54+
// expected-note@-3 {{explicitly cast to 'Any?' with 'as Any?' to silence this warning}}{{24-24= as Any?}}
55+
56+
takesOptionalAny(a, b ?? "")
57+
takesOptionalAny(a, b!)
58+
takesOptionalAny(a, b as Any?)
59+
60+
takesOptionalAny(c, d) // expected-warning {{expression implicitly coerced from 'Int???' to 'Any?'}}
61+
// expected-note@-1 {{force-unwrap the value to avoid this warning}}{{21-21=!!}}
62+
// expected-note@-2 {{explicitly cast to 'Any?' with 'as Any?' to silence this warning}}{{21-21= as Any?}}
63+
// expected-warning@-3 {{expression implicitly coerced from 'Any????' to 'Any?'}}
64+
// expected-note@-4 {{force-unwrap the value to avoid this warning}}{{24-24=!!!}}
65+
// expected-note@-5 {{explicitly cast to 'Any?' with 'as Any?' to silence this warning}}{{24-24= as Any?}}
66+
67+
takesOptionalAny(c!!, d!!!)
68+
takesOptionalAny(c as Any?, d as Any?)
69+
}

0 commit comments

Comments
 (0)