Skip to content

Commit 55f5d21

Browse files
Rollup merge of rust-lang#98283 - TaKO8Ki:point-at-private-fields-in-struct-literal, r=compiler-errors
Point at private fields in struct literal closes rust-lang#95872
2 parents d075e57 + eb86daa commit 55f5d21

10 files changed

+107
-19
lines changed

compiler/rustc_lint_defs/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ macro_rules! pluralize {
2626
("is", $x:expr) => {
2727
if $x == 1 { "is" } else { "are" }
2828
};
29+
("was", $x:expr) => {
30+
if $x == 1 { "was" } else { "were" }
31+
};
2932
("this", $x:expr) => {
3033
if $x == 1 { "this" } else { "these" }
3134
};

compiler/rustc_typeck/src/check/expr.rs

+59-13
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructEx
2626
use rustc_ast as ast;
2727
use rustc_data_structures::fx::FxHashMap;
2828
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};
29+
use rustc_errors::{
30+
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
31+
EmissionGuarantee, ErrorGuaranteed,
32+
};
3333
use rustc_hir as hir;
3434
use rustc_hir::def::{CtorKind, DefKind, Res};
3535
use rustc_hir::def_id::DefId;
@@ -1701,12 +1701,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17011701
};
17021702
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
17031703
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
1704-
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
1705-
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
1706-
});
1704+
debug!(?remaining_fields);
1705+
let private_fields: Vec<&ty::FieldDef> = variant
1706+
.fields
1707+
.iter()
1708+
.filter(|field| {
1709+
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
1710+
})
1711+
.collect();
17071712

1708-
if inaccessible_remaining_fields {
1709-
self.report_inaccessible_fields(adt_ty, span);
1713+
if !private_fields.is_empty() {
1714+
self.report_private_fields(adt_ty, span, private_fields, ast_fields);
17101715
} else {
17111716
self.report_missing_fields(
17121717
adt_ty,
@@ -1830,21 +1835,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18301835
/// Report an error for a struct field expression when there are invisible fields.
18311836
///
18321837
/// ```text
1833-
/// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
1838+
/// error: cannot construct `Foo` with struct literal syntax due to private fields
18341839
/// --> src/main.rs:8:5
18351840
/// |
18361841
/// 8 | foo::Foo {};
18371842
/// | ^^^^^^^^
18381843
///
18391844
/// error: aborting due to previous error
18401845
/// ```
1841-
fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
1842-
self.tcx.sess.span_err(
1846+
fn report_private_fields(
1847+
&self,
1848+
adt_ty: Ty<'tcx>,
1849+
span: Span,
1850+
private_fields: Vec<&ty::FieldDef>,
1851+
used_fields: &'tcx [hir::ExprField<'tcx>],
1852+
) {
1853+
let mut err = self.tcx.sess.struct_span_err(
18431854
span,
18441855
&format!(
1845-
"cannot construct `{adt_ty}` with struct literal syntax due to inaccessible fields",
1856+
"cannot construct `{adt_ty}` with struct literal syntax due to private fields",
18461857
),
18471858
);
1859+
let (used_private_fields, remaining_private_fields): (
1860+
Vec<(Symbol, Span, bool)>,
1861+
Vec<(Symbol, Span, bool)>,
1862+
) = private_fields
1863+
.iter()
1864+
.map(|field| {
1865+
match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
1866+
Some(used_field) => (field.name, used_field.span, true),
1867+
None => (field.name, self.tcx.def_span(field.did), false),
1868+
}
1869+
})
1870+
.partition(|field| field.2);
1871+
err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
1872+
if !remaining_private_fields.is_empty() {
1873+
let remaining_private_fields_len = remaining_private_fields.len();
1874+
let names = match &remaining_private_fields
1875+
.iter()
1876+
.map(|(name, _, _)| name.to_string())
1877+
.collect::<Vec<_>>()[..]
1878+
{
1879+
_ if remaining_private_fields_len > 6 => String::new(),
1880+
[name] => format!("`{name}` "),
1881+
[names @ .., last] => {
1882+
let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
1883+
format!("{} and `{last}` ", names.join(", "))
1884+
}
1885+
[] => unreachable!(),
1886+
};
1887+
err.note(format!(
1888+
"... and other private field{s} {names}that {were} not provided",
1889+
s = pluralize!(remaining_private_fields_len),
1890+
were = pluralize!("was", remaining_private_fields_len),
1891+
));
1892+
}
1893+
err.emit();
18481894
}
18491895

18501896
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

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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: ... and other private field `you_cant_use_this_field` that was not provided
68

79
error: aborting due to previous error
810

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ 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: ... and other private field `private` that was not provided
1820

1921
error[E0063]: missing field `y` in initializer of `Enum`
2022
--> $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,10 @@
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: ... and other private field `you_cant_use_this_field` that was not provided
68

79
error: aborting due to previous error
810

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,15 @@
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: ... and other private fields `c`, `d` and `e` that were not provided
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)