use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable};
+use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::{Span, Spanned};
};
// Require `..` if struct has non_exhaustive attribute.
- if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
+ let non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local();
+ if non_exhaustive && !etc {
self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
}
if etc {
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
}
- } else if !etc && !unmentioned_fields.is_empty() {
+ } else if !unmentioned_fields.is_empty() {
let accessible_unmentioned_fields: Vec<_> = unmentioned_fields
.iter()
.copied()
field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx)
})
.collect();
-
- if accessible_unmentioned_fields.is_empty() {
- unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
- } else {
- unmentioned_err = Some(self.error_unmentioned_fields(
- pat,
- &accessible_unmentioned_fields,
- accessible_unmentioned_fields.len() != unmentioned_fields.len(),
- &fields,
- ));
+ if non_exhaustive {
+ self.non_exhaustive_reachable_pattern(pat, &accessible_unmentioned_fields, adt_ty)
+ } else if !etc {
+ if accessible_unmentioned_fields.is_empty() {
+ unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
+ } else {
+ unmentioned_err = Some(self.error_unmentioned_fields(
+ pat,
+ &accessible_unmentioned_fields,
+ accessible_unmentioned_fields.len() != unmentioned_fields.len(),
+ &fields,
+ ));
+ }
}
}
match (inexistent_fields_err, unmentioned_err) {
plural
),
);
- if plural == "" {
+
+ if unmentioned_fields.len() == 1 {
let input =
unmentioned_fields.iter().map(|(_, field)| field.name).collect::<Vec<_>>();
let suggested_name = find_best_match_for_name(&input, ident.name, None);
// We don't want to throw `E0027` in case we have thrown `E0026` for them.
unmentioned_fields.retain(|&(_, x)| x.name != suggested_name);
}
+ } else if inexistent_fields.len() == 1 {
+ let unmentioned_field = unmentioned_fields[0].1.name;
+ err.span_suggestion_short(
+ ident.span,
+ &format!(
+ "`{}` has a field named `{}`",
+ tcx.def_path_str(variant.def_id),
+ unmentioned_field
+ ),
+ unmentioned_field.to_string(),
+ Applicability::MaybeIncorrect,
+ );
}
}
}
err
}
+ /// Report that a pattern for a `#[non_exhaustive]` struct marked with `non_exhaustive_omitted_patterns`
+ /// is not exhaustive enough.
+ ///
+ /// Nb: the partner lint for enums lives in `compiler/rustc_mir_build/src/thir/pattern/usefulness.rs`.
+ fn non_exhaustive_reachable_pattern(
+ &self,
+ pat: &Pat<'_>,
+ unmentioned_fields: &[(&ty::FieldDef, Ident)],
+ ty: Ty<'tcx>,
+ ) {
+ fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String {
+ const LIMIT: usize = 3;
+ match witnesses {
+ [] => bug!(),
+ [witness] => format!("`{}`", witness),
+ [head @ .., tail] if head.len() < LIMIT => {
+ let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+ format!("`{}` and `{}`", head.join("`, `"), tail)
+ }
+ _ => {
+ let (head, tail) = witnesses.split_at(LIMIT);
+ let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+ format!("`{}` and {} more", head.join("`, `"), tail.len())
+ }
+ }
+ }
+ let joined_patterns = joined_uncovered_patterns(
+ &unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
+ );
+
+ self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| {
+ let mut lint = build.build("some fields are not explicitly listed");
+ lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns));
+
+ lint.help(
+ "ensure that all fields are mentioned explicitly by adding the suggested fields",
+ );
+ lint.note(&format!(
+ "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
+ ty,
+ ));
+ lint.emit();
+ });
+ }
+
/// Returns a diagnostic reporting a struct pattern which does not mention some fields.
///
/// ```text