Skip to content

Commit 963e4bc

Browse files
authored
Rollup merge of #65248 - estebank:mention-if-let, r=cramertj
Suggest `if let` on `let` refutable binding Fix #58385.
2 parents 82fb193 + 4bb1592 commit 963e4bc

14 files changed

+169
-9
lines changed

src/librustc_mir/hair/pattern/check_match.rs

+27-9
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
6262
fn visit_local(&mut self, loc: &'tcx hir::Local) {
6363
intravisit::walk_local(self, loc);
6464

65-
self.check_irrefutable(&loc.pat, match loc.source {
66-
hir::LocalSource::Normal => "local binding",
67-
hir::LocalSource::ForLoopDesugar => "`for` loop binding",
68-
hir::LocalSource::AsyncFn => "async fn binding",
69-
hir::LocalSource::AwaitDesugar => "`await` future binding",
70-
});
65+
let (msg, sp) = match loc.source {
66+
hir::LocalSource::Normal => ("local binding", Some(loc.span)),
67+
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
68+
hir::LocalSource::AsyncFn => ("async fn binding", None),
69+
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
70+
};
71+
self.check_irrefutable(&loc.pat, msg, sp);
7172

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

7980
for param in &body.params {
80-
self.check_irrefutable(&param.pat, "function argument");
81+
self.check_irrefutable(&param.pat, "function argument", None);
8182
self.check_patterns(false, &param.pat);
8283
}
8384
}
@@ -242,7 +243,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
242243
})
243244
}
244245

