Skip to content

Commit d3b3106

Browse files
authored
Rollup merge of #80017 - camelid:sugg-rest-pattern, r=estebank
Suggest `_` and `..` if a pattern has too few fields Fixes #80010.
2 parents 8ac21fb + e8c8793 commit d3b3106

9 files changed

+291
-7
lines changed

compiler/rustc_typeck/src/check/pat.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_span::hygiene::DesugaringKind;
1515
use rustc_span::lev_distance::find_best_match_for_name;
1616
use rustc_span::source_map::{Span, Spanned};
1717
use rustc_span::symbol::Ident;
18+
use rustc_span::{BytePos, DUMMY_SP};
1819
use rustc_trait_selection::traits::{ObligationCause, Pattern};
1920

2021
use std::cmp;
@@ -1001,7 +1002,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10011002
// More generally, the expected type wants a tuple variant with one field of an
10021003
// N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
10031004
// with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
1004-
let missing_parenthesis = match (&expected.kind(), fields, had_err) {
1005+
let missing_parentheses = match (&expected.kind(), fields, had_err) {
10051006
// #67037: only do this if we could successfully type-check the expected type against
10061007
// the tuple struct pattern. Otherwise the substs could get out of range on e.g.,
10071008
// `let P() = U;` where `P != U` with `struct P<T>(T);`.
@@ -1014,13 +1015,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10141015
}
10151016
_ => false,
10161017
};
1017-
if missing_parenthesis {
1018+
if missing_parentheses {
10181019
let (left, right) = match subpats {
10191020
// This is the zero case; we aim to get the "hi" part of the `QPath`'s
10201021
// span as the "lo" and then the "hi" part of the pattern's span as the "hi".
10211022
// This looks like:
10221023
//
1023-
// help: missing parenthesis
1024+
// help: missing parentheses
10241025
// |
10251026
// L | let A(()) = A(());
10261027
// | ^ ^
@@ -1029,17 +1030,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10291030
// last sub-pattern. In the case of `A(x)` the first and last may coincide.
10301031
// This looks like:
10311032
//
1032-
// help: missing parenthesis
1033+
// help: missing parentheses
10331034
// |
10341035
// L | let A((x, y)) = A((1, 2));
10351036
// | ^ ^
10361037
[first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span),
10371038
};
10381039
err.multipart_suggestion(
1039-
"missing parenthesis",
1040+
"missing parentheses",
10401041
vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())],
10411042
Applicability::MachineApplicable,
10421043
);
1044+
} else if fields.len() > subpats.len() {
1045+
let after_fields_span = if pat_span == DUMMY_SP {
1046+
pat_span
1047+
} else {
1048+
pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi()
1049+
};
1050+
let all_fields_span = match subpats {
1051+
[] => after_fields_span,
1052+
[field] => field.span,
1053+
[first, .., last] => first.span.to(last.span),
1054+
};
1055+
1056+
// Check if all the fields in the pattern are wildcards.
1057+
let all_wildcards = subpats.iter().all(|pat| matches!(pat.kind, PatKind::Wild));
1058+
1059+
let mut wildcard_sugg = vec!["_"; fields.len() - subpats.len()].join(", ");
1060+
if !subpats.is_empty() {
1061+
wildcard_sugg = String::from(", ") + &wildcard_sugg;
1062+
}
1063+
1064+
err.span_suggestion_verbose(
1065+
after_fields_span,
1066+
"use `_` to explicitly ignore each field",
1067+
wildcard_sugg,
1068+
Applicability::MaybeIncorrect,
1069+
);
1070+
1071+
// Only suggest `..` if more than one field is missing
1072+
// or the pattern consists of all wildcards.
1073+
if fields.len() - subpats.len() > 1 || all_wildcards {
1074+
if subpats.is_empty() || all_wildcards {
1075+
err.span_suggestion_verbose(
1076+
all_fields_span,
1077+
"use `..` to ignore all fields",
1078+
String::from(".."),
1079+
Applicability::MaybeIncorrect,
1080+
);
1081+
} else {
1082+
err.span_suggestion_verbose(
1083+
after_fields_span,
1084+
"use `..` to ignore the rest of the fields",
1085+
String::from(", .."),
1086+
Applicability::MaybeIncorrect,
1087+
);
1088+
}
1089+
}
10431090
}
10441091

10451092
err.emit();

src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr

+18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ LL | struct TupleStruct<S, T>(S, T);
3131
...
3232
LL | TupleStruct(_) = TupleStruct(1, 2);
3333
| ^^^^^^^^^^^^^^ expected 2 fields, found 1
34+
|
35+
help: use `_` to explicitly ignore each field
36+
|
37+
LL | TupleStruct(_, _) = TupleStruct(1, 2);
38+
| ^^^
39+
help: use `..` to ignore all fields
40+
|
41+
LL | TupleStruct(..) = TupleStruct(1, 2);
42+
| ^^
3443

3544
error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
3645
--> $DIR/tuple_struct_destructure_fail.rs:34:5
@@ -49,6 +58,15 @@ LL | SingleVariant(S, T)
4958
...
5059
LL | Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
5160
| ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1
61+
|
62+
help: use `_` to explicitly ignore each field
63+
|
64+
LL | Enum::SingleVariant(_, _) = Enum::SingleVariant(1, 2);
65+
| ^^^
66+
help: use `..` to ignore all fields
67+
|
68+
LL | Enum::SingleVariant(..) = Enum::SingleVariant(1, 2);
69+
| ^^
5270

