Skip to content

Commit 1e7ab0b

Browse files
committed
point at private fields in struct literal
1 parent 349bda2 commit 1e7ab0b

11 files changed

+134
-19
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4590,6 +4590,7 @@ dependencies = [
45904590
name = "rustc_typeck"
45914591
version = "0.0.0"
45924592
dependencies = [
4593+
"itertools",
45934594
"rustc_arena",
45944595
"rustc_ast",
45954596
"rustc_attr",

compiler/rustc_typeck/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ doctest = false
1010
[dependencies]
1111
rustc_arena = { path = "../rustc_arena" }
1212
tracing = "0.1"
13+
itertools = "0.10.1"
1314
rustc_macros = { path = "../rustc_macros" }
1415
rustc_middle = { path = "../rustc_middle" }
1516
rustc_attr = { path = "../rustc_attr" }

compiler/rustc_typeck/src/check/expr.rs

+67-13
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ use crate::type_error_struct;
2323

2424
use super::suggest_call_constructor;
2525
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
26+
use itertools::{Either, Itertools};
2627
use rustc_ast as ast;
2728
use rustc_data_structures::fx::FxHashMap;
2829
use rustc_data_structures::stack::ensure_sufficient_stack;
29-
use rustc_errors::Diagnostic;
30-
use rustc_errors::EmissionGuarantee;
31-
use rustc_errors::ErrorGuaranteed;
32-
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
30+
use rustc_errors::{
31+
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
32+
EmissionGuarantee, ErrorGuaranteed, MultiSpan,
33+
};
3334
use rustc_hir as hir;
3435
use rustc_hir::def::{CtorKind, DefKind, Res};
3536
use rustc_hir::def_id::DefId;
@@ -1672,12 +1673,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16721673
};
16731674
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
16741675
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
1675-
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
1676-
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
1677-
});
1676+
debug!(?remaining_fields);
1677+
let private_fields: Vec<&ty::FieldDef> = variant
1678+
.fields
1679+
.iter()
1680+
.filter(|field| {
1681+
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
1682+
})
1683+
.collect();
16781684

