Skip to content

Commit c14e1a3

Browse files
authored
Merge pull request #21124 from xedin/rdar-45218255-5.0
[5.0][CSDiagnostics] Diagnose invalid optional unwrap via fixes
2 parents cc3dc38 + b71356c commit c14e1a3

File tree

10 files changed

+113
-7
lines changed

10 files changed

+113
-7
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,3 +1222,17 @@ bool AutoClosureForwardingFailure::diagnoseAsError() {
12221222
.fixItInsertAfter(argExpr->getEndLoc(), "()");
12231223
return true;
12241224
}
1225+
1226+
bool NonOptionalUnwrapFailure::diagnoseAsError() {
1227+
auto *anchor = getAnchor();
1228+
1229+
auto diagnostic = diag::invalid_optional_chain;
1230+
if (isa<ForceValueExpr>(anchor))
1231+
diagnostic = diag::invalid_force_unwrap;
1232+
1233+
emitDiagnostic(anchor->getLoc(), diagnostic, BaseType)
1234+
.highlight(anchor->getSourceRange())
1235+
.fixItRemove(anchor->getEndLoc());
1236+
1237+
return true;
1238+
}

lib/Sema/CSDiagnostics.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,29 @@ class AutoClosureForwardingFailure final : public FailureDiagnostic {
607607
bool diagnoseAsError() override;
608608
};
609609

610+
/// Diagnose situations when there was an attempt to unwrap entity
611+
/// of non-optional type e.g.
612+
///
613+
/// ```swift
614+
/// let i: Int = 0
615+
/// _ = i!
616+
///
617+
/// struct A { func foo() {} }
618+
/// func foo(_ a: A) {
619+
/// a?.foo()
620+
/// }
621+
/// ```
622+
class NonOptionalUnwrapFailure final : public FailureDiagnostic {
623+
Type BaseType;
624+
625+
public:
626+
NonOptionalUnwrapFailure(Expr *root, ConstraintSystem &cs, Type baseType,
627+
ConstraintLocator *locator)
628+
: FailureDiagnostic(root, cs, locator), BaseType(baseType) {}
629+
630+
bool diagnoseAsError() override;
631+
};
632+
610633
} // end namespace constraints
611634
} // end namespace swift
612635

lib/Sema/CSFix.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,14 @@ AutoClosureForwarding *AutoClosureForwarding::create(ConstraintSystem &cs,
213213
ConstraintLocator *locator) {
214214
return new (cs.getAllocator()) AutoClosureForwarding(cs, locator);
215215
}
216+
217+
bool RemoveUnwrap::diagnose(Expr *root, bool asNote) const {
218+
auto failure = NonOptionalUnwrapFailure(root, getConstraintSystem(), BaseType,
219+
getLocator());
220+
return failure.diagnose(asNote);
221+
}
222+
223+
RemoveUnwrap *RemoveUnwrap::create(ConstraintSystem &cs, Type baseType,
224+
ConstraintLocator *locator) {
225+
return new (cs.getAllocator()) RemoveUnwrap(cs, baseType, locator);
226+
}

lib/Sema/CSFix.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ enum class FixKind : uint8_t {
8989
/// @autoclosure conversions are unsupported starting from
9090
/// Swift version 5.
9191
AutoClosureForwarding,
92+
93+
/// Remove `!` or `?` because base is not an optional type.
94+
RemoveUnwrap,
9295
};
9396

9497
class ConstraintFix {
@@ -408,6 +411,23 @@ class AutoClosureForwarding final : public ConstraintFix {
408411
ConstraintLocator *locator);
409412
};
410413

414+
class RemoveUnwrap final : public ConstraintFix {
415+
Type BaseType;
416+
417+
public:
418+
RemoveUnwrap(ConstraintSystem &cs, Type baseType, ConstraintLocator *locator)
419+
: ConstraintFix(cs, FixKind::RemoveUnwrap, locator), BaseType(baseType) {}
420+
421+
std::string getName() const override {
422+
return "remove unwrap operator `!` or `?`";
423+
}
424+
425+
bool diagnose(Expr *root, bool asNote = false) const override;
426+
427+
static RemoveUnwrap *create(ConstraintSystem &cs, Type baseType,
428+
ConstraintLocator *locator);
429+
};
430+
411431
} // end namespace constraints
412432
} // end namespace swift
413433

lib/Sema/CSSimplify.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2979,10 +2979,27 @@ ConstraintSystem::simplifyOptionalObjectConstraint(
29792979
return SolutionKind::Unsolved;
29802980
}
29812981

