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::*;
};
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() {
}
let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
+
+ let mut bindings = vec![];
+
let mut err = struct_span_err!(
self.tcx.sess,
pat.span,
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
}
"`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::<Vec<_>>()
+ .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::<Vec<_>>()
+ .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 \
= 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
= 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
= 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
= 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
= 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
= 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
= note: the matched value is of type `Helper<T, U>`
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
= note: the matched value is of type `Option<i32>`
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
= note: the matched value is of type `Result<u32, !>`
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
= 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
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
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
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
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
= 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
= 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>, 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
= note: the matched value is of type `Result<u32, &R>`
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
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
}
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
= 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
= note: the matched value is of type `Result<u32, Void>`
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