[CS] Avoid emitting invalid binding diagnostic in more cases

Also avoid emitting the diagnostic for an out of
place binding pattern if there's an existing fix
on a parent call expression, as that's the real
source of the failure.

rdar://113025351
This commit is contained in:
Hamish Knight
2023-07-28 11:22:27 +01:00
parent a219cfa632
commit 2210ea6856
2 changed files with 31 additions and 7 deletions

View File

@@ -8365,12 +8365,13 @@ bool InvalidEmptyKeyPathFailure::diagnoseAsError() {
}
bool InvalidPatternInExprFailure::diagnoseAsError() {
// Check to see if we have something like 'case <fn>(let foo)', where <fn>
// has a fix associated with it. In such a case, it's more likely than not
// that the user is trying to write an EnumElementPattern, but has made some
// kind of mistake in the function expr that causes it to be treated as an
// ExprPattern. Emitting an additional error for the out of place 'let foo' is
// just noise in that case, so let's avoid diagnosing.
// Check to see if we have something like 'case <fn>(let foo)', where either
// <fn> or the call itself has a fix associated with it. In such a case, it's
// more likely than not that the user is trying to write an
// EnumElementPattern, but has made some kind of mistake in the function expr
// that causes it to be treated as an ExprPattern. Emitting an additional
// error for the out of place 'let foo' is just noise in that case, so let's
// avoid diagnosing.
llvm::SmallPtrSet<Expr *, 4> fixAnchors;
for (auto *fix : getSolution().Fixes) {
if (auto *anchor = getAsExpr(fix->getAnchor()))
@@ -8380,7 +8381,7 @@ bool InvalidPatternInExprFailure::diagnoseAsError() {
auto *E = castToExpr(getLocator()->getAnchor());
while (auto *parent = findParentExpr(E)) {
if (auto *CE = dyn_cast<CallExpr>(parent)) {
if (fixAnchors.contains(CE->getFn()))
if (fixAnchors.contains(CE) || fixAnchors.contains(CE->getFn()))
return false;
}
E = parent;

View File

@@ -0,0 +1,23 @@
// RUN: %target-typecheck-verify-swift
struct S {
enum E: Error {
case e(Int)
case f
}
}
func foo() throws {}
// rdar://113025351: Avoid emitting a separate diagnostic complaining that a
// 'let' cannot be nested in an expression, as it just adds noise.
func bar() throws {
do {
try foo()
} catch S.E(let x) {} // expected-error {{'S.E' cannot be constructed because it has no accessible initializers}}
}
func baz(_ x: S.E) {
if case S.E(let y) = x {} // expected-error {{'S.E' cannot be constructed because it has no accessible initializers}}
if case S.E(S.E.e(let y)) = x {} // expected-error {{'S.E' cannot be constructed because it has no accessible initializers}}
}