1679-
if inaccessible_remaining_fields {
1680-
self.report_inaccessible_fields(adt_ty, span);
1685+
if !private_fields.is_empty()
1686+
&& tcx
1687+
.visibility(variant.def_id)
1688+
.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
1689+
{
1690+
self.report_private_fields(adt_ty, span, private_fields, ast_fields);
16811691
} else {
16821692
self.report_missing_fields(
16831693
adt_ty,
@@ -1801,21 +1811,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18011811
/// Report an error for a struct field expression when there are invisible fields.
18021812
///
18031813
/// ```text
1804-
/// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
1814+
/// error: cannot construct `Foo` with struct literal syntax due to private fields
18051815
/// --> src/main.rs:8:5
18061816
/// |
18071817
/// 8 | foo::Foo {};
18081818
/// | ^^^^^^^^
18091819
///
18101820
/// error: aborting due to previous error
18111821
/// ```
1812-
fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
1813-
self.tcx.sess.span_err(
1822+
fn report_private_fields(
1823+
&self,
1824+
adt_ty: Ty<'tcx>,
1825+
span: Span,
1826+
private_fields: Vec<&ty::FieldDef>,
1827+
used_fields: &'tcx [hir::ExprField<'tcx>],
1828+
) {
1829+
let field_names = |fields: Vec<Symbol>, len: usize| match &fields
1830+
.iter()
1831+
.map(|field| field.to_string())
1832+
.collect::<Vec<_>>()[..]
1833+
{
1834+
_ if len > 6 => String::new(),
1835+
[name] => format!("`{name}` "),
1836+
[names @ .., last] => {
1837+
let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
1838+
format!("{} and `{last}` ", names.join(", "))
1839+
}
1840+
[] => unreachable!(),
1841+
};
1842+
1843+
let mut err = self.tcx.sess.struct_span_err(
18141844
span,
18151845
&format!(
1816-
"cannot construct `{adt_ty}` with struct literal syntax due to inaccessible fields",
1846+
"cannot construct `{adt_ty}` with struct literal syntax due to private fields",
18171847
),
18181848
);
1849+
let (used_private_fields, remaining_private_fields): (
1850+
Vec<(Symbol, Span)>,
1851+
Vec<(Symbol, Span)>,
1852+
) = private_fields.iter().partition_map(|field| {
1853+
match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
1854+
Some(used_field) => Either::Left((field.name, used_field.span)),
1855+
None => Either::Right((field.name, self.tcx.def_span(field.did))),
1856+
}
1857+
});
1858+
let remaining_private_fields_len = remaining_private_fields.len();
1859+
err.span_labels(used_private_fields.iter().map(|(_, span)| *span), "private field");
1860+
err.span_note(
1861+
MultiSpan::from_spans(remaining_private_fields.iter().map(|(_, span)| *span).collect()),
1862+
format!(
1863+
"missing field{s} {names}{are} private",
1864+
s = pluralize!(remaining_private_fields_len),
1865+
are = pluralize!("is", remaining_private_fields_len),
1866+
names = field_names(
1867+
remaining_private_fields.iter().map(|(name, _)| *name).collect(),
1868+
remaining_private_fields_len
1869+
)
1870+
),
1871+
);
1872+
err.emit();
18191873
}
18201874

18211875
fn report_unknown_field(

src/test/ui/issues/issue-76077.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ pub mod foo {
66

77
fn main() {
88
foo::Foo {};
9-
//~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields
9+
//~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields
1010
}

src/test/ui/issues/issue-76077.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
1+
error: cannot construct `Foo` with struct literal syntax due to private fields
22
--> $DIR/issue-76077.rs:8:5
33
|
44
LL | foo::Foo {};
55
| ^^^^^^^^
6+
|
7+
note: missing field `you_cant_use_this_field` is private
8+
--> $DIR/issue-76077.rs:3:9
9+
|
10+
LL | you_cant_use_this_field: bool,
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
612

713
error: aborting due to previous error
814

src/test/ui/privacy/issue-79593.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod foo {
1616

1717
fn correct() {
1818
foo::Pub {};
19-
//~^ ERROR cannot construct `Pub` with struct literal syntax due to inaccessible fields
19+
//~^ ERROR cannot construct `Pub` with struct literal syntax due to private fields
2020
}
2121

2222
fn wrong() {

src/test/ui/privacy/issue-79593.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ error[E0063]: missing field `y` in initializer of `Enum`
1010
LL | Enum::Variant { x: () };
1111
| ^^^^^^^^^^^^^ missing `y`
1212

13-
error: cannot construct `Pub` with struct literal syntax due to inaccessible fields
13+
error: cannot construct `Pub` with struct literal syntax due to private fields
1414
--> $DIR/issue-79593.rs:18:5
1515
|
1616
LL | foo::Pub {};
1717
| ^^^^^^^^
18+
|
19+
note: missing field `private` is private
20+
--> $DIR/issue-79593.rs:2:22
21+
|
22+
LL | pub struct Pub { private: () }
23+
| ^^^^^^^^^^^
1824

1925
error[E0063]: missing field `y` in initializer of `Enum`
2026
--> $DIR/issue-79593.rs:23:5

src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ pub mod foo {
77

88
fn main() {
99
foo::Foo {};
10-
//~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields
10+
//~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields
1111
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
1+
error: cannot construct `Foo` with struct literal syntax due to private fields
22
--> $DIR/issue-87872-missing-inaccessible-field-literal.rs:9:5
33
|
44
LL | foo::Foo {};
55
| ^^^^^^^^
6+
|
7+
note: missing field `you_cant_use_this_field` is private
8+
--> $DIR/issue-87872-missing-inaccessible-field-literal.rs:4:9
9+
|
10+
LL | you_cant_use_this_field: bool,
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
612

713
error: aborting due to previous error
814

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pub mod m {
2+
pub struct S {
3+
pub visible: bool,
4+
a: (),
5+
b: (),
6+
c: (),
7+
d: (),
8+
e: (),
9+
}
10+
}
11+
12+
fn main() {
13+
let _ = m::S { //~ ERROR cannot construct `S` with struct literal syntax due to private fields
14+
visible: true,
15+
a: (),
16+
b: (),
17+
};
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: cannot construct `S` with struct literal syntax due to private fields
2+
--> $DIR/missing-private-fields-in-struct-literal.rs:13:13
3+
|
4+
LL | let _ = m::S {
5+
| ^^^^
6+
LL | visible: true,
7+
LL | a: (),
8+
| ----- private field
9+
LL | b: (),
10+
| ----- private field
11+
|
12+
note: missing fields `c`, `d` and `e` are private
13+
--> $DIR/missing-private-fields-in-struct-literal.rs:6:9
14+
|
15+
LL | c: (),
16+
| ^^^^^
17+
LL | d: (),
18+
| ^^^^^
19+
LL | e: (),
20+
| ^^^^^
21+
22+
error: aborting due to previous error
23+

0 commit comments

Comments
 (0)