Rollup merge of #87895 - TheWastl:issue-87872, r=estebank

typeck: don't suggest inaccessible fields in struct literals and suggest ignoring inaccessible fields in struct patterns

Fixes #87872.

This PR adjusts the missing field diagnostic logic in typeck so that when any of the missing fields in a struct literal or pattern is inaccessible then the error is less confusing, even if some of the missing fields are accessible.

See also #76524.
This commit is contained in:
Yuki Okushi 2021-08-11 04:18:47 +09:00 committed by GitHub
commit 4be63b2b5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 20 deletions

View File

@ -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!(

View File

@ -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, _)| {
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::<Vec<String>>()
.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::<Vec<_>>()
.join(", "),
if have_inaccessible_fields { ", .." } else { "" },
postfix,
),
Applicability::MachineApplicable,

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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`.