@@ -15,6 +15,7 @@ use rustc_span::hygiene::DesugaringKind;
1515use rustc_span::lev_distance::find_best_match_for_name;
1616use rustc_span::source_map::{Span, Spanned};
1717use rustc_span::symbol::Ident;
18+ use rustc_span::{BytePos, DUMMY_SP};
1819use rustc_trait_selection::traits::{ObligationCause, Pattern};
1920
2021use 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();
0 commit comments