Skip to content

Commit

Permalink
Suggest _ and .. if a pattern has too few fields
Browse files Browse the repository at this point in the history
For example, this code:

    struct S(i32, f32);

    let S(x) = S(0, 1.0);

will make the compiler suggest either:

    let S(x, _) = S(0, 1.0);

or:

    let S(x, ..) = S(0, 1.0);
  • Loading branch information
camelid committed Jan 13, 2021
1 parent 058a710 commit 16692ab
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 0 deletions.
29 changes: 29 additions & 0 deletions compiler/rustc_typeck/src/check/pat.rs
Expand Up @@ -15,10 +15,12 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::Ident;
use rustc_span::{BytePos, DUMMY_SP};
use rustc_trait_selection::traits::{ObligationCause, Pattern};

use std::cmp;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::iter;

use super::report_unexpected_variant_res;

Expand Down Expand Up @@ -1040,6 +1042,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())],
Applicability::MachineApplicable,
);
} else if fields.len() > subpats.len() {
let after_fields_span = if pat_span == DUMMY_SP {
pat_span
} else {
pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi()
};

let mut wildcard_sugg =
iter::repeat("_").take(fields.len() - subpats.len()).collect::<Vec<_>>().join(", ");
if !subpats.is_empty() {
wildcard_sugg = String::from(", ") + &wildcard_sugg;
}

let rest_sugg = if subpats.is_empty() { "..".to_owned() } else { ", ..".to_owned() };

err.span_suggestion(
after_fields_span,
"use `_` to explicitly ignore each field",
wildcard_sugg,
Applicability::MaybeIncorrect,
);
err.span_suggestion(
after_fields_span,
"use `..` to ignore all unmentioned fields",
rest_sugg,
Applicability::MaybeIncorrect,
);
}

err.emit();
Expand Down
Expand Up @@ -31,6 +31,15 @@ LL | struct TupleStruct<S, T>(S, T);
...
LL | TupleStruct(_) = TupleStruct(1, 2);
| ^^^^^^^^^^^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | TupleStruct(_, _) = TupleStruct(1, 2);
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | TupleStruct(_, ..) = TupleStruct(1, 2);
| ^^^^

error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
--> $DIR/tuple_struct_destructure_fail.rs:34:5
Expand All @@ -49,6 +58,15 @@ LL | SingleVariant(S, T)
...
LL | Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
| ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | Enum::SingleVariant(_, _) = Enum::SingleVariant(1, 2);
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | Enum::SingleVariant(_, ..) = Enum::SingleVariant(1, 2);
| ^^^^

error[E0070]: invalid left-hand side of assignment
--> $DIR/tuple_struct_destructure_fail.rs:40:12
Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/error-codes/E0023.stderr
Expand Up @@ -6,6 +6,15 @@ LL | Apple(String, String),
...
LL | Fruit::Apple(a) => {},
| ^^^^^^^^^^^^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | Fruit::Apple(a, _) => {},
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | Fruit::Apple(a, ..) => {},
| ^^^^

error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
--> $DIR/E0023.rs:12:9
Expand Down
Expand Up @@ -17,6 +17,15 @@ LL | struct P<T>(T); // 1 type parameter wanted
...
LL | let P() = U {};
| ^^^ expected 1 field, found 0
|
help: use `_` to explicitly ignore each field
|
LL | let P(_) = U {};
| ^
help: use `..` to ignore all unmentioned fields
|
LL | let P(..) = U {};
| ^^

error: aborting due to 2 previous errors

Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/issues/issue-72574-2.stderr
Expand Up @@ -26,6 +26,15 @@ LL | struct Binder(i32, i32, i32);
...
LL | Binder(_a, _x @ ..) => {}
| ^^^^^^^^^^^^^^^^^^^ expected 3 fields, found 2
|
help: use `_` to explicitly ignore each field
|
LL | Binder(_a, _x @ .., _) => {}
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | Binder(_a, _x @ .., ..) => {}
| ^^^^

error: aborting due to 3 previous errors

Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/match/match-pattern-field-mismatch.stderr
Expand Up @@ -6,6 +6,15 @@ LL | Rgb(usize, usize, usize),
...
LL | Color::Rgb(_, _) => { }
| ^^^^^^^^^^^^^^^^ expected 3 fields, found 2
|
help: use `_` to explicitly ignore each field
|
LL | Color::Rgb(_, _, _) => { }
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | Color::Rgb(_, _, ..) => { }
| ^^^^

