1
Fork 0

point at private fields in struct literal

This commit is contained in:
Takayuki Maeda 2022-06-20 16:29:05 +09:00
parent 349bda2051
commit 1e7ab0bbd7
11 changed files with 134 additions and 19 deletions

View file

@ -4590,6 +4590,7 @@ dependencies = [
name = "rustc_typeck" name = "rustc_typeck"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"itertools",
"rustc_arena", "rustc_arena",
"rustc_ast", "rustc_ast",
"rustc_attr", "rustc_attr",

View file

@ -10,6 +10,7 @@ doctest = false
[dependencies] [dependencies]
rustc_arena = { path = "../rustc_arena" } rustc_arena = { path = "../rustc_arena" }
tracing = "0.1" tracing = "0.1"
itertools = "0.10.1"
rustc_macros = { path = "../rustc_macros" } rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" } rustc_middle = { path = "../rustc_middle" }
rustc_attr = { path = "../rustc_attr" } rustc_attr = { path = "../rustc_attr" }

View file

@ -23,13 +23,14 @@ use crate::type_error_struct;
use super::suggest_call_constructor; use super::suggest_call_constructor;
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
use itertools::{Either, Itertools};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Diagnostic; use rustc_errors::{
use rustc_errors::EmissionGuarantee; pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
use rustc_errors::ErrorGuaranteed; EmissionGuarantee, ErrorGuaranteed, MultiSpan,
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -1672,12 +1673,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}; };
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() { } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { debug!(?remaining_fields);
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) let private_fields: Vec<&ty::FieldDef> = variant
}); .fields
.iter()
.filter(|field| {
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
})
.collect();
if inaccessible_remaining_fields { if !private_fields.is_empty()
self.report_inaccessible_fields(adt_ty, span); && tcx
.visibility(variant.def_id)
.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
{
self.report_private_fields(adt_ty, span, private_fields, ast_fields);
} else { } else {
self.report_missing_fields( self.report_missing_fields(
adt_ty, adt_ty,
@ -1801,7 +1811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Report an error for a struct field expression when there are invisible fields. /// Report an error for a struct field expression when there are invisible fields.
/// ///
/// ```text /// ```text
/// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields /// error: cannot construct `Foo` with struct literal syntax due to private fields
/// --> src/main.rs:8:5 /// --> src/main.rs:8:5
/// | /// |
/// 8 | foo::Foo {}; /// 8 | foo::Foo {};
@ -1809,13 +1819,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ///
/// error: aborting due to previous error /// error: aborting due to previous error
/// ``` /// ```
fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { fn report_private_fields(
self.tcx.sess.span_err( &self,
adt_ty: Ty<'tcx>,
span: Span,
private_fields: Vec<&ty::FieldDef>,
used_fields: &'tcx [hir::ExprField<'tcx>],
) {
let field_names = |fields: Vec<Symbol>, len: usize| match &fields
.iter()
.map(|field| field.to_string())
.collect::<Vec<_>>()[..]
{
_ if len > 6 => String::new(),
[name] => format!("`{name}` "),
[names @ .., last] => {
let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
format!("{} and `{last}` ", names.join(", "))
}
[] => unreachable!(),
};
let mut err = self.tcx.sess.struct_span_err(
span, span,
&format!( &format!(
"cannot construct `{adt_ty}` with struct literal syntax due to inaccessible fields", "cannot construct `{adt_ty}` with struct literal syntax due to private fields",
), ),
); );
let (used_private_fields, remaining_private_fields): (
Vec<(Symbol, Span)>,
Vec<(Symbol, Span)>,
) = private_fields.iter().partition_map(|field| {
match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
Some(used_field) => Either::Left((field.name, used_field.span)),
None => Either::Right((field.name, self.tcx.def_span(field.did))),
}
});
let remaining_private_fields_len = remaining_private_fields.len();
err.span_labels(used_private_fields.iter().map(|(_, span)| *span), "private field");
err.span_note(
MultiSpan::from_spans(remaining_private_fields.iter().map(|(_, span)| *span).collect()),
format!(
"missing field{s} {names}{are} private",
s = pluralize!(remaining_private_fields_len),
are = pluralize!("is", remaining_private_fields_len),
names = field_names(
remaining_private_fields.iter().map(|(name, _)| *name).collect(),
remaining_private_fields_len
)
),
);
err.emit();
} }
fn report_unknown_field( fn report_unknown_field(

View file

@ -6,5 +6,5 @@ pub mod foo {
fn main() { fn main() {
foo::Foo {}; foo::Foo {};
//~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields //~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields
} }

View file

@ -1,8 +1,14 @@
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields error: cannot construct `Foo` with struct literal syntax due to private fields
--> $DIR/issue-76077.rs:8:5 --> $DIR/issue-76077.rs:8:5
| |
LL | foo::Foo {}; LL | foo::Foo {};
| ^^^^^^^^ | ^^^^^^^^
|
note: missing field `you_cant_use_this_field` is private
--> $DIR/issue-76077.rs:3:9
|
LL | you_cant_use_this_field: bool,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error error: aborting due to previous error

View file

@ -16,7 +16,7 @@ mod foo {
fn correct() { fn correct() {
foo::Pub {}; foo::Pub {};
//~^ ERROR cannot construct `Pub` with struct literal syntax due to inaccessible fields //~^ ERROR cannot construct `Pub` with struct literal syntax due to private fields
} }
fn wrong() { fn wrong() {

View file

@ -10,11 +10,17 @@ error[E0063]: missing field `y` in initializer of `Enum`
LL | Enum::Variant { x: () }; LL | Enum::Variant { x: () };
| ^^^^^^^^^^^^^ missing `y` | ^^^^^^^^^^^^^ missing `y`
error: cannot construct `Pub` with struct literal syntax due to inaccessible fields error: cannot construct `Pub` with struct literal syntax due to private fields
--> $DIR/issue-79593.rs:18:5 --> $DIR/issue-79593.rs:18:5
| |
LL | foo::Pub {}; LL | foo::Pub {};
| ^^^^^^^^ | ^^^^^^^^
|
note: missing field `private` is private
--> $DIR/issue-79593.rs:2:22
|
LL | pub struct Pub { private: () }
| ^^^^^^^^^^^
error[E0063]: missing field `y` in initializer of `Enum` error[E0063]: missing field `y` in initializer of `Enum`
--> $DIR/issue-79593.rs:23:5 --> $DIR/issue-79593.rs:23:5

View file

@ -7,5 +7,5 @@ pub mod foo {
fn main() { fn main() {
foo::Foo {}; foo::Foo {};
//~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields //~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields
} }

View file

@ -1,8 +1,14 @@
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields error: cannot construct `Foo` with struct literal syntax due to private fields
--> $DIR/issue-87872-missing-inaccessible-field-literal.rs:9:5 --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:9:5
| |
LL | foo::Foo {}; LL | foo::Foo {};
| ^^^^^^^^ | ^^^^^^^^
|
note: missing field `you_cant_use_this_field` is private
--> $DIR/issue-87872-missing-inaccessible-field-literal.rs:4:9
|
LL | you_cant_use_this_field: bool,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,18 @@
pub mod m {
pub struct S {
pub visible: bool,
a: (),
b: (),
c: (),
d: (),
e: (),
}
}
fn main() {
let _ = m::S { //~ ERROR cannot construct `S` with struct literal syntax due to private fields
visible: true,
a: (),
b: (),
};
}

View file

@ -0,0 +1,23 @@
error: cannot construct `S` with struct literal syntax due to private fields
--> $DIR/missing-private-fields-in-struct-literal.rs:13:13
|
LL | let _ = m::S {
| ^^^^
LL | visible: true,
LL | a: (),
| ----- private field
LL | b: (),
| ----- private field
|
note: missing fields `c`, `d` and `e` are private
--> $DIR/missing-private-fields-in-struct-literal.rs:6:9
|
LL | c: (),
| ^^^^^
LL | d: (),
| ^^^^^
LL | e: (),
| ^^^^^
error: aborting due to previous error