2982-
// If the base type is not optional, the constraint fails.
2982+
29832983
Type objectTy = optTy->getOptionalObjectType();
2984-
if (!objectTy)
2985-
return SolutionKind::Error;
2984+
// If the base type is not optional, let's attempt a fix (if possible)
2985+
// and assume that `!` is just not there.
2986+
if (!objectTy) {
2987+
// Let's see if we can apply a specific fix here.
2988+
if (shouldAttemptFixes()) {
2989+
auto *fix =
2990+
RemoveUnwrap::create(*this, optTy, getConstraintLocator(locator));
2991+
2992+
if (recordFix(fix))
2993+
return SolutionKind::Error;
2994+
2995+
// If the fix was successful let's record
2996+
// "fixed" object type and continue.
2997+
objectTy = optTy;
2998+
} else {
2999+
// If fixes are not allowed, no choice but to fail.
3000+
return SolutionKind::Error;
3001+
}
3002+
}
29863003

29873004
// The object type is an lvalue if the optional was.
29883005
if (optLValueTy->is<LValueType>())
@@ -5427,6 +5444,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
54275444
case FixKind::RelabelArguments:
54285445
case FixKind::AddConformance:
54295446
case FixKind::AutoClosureForwarding:
5447+
case FixKind::RemoveUnwrap:
54305448
llvm_unreachable("handled elsewhere");
54315449
}
54325450

test/Constraints/diagnostics.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,14 @@ func rdar17170728() {
11831183

11841184
let _ = [i, j, k].reduce(0 as Int?) {
11851185
$0 && $1 ? $0! + $1! : ($0 ? $0! : ($1 ? $1! : nil))
1186+
// expected-error@-1 {{cannot force unwrap value of non-optional type 'Bool'}} {{18-19=}}
1187+
// expected-error@-2 {{cannot force unwrap value of non-optional type 'Bool'}} {{24-25=}}
1188+
// expected-error@-3 {{cannot force unwrap value of non-optional type 'Bool'}} {{36-37=}}
1189+
// expected-error@-4 {{cannot force unwrap value of non-optional type 'Bool'}} {{48-49=}}
1190+
}
1191+
1192+
let _ = [i, j, k].reduce(0 as Int?) {
1193+
$0 && $1 ? $0 + $1 : ($0 ? $0 : ($1 ? $1 : nil))
11861194
// expected-error@-1 {{ambiguous use of operator '+'}}
11871195
}
11881196
}

test/Constraints/dynamic_lookup.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ type(of: obj).foo!(obj)(5) // expected-error{{instance member 'foo' cannot be us
210210

211211
// Checked casts to AnyObject
212212
var p: P = Y()
213-
// expected-warning @+1 {{forced cast from 'P' to 'AnyObject' always succeeds; did you mean to use 'as'?}}
214213
var obj3 : AnyObject = (p as! AnyObject)! // expected-error{{cannot force unwrap value of non-optional type 'AnyObject'}} {{41-42=}}
215214

216215
// Implicit force of an implicitly unwrapped optional

test/Constraints/optional.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,13 @@ func se0213() {
292292
_ = Q("who")!.foo // Ok
293293
_ = Q?("how") // Ok
294294
}
295+
296+
func rdar45218255(_ i: Int) {
297+
struct S<T> {
298+
init(_:[T]) {}
299+
}
300+
301+
_ = i! // expected-error {{cannot force unwrap value of non-optional type 'Int'}} {{8-9=}}
302+
_ = [i!] // expected-error {{cannot force unwrap value of non-optional type 'Int'}} {{9-10=}}
303+
_ = S<Int>([i!]) // expected-error {{cannot force unwrap value of non-optional type 'Int'}} {{16-17=}}
304+
}

test/Parse/operators.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ infix operator !!
116116
func !!(x: Man, y: Man) {}
117117
let foo = Man()
118118
let bar = TheDevil()
119-
foo!!foo // expected-error{{cannot force unwrap value of non-optional type 'Man'}} {{4-5=}} expected-error{{consecutive statements}} {{6-6=;}}
120-
// expected-warning @-1 {{expression of type 'Man' is unused}}
119+
foo!!foo
120+
// expected-error@-1 {{cannot force unwrap value of non-optional type 'Man'}} {{4-5=}}
121+
// expected-error@-2 {{cannot force unwrap value of non-optional type 'Man'}} {{5-6=}}
122+
// expected-error@-3 {{consecutive statements}} {{6-6=;}}
123+
// expected-warning@-4 {{expression of type 'Man' is unused}}
121124

122125
foo??bar // expected-error{{broken standard library}} expected-error{{consecutive statements}} {{6-6=;}}
123126
// expected-warning @-1 {{expression of type 'TheDevil' is unused}}

test/decl/init/failable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Sub : Super {
6060
}
6161

6262
convenience init(forceNonfail: Int) {
63-
self.init(nonfail: forceNonfail)! // expected-error{{cannot force unwrap value of non-optional type '()'}} {{37-38=}}
63+
self.init(nonfail: forceNonfail)! // expected-error{{cannot force unwrap value of non-optional type 'Sub'}} {{37-38=}}
6464
}
6565

6666
init(nonfail2: Int) { // okay, traps on nil

0 commit comments

Comments
 (0)