From 0d92752b8aac53e033541d04fc7d9677d8bca227 Mon Sep 17 00:00:00 2001 From: Esteban Kuber Date: Tue, 8 Mar 2022 05:54:38 +0000 Subject: [PATCH 1/2] Suggest `if let`/`let_else` for refutable pat in `let` --- .../src/thir/pattern/check_match.rs | 86 +++++++++++++++++-- .../ui/consts/const-match-check.eval1.stderr | 10 ++- .../ui/consts/const-match-check.eval2.stderr | 10 ++- .../consts/const-match-check.matchck.stderr | 40 ++++++--- src/test/ui/empty/empty-never-array.stderr | 6 +- src/test/ui/error-codes/E0005.stderr | 8 +- .../feature-gate-exhaustive-patterns.stderr | 8 +- ...een-expanded-earlier-non-exhaustive.stderr | 10 ++- .../ui/pattern/usefulness/issue-31561.stderr | 8 +- .../non-exhaustive-defined-here.stderr | 36 +++++--- .../refutable-pattern-errors.stderr | 8 +- ...recursive-types-are-not-uninhabited.stderr | 6 +- .../ui/uninhabited/uninhabited-irrefutable.rs | 6 +- .../uninhabited-irrefutable.stderr | 12 ++- .../uninhabited-matches-feature-gated.stderr | 6 +- 15 files changed, 204 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index b80d2e52ee709..dae313da8d993 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -7,7 +7,8 @@ use super::{PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, + ErrorGuaranteed, }; use rustc_hir as hir; use rustc_hir::def::*; @@ -20,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{DesugaringKind, ExpnKind, MultiSpan, Span}; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -241,6 +242,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } let joined_patterns = joined_uncovered_patterns(&cx, &witnesses); + + let mut bindings = vec![]; + let mut err = struct_span_err!( self.tcx.sess, pat.span, @@ -257,6 +261,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { false } _ => { + pat.walk(&mut |pat: &hir::Pat<'_>| { + match pat.kind { + hir::PatKind::Binding(_, _, ident, _) => { + bindings.push(ident); + } + _ => {} + } + true + }); + err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns)); true } @@ -267,13 +281,71 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ an `enum` with only one variant", ); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - err.span_suggestion( - span, - "you might want to use `if let` to ignore the variant that isn't matched", - format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]), + if self.tcx.sess.source_map().span_to_snippet(span).is_ok() { + let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1)); + let start_span = span.shrink_to_lo(); + let end_span = semi_span.shrink_to_lo(); + err.multipart_suggestion( + &format!( + "you might want to use `if let` to ignore the variant{} that {} matched", + pluralize!(witnesses.len()), + match witnesses.len() { + 1 => "isn't", + _ => "aren't", + }, + ), + vec![ + match &bindings[..] { + [] => (start_span, "if ".to_string()), + [binding] => (start_span, format!("let {} = if ", binding)), + bindings => ( + start_span, + format!( + "let ({}) = if ", + bindings + .iter() + .map(|ident| ident.to_string()) + .collect::>() + .join(", ") + ), + ), + }, + match &bindings[..] { + [] => (semi_span, " { todo!() }".to_string()), + [binding] => { + (end_span, format!(" {{ {} }} else {{ todo!() }}", binding)) + } + bindings => ( + end_span, + format!( + " {{ ({}) }} else {{ todo!() }}", + bindings + .iter() + .map(|ident| ident.to_string()) + .collect::>() + .join(", ") + ), + ), + }, + ], Applicability::HasPlaceholders, ); + if cx.tcx.sess.is_nightly_build() { + err.span_suggestion_verbose( + semi_span.shrink_to_lo(), + &format!( + "alternatively, on nightly, you might want to use \ + `#![feature(let_else)]` to handle the variant{} that {} matched", + pluralize!(witnesses.len()), + match witnesses.len() { + 1 => "isn't", + _ => "aren't", + }, + ), + " else { todo!() }".to_string(), + Applicability::HasPlaceholders, + ); + } } err.note( "for more information, visit \ diff --git a/src/test/ui/consts/const-match-check.eval1.stderr b/src/test/ui/consts/const-match-check.eval1.stderr index 4141cc4ab1a48..08ee800f138d2 100644 --- a/src/test/ui/consts/const-match-check.eval1.stderr +++ b/src/test/ui/consts/const-match-check.eval1.stderr @@ -7,10 +7,14 @@ LL | A = { let 0 = 0; 0 }, = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | A = { if let 0 = 0 { /* */ } 0 }, - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | A = { if let 0 = 0 { todo!() } 0 }, + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched + | +LL | A = { let 0 = 0 else { todo!() }; 0 }, + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.eval2.stderr b/src/test/ui/consts/const-match-check.eval2.stderr index af86ba0cc82f8..579cb7e780007 100644 --- a/src/test/ui/consts/const-match-check.eval2.stderr +++ b/src/test/ui/consts/const-match-check.eval2.stderr @@ -7,10 +7,14 @@ LL | let x: [i32; { let 0 = 0; 0 }] = []; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | let x: [i32; { if let 0 = 0 { /* */ } 0 }] = []; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | let x: [i32; { if let 0 = 0 { todo!() } 0 }] = []; + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched + | +LL | let x: [i32; { let 0 = 0 else { todo!() }; 0 }] = []; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.matchck.stderr b/src/test/ui/consts/const-match-check.matchck.stderr index f71490eba6135..f89bbc0d42234 100644 --- a/src/test/ui/consts/const-match-check.matchck.stderr +++ b/src/test/ui/consts/const-match-check.matchck.stderr @@ -7,10 +7,14 @@ LL | const X: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched + | +LL | const X: i32 = { let 0 = 0 else { todo!() }; 0 }; + | ++++++++++++++++ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:8:23 @@ -21,10 +25,14 @@ LL | static Y: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched + | +LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | -LL | static Y: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | static Y: i32 = { let 0 = 0 else { todo!() }; 0 }; + | ++++++++++++++++ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:13:26 @@ -35,10 +43,14 @@ LL | const X: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched + | +LL | const X: i32 = { let 0 = 0 else { todo!() }; 0 }; + | ++++++++++++++++ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:19:26 @@ -49,10 +61,14 @@ LL | const X: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched + | +LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | -LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | const X: i32 = { let 0 = 0 else { todo!() }; 0 }; + | ++++++++++++++++ error: aborting due to 4 previous errors diff --git a/src/test/ui/empty/empty-never-array.stderr b/src/test/ui/empty/empty-never-array.stderr index 8dd0f377533ce..909aa73a74a38 100644 --- a/src/test/ui/empty/empty-never-array.stderr +++ b/src/test/ui/empty/empty-never-array.stderr @@ -16,8 +16,12 @@ LL | T(T, [!; 0]), = note: the matched value is of type `Helper` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Helper::U(u) = Helper::T(t, []) { /* */ } +LL | let u = if let Helper::U(u) = Helper::T(t, []) { u } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched | +LL | let Helper::U(u) = Helper::T(t, []) else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0005.stderr b/src/test/ui/error-codes/E0005.stderr index 208c625a53e95..55b1112b5f8ec 100644 --- a/src/test/ui/error-codes/E0005.stderr +++ b/src/test/ui/error-codes/E0005.stderr @@ -22,8 +22,12 @@ LL | | } = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Some(y) = x { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let y = if let Some(y) = x { y } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Some(y) = x else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index c2ffda6bb72d2..21180f31bbd26 100644 --- a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -21,8 +21,12 @@ LL | | } = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Ok(_x) = foo() { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let _x = if let Ok(_x) = foo() { _x } else { todo!() }; + | +++++++++++ +++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Ok(_x) = foo() else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr index 37a35700b36d5..aa1aa4434c3fa 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -7,10 +7,14 @@ LL | let (0 | (1 | 2)) = 0; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let (0 | (1 | 2)) = 0 { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | if let (0 | (1 | 2)) = 0 { todo!() } + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched + | +LL | let (0 | (1 | 2)) = 0 else { todo!() }; + | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:3:11 diff --git a/src/test/ui/pattern/usefulness/issue-31561.stderr b/src/test/ui/pattern/usefulness/issue-31561.stderr index dffcfc016072f..9da6b5eeead23 100644 --- a/src/test/ui/pattern/usefulness/issue-31561.stderr +++ b/src/test/ui/pattern/usefulness/issue-31561.stderr @@ -17,10 +17,14 @@ LL | Bar, LL | Baz | ^^^ not covered = note: the matched value is of type `Thing` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let Thing::Foo(y) = Thing::Foo(1) { /* */ } +LL | let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | +LL | let Thing::Foo(y) = Thing::Foo(1) else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index 8f5adccea806d..f7dc070f80248 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -42,10 +42,14 @@ LL | B, LL | C | ^ not covered = note: the matched value is of type `E` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched + | +LL | if let E::A = e { todo!() } + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | -LL | if let E::A = e { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let E::A = e else { todo!() }; + | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered --> $DIR/non-exhaustive-defined-here.rs:52:11 @@ -91,10 +95,14 @@ LL | B, LL | C | ^ not covered = note: the matched value is of type `&E` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let E::A = e { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | if let E::A = e { todo!() } + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched + | +LL | let E::A = e else { todo!() }; + | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered --> $DIR/non-exhaustive-defined-here.rs:66:11 @@ -140,10 +148,14 @@ LL | B, LL | C | ^ not covered = note: the matched value is of type `&&mut &E` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let E::A = e { /* */ } +LL | if let E::A = e { todo!() } + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | +LL | let E::A = e else { todo!() }; + | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `None` not covered --> $DIR/non-exhaustive-defined-here.rs:92:11 @@ -185,8 +197,12 @@ LL | None, = note: the matched value is of type `Opt` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Opt::Some(ref _x) = e { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() }; + | +++++++++++ +++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Opt::Some(ref _x) = e else { todo!() }; + | ++++++++++++++++ error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index 74ec646e31cca..e3ffc092327ac 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -15,10 +15,14 @@ LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `(i32, (Option, i32))` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { /* */ } +LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { todo!() } + | ++ ~~~~~~~~~~~ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | +LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)) else { todo!() }; + | ++++++++++++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr index ded3cf3ad1d44..a9159562d9d51 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -21,8 +21,12 @@ LL | | } = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Ok(x) = res { /* */ } +LL | let x = if let Ok(x) = res { x } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched | +LL | let Ok(x) = res else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.rs b/src/test/ui/uninhabited/uninhabited-irrefutable.rs index 48cd92719b49a..661b5486adc12 100644 --- a/src/test/ui/uninhabited/uninhabited-irrefutable.rs +++ b/src/test/ui/uninhabited/uninhabited-irrefutable.rs @@ -19,10 +19,10 @@ enum Foo { A(foo::SecretlyEmpty), B(foo::NotSoSecretlyEmpty), C(NotSoSecretlyEmpty), - D(u32), + D(u32, u32), } fn main() { - let x: Foo = Foo::D(123); - let Foo::D(_y) = x; //~ ERROR refutable pattern in local binding: `A(_)` not covered + let x: Foo = Foo::D(123, 456); + let Foo::D(_y, _z) = x; //~ ERROR refutable pattern in local binding: `A(_)` not covered } diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr index ad19c34a40a11..c571e17a7b372 100644 --- a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr @@ -1,8 +1,8 @@ error[E0005]: refutable pattern in local binding: `A(_)` not covered --> $DIR/uninhabited-irrefutable.rs:27:9 | -LL | let Foo::D(_y) = x; - | ^^^^^^^^^^ pattern `A(_)` not covered +LL | let Foo::D(_y, _z) = x; + | ^^^^^^^^^^^^^^ pattern `A(_)` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html @@ -16,8 +16,12 @@ LL | A(foo::SecretlyEmpty), = note: the matched value is of type `Foo` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Foo::D(_y) = x { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let (_y, _z) = if let Foo::D(_y, _z) = x { (_y, _z) } else { todo!() }; + | +++++++++++++++++ +++++++++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Foo::D(_y, _z) = x else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index d90075d82f47b..74216d265d034 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -132,8 +132,12 @@ LL | | } = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Ok(x) = x { /* */ } +LL | let x = if let Ok(x) = x { x } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched | +LL | let Ok(x) = x else { todo!() }; + | ++++++++++++++++ error: aborting due to 7 previous errors From c3a998e82a50b66e8f6f97170cd9117fadf03618 Mon Sep 17 00:00:00 2001 From: Esteban Kuber Date: Tue, 8 Mar 2022 17:20:05 +0000 Subject: [PATCH 2/2] Do not suggest `let_else` if no bindings would be introduced --- .../src/thir/pattern/check_match.rs | 2 +- .../ui/consts/const-match-check.eval1.stderr | 4 ---- .../ui/consts/const-match-check.eval2.stderr | 4 ---- .../ui/consts/const-match-check.matchck.stderr | 16 ---------------- ...e-been-expanded-earlier-non-exhaustive.stderr | 4 ---- .../non-exhaustive-defined-here.stderr | 12 ------------ .../usefulness/refutable-pattern-errors.stderr | 4 ---- 7 files changed, 1 insertion(+), 45 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index dae313da8d993..c94da838680e0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -330,7 +330,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { ], Applicability::HasPlaceholders, ); - if cx.tcx.sess.is_nightly_build() { + if !bindings.is_empty() && cx.tcx.sess.is_nightly_build() { err.span_suggestion_verbose( semi_span.shrink_to_lo(), &format!( diff --git a/src/test/ui/consts/const-match-check.eval1.stderr b/src/test/ui/consts/const-match-check.eval1.stderr index 08ee800f138d2..6e61dbbd8eee3 100644 --- a/src/test/ui/consts/const-match-check.eval1.stderr +++ b/src/test/ui/consts/const-match-check.eval1.stderr @@ -11,10 +11,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | A = { if let 0 = 0 { todo!() } 0 }, | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | A = { let 0 = 0 else { todo!() }; 0 }, - | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.eval2.stderr b/src/test/ui/consts/const-match-check.eval2.stderr index 579cb7e780007..1b3b6e06c3df6 100644 --- a/src/test/ui/consts/const-match-check.eval2.stderr +++ b/src/test/ui/consts/const-match-check.eval2.stderr @@ -11,10 +11,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | let x: [i32; { if let 0 = 0 { todo!() } 0 }] = []; | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | let x: [i32; { let 0 = 0 else { todo!() }; 0 }] = []; - | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.matchck.stderr b/src/test/ui/consts/const-match-check.matchck.stderr index f89bbc0d42234..bc8edfa7af9f4 100644 --- a/src/test/ui/consts/const-match-check.matchck.stderr +++ b/src/test/ui/consts/const-match-check.matchck.stderr @@ -11,10 +11,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | const X: i32 = { let 0 = 0 else { todo!() }; 0 }; - | ++++++++++++++++ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:8:23 @@ -29,10 +25,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | static Y: i32 = { let 0 = 0 else { todo!() }; 0 }; - | ++++++++++++++++ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:13:26 @@ -47,10 +39,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | const X: i32 = { let 0 = 0 else { todo!() }; 0 }; - | ++++++++++++++++ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:19:26 @@ -65,10 +53,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | const X: i32 = { let 0 = 0 else { todo!() }; 0 }; - | ++++++++++++++++ error: aborting due to 4 previous errors diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr index aa1aa4434c3fa..95b22ac059482 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -11,10 +11,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | if let (0 | (1 | 2)) = 0 { todo!() } | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | let (0 | (1 | 2)) = 0 else { todo!() }; - | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:3:11 diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index f7dc070f80248..0f06c31c468b1 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -46,10 +46,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | if let E::A = e { todo!() } | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | let E::A = e else { todo!() }; - | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered --> $DIR/non-exhaustive-defined-here.rs:52:11 @@ -99,10 +95,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | if let E::A = e { todo!() } | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | let E::A = e else { todo!() }; - | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered --> $DIR/non-exhaustive-defined-here.rs:66:11 @@ -152,10 +144,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | if let E::A = e { todo!() } | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | let E::A = e else { todo!() }; - | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `None` not covered --> $DIR/non-exhaustive-defined-here.rs:92:11 diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index e3ffc092327ac..d1dacc822e942 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -19,10 +19,6 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { todo!() } | ++ ~~~~~~~~~~~ -help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched - | -LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)) else { todo!() }; - | ++++++++++++++++ error: aborting due to 2 previous errors