Skip to content

Commit

Permalink
Rollup merge of rust-lang#98283 - TaKO8Ki:point-at-private-fields-in-…
Browse files Browse the repository at this point in the history
…struct-literal, r=compiler-errors

Point at private fields in struct literal

closes rust-lang#95872
  • Loading branch information
compiler-errors authored Jun 23, 2022
2 parents d075e57 + eb86daa commit 55f5d21
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 19 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ macro_rules! pluralize {
("is", $x:expr) => {
if $x == 1 { "is" } else { "are" }
};
("was", $x:expr) => {
if $x == 1 { "was" } else { "were" }
};
("this", $x:expr) => {
if $x == 1 { "this" } else { "these" }
};
Expand Down
72 changes: 59 additions & 13 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructEx
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Diagnostic;
use rustc_errors::EmissionGuarantee;
use rustc_errors::ErrorGuaranteed;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
EmissionGuarantee, ErrorGuaranteed,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -1701,12 +1701,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
});
debug!(?remaining_fields);
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 {
self.report_inaccessible_fields(adt_ty, span);
if !private_fields.is_empty() {
self.report_private_fields(adt_ty, span, private_fields, ast_fields);
} else {
self.report_missing_fields(
adt_ty,
Expand Down Expand Up @@ -1830,21 +1835,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 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
/// error: cannot construct `Foo` with struct literal syntax due to private fields
/// --> src/main.rs:8:5
/// |
/// 8 | foo::Foo {};
/// | ^^^^^^^^
///
/// error: aborting due to previous error
/// ```
fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
self.tcx.sess.span_err(
fn report_private_fields(
&self,
adt_ty: Ty<'tcx>,
span: Span,
private_fields: Vec<&ty::FieldDef>,
used_fields: &'tcx [hir::ExprField<'tcx>],
) {
let mut err = self.tcx.sess.struct_span_err(
span,
&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, bool)>,
Vec<(Symbol, Span, bool)>,
) = private_fields
.iter()
.map(|field| {
match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
Some(used_field) => (field.name, used_field.span, true),
None => (field.name, self.tcx.def_span(field.did), false),
}
})
.partition(|field| field.2);
err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
if !remaining_private_fields.is_empty() {
let remaining_private_fields_len = remaining_private_fields.len();
let names = match &remaining_private_fields
.iter()
.map(|(name, _, _)| name.to_string())
.collect::<Vec<_>>()[..]
{
_ if remaining_private_fields_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!(),
};
err.note(format!(
"... and other private field{s} {names}that {were} not provided",
s = pluralize!(remaining_private_fields_len),
were = pluralize!("was", remaining_private_fields_len),
));
}
err.emit();
}

fn report_unknown_field(
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-76077.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ pub mod foo {

fn main() {
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
}
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-76077.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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
|
LL | foo::Foo {};
| ^^^^^^^^
|
= note: ... and other private field `you_cant_use_this_field` that was not provided

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/privacy/issue-79593.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod foo {

fn correct() {
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() {
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/privacy/issue-79593.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ error[E0063]: missing field `y` in initializer of `Enum`
LL | Enum::Variant { x: () };
| ^^^^^^^^^^^^^ 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
|
LL | foo::Pub {};
| ^^^^^^^^
|
= note: ... and other private field `private` that was not provided

error[E0063]: missing field `y` in initializer of `Enum`
--> $DIR/issue-79593.rs:23:5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ pub mod foo {

fn main() {
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
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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
|
LL | foo::Foo {};
| ^^^^^^^^
|
= note: ... and other private field `you_cant_use_this_field` that was not provided

error: aborting due to previous error

18 changes: 18 additions & 0 deletions src/test/ui/typeck/missing-private-fields-in-struct-literal.rs
Original file line number Diff line number Diff line change
@@ -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: (),
};
}
15 changes: 15 additions & 0 deletions src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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: ... and other private fields `c`, `d` and `e` that were not provided

error: aborting due to previous error

0 comments on commit 55f5d21

Please sign in to comment.