Skip to content

Commit b7e61e0

Browse files
committed
[Type checker] Emit coalesce-or-force-unwrap Fix-Its for necessary unwraps more consistently.
Replace the last (and most obscure) use of the poor “use ‘?’ or ‘!’” diagnostic with the new, more explanatory version that provides separate notes with Fix-Its for coalescing or force-unwrapping the value. Finishes rdar://problem/42081852. (cherry picked from commit 5db1901)
1 parent 5297ca2 commit b7e61e0

File tree

6 files changed

+86
-65
lines changed

6 files changed

+86
-65
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -885,10 +885,6 @@ NOTE(note_init_parameter,none,
885885
ERROR(missing_nullary_call,none,
886886
"function produces expected type %0; did you mean to call it with '()'?",
887887
(Type))
888-
ERROR(missing_unwrap_optional,none,
889-
"value of optional type %0 not unwrapped; did you mean to use '!' "
890-
"or '?'?",
891-
(Type))
892888
ERROR(optional_not_unwrapped,none,
893889
"value of optional type %0 must be unwrapped to a value of type %1",
894890
(Type, Type))

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7906,9 +7906,7 @@ static bool exprNeedsParensAfterAddingAs(TypeChecker &TC, DeclContext *DC,
79067906
return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr, asPG);
79077907
}
79087908

7909-
// Return true if, when replacing "<expr>" with "<expr> ?? T", parentheses need
7910-
// to be added around <expr> first in order to maintain the correct precedence.
7911-
static bool exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC,
7909+
bool swift::exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC,
79127910
DeclContext *DC,
79137911
Expr *expr) {
79147912
auto asPG =
@@ -7918,10 +7916,7 @@ static bool exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC,
79187916
return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG);
79197917
}
79207918

7921-
// Return true if, when replacing "<expr>" with "<expr> as T", parentheses need
7922-
// to be added around the new expression in order to maintain the correct
7923-
// precedence.
7924-
static bool exprNeedsParensAfterAddingNilCoalescing(TypeChecker &TC,
7919+
bool swift::exprNeedsParensAfterAddingNilCoalescing(TypeChecker &TC,
79257920
DeclContext *DC,
79267921
Expr *expr,
79277922
Expr *rootExpr) {
@@ -8093,7 +8088,7 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
80938088

80948089
switch (fix.first.getKind()) {
80958090
case FixKind::ForceOptional: {
8096-
const Expr *unwrapped = affected->getValueProvidingExpr();
8091+
Expr *unwrapped = affected->getValueProvidingExpr();
80978092
auto type = solution.simplifyType(getType(affected))
80988093
->getRValueObjectType();
80998094

@@ -8104,54 +8099,7 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
81048099
"try!");
81058100

81068101
} else {
8107-
Type unwrappedType = type->getOptionalObjectType();
8108-
if (!unwrappedType)
8109-
return false;
8110-
8111-
TC.diagnose(affected->getLoc(), diag::optional_not_unwrapped, type,
8112-
unwrappedType);
8113-
8114-
// Suggest a default value via ?? <default value>
8115-
{
8116-
auto diag =
8117-
TC.diagnose(affected->getLoc(), diag::unwrap_with_default_value);
8118-
8119-
// Figure out what we need to parenthesize.
8120-
bool needsParensInside =
8121-
exprNeedsParensBeforeAddingNilCoalescing(TC, DC, affected);
8122-
bool needsParensOutside =
8123-
exprNeedsParensAfterAddingNilCoalescing(TC, DC, affected, expr);
8124-
8125-
llvm::SmallString<2> insertBefore;
8126-
llvm::SmallString<32> insertAfter;
8127-
if (needsParensOutside) {
8128-
insertBefore += "(";
8129-
}
8130-
if (needsParensInside) {
8131-
insertBefore += "(";
8132-
insertAfter += ")";
8133-
}
8134-
insertAfter += " ?? <" "#default value#" ">";
8135-
if (needsParensOutside)
8136-
insertAfter += ")";
8137-
8138-
if (!insertBefore.empty()) {
8139-
diag.fixItInsert(affected->getStartLoc(), insertBefore);
8140-
}
8141-
diag.fixItInsertAfter(affected->getEndLoc(), insertAfter);
8142-
}
8143-
8144-
// Suggest a force-unwrap.
8145-
{
8146-
auto diag =
8147-
TC.diagnose(affected->getLoc(), diag::unwrap_with_force_value);
8148-
if (affected->canAppendPostfixExpression(true)) {
8149-
diag.fixItInsertAfter(affected->getEndLoc(), "!");
8150-
} else {
8151-
diag.fixItInsert(affected->getStartLoc(), "(")
8152-
.fixItInsertAfter(affected->getEndLoc(), ")!");
8153-
}
8154-
}
8102+
return diagnoseUnwrap(TC, DC, unwrapped, type);
81558103
}
81568104
return true;
81578105
}

lib/Sema/CSDiag.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8652,6 +8652,60 @@ bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
86528652
return true;
86538653
}
86548654

