From 0883b4ba5d551681f260df214a58392fb3fc70a9 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 23 Mar 2023 11:46:22 +0000 Subject: [PATCH] [Parse] Recover slightly better from bad shorthand `if let` Instead of assuming that `if let ` is meant to be `if case = ...`, turn it into `if let _ = `, which is consistent with the fix-it we suggest. This currently doesn't have much of an effect on the diagnostics we produce, but will be important once we start doing bidirectional inference for ExprPatterns, as it avoids unhelpful diagnostics. --- lib/Parse/ParseStmt.cpp | 10 ++++++++++ test/stmt/if_while_var.swift | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index b338f1ab433c5..a947ab5877fe4 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1728,6 +1728,16 @@ Parser::parseStmtConditionElement(SmallVectorImpl &result, auto diagLoc = ThePattern.get()->getSemanticsProvidingPattern()->getStartLoc(); diagnose(diagLoc, diag::conditional_var_valid_identifiers_only) .fixItInsert(diagLoc, "<#identifier#> = "); + + // For better recovery, assume the expression pattern as the initializer, + // and synthesize an optional AnyPattern. + auto *semanticPattern = ThePattern.get()->getSemanticsProvidingPattern(); + if (auto *EP = dyn_cast(semanticPattern)) { + Init = makeParserResult(EP->getSubExpr()); + auto *AP = AnyPattern::createImplicit(Context); + ThePattern = + makeParserResult(OptionalSomePattern::createImplicit(Context, AP)); + } } else { diagnose(Tok, diag::conditional_var_initializer_required); } diff --git a/test/stmt/if_while_var.swift b/test/stmt/if_while_var.swift index b2a354293f2c8..ea5a9e347231e 100644 --- a/test/stmt/if_while_var.swift +++ b/test/stmt/if_while_var.swift @@ -42,8 +42,13 @@ if var nonOptional { nonOptional = nonOptionalStruct(); _ = nonOptional } // exp guard let nonOptional else { _ = nonOptional; fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}} guard var nonOptional else { _ = nonOptional; fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}} -if let nonOptional.property { } // expected-error{{unwrap condition requires a valid identifier}} expected-error{{pattern matching in a condition requires the 'case' keyword}} -if var nonOptional.property { } // expected-error{{unwrap condition requires a valid identifier}} expected-error{{pattern matching in a condition requires the 'case' keyword}} +if let nonOptional.property { } +// expected-error@-1 {{unwrap condition requires a valid identifier}} +// expected-error@-2 {{initializer for conditional binding must have Optional type, not 'Any'}} + +if var nonOptional.property { } +// expected-error@-1 {{unwrap condition requires a valid identifier}} +// expected-error@-2 {{initializer for conditional binding must have Optional type, not 'Any'}} guard let _ = nonOptionalStruct() else { fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}} guard let _ = nonOptionalEnum() else { fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalEnum'}} @@ -65,7 +70,11 @@ class B {} // expected-note * {{did you mean 'B'?}} class D : B {}// expected-note * {{did you mean 'D'?}} // TODO poor recovery in these cases -if let {} // expected-error {{expected '{' after 'if' condition}} expected-error {{pattern matching in a condition requires the 'case' keyword}} expected-error {{unwrap condition requires a valid identifier}} +if let {} +// expected-error@-1 {{expected '{' after 'if' condition}} +// expected-error@-2 {{unwrap condition requires a valid identifier}} +// expected-error@-3 {{initializer for conditional binding must have Optional type, not '() -> ()'}} + if let x = { } // expected-error{{'{' after 'if'}} expected-error{{initializer for conditional binding must have Optional type, not '() -> ()'}} // expected-warning@-1{{value 'x' was defined but never used}}