Skip to content

Commit

Permalink
typeck: better diagnostics for missing inaccessible fields in struct …
Browse files Browse the repository at this point in the history
…literals/patterns

- typeck/expr: don't suggest adding fields in struct literals with inaccessible fields
- typeck/pat: suggest ignoring inaccessible fields in struct patterns
  • Loading branch information
TheWastl committed Aug 10, 2021
1 parent eaf6f46 commit cda6ecf
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 20 deletions.
17 changes: 7 additions & 10 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -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!(
Expand Down
32 changes: 22 additions & 10 deletions compiler/rustc_typeck/src/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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::<Vec<String>>()
.join(", ");
format!("fields {}", fields)
format!("fields {}{}", fields, inaccessible)
};
let mut err = struct_span_err!(
self.tcx.sess,
Expand Down Expand Up @@ -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::<Vec<_>>()
.join(", "),
if have_inaccessible_fields { ", .." } else { "" },
postfix,
),
Applicability::MachineApplicable,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit cda6ecf

Please sign in to comment.