5371
error[E0070]: invalid left-hand side of assignment
5472
--> $DIR/tuple_struct_destructure_fail.rs:40:12

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | Apple(String, String),
66
...
77
LL | Fruit::Apple(a) => {},
88
| ^^^^^^^^^^^^^^^ expected 2 fields, found 1
9+
|
10+
help: use `_` to explicitly ignore each field
11+
|
12+
LL | Fruit::Apple(a, _) => {},
13+
| ^^^
914

1015
error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
1116
--> $DIR/E0023.rs:12:9
@@ -34,7 +39,7 @@ LL | Orange((String, String)),
3439
LL | Fruit::Orange(a, b) => {},
3540
| ^^^^^^^^^^^^^^^^^^^ expected 1 field, found 2
3641
|
37-
help: missing parenthesis
42+
help: missing parentheses
3843
|
3944
LL | Fruit::Orange((a, b)) => {},
4045
| ^ ^
@@ -48,7 +53,7 @@ LL | Banana(()),
4853
LL | Fruit::Banana() => {},
4954
| ^^^^^^^^^^^^^^^ expected 1 field, found 0
5055
|
51-
help: missing parenthesis
56+
help: missing parentheses
5257
|
5358
LL | Fruit::Banana(()) => {},
5459
| ^ ^

src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ LL | struct P<T>(T); // 1 type parameter wanted
1717
...
1818
LL | let P() = U {};
1919
| ^^^ expected 1 field, found 0
20+
|
21+
help: use `_` to explicitly ignore each field
22+
|
23+
LL | let P(_) = U {};
24+
| ^
25+
help: use `..` to ignore all fields
26+
|
27+
LL | let P(..) = U {};
28+
| ^^
2029

2130
error: aborting due to 2 previous errors
2231

src/test/ui/issues/issue-72574-2.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ LL | struct Binder(i32, i32, i32);
2626
...
2727
LL | Binder(_a, _x @ ..) => {}
2828
| ^^^^^^^^^^^^^^^^^^^ expected 3 fields, found 2
29+
|
30+
help: use `_` to explicitly ignore each field
31+
|
32+
LL | Binder(_a, _x @ .., _) => {}
33+
| ^^^
2934

3035
error: aborting due to 3 previous errors
3136

src/test/ui/match/match-pattern-field-mismatch.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ LL | Rgb(usize, usize, usize),
66
...
77
LL | Color::Rgb(_, _) => { }
88
| ^^^^^^^^^^^^^^^^ expected 3 fields, found 2
9+
|
10+
help: use `_` to explicitly ignore each field
11+
|
12+
LL | Color::Rgb(_, _, _) => { }
13+
| ^^^
14+
help: use `..` to ignore all fields
15+
|
16+
LL | Color::Rgb(..) => { }
17+
| ^^
918

1019
error: aborting due to previous error
1120

src/test/ui/pattern/issue-74539.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ LL | A(u8, u8),
2626
...
2727
LL | E::A(x @ ..) => {
2828
| ^^^^^^^^^^^^ expected 2 fields, found 1
29+
|
30+
help: use `_` to explicitly ignore each field
31+
|
32+
LL | E::A(x @ .., _) => {
33+
| ^^^
2934

3035
error: aborting due to 3 previous errors
3136

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
struct S(i32, f32);
2+
enum E {
3+
S(i32, f32),
4+
}
5+
struct Point4(i32, i32, i32, i32);
6+
7+
fn main() {
8+
match S(0, 1.0) {
9+
S(x) => {}
10+
//~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
11+
//~| HELP use `_` to explicitly ignore each field
12+
}
13+
match S(0, 1.0) {
14+
S(_) => {}
15+
//~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
16+
//~| HELP use `_` to explicitly ignore each field
17+
//~| HELP use `..` to ignore all fields
18+
}
19+
match S(0, 1.0) {
20+
S() => {}
21+
//~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 2 fields
22+
//~| HELP use `_` to explicitly ignore each field
23+
//~| HELP use `..` to ignore all fields
24+
}
25+
26+
match E::S(0, 1.0) {
27+
E::S(x) => {}
28+
//~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
29+
//~| HELP use `_` to explicitly ignore each field
30+
}
31+
match E::S(0, 1.0) {
32+
E::S(_) => {}
33+
//~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
34+
//~| HELP use `_` to explicitly ignore each field
35+
//~| HELP use `..` to ignore all fields
36+
}
37+
match E::S(0, 1.0) {
38+
E::S() => {}
39+
//~^ ERROR this pattern has 0 fields, but the corresponding tuple variant has 2 fields
40+
//~| HELP use `_` to explicitly ignore each field
41+
//~| HELP use `..` to ignore all fields
42+
}
43+
match E::S(0, 1.0) {
44+
E::S => {}
45+
//~^ ERROR expected unit struct, unit variant or constant, found tuple variant `E::S`
46+
//~| HELP use the tuple variant pattern syntax instead
47+
}
48+
49+
match Point4(0, 1, 2, 3) {
50+
Point4( a , _ ) => {}
51+
//~^ ERROR this pattern has 2 fields, but the corresponding tuple struct has 4 fields
52+
//~| HELP use `_` to explicitly ignore each field
53+
//~| HELP use `..` to ignore the rest of the fields
54+
}
55+
}

0 commit comments

Comments
 (0)