Skip to content

Commit

Permalink
Suggest if let on let refutable binding
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Oct 9, 2019
1 parent ec557aa commit 4bb1592
Show file tree
Hide file tree
Showing 14 changed files with 169 additions and 9 deletions.
36 changes: 27 additions & 9 deletions src/librustc_mir/hair/pattern/check_match.rs
Expand Up @@ -62,12 +62,13 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
fn visit_local(&mut self, loc: &'tcx hir::Local) {
intravisit::walk_local(self, loc);

self.check_irrefutable(&loc.pat, match loc.source {
hir::LocalSource::Normal => "local binding",
hir::LocalSource::ForLoopDesugar => "`for` loop binding",
hir::LocalSource::AsyncFn => "async fn binding",
hir::LocalSource::AwaitDesugar => "`await` future binding",
});
let (msg, sp) = match loc.source {
hir::LocalSource::Normal => ("local binding", Some(loc.span)),
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
hir::LocalSource::AsyncFn => ("async fn binding", None),
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
};
self.check_irrefutable(&loc.pat, msg, sp);

// Check legality of move bindings and `@` patterns.
self.check_patterns(false, &loc.pat);
Expand All @@ -77,7 +78,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
intravisit::walk_body(self, body);

for param in &body.params {
self.check_irrefutable(&param.pat, "function argument");
self.check_irrefutable(&param.pat, "function argument", None);
self.check_patterns(false, &param.pat);
}
}
Expand Down Expand Up @@ -242,7 +243,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
})
}

fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str, sp: Option<Span>) {
let module = self.tcx.hir().get_module_parent(pat.hir_id);
MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
let mut patcx = PatCtxt::new(self.tcx,
Expand All @@ -266,18 +267,35 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
"refutable pattern in {}: {} not covered",
origin, joined_patterns
);
match &pat.kind {
let suggest_if_let = match &pat.kind {
hir::PatKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].args.is_none() =>
{
const_not_var(&mut err, cx.tcx, pat, path);
false
}
_ => {
err.span_label(
pat.span,
pattern_not_covered_label(&witnesses, &joined_patterns),
);
true
}
};

if let (Some(span), true) = (sp, suggest_if_let) {
err.note("`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]),
Applicability::HasPlaceholders,
);
}
err.note("for more information, visit \
https://doc.rust-lang.org/book/ch18-02-refutability.html");
}

adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/consts/const-match-check.eval1.stderr
Expand Up @@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1
|
LL | A = { let 0 = 0; 0 },
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | A = { if let 0 = 0 { /* */ } 0 },
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/consts/const-match-check.eval2.stderr
Expand Up @@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1
|
LL | let x: [i32; { let 0 = 0; 0 }] = [];
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | let x: [i32; { if let 0 = 0 { /* */ } 0 }] = [];
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/consts/const-match-check.matchck.stderr
Expand Up @@ -3,24 +3,52 @@ error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1
|
LL | const X: i32 = { let 0 = 0; 0 };
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | const X: i32 = { if let 0 = 0 { /* */ } 0 };
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:8:23
|
LL | static Y: i32 = { let 0 = 0; 0 };
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | static Y: i32 = { if let 0 = 0 { /* */ } 0 };
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:13:26
|
LL | const X: i32 = { let 0 = 0; 0 };
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | const X: i32 = { if let 0 = 0 { /* */ } 0 };
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:19:26
|
LL | const X: i32 = { let 0 = 0; 0 };
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | const X: i32 = { if let 0 = 0 { /* */ } 0 };
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors

Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/empty/empty-never-array.stderr
Expand Up @@ -11,6 +11,13 @@ LL | | }
...
LL | let Helper::U(u) = Helper::T(t, []);
| ^^^^^^^^^^^^ pattern `T(_, _)` 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
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, []) { /* */ }
|

error[E0381]: use of possibly-uninitialized variable: `u`
--> $DIR/empty-never-array.rs:12:5
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/error-codes/E0005.stderr
Expand Up @@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `None` not covered
|
LL | let Some(y) = x;
| ^^^^^^^ pattern `None` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Some(y) = x { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
@@ -0,0 +1,16 @@
error[E0005]: refutable pattern in local binding: `Err(_)` not covered
--> $DIR/feature-gate-exhaustive-patterns.rs:7:9
|
LL | let Ok(_x) = foo();
| ^^^^^^ pattern `Err(_)` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Ok(_x) = foo() { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0005`.
Expand Up @@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
|
LL | let Ok(_x) = foo();
| ^^^^^^ pattern `Err(_)` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Ok(_x) = foo() { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/issues/issue-31561.stderr
Expand Up @@ -12,6 +12,13 @@ LL | | }
...
LL | let Thing::Foo(y) = Thing::Foo(1);
| ^^^^^^^^^^^^^ patterns `Bar` and `Baz` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Thing::Foo(y) = Thing::Foo(1) { /* */ }
|

error: aborting due to previous error

Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/match/non-exhaustive-defined-here.stderr
Expand Up @@ -41,6 +41,13 @@ LL | | }
...
LL | let E::A = e;
| ^^^^ patterns `B` and `C` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let E::A = e { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered
--> $DIR/non-exhaustive-defined-here.rs:40:11
Expand Down Expand Up @@ -85,6 +92,13 @@ LL | | }
...
LL | let E::A = e;
| ^^^^ patterns `&B` and `&C` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let E::A = e { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered
--> $DIR/non-exhaustive-defined-here.rs:48:11
Expand Down Expand Up @@ -129,6 +143,13 @@ LL | | }
...
LL | let E::A = e;
| ^^^^ patterns `&&mut &B` and `&&mut &C` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let E::A = e { /* */ }
|

error[E0004]: non-exhaustive patterns: `None` not covered
--> $DIR/non-exhaustive-defined-here.rs:65:11
Expand Down Expand Up @@ -163,6 +184,13 @@ LL | | }
...
LL | let Opt::Some(ref _x) = e;
| ^^^^^^^^^^^^^^^^^ pattern `None` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Opt::Some(ref _x) = e { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 8 previous errors

Expand Down
Expand Up @@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
|
LL | let Ok(x) = res;
| ^^^^^ pattern `Err(_)` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Ok(x) = res { /* */ }
|

error[E0381]: use of possibly-uninitialized variable: `x`
--> $DIR/recursive-types-are-not-uninhabited.rs:8:5
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/refutable-pattern-errors.stderr
Expand Up @@ -9,6 +9,13 @@ error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` an
|
LL | let (1, (Some(1), 2..=3)) = (1, (None, 2));
| ^^^^^^^^^^^^^^^^^^^^^ patterns `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { /* */ }
|

error: aborting due to 2 previous errors

Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/uninhabited/uninhabited-irrefutable.stderr
Expand Up @@ -12,6 +12,13 @@ LL | | }
...
LL | let Foo::D(_y) = 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Foo::D(_y) = x { /* */ }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
Expand Up @@ -51,6 +51,13 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
|
LL | let Ok(x) = x;
| ^^^^^ pattern `Err(_)` 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
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let Ok(x) = x { /* */ }
|

error: aborting due to 7 previous errors

Expand Down

0 comments on commit 4bb1592

Please sign in to comment.