Skip to content

Commit 762fd4a

Browse files
authored
Merge pull request swiftlang#76644 from jameesbrown/issue-44631
[CS] Diagnose misuse of CheckedCastExpr with ~=
2 parents fffe2ce + f0eef0f commit 762fd4a

File tree

4 files changed

+73
-3
lines changed

4 files changed

+73
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,15 @@ ERROR(optional_chain_isnt_chaining,none,
14191419
())
14201420
ERROR(pattern_in_expr,none,
14211421
"%0 cannot appear in an expression", (DescriptivePatternKind))
1422+
ERROR(conditional_cast_in_type_casting_pattern,none,
1423+
"cannot conditionally downcast in a type-casting pattern",
1424+
())
1425+
ERROR(force_cast_in_type_casting_pattern,none,
1426+
"cannot force downcast in a type-casting pattern",
1427+
())
1428+
ERROR(cannot_bind_value_with_is,none,
1429+
"use 'as' keyword to bind a matched value",
1430+
())
14221431
NOTE(note_call_to_operator,none,
14231432
"in call to operator %0", (const ValueDecl *))
14241433
NOTE(note_call_to_func,none,

lib/Sema/CSDiagnostics.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8699,10 +8699,47 @@ bool InvalidPatternInExprFailure::diagnoseAsError() {
86998699
E = parent;
87008700
}
87018701
}
8702-
emitDiagnostic(diag::pattern_in_expr, P->getDescriptiveKind());
8702+
if (!diagnoseInvalidCheckedCast()) {
8703+
emitDiagnostic(diag::pattern_in_expr, P->getDescriptiveKind());
8704+
}
87038705
return true;
87048706
}
87058707

8708+
bool InvalidPatternInExprFailure::diagnoseInvalidCheckedCast() const {
8709+
auto *E = findParentExpr(castToExpr(getAnchor()));
8710+
// Make sure we have a CheckedCastExpr and are in an argument of `~=`.
8711+
while (E && !isa<CheckedCastExpr>(E))
8712+
E = findParentExpr(E);
8713+
auto *castExpr = cast_or_null<CheckedCastExpr>(E);
8714+
if (!castExpr)
8715+
return false;
8716+
auto *parent = findParentExpr(castExpr);
8717+
while (parent && !isa<BinaryExpr>(parent))
8718+
parent = findParentExpr(parent);
8719+
auto *BE = cast_or_null<BinaryExpr>(parent);
8720+
if (!BE || !isPatternMatchingOperator(BE->getFn()))
8721+
return false;
8722+
// Emit the appropriate diagnostic based on the cast kind.
8723+
if (auto *forced = dyn_cast<ForcedCheckedCastExpr>(castExpr)) {
8724+
emitDiagnosticAt(castExpr->getLoc(),
8725+
diag::force_cast_in_type_casting_pattern)
8726+
.fixItRemove(forced->getExclaimLoc());
8727+
return true;
8728+
}
8729+
if (auto *conditional = dyn_cast<ConditionalCheckedCastExpr>(castExpr)) {
8730+
emitDiagnosticAt(castExpr->getLoc(),
8731+
diag::conditional_cast_in_type_casting_pattern)
8732+
.fixItRemove(conditional->getQuestionLoc());
8733+
return true;
8734+
}
8735+
if (auto *isExpr = dyn_cast<IsExpr>(castExpr)) {
8736+
emitDiagnosticAt(castExpr->getLoc(), diag::cannot_bind_value_with_is)
8737+
.fixItReplace(isExpr->getAsLoc(), "as");
8738+
return true;
8739+
}
8740+
return false;
8741+
}
8742+
87068743
bool MissingContextualTypeForNil::diagnoseAsError() {
87078744
auto *expr = castToExpr<NilLiteralExpr>(getAnchor());
87088745

lib/Sema/CSDiagnostics.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,18 @@ class InvalidPatternInExprFailure final : public FailureDiagnostic {
26042604
: FailureDiagnostic(solution, locator), P(pattern) {}
26052605

26062606
bool diagnoseAsError() override;
2607+
2608+
private:
2609+
/// Diagnose situations where a type-casting pattern that binds a value
2610+
/// expects 'as' but is given 'as!', 'as?' or 'is' instead
2611+
/// e.g:
2612+
///
2613+
/// \code
2614+
/// case let x as? Int = y
2615+
/// case let x as! Int = y
2616+
/// case let x is Int = y
2617+
/// \endcode
2618+
bool diagnoseInvalidCheckedCast() const;
26072619
};
26082620

26092621
/// Diagnose situations where there is no context to determine a

test/Constraints/rdar106598067.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,22 @@
33
enum E: Error { case e }
44

55
// rdar://106598067 – Make sure we don't crash.
6-
// FIXME: We ought to have a tailored diagnostic to change to 'as' instead of 'as?'
76
let fn = {
87
do {} catch let x as? E {}
9-
// expected-error@-1 {{pattern variable binding cannot appear in an expression}}
8+
// expected-error@-1 {{cannot conditionally downcast in a type-casting pattern}}{{23-24=}}
109
// expected-error@-2 {{expression pattern of type 'E?' cannot match values of type 'any Error'}}
1110
// expected-warning@-3 {{'catch' block is unreachable because no errors are thrown in 'do' block}}
1211
}
12+
13+
// https://github.com/swiftlang/swift/issues/44631
14+
let maybeInt: Any = 1
15+
switch maybeInt {
16+
case let intValue as? Int: _ = intValue
17+
// expected-error@-1 {{cannot conditionally downcast in a type-casting pattern}}{{21-22=}}
18+
// expected-error@-2 {{expression pattern of type 'Int?' cannot match values of type 'Any'}}
19+
case let intValue as! Int: _ = intValue
20+
// expected-error@-1 {{cannot force downcast in a type-casting pattern}}{{21-22=}}
21+
case let intValue is Int: _ = intValue
22+
// expected-error@-1 {{use 'as' keyword to bind a matched value}}{{19-21=as}}
23+
default: break
24+
}

0 commit comments

Comments
 (0)