From 16692ab66a6e2c62b416f606f144d60711c14414 Mon Sep 17 00:00:00 2001 From: Camelid Date: Sun, 13 Dec 2020 14:13:52 -0800 Subject: [PATCH] Suggest `_` and `..` if a pattern has too few fields 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); --- compiler/rustc_typeck/src/check/pat.rs | 29 +++++ .../tuple_struct_destructure_fail.stderr | 18 +++ src/test/ui/error-codes/E0023.stderr | 9 ++ ...7-pat-tup-scrut-ty-diff-less-fields.stderr | 9 ++ src/test/ui/issues/issue-72574-2.stderr | 9 ++ .../match/match-pattern-field-mismatch.stderr | 9 ++ src/test/ui/pattern/issue-74539.stderr | 9 ++ src/test/ui/pattern/pat-tuple-underfield.rs | 49 +++++++ .../ui/pattern/pat-tuple-underfield.stderr | 121 ++++++++++++++++++ 9 files changed, 262 insertions(+) create mode 100644 src/test/ui/pattern/pat-tuple-underfield.rs create mode 100644 src/test/ui/pattern/pat-tuple-underfield.stderr diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 5fc573a57ad..942be1007f0 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -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; @@ -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::>().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(); diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr index 0e7174e5b19..623e1d674d7 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -31,6 +31,15 @@ LL | struct TupleStruct(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 @@ -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 diff --git a/src/test/ui/error-codes/E0023.stderr b/src/test/ui/error-codes/E0023.stderr index a3610099294..1d32eab15d3 100644 --- a/src/test/ui/error-codes/E0023.stderr +++ b/src/test/ui/error-codes/E0023.stderr @@ -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 diff --git a/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr index 6e8ea6bf618..38177d15158 100644 --- a/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr +++ b/src/test/ui/issues/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr @@ -17,6 +17,15 @@ LL | struct P(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 diff --git a/src/test/ui/issues/issue-72574-2.stderr b/src/test/ui/issues/issue-72574-2.stderr index 0a9c868af7a..8edc6ca8f0e 100644 --- a/src/test/ui/issues/issue-72574-2.stderr +++ b/src/test/ui/issues/issue-72574-2.stderr @@ -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 diff --git a/src/test/ui/match/match-pattern-field-mismatch.stderr b/src/test/ui/match/match-pattern-field-mismatch.stderr index c2298d6fbbf..299b2d216db 100644 --- a/src/test/ui/match/match-pattern-field-mismatch.stderr +++ b/src/test/ui/match/match-pattern-field-mismatch.stderr @@ -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 diff --git a/src/test/ui/pattern/issue-74539.stderr b/src/test/ui/pattern/issue-74539.stderr index cbc90b5397d..7a73d39dbc0 100644 --- a/src/test/ui/pattern/issue-74539.stderr +++ b/src/test/ui/pattern/issue-74539.stderr @@ -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 diff --git a/src/test/ui/pattern/pat-tuple-underfield.rs b/src/test/ui/pattern/pat-tuple-underfield.rs new file mode 100644 index 00000000000..246dd0b37b6 --- /dev/null +++ b/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 + } +} diff --git a/src/test/ui/pattern/pat-tuple-underfield.stderr b/src/test/ui/pattern/pat-tuple-underfield.stderr new file mode 100644 index 00000000000..5364053b8e5 --- /dev/null +++ b/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`.