diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 21d77dc607818..6341287269f12 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1419,6 +1419,15 @@ ERROR(optional_chain_isnt_chaining,none, ()) ERROR(pattern_in_expr,none, "%0 cannot appear in an expression", (DescriptivePatternKind)) +ERROR(conditional_cast_in_type_casting_pattern,none, + "cannot conditionally downcast in a type-casting pattern", + ()) +ERROR(force_cast_in_type_casting_pattern,none, + "cannot force downcast in a type-casting pattern", + ()) +ERROR(cannot_bind_value_with_is,none, + "use 'as' keyword to bind a matched value", + ()) NOTE(note_call_to_operator,none, "in call to operator %0", (const ValueDecl *)) NOTE(note_call_to_func,none, diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 6539f5f47832e..b1e3adedbb3d4 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -8699,10 +8699,47 @@ bool InvalidPatternInExprFailure::diagnoseAsError() { E = parent; } } - emitDiagnostic(diag::pattern_in_expr, P->getDescriptiveKind()); + if (!diagnoseInvalidCheckedCast()) { + emitDiagnostic(diag::pattern_in_expr, P->getDescriptiveKind()); + } return true; } +bool InvalidPatternInExprFailure::diagnoseInvalidCheckedCast() const { + auto *E = findParentExpr(castToExpr(getAnchor())); + // Make sure we have a CheckedCastExpr and are in an argument of `~=`. + while (E && !isa(E)) + E = findParentExpr(E); + auto *castExpr = cast_or_null(E); + if (!castExpr) + return false; + auto *parent = findParentExpr(castExpr); + while (parent && !isa(parent)) + parent = findParentExpr(parent); + auto *BE = cast_or_null(parent); + if (!BE || !isPatternMatchingOperator(BE->getFn())) + return false; + // Emit the appropriate diagnostic based on the cast kind. + if (auto *forced = dyn_cast(castExpr)) { + emitDiagnosticAt(castExpr->getLoc(), + diag::force_cast_in_type_casting_pattern) + .fixItRemove(forced->getExclaimLoc()); + return true; + } + if (auto *conditional = dyn_cast(castExpr)) { + emitDiagnosticAt(castExpr->getLoc(), + diag::conditional_cast_in_type_casting_pattern) + .fixItRemove(conditional->getQuestionLoc()); + return true; + } + if (auto *isExpr = dyn_cast(castExpr)) { + emitDiagnosticAt(castExpr->getLoc(), diag::cannot_bind_value_with_is) + .fixItReplace(isExpr->getAsLoc(), "as"); + return true; + } + return false; +} + bool MissingContextualTypeForNil::diagnoseAsError() { auto *expr = castToExpr(getAnchor()); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index cd20db5e86cf9..df73e2ab64619 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -2604,6 +2604,18 @@ class InvalidPatternInExprFailure final : public FailureDiagnostic { : FailureDiagnostic(solution, locator), P(pattern) {} bool diagnoseAsError() override; + +private: + /// Diagnose situations where a type-casting pattern that binds a value + /// expects 'as' but is given 'as!', 'as?' or 'is' instead + /// e.g: + /// + /// \code + /// case let x as? Int = y + /// case let x as! Int = y + /// case let x is Int = y + /// \endcode + bool diagnoseInvalidCheckedCast() const; }; /// Diagnose situations where there is no context to determine a diff --git a/test/Constraints/rdar106598067.swift b/test/Constraints/rdar106598067.swift index 1579dc002216a..e237f66a21033 100644 --- a/test/Constraints/rdar106598067.swift +++ b/test/Constraints/rdar106598067.swift @@ -3,10 +3,22 @@ enum E: Error { case e } // rdar://106598067 – Make sure we don't crash. -// FIXME: We ought to have a tailored diagnostic to change to 'as' instead of 'as?' let fn = { do {} catch let x as? E {} - // expected-error@-1 {{pattern variable binding cannot appear in an expression}} + // expected-error@-1 {{cannot conditionally downcast in a type-casting pattern}}{{23-24=}} // expected-error@-2 {{expression pattern of type 'E?' cannot match values of type 'any Error'}} // expected-warning@-3 {{'catch' block is unreachable because no errors are thrown in 'do' block}} } + +// https://github.com/swiftlang/swift/issues/44631 +let maybeInt: Any = 1 +switch maybeInt { +case let intValue as? Int: _ = intValue + // expected-error@-1 {{cannot conditionally downcast in a type-casting pattern}}{{21-22=}} + // expected-error@-2 {{expression pattern of type 'Int?' cannot match values of type 'Any'}} +case let intValue as! Int: _ = intValue + // expected-error@-1 {{cannot force downcast in a type-casting pattern}}{{21-22=}} +case let intValue is Int: _ = intValue + // expected-error@-1 {{use 'as' keyword to bind a matched value}}{{19-21=as}} +default: break +}