Skip to content

Commit e4adcc9

Browse files
authored
Merge pull request #4900 from rudkx/optional-to-any-warning-swift-3.0-branch
Emit a warning when optionals are coerced to Any.
2 parents fa92152 + 8628b94 commit e4adcc9

File tree

7 files changed

+123
-7
lines changed

7 files changed

+123
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2393,7 +2393,14 @@ WARNING(optional_check_promotion,none,
23932393
WARNING(optional_pattern_match_promotion,none,
23942394
"pattern match introduces an implicit promotion from %0 to %1",
23952395
(Type, Type))
2396-
2396+
WARNING(optional_to_any_coercion,none,
2397+
"expression implicitly coerced from %0 to Any", (Type))
2398+
NOTE(default_optional_to_any,none,
2399+
"provide a default value to avoid this warning", ())
2400+
NOTE(force_optional_to_any,none,
2401+
"force-unwrap the value to avoid this warning", ())
2402+
NOTE(silence_optional_to_any,none,
2403+
"explicitly cast to Any with 'as Any' to silence this warning", ())
23972404

23982405
ERROR(invalid_noescape_use,none,
23992406
"non-escaping %select{value|parameter}1 %0 may only be called",

lib/Sema/MiscDiagnostics.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3593,7 +3593,55 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
35933593
.highlight(subExpr->getSourceRange());
35943594
}
35953595
}
3596-
3596+
3597+
static void diagnoseOptionalToAnyCoercion(TypeChecker &TC, const Expr *E,
3598+
const DeclContext *DC) {
3599+
if (!E || isa<ErrorExpr>(E) || !E->getType())
3600+
return;
3601+
3602+
class OptionalToAnyCoercionWalker : public ASTWalker {
3603+
TypeChecker &TC;
3604+
SmallPtrSet<Expr *, 4> ErasureCoercedToAny;
3605+
3606+
virtual std::pair<bool, Expr *> walkToExprPre(Expr *E) {
3607+
if (!E || isa<ErrorExpr>(E) || !E->getType())
3608+
return { false, E };
3609+
3610+
if (auto *coercion = dyn_cast<CoerceExpr>(E)) {
3611+
if (E->getType()->isAny() && isa<ErasureExpr>(coercion->getSubExpr()))
3612+
ErasureCoercedToAny.insert(coercion->getSubExpr());
3613+
} else if (isa<ErasureExpr>(E) && !ErasureCoercedToAny.count(E) &&
3614+
E->getType()->isAny()) {
3615+
auto subExpr = cast<ErasureExpr>(E)->getSubExpr();
3616+
auto erasedTy = subExpr->getType();
3617+
if (erasedTy->getOptionalObjectType()) {
3618+
TC.diagnose(subExpr->getStartLoc(), diag::optional_to_any_coercion,
3619+
erasedTy)
3620+
.highlight(subExpr->getSourceRange());
3621+
3622+
TC.diagnose(subExpr->getLoc(), diag::default_optional_to_any)
3623+
.highlight(subExpr->getSourceRange())
3624+
.fixItInsertAfter(subExpr->getEndLoc(), " ?? <#default value#>");
3625+
TC.diagnose(subExpr->getLoc(), diag::force_optional_to_any)
3626+
.highlight(subExpr->getSourceRange())
3627+
.fixItInsertAfter(subExpr->getEndLoc(), "!");
3628+
TC.diagnose(subExpr->getLoc(), diag::silence_optional_to_any)
3629+
.highlight(subExpr->getSourceRange())
3630+
.fixItInsertAfter(subExpr->getEndLoc(), " as Any");
3631+
}
3632+
}
3633+
3634+
return { true, E };
3635+
}
3636+
3637+
public:
3638+
OptionalToAnyCoercionWalker(TypeChecker &tc) : TC(tc) { }
3639+
};
3640+
3641+
OptionalToAnyCoercionWalker Walker(TC);
3642+
const_cast<Expr *>(E)->walk(Walker);
3643+
}
3644+
35973645
//===----------------------------------------------------------------------===//
35983646
// High-level entry points.
35993647
//===----------------------------------------------------------------------===//
@@ -3606,6 +3654,7 @@ void swift::performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E,
36063654
diagSyntacticUseRestrictions(TC, E, DC, isExprStmt);
36073655
diagRecursivePropertyAccess(TC, E, DC);
36083656
diagnoseImplicitSelfUseInClosure(TC, E, DC);
3657+
diagnoseOptionalToAnyCoercion(TC, E, DC);
36093658
if (!TC.getLangOpts().DisableAvailabilityChecking)
36103659
diagAvailability(TC, E, const_cast<DeclContext*>(DC));
36113660
if (TC.Context.LangOpts.EnableObjCInterop)