8655+
bool swift::diagnoseUnwrap(TypeChecker &TC, DeclContext *DC,
8656+
Expr *expr, Type type) {
8657+
Type unwrappedType = type->getOptionalObjectType();
8658+
if (!unwrappedType)
8659+
return false;
8660+
8661+
TC.diagnose(expr->getLoc(), diag::optional_not_unwrapped, type,
8662+
unwrappedType);
8663+
8664+
// Suggest a default value via ?? <default value>
8665+
{
8666+
auto diag =
8667+
TC.diagnose(expr->getLoc(), diag::unwrap_with_default_value);
8668+
8669+
// Figure out what we need to parenthesize.
8670+
bool needsParensInside =
8671+
exprNeedsParensBeforeAddingNilCoalescing(TC, DC, expr);
8672+
bool needsParensOutside =
8673+
exprNeedsParensAfterAddingNilCoalescing(TC, DC, expr, expr);
8674+
8675+
llvm::SmallString<2> insertBefore;
8676+
llvm::SmallString<32> insertAfter;
8677+
if (needsParensOutside) {
8678+
insertBefore += "(";
8679+
}
8680+
if (needsParensInside) {
8681+
insertBefore += "(";
8682+
insertAfter += ")";
8683+
}
8684+
insertAfter += " ?? <" "#default value#" ">";
8685+
if (needsParensOutside)
8686+
insertAfter += ")";
8687+
8688+
if (!insertBefore.empty()) {
8689+
diag.fixItInsert(expr->getStartLoc(), insertBefore);
8690+
}
8691+
diag.fixItInsertAfter(expr->getEndLoc(), insertAfter);
8692+
}
8693+
8694+
// Suggest a force-unwrap.
8695+
{
8696+
auto diag =
8697+
TC.diagnose(expr->getLoc(), diag::unwrap_with_force_value);
8698+
if (expr->canAppendPostfixExpression(true)) {
8699+
diag.fixItInsertAfter(expr->getEndLoc(), "!");
8700+
} else {
8701+
diag.fixItInsert(expr->getStartLoc(), "(")
8702+
.fixItInsertAfter(expr->getEndLoc(), ")!");
8703+
}
8704+
}
8705+
8706+
return true;
8707+
}
8708+
86558709
bool swift::diagnoseBaseUnwrapForMemberAccess(Expr *baseExpr, Type baseType,
86568710
DeclName memberName,
86578711
SourceRange memberRange) {

lib/Sema/CalleeCandidateInfo.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -992,10 +992,10 @@ bool CalleeCandidateInfo::diagnoseGenericParameterErrors(Expr *badArgExpr) {
992992
// Check for optional near miss.
993993
if (auto argOptType = substitution->getOptionalObjectType()) {
994994
if (isSubstitutableFor(argOptType, paramArchetype, CS.DC)) {
995-
CS.TC.diagnose(badArgExpr->getLoc(), diag::missing_unwrap_optional,
996-
argType);
997-
foundFailure = true;
998-
break;
995+
if (diagnoseUnwrap(CS.TC, CS.DC, badArgExpr, substitution)) {
996+
foundFailure = true;
997+
break;
998+
}
999999
}
10001000
}
10011001

lib/Sema/ConstraintSystem.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3508,6 +3508,13 @@ class OverloadSetCounter : public ASTWalker {
35083508
}
35093509
};
35103510

3511+
3512+
/// Diagnose an attempt to recover when we have a value of optional type
3513+
/// that needs to be unwrapped.
3514+
///
3515+
/// \returns true if a diagnostic was produced.
3516+
bool diagnoseUnwrap(TypeChecker &TC, DeclContext *DC, Expr *expr, Type type);
3517+
35113518
/// Diagnose an attempt to recover from a member access into a value of
35123519
/// optional type which needed to be unwrapped for the member to be found.
35133520
///
@@ -3516,6 +3523,20 @@ bool diagnoseBaseUnwrapForMemberAccess(Expr *baseExpr, Type baseType,
35163523
DeclName memberName,
35173524
SourceRange memberRange);
35183525

3526+
// Return true if, when replacing "<expr>" with "<expr> ?? T", parentheses need
3527+
// to be added around <expr> first in order to maintain the correct precedence.
3528+
bool exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC,
3529+
DeclContext *DC,
3530+
Expr *expr);
3531+
3532+
// Return true if, when replacing "<expr>" with "<expr> as T", parentheses need
3533+
// to be added around the new expression in order to maintain the correct
3534+
// precedence.
3535+
bool exprNeedsParensAfterAddingNilCoalescing(TypeChecker &TC,
3536+
DeclContext *DC,
3537+
Expr *expr,
3538+
Expr *rootExpr);
3539+
35193540
} // end namespace swift
35203541

35213542
#endif // LLVM_SWIFT_SEMA_CONSTRAINT_SYSTEM_H

test/Constraints/closures.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,9 @@ struct S<T> {
282282

283283
func subscribe<Object: AnyObject>(object: Object?, method: (Object, T) -> ()) where Object: Hashable {
284284
let wrappedMethod = { (object: AnyObject, value: T) in }
285-
// expected-error @+1 {{value of optional type 'Object?' not unwrapped; did you mean to use '!' or '?'?}}
285+
// expected-error @+3 {{value of optional type 'Object?' must be unwrapped to a value of type 'Object'}}
286+
// expected-note @+2{{coalesce using '??' to provide a default when the optional value contains 'nil'}}
287+
// expected-note @+1{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
286288
cs.forEach { $0.w.append(value: wrappedMethod, forKey: object) }
287289
}
288290
}

0 commit comments

Comments
 (0)