From cda6ecfc3d463119ae730ff3c85267d0a2724b60 Mon Sep 17 00:00:00 2001 From: TheWastl <36932506+TheWastl@users.noreply.github.com> Date: Tue, 10 Aug 2021 16:17:45 +0200 Subject: [PATCH] typeck: better diagnostics for missing inaccessible fields in struct literals/patterns - typeck/expr: don't suggest adding fields in struct literals with inaccessible fields - typeck/pat: suggest ignoring inaccessible fields in struct patterns --- compiler/rustc_typeck/src/check/expr.rs | 17 ++++------ compiler/rustc_typeck/src/check/pat.rs | 32 +++++++++++++------ ...7872-missing-inaccessible-field-literal.rs | 11 +++++++ ...-missing-inaccessible-field-literal.stderr | 8 +++++ ...7872-missing-inaccessible-field-pattern.rs | 11 +++++++ ...-missing-inaccessible-field-pattern.stderr | 18 +++++++++++ 6 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs create mode 100644 src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr create mode 100644 src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.rs create mode 100644 src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 831b573e1568a..a536285651102 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1313,15 +1313,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } } else if check_completeness && !error_happened && !remaining_fields.is_empty() { - let no_accessible_remaining_fields = remaining_fields - .iter() - .find(|(_, (_, field))| { - field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) - }) - .is_none(); + let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { + !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) + }); - if no_accessible_remaining_fields { - self.report_no_accessible_fields(adt_ty, span); + if inaccessible_remaining_fields { + self.report_inaccessible_fields(adt_ty, span); } else { self.report_missing_fields(adt_ty, span, remaining_fields); } @@ -1398,7 +1395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } - /// Report an error for a struct field expression when there are no visible fields. + /// Report an error for a struct field expression when there are invisible fields. /// /// ```text /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields @@ -1409,7 +1406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// error: aborting due to previous error /// ``` - fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { + fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { self.tcx.sess.span_err( span, &format!( diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index db77d155a2bae..dae574bb7bf0f 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1250,15 +1250,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); } } else if !etc && !unmentioned_fields.is_empty() { - let no_accessible_unmentioned_fields = !unmentioned_fields.iter().any(|(field, _)| { - field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) - }); + let accessible_unmentioned_fields: Vec<_> = unmentioned_fields + .iter() + .copied() + .filter(|(field, _)| { + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + }) + .collect(); - if no_accessible_unmentioned_fields { + 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, &unmentioned_fields, &fields)); + 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) { @@ -1583,17 +1591,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'_>, unmentioned_fields: &[(&ty::FieldDef, Ident)], + have_inaccessible_fields: bool, fields: &'tcx [hir::PatField<'tcx>], ) -> DiagnosticBuilder<'tcx> { + let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" }; let field_names = if unmentioned_fields.len() == 1 { - format!("field `{}`", unmentioned_fields[0].1) + format!("field `{}`{}", unmentioned_fields[0].1, inaccessible) } else { let fields = unmentioned_fields .iter() .map(|(_, name)| format!("`{}`", name)) .collect::>() .join(", "); - format!("fields {}", fields) + format!("fields {}{}", fields, inaccessible) }; let mut err = struct_span_err!( self.tcx.sess, @@ -1624,17 +1634,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion( sp, &format!( - "include the missing field{} in the pattern", + "include the missing field{} in the pattern{}", if len == 1 { "" } else { "s" }, + if have_inaccessible_fields { " and ignore the inaccessible fields" } else { "" } ), format!( - "{}{}{}", + "{}{}{}{}", prefix, unmentioned_fields .iter() .map(|(_, name)| name.to_string()) .collect::>() .join(", "), + if have_inaccessible_fields { ", .." } else { "" }, postfix, ), Applicability::MachineApplicable, diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs new file mode 100644 index 0000000000000..3176144133760 --- /dev/null +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs @@ -0,0 +1,11 @@ +pub mod foo { + pub struct Foo { + pub you_can_use_this_field: bool, + you_cant_use_this_field: bool, + } +} + +fn main() { + foo::Foo {}; + //~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields +} diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr new file mode 100644 index 0000000000000..81b73c00e8600 --- /dev/null +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -0,0 +1,8 @@ +error: cannot construct `Foo` with struct literal syntax due to inaccessible fields + --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:9:5 + | +LL | foo::Foo {}; + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.rs b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.rs new file mode 100644 index 0000000000000..d28e17559d879 --- /dev/null +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.rs @@ -0,0 +1,11 @@ +#![allow(dead_code, unused_variables)] + +pub mod foo { + #[derive(Default)] + pub struct Foo { pub visible: bool, invisible: bool, } +} + +fn main() { + let foo::Foo {} = foo::Foo::default(); + //~^ ERROR pattern does not mention field `visible` and inaccessible fields +} diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr new file mode 100644 index 0000000000000..51b8e39b101a3 --- /dev/null +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr @@ -0,0 +1,18 @@ +error[E0027]: pattern does not mention field `visible` and inaccessible fields + --> $DIR/issue-87872-missing-inaccessible-field-pattern.rs:9:9 + | +LL | let foo::Foo {} = foo::Foo::default(); + | ^^^^^^^^^^^ missing field `visible` and inaccessible fields + | +help: include the missing field in the pattern and ignore the inaccessible fields + | +LL | let foo::Foo { visible, .. } = foo::Foo::default(); + | ^^^^^^^^^^^^^^^ +help: if you don't care about this missing field, you can explicitly ignore it + | +LL | let foo::Foo { .. } = foo::Foo::default(); + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0027`.