245-
fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
246+
fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str, sp: Option<Span>) {
246247
let module = self.tcx.hir().get_module_parent(pat.hir_id);
247248
MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
248249
let mut patcx = PatCtxt::new(self.tcx,
@@ -266,18 +267,35 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
266267
"refutable pattern in {}: {} not covered",
267268
origin, joined_patterns
268269
);
269-
match &pat.kind {
270+
let suggest_if_let = match &pat.kind {
270271
hir::PatKind::Path(hir::QPath::Resolved(None, path))
271272
if path.segments.len() == 1 && path.segments[0].args.is_none() =>
272273
{
273274
const_not_var(&mut err, cx.tcx, pat, path);
275+
false
274276
}
275277
_ => {
276278
err.span_label(
277279
pat.span,
278280
pattern_not_covered_label(&witnesses, &joined_patterns),
279281
);
282+
true
283+
}
284+
};
285+
286+
if let (Some(span), true) = (sp, suggest_if_let) {
287+
err.note("`let` bindings require an \"irrefutable pattern\", like a `struct` or \
288+
an `enum` with only one variant");
289+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
290+
err.span_suggestion(
291+
span,
292+
"you might want to use `if let` to ignore the variant that isn't matched",
293+
format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
294+
Applicability::HasPlaceholders,
295+
);
280296
}
297+
err.note("for more information, visit \
298+
https://doc.rust-lang.org/book/ch18-02-refutability.html");
281299
}
282300

283301
adt_defined_here(cx, &mut err, pattern_ty, &witnesses);

src/test/ui/consts/const-match-check.eval1.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1
33
|
44
LL | A = { let 0 = 0; 0 },
55
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | A = { if let 0 = 0 { /* */ } 0 },
12+
| ^^^^^^^^^^^^^^^^^^^^^^
613

714
error: aborting due to previous error
815

src/test/ui/consts/const-match-check.eval2.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1
33
|
44
LL | let x: [i32; { let 0 = 0; 0 }] = [];
55
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | let x: [i32; { if let 0 = 0 { /* */ } 0 }] = [];
12+
| ^^^^^^^^^^^^^^^^^^^^^^
613

714
error: aborting due to previous error
815

src/test/ui/consts/const-match-check.matchck.stderr

+28
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,52 @@ error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1
33
|
44
LL | const X: i32 = { let 0 = 0; 0 };
55
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | const X: i32 = { if let 0 = 0 { /* */ } 0 };
12+
| ^^^^^^^^^^^^^^^^^^^^^^
613

714
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
815
--> $DIR/const-match-check.rs:8:23
916
|
1017
LL | static Y: i32 = { let 0 = 0; 0 };
1118
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
19+
|
20+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
21+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
22+
help: you might want to use `if let` to ignore the variant that isn't matched
23+
|
24+
LL | static Y: i32 = { if let 0 = 0 { /* */ } 0 };
25+
| ^^^^^^^^^^^^^^^^^^^^^^
1226

1327
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
1428
--> $DIR/const-match-check.rs:13:26
1529
|
1630
LL | const X: i32 = { let 0 = 0; 0 };
1731
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
32+
|
33+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
34+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
35+
help: you might want to use `if let` to ignore the variant that isn't matched
36+
|
37+
LL | const X: i32 = { if let 0 = 0 { /* */ } 0 };
38+
| ^^^^^^^^^^^^^^^^^^^^^^
1839

1940
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
2041
--> $DIR/const-match-check.rs:19:26
2142
|
2243
LL | const X: i32 = { let 0 = 0; 0 };
2344
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
45+
|
46+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
47+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
48+
help: you might want to use `if let` to ignore the variant that isn't matched
49+
|
50+
LL | const X: i32 = { if let 0 = 0 { /* */ } 0 };
51+
| ^^^^^^^^^^^^^^^^^^^^^^
2452

2553
error: aborting due to 4 previous errors
2654

src/test/ui/empty/empty-never-array.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ LL | | }
1111
...
1212
LL | let Helper::U(u) = Helper::T(t, []);
1313
| ^^^^^^^^^^^^ pattern `T(_, _)` not covered
14+
|
15+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
16+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
17+
help: you might want to use `if let` to ignore the variant that isn't matched
18+
|
19+
LL | if let Helper::U(u) = Helper::T(t, []) { /* */ }
20+
|
1421

1522
error[E0381]: use of possibly-uninitialized variable: `u`
1623
--> $DIR/empty-never-array.rs:12:5

src/test/ui/error-codes/E0005.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `None` not covered
33
|
44
LL | let Some(y) = x;
55
| ^^^^^^^ pattern `None` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | if let Some(y) = x { /* */ }
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
613

714
error: aborting due to previous error
815

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0005]: refutable pattern in local binding: `Err(_)` not covered
2+
--> $DIR/feature-gate-exhaustive-patterns.rs:7:9
3+
|
4+
LL | let Ok(_x) = foo();
5+
| ^^^^^^ pattern `Err(_)` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | if let Ok(_x) = foo() { /* */ }
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0005`.

src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
33
|
44
LL | let Ok(_x) = foo();
55
| ^^^^^^ pattern `Err(_)` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | if let Ok(_x) = foo() { /* */ }
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
613

714
error: aborting due to previous error
815

src/test/ui/issues/issue-31561.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ LL | | }
1212
...
1313
LL | let Thing::Foo(y) = Thing::Foo(1);
1414
| ^^^^^^^^^^^^^ patterns `Bar` and `Baz` not covered
15+
|
16+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
17+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
18+
help: you might want to use `if let` to ignore the variant that isn't matched
19+
|
20+
LL | if let Thing::Foo(y) = Thing::Foo(1) { /* */ }
21+
|
1522

1623
error: aborting due to previous error
1724

src/test/ui/match/non-exhaustive-defined-here.stderr

+28
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ LL | | }
4141
...
4242
LL | let E::A = e;
4343
| ^^^^ patterns `B` and `C` not covered
44+
|
45+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
46+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
47+
help: you might want to use `if let` to ignore the variant that isn't matched
48+
|
49+
LL | if let E::A = e { /* */ }
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
4451

4552
error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered
4653
--> $DIR/non-exhaustive-defined-here.rs:40:11
@@ -85,6 +92,13 @@ LL | | }
8592
...
8693
LL | let E::A = e;
8794
| ^^^^ patterns `&B` and `&C` not covered
95+
|
96+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
97+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
98+
help: you might want to use `if let` to ignore the variant that isn't matched
99+
|
100+
LL | if let E::A = e { /* */ }
101+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
88102

89103
error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered
90104
--> $DIR/non-exhaustive-defined-here.rs:48:11
@@ -129,6 +143,13 @@ LL | | }
129143
...
130144
LL | let E::A = e;
131145
| ^^^^ patterns `&&mut &B` and `&&mut &C` not covered
146+
|
147+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
148+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
149+
help: you might want to use `if let` to ignore the variant that isn't matched
150+
|
151+
LL | if let E::A = e { /* */ }
152+
|
132153

133154
error[E0004]: non-exhaustive patterns: `None` not covered
134155
--> $DIR/non-exhaustive-defined-here.rs:65:11
@@ -163,6 +184,13 @@ LL | | }
163184
...
164185
LL | let Opt::Some(ref _x) = e;
165186
| ^^^^^^^^^^^^^^^^^ pattern `None` not covered
187+
|
188+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
189+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
190+
help: you might want to use `if let` to ignore the variant that isn't matched
191+
|
192+
LL | if let Opt::Some(ref _x) = e { /* */ }
193+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
166194

167195
error: aborting due to 8 previous errors
168196

src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
33
|
44
LL | let Ok(x) = res;
55
| ^^^^^ pattern `Err(_)` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
help: you might want to use `if let` to ignore the variant that isn't matched
10+
|
11+
LL | if let Ok(x) = res { /* */ }
12+
|
613

714
error[E0381]: use of possibly-uninitialized variable: `x`
815
--> $DIR/recursive-types-are-not-uninhabited.rs:8:5

src/test/ui/refutable-pattern-errors.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` an
99
|
1010
LL | let (1, (Some(1), 2..=3)) = (1, (None, 2));
1111
| ^^^^^^^^^^^^^^^^^^^^^ patterns `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered
12+
|
13+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
14+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
15+
help: you might want to use `if let` to ignore the variant that isn't matched
16+
|
17+
LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { /* */ }
18+
|
1219

1320
error: aborting due to 2 previous errors
1421

src/test/ui/uninhabited/uninhabited-irrefutable.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ LL | | }
1212
...
1313
LL | let Foo::D(_y) = x;
1414
| ^^^^^^^^^^ pattern `A(_)` not covered
15+
|
16+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
17+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
18+
help: you might want to use `if let` to ignore the variant that isn't matched
19+
|
20+
LL | if let Foo::D(_y) = x { /* */ }
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1522

1623
error: aborting due to previous error
1724

src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
5151
|
5252
LL | let Ok(x) = x;
5353
| ^^^^^ pattern `Err(_)` not covered
54+
|
55+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
56+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
57+
help: you might want to use `if let` to ignore the variant that isn't matched
58+
|
59+
LL | if let Ok(x) = x { /* */ }
60+
|
5461

5562
error: aborting due to 7 previous errors
5663

0 commit comments

Comments
 (0)