test/ClangModules/accessibility_framework.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ class AA: NSView {
2626
}
2727

2828
let a = A()
29-
print(a.accessibilityLabel(), terminator: "")
29+
print(a.accessibilityLabel() as Any, terminator: "")

test/Constraints/bridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func dictionaryToNSDictionary() {
171171

172172
// <rdar://problem/17134986>
173173
var bcOpt: BridgedClass?
174-
nsd = [BridgedStruct() : bcOpt]
174+
nsd = [BridgedStruct() : bcOpt as Any]
175175
bcOpt = nil
176176
_ = nsd
177177
}

test/Constraints/patterns.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ func SR2066(x: Int?) {
146146
// Test x???? patterns.
147147
switch (nil as Int???) {
148148
case let x???: print(x, terminator: "")
149-
case let x??: print(x, terminator: "")
150-
case let x?: print(x, terminator: "")
149+
case let x??: print(x as Any, terminator: "")
150+
case let x?: print(x as Any, terminator: "")
151151
case 4???: break
152152
case nil??: break
153153
case nil?: break

test/SILOptimizer/definite_init_diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ func test22436880() {
11211121

11221122
// sr-184
11231123
let x: String? // expected-note 2 {{constant defined here}}
1124-
print(x?.characters.count) // expected-error {{constant 'x' used before being initialized}}
1124+
print(x?.characters.count as Any) // expected-error {{constant 'x' used before being initialized}}
11251125
print(x!) // expected-error {{constant 'x' used before being initialized}}
11261126

11271127

test/Sema/diag_optional_to_any.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-parse-verify-swift
2+
3+
func takeAny(_ left: Any, _ right: Any) -> Int? {
4+
return left as? Int
5+
}
6+
7+
func throwing() throws -> Int? {}
8+
9+
func warnOptionalToAnyCoercion(value x: Int?) -> Any {
10+
let a: Any = x // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
11+
// expected-note@-1 {{provide a default value to avoid this warning}}
12+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
13+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
14+
15+
let b: Any = x as Any
16+
17+
let c: Any = takeAny(a, b) // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
18+
// expected-note@-1 {{provide a default value to avoid this warning}}
19+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
20+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
21+
22+
let _: Any = takeAny(c, c) as Any
23+
24+
let _: Any = (x) // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
25+
// expected-note@-1 {{provide a default value to avoid this warning}}
26+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
27+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
28+
29+
let f: Any = (x as Any)
30+
let g: Any = (x) as (Any)
31+
32+
_ = takeAny(f as? Int, g) // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
33+
// expected-note@-1 {{provide a default value to avoid this warning}}
34+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
35+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
36+
37+
let _: Any = takeAny(f as? Int, g) as Any // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
38+
// expected-note@-1 {{provide a default value to avoid this warning}}
39+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
40+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
41+
42+
let _: Any = takeAny(f as? Int as Any, g) as Any
43+
44+
let _: Any = x! == x! ? 1 : x // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
45+
// expected-note@-1 {{provide a default value to avoid this warning}}
46+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
47+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
48+
49+
do {
50+
let _: Any = try throwing() // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
51+
// expected-note@-1 {{provide a default value to avoid this warning}}
52+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
53+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
54+
} catch {}
55+
56+
return x // expected-warning {{expression implicitly coerced from 'Int?' to Any}}
57+
// expected-note@-1 {{provide a default value to avoid this warning}}
58+
// expected-note@-2 {{force-unwrap the value to avoid this warning}}
59+
// expected-note@-3 {{explicitly cast to Any with 'as Any' to silence this warning}}
60+
}

0 commit comments

Comments
 (0)