Rollup merge of #76524 - davidtwco:issue-76077-inaccessible-private-fields, r=estebank
typeck: don't suggest inaccessible private fields Fixes #76077. This PR adjusts the missing field diagnostic logic in typeck so that when none of the missing fields in a struct expr are accessible then the error is less confusing. r? @estebank
This commit is contained in:
commit
9f8a7827a1
7 changed files with 244 additions and 42 deletions
|
@ -1241,6 +1241,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
tcx.sess.span_err(span, "union expressions should have exactly one field");
|
tcx.sess.span_err(span, "union expressions should have exactly one field");
|
||||||
}
|
}
|
||||||
} else if check_completeness && !error_happened && !remaining_fields.is_empty() {
|
} else if check_completeness && !error_happened && !remaining_fields.is_empty() {
|
||||||
|
let no_accessible_remaining_fields = remaining_fields
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, (_, field))| {
|
||||||
|
field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.is_none();
|
||||||
|
|
||||||
|
if no_accessible_remaining_fields {
|
||||||
|
self.report_no_accessible_fields(adt_ty, span);
|
||||||
|
} else {
|
||||||
|
self.report_missing_field(adt_ty, span, remaining_fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_happened
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_struct_fields_on_error(
|
||||||
|
&self,
|
||||||
|
fields: &'tcx [hir::Field<'tcx>],
|
||||||
|
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
||||||
|
) {
|
||||||
|
for field in fields {
|
||||||
|
self.check_expr(&field.expr);
|
||||||
|
}
|
||||||
|
if let Some(ref base) = *base_expr {
|
||||||
|
self.check_expr(&base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Report an error for a struct field expression when there are fields which aren't provided.
|
||||||
|
///
|
||||||
|
/// ```ignore (diagnostic)
|
||||||
|
/// error: missing field `you_can_use_this_field` in initializer of `foo::Foo`
|
||||||
|
/// --> src/main.rs:8:5
|
||||||
|
/// |
|
||||||
|
/// 8 | foo::Foo {};
|
||||||
|
/// | ^^^^^^^^ missing `you_can_use_this_field`
|
||||||
|
///
|
||||||
|
/// error: aborting due to previous error
|
||||||
|
/// ```
|
||||||
|
fn report_missing_field(
|
||||||
|
&self,
|
||||||
|
adt_ty: Ty<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>,
|
||||||
|
) {
|
||||||
|
let tcx = self.tcx;
|
||||||
let len = remaining_fields.len();
|
let len = remaining_fields.len();
|
||||||
|
|
||||||
let mut displayable_field_names =
|
let mut displayable_field_names =
|
||||||
|
@ -1271,26 +1320,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
truncated_fields_error,
|
truncated_fields_error,
|
||||||
adt_ty
|
adt_ty
|
||||||
)
|
)
|
||||||
.span_label(
|
.span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error))
|
||||||
span,
|
|
||||||
format!("missing {}{}", remaining_fields_names, truncated_fields_error),
|
|
||||||
)
|
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
error_happened
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_struct_fields_on_error(
|
/// Report an error for a struct field expression when there are no visible fields.
|
||||||
&self,
|
///
|
||||||
fields: &'tcx [hir::Field<'tcx>],
|
/// ```ignore (diagnostic)
|
||||||
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
/// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
|
||||||
) {
|
/// --> src/main.rs:8:5
|
||||||
for field in fields {
|
/// |
|
||||||
self.check_expr(&field.expr);
|
/// 8 | foo::Foo {};
|
||||||
}
|
/// | ^^^^^^^^
|
||||||
if let Some(ref base) = *base_expr {
|
///
|
||||||
self.check_expr(&base);
|
/// error: aborting due to previous error
|
||||||
}
|
/// ```
|
||||||
|
fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
|
||||||
|
self.tcx.sess.span_err(
|
||||||
|
span,
|
||||||
|
&format!(
|
||||||
|
"cannot construct `{}` with struct literal syntax due to inaccessible fields",
|
||||||
|
adt_ty,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_unknown_field(
|
fn report_unknown_field(
|
||||||
|
|
|
@ -1078,8 +1078,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let mut unmentioned_fields = variant
|
let mut unmentioned_fields = variant
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| field.ident.normalize_to_macros_2_0())
|
.map(|field| (field, field.ident.normalize_to_macros_2_0()))
|
||||||
.filter(|ident| !used_fields.contains_key(&ident))
|
.filter(|(_, ident)| !used_fields.contains_key(&ident))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) {
|
let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) {
|
||||||
|
@ -1110,8 +1110,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
|
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
|
||||||
}
|
}
|
||||||
} else if !etc && !unmentioned_fields.is_empty() {
|
} else if !etc && !unmentioned_fields.is_empty() {
|
||||||
|
let no_accessible_unmentioned_fields = unmentioned_fields
|
||||||
|
.iter()
|
||||||
|
.filter(|(field, _)| {
|
||||||
|
field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx)
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.is_none();
|
||||||
|
|
||||||
|
if no_accessible_unmentioned_fields {
|
||||||
|
unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
|
||||||
|
} else {
|
||||||
unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
|
unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
match (inexistent_fields_err, unmentioned_err) {
|
match (inexistent_fields_err, unmentioned_err) {
|
||||||
(Some(mut i), Some(mut u)) => {
|
(Some(mut i), Some(mut u)) => {
|
||||||
if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
|
if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
|
||||||
|
@ -1173,7 +1185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
&self,
|
&self,
|
||||||
kind_name: &str,
|
kind_name: &str,
|
||||||
inexistent_fields: &[Ident],
|
inexistent_fields: &[Ident],
|
||||||
unmentioned_fields: &mut Vec<Ident>,
|
unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>,
|
||||||
variant: &ty::VariantDef,
|
variant: &ty::VariantDef,
|
||||||
) -> DiagnosticBuilder<'tcx> {
|
) -> DiagnosticBuilder<'tcx> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
|
@ -1215,7 +1227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if plural == "" {
|
if plural == "" {
|
||||||
let input = unmentioned_fields.iter().map(|field| &field.name);
|
let input = unmentioned_fields.iter().map(|(_, field)| &field.name);
|
||||||
let suggested_name = find_best_match_for_name(input, ident.name, None);
|
let suggested_name = find_best_match_for_name(input, ident.name, None);
|
||||||
if let Some(suggested_name) = suggested_name {
|
if let Some(suggested_name) = suggested_name {
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
|
@ -1232,7 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// `smart_resolve_context_dependent_help`.
|
// `smart_resolve_context_dependent_help`.
|
||||||
if suggested_name.to_ident_string().parse::<usize>().is_err() {
|
if suggested_name.to_ident_string().parse::<usize>().is_err() {
|
||||||
// We don't want to throw `E0027` in case we have thrown `E0026` for them.
|
// We don't want to throw `E0027` in case we have thrown `E0026` for them.
|
||||||
unmentioned_fields.retain(|&x| x.name != suggested_name);
|
unmentioned_fields.retain(|&(_, x)| x.name != suggested_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1300,17 +1312,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a diagnostic reporting a struct pattern which is missing an `..` due to
|
||||||
|
/// inaccessible fields.
|
||||||
|
///
|
||||||
|
/// ```ignore (diagnostic)
|
||||||
|
/// error: pattern requires `..` due to inaccessible fields
|
||||||
|
/// --> src/main.rs:10:9
|
||||||
|
/// |
|
||||||
|
/// LL | let foo::Foo {} = foo::Foo::default();
|
||||||
|
/// | ^^^^^^^^^^^
|
||||||
|
/// |
|
||||||
|
/// help: add a `..`
|
||||||
|
/// |
|
||||||
|
/// LL | let foo::Foo { .. } = foo::Foo::default();
|
||||||
|
/// | ^^^^^^
|
||||||
|
/// ```
|
||||||
|
fn error_no_accessible_fields(
|
||||||
|
&self,
|
||||||
|
pat: &Pat<'_>,
|
||||||
|
fields: &'tcx [hir::FieldPat<'tcx>],
|
||||||
|
) -> DiagnosticBuilder<'tcx> {
|
||||||
|
let mut err = self
|
||||||
|
.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields");
|
||||||
|
|
||||||
|
if let Some(field) = fields.last() {
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
field.span.shrink_to_hi(),
|
||||||
|
"ignore the inaccessible and unused fields",
|
||||||
|
", ..".to_string(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind {
|
||||||
|
qpath.span()
|
||||||
|
} else {
|
||||||
|
bug!("`error_no_accessible_fields` called on non-struct pattern");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`.
|
||||||
|
let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi());
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
span,
|
||||||
|
"ignore the inaccessible and unused fields",
|
||||||
|
" { .. }".to_string(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
err
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a diagnostic reporting a struct pattern which does not mention some fields.
|
||||||
|
///
|
||||||
|
/// ```ignore (diagnostic)
|
||||||
|
/// error[E0027]: pattern does not mention field `you_cant_use_this_field`
|
||||||
|
/// --> src/main.rs:15:9
|
||||||
|
/// |
|
||||||
|
/// LL | let foo::Foo {} = foo::Foo::new();
|
||||||
|
/// | ^^^^^^^^^^^ missing field `you_cant_use_this_field`
|
||||||
|
/// ```
|
||||||
fn error_unmentioned_fields(
|
fn error_unmentioned_fields(
|
||||||
&self,
|
&self,
|
||||||
pat: &Pat<'_>,
|
pat: &Pat<'_>,
|
||||||
unmentioned_fields: &[Ident],
|
unmentioned_fields: &[(&ty::FieldDef, Ident)],
|
||||||
) -> DiagnosticBuilder<'tcx> {
|
) -> DiagnosticBuilder<'tcx> {
|
||||||
let field_names = if unmentioned_fields.len() == 1 {
|
let field_names = if unmentioned_fields.len() == 1 {
|
||||||
format!("field `{}`", unmentioned_fields[0])
|
format!("field `{}`", unmentioned_fields[0].1)
|
||||||
} else {
|
} else {
|
||||||
let fields = unmentioned_fields
|
let fields = unmentioned_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| format!("`{}`", name))
|
.map(|(_, name)| format!("`{}`", name))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
format!("fields {}", fields)
|
format!("fields {}", fields)
|
||||||
|
|
18
src/test/ui/issues/issue-76077-1.fixed
Normal file
18
src/test/ui/issues/issue-76077-1.fixed
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// run-rustfix
|
||||||
|
#![allow(dead_code, unused_variables)]
|
||||||
|
|
||||||
|
pub mod foo {
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Foo { invisible: bool, }
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Bar { pub visible: bool, invisible: bool, }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo::Foo { .. } = foo::Foo::default();
|
||||||
|
//~^ ERROR pattern requires `..` due to inaccessible fields
|
||||||
|
|
||||||
|
let foo::Bar { visible, .. } = foo::Bar::default();
|
||||||
|
//~^ ERROR pattern requires `..` due to inaccessible fields
|
||||||
|
}
|
18
src/test/ui/issues/issue-76077-1.rs
Normal file
18
src/test/ui/issues/issue-76077-1.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// run-rustfix
|
||||||
|
#![allow(dead_code, unused_variables)]
|
||||||
|
|
||||||
|
pub mod foo {
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Foo { invisible: bool, }
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Bar { pub visible: bool, invisible: bool, }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo::Foo {} = foo::Foo::default();
|
||||||
|
//~^ ERROR pattern requires `..` due to inaccessible fields
|
||||||
|
|
||||||
|
let foo::Bar { visible } = foo::Bar::default();
|
||||||
|
//~^ ERROR pattern requires `..` due to inaccessible fields
|
||||||
|
}
|
24
src/test/ui/issues/issue-76077-1.stderr
Normal file
24
src/test/ui/issues/issue-76077-1.stderr
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
error: pattern requires `..` due to inaccessible fields
|
||||||
|
--> $DIR/issue-76077-1.rs:13:9
|
||||||
|
|
|
||||||
|
LL | let foo::Foo {} = foo::Foo::default();
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: ignore the inaccessible and unused fields
|
||||||
|
|
|
||||||
|
LL | let foo::Foo { .. } = foo::Foo::default();
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
|
error: pattern requires `..` due to inaccessible fields
|
||||||
|
--> $DIR/issue-76077-1.rs:16:9
|
||||||
|
|
|
||||||
|
LL | let foo::Bar { visible } = foo::Bar::default();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: ignore the inaccessible and unused fields
|
||||||
|
|
|
||||||
|
LL | let foo::Bar { visible, .. } = foo::Bar::default();
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
10
src/test/ui/issues/issue-76077.rs
Normal file
10
src/test/ui/issues/issue-76077.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
pub mod foo {
|
||||||
|
pub struct Foo {
|
||||||
|
you_cant_use_this_field: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo::Foo {};
|
||||||
|
//~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields
|
||||||
|
}
|
8
src/test/ui/issues/issue-76077.stderr
Normal file
8
src/test/ui/issues/issue-76077.stderr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
|
||||||
|
--> $DIR/issue-76077.rs:8:5
|
||||||
|
|
|
||||||
|
LL | foo::Foo {};
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Reference in a new issue