Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest if let on let refutable binding #65248

Merged
merged 1 commit into from Oct 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
format!("if {} {{ /* ... */ }}", &snippet[..snippet.len() - 1]),

Applicability::HasPlaceholders,
);
}
err.note("for more information, visit \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this note perhaps be in the rustc --explain rather than inline here? it seems like it's a lot of extra text

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