use std::cmp;
use std::collections::hash_map::Entry::{Occupied, Vacant};
-use std::iter;
use super::report_unexpected_variant_res;
// More generally, the expected type wants a tuple variant with one field of an
// N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
// with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
- let missing_parenthesis = match (&expected.kind(), fields, had_err) {
+ let missing_parentheses = match (&expected.kind(), fields, had_err) {
// #67037: only do this if we could successfully type-check the expected type against
// the tuple struct pattern. Otherwise the substs could get out of range on e.g.,
// `let P() = U;` where `P != U` with `struct P<T>(T);`.
}
_ => false,
};
- if missing_parenthesis {
+ if missing_parentheses {
let (left, right) = match subpats {
// This is the zero case; we aim to get the "hi" part of the `QPath`'s
// span as the "lo" and then the "hi" part of the pattern's span as the "hi".
// This looks like:
//
- // help: missing parenthesis
+ // help: missing parentheses
// |
// L | let A(()) = A(());
// | ^ ^
// last sub-pattern. In the case of `A(x)` the first and last may coincide.
// This looks like:
//
- // help: missing parenthesis
+ // help: missing parentheses
// |
// L | let A((x, y)) = A((1, 2));
// | ^ ^
[first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span),
};
err.multipart_suggestion(
- "missing parenthesis",
+ "missing parentheses",
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()
+ } else if fields.len() > subpats.len() && pat_span != DUMMY_SP {
+ let after_fields_span = pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi();
+ let all_fields_span = match subpats {
+ [] => after_fields_span,
+ [field] => field.span,
+ [first, .., last] => first.span.to(last.span),
+ };
+
+ // Check if all the fields in the pattern are wildcards.
+ let all_wildcards = subpats.iter().all(|pat| matches!(pat.kind, PatKind::Wild));
+ let first_tail_wildcard =
+ subpats.iter().enumerate().fold(None, |acc, (pos, pat)| match (acc, &pat.kind) {
+ (None, PatKind::Wild) => Some(pos),
+ (Some(_), PatKind::Wild) => acc,
+ _ => None,
+ });
+ let tail_span = match first_tail_wildcard {
+ None => after_fields_span,
+ Some(0) => subpats[0].span.to(after_fields_span),
+ Some(pos) => subpats[pos - 1].span.shrink_to_hi().to(after_fields_span),
};
+ // FIXME: heuristic-based suggestion to check current types for where to add `_`.
let mut wildcard_sugg = vec!["_"; fields.len() - subpats.len()].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(
+ err.span_suggestion_verbose(
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,
- );
+
+ // Only suggest `..` if more than one field is missing
+ // or the pattern consists of all wildcards.
+ if fields.len() - subpats.len() > 1 || all_wildcards {
+ if subpats.is_empty() || all_wildcards {
+ err.span_suggestion_verbose(
+ all_fields_span,
+ "use `..` to ignore all fields",
+ String::from(".."),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ tail_span,
+ "use `..` to ignore the rest of the fields",
+ String::from(", .."),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
}
err.emit();
/// Returns a diagnostic reporting a struct pattern which does not mention some fields.
///
/// ```text
- /// error[E0027]: pattern does not mention field `you_cant_use_this_field`
+ /// error[E0027]: pattern does not mention field `bar`
/// --> src/main.rs:15:9
/// |
/// LL | let foo::Foo {} = foo::Foo::new();
- /// | ^^^^^^^^^^^ missing field `you_cant_use_this_field`
+ /// | ^^^^^^^^^^^ missing field `bar`
/// ```
fn error_unmentioned_fields(
&self,
}
_ => return err,
},
- [.., field] => (
- match pat.kind {
- PatKind::Struct(_, [_, ..], _) => ", ",
- _ => "",
- },
- "",
- field.span.shrink_to_hi(),
- ),
+ [.., field] => {
+ // if last field has a trailing comma, use the comma
+ // as the span to avoid trailing comma in ultimate
+ // suggestion (Issue #78511)
+ let tail = field.span.shrink_to_hi().until(pat.span.shrink_to_hi());
+ let tail_through_comma = self.tcx.sess.source_map().span_through_char(tail, ',');
+ let sp = if tail_through_comma == tail {
+ field.span.shrink_to_hi()
+ } else {
+ tail_through_comma
+ };
+ (
+ match pat.kind {
+ PatKind::Struct(_, [_, ..], _) => ", ",
+ _ => "",
+ },
+ "",
+ sp,
+ )
+ }
};
err.span_suggestion(
sp,