error: aborting due to previous error

Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/pattern/issue-74539.stderr
Expand Up @@ -26,6 +26,15 @@ LL | A(u8, u8),
...
LL | E::A(x @ ..) => {
| ^^^^^^^^^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | E::A(x @ .., _) => {
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | E::A(x @ .., ..) => {
| ^^^^

error: aborting due to 3 previous errors

Expand Down
49 changes: 49 additions & 0 deletions src/test/ui/pattern/pat-tuple-underfield.rs
@@ -0,0 +1,49 @@
struct S(i32, f32);
enum E {
S(i32, f32),
}

fn main() {
match S(0, 1.0) {
S(x) => {}
//~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
//~| HELP use `_` to explicitly ignore each field
//~| HELP use `..` to ignore all unmentioned fields
}
match S(0, 1.0) {
S(_) => {}
//~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
//~| HELP use `_` to explicitly ignore each field
//~| HELP use `..` to ignore all unmentioned fields
}
match S(0, 1.0) {
S() => {}
//~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 2 fields
//~| HELP use `_` to explicitly ignore each field
//~| HELP use `..` to ignore all unmentioned fields
}

match E::S(0, 1.0) {
E::S(x) => {}
//~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
//~| HELP use `_` to explicitly ignore each field
//~| HELP use `..` to ignore all unmentioned fields
}
match E::S(0, 1.0) {
E::S(_) => {}
//~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
//~| HELP use `_` to explicitly ignore each field
//~| HELP use `..` to ignore all unmentioned fields
}
match E::S(0, 1.0) {
E::S() => {}
//~^ ERROR this pattern has 0 fields, but the corresponding tuple variant has 2 fields
//~| HELP use `_` to explicitly ignore each field
//~| HELP use `..` to ignore all unmentioned fields
}
match E::S(0, 1.0) {
E::S => {}
//~^ ERROR expected unit struct, unit variant or constant, found tuple variant `E::S`
//~| HELP use the tuple variant pattern syntax instead
}
}
121 changes: 121 additions & 0 deletions src/test/ui/pattern/pat-tuple-underfield.stderr
@@ -0,0 +1,121 @@
error[E0532]: expected unit struct, unit variant or constant, found tuple variant `E::S`
--> $DIR/pat-tuple-underfield.rs:45:9
|
LL | S(i32, f32),
| ----------- `E::S` defined here
...
LL | E::S => {}
| ^^^^ help: use the tuple variant pattern syntax instead: `E::S(_, _)`

error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
--> $DIR/pat-tuple-underfield.rs:8:9
|
LL | struct S(i32, f32);
| ------------------- tuple struct defined here
...
LL | S(x) => {}
| ^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | S(x, _) => {}
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | S(x, ..) => {}
| ^^^^

error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
--> $DIR/pat-tuple-underfield.rs:14:9
|
LL | struct S(i32, f32);
| ------------------- tuple struct defined here
...
LL | S(_) => {}
| ^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | S(_, _) => {}
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | S(_, ..) => {}
| ^^^^

error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
--> $DIR/pat-tuple-underfield.rs:20:9
|
LL | struct S(i32, f32);
| ------------------- tuple struct defined here
...
LL | S() => {}
| ^^^ expected 2 fields, found 0
|
help: use `_` to explicitly ignore each field
|
LL | S(_, _) => {}
| ^^^^
help: use `..` to ignore all unmentioned fields
|
LL | S(..) => {}
| ^^

error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
--> $DIR/pat-tuple-underfield.rs:27:9
|
LL | S(i32, f32),
| ----------- tuple variant defined here
...
LL | E::S(x) => {}
| ^^^^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | E::S(x, _) => {}
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | E::S(x, ..) => {}
| ^^^^

error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
--> $DIR/pat-tuple-underfield.rs:33:9
|
LL | S(i32, f32),
| ----------- tuple variant defined here
...
LL | E::S(_) => {}
| ^^^^^^^ expected 2 fields, found 1
|
help: use `_` to explicitly ignore each field
|
LL | E::S(_, _) => {}
| ^^^
help: use `..` to ignore all unmentioned fields
|
LL | E::S(_, ..) => {}
| ^^^^

error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 2 fields
--> $DIR/pat-tuple-underfield.rs:39:9
|
LL | S(i32, f32),
| ----------- tuple variant defined here
...
LL | E::S() => {}
| ^^^^^^ expected 2 fields, found 0
|
help: use `_` to explicitly ignore each field
|
LL | E::S(_, _) => {}
| ^^^^
help: use `..` to ignore all unmentioned fields
|
LL | E::S(..) => {}
| ^^

error: aborting due to 7 previous errors

Some errors have detailed explanations: E0023, E0532.
For more information about an error, try `rustc --explain E0023`.

0 comments on commit 16692ab

Please sign in to comment.