Skip to content

Commit d87caf8

Browse files
authored
Unrolled build for rust-lang#135700
Rollup merge of rust-lang#135700 - estebank:priv-field-dfv, r=wesleywiser Emit single privacy error for struct literal with multiple private fields and add test for `default_field_values` privacy Add test ensuring that struct with default field values is not constructable if the fields are not accessible. Collect all unreachable fields in a single struct literal struct and emit a single error, instead of one error per private field. ``` error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private --> $DIR/visibility.rs:18:13 | LL | let _x = Alpha { | ----- in this type LL | beta: 0, | ^^^^^^^ private field LL | .. | ^^ field `gamma` is private ```
2 parents ecda83b + deef3eb commit d87caf8

File tree

13 files changed

+261
-48
lines changed

13 files changed

+261
-48
lines changed

compiler/rustc_privacy/messages.ftl

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
privacy_field_is_private = field `{$field_name}` of {$variant_descr} `{$def_path_str}` is private
2-
privacy_field_is_private_is_update_syntax_label = field `{$field_name}` is private
1+
privacy_field_is_private =
2+
{$len ->
3+
[1] field
4+
*[other] fields
5+
} {$field_names} of {$variant_descr} `{$def_path_str}` {$len ->
6+
[1] is
7+
*[other] are
8+
} private
9+
.label = in this type
10+
privacy_field_is_private_is_update_syntax_label = {$rest_len ->
11+
[1] field
12+
*[other] fields
13+
} {$rest_field_names} {$rest_len ->
14+
[1] is
15+
*[other] are
16+
} private
317
privacy_field_is_private_label = private field
418
519
privacy_from_private_dep_in_public_interface =

compiler/rustc_privacy/src/errors.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
use rustc_errors::DiagArgFromDisplay;
21
use rustc_errors::codes::*;
2+
use rustc_errors::{DiagArgFromDisplay, MultiSpan};
33
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
44
use rustc_span::{Span, Symbol};
55

66
#[derive(Diagnostic)]
77
#[diag(privacy_field_is_private, code = E0451)]
88
pub(crate) struct FieldIsPrivate {
99
#[primary_span]
10-
pub span: Span,
11-
pub field_name: Symbol,
10+
pub span: MultiSpan,
11+
#[label]
12+
pub struct_span: Option<Span>,
13+
pub field_names: String,
1214
pub variant_descr: &'static str,
1315
pub def_path_str: String,
1416
#[subdiagnostic]
15-
pub label: FieldIsPrivateLabel,
17+
pub labels: Vec<FieldIsPrivateLabel>,
18+
pub len: usize,
1619
}
1720

1821
#[derive(Subdiagnostic)]
@@ -21,7 +24,8 @@ pub(crate) enum FieldIsPrivateLabel {
2124
IsUpdateSyntax {
2225
#[primary_span]
2326
span: Span,
24-
field_name: Symbol,
27+
rest_field_names: String,
28+
rest_len: usize,
2529
},
2630
#[label(privacy_field_is_private_label)]
2731
Other {

compiler/rustc_privacy/src/lib.rs

+120-31
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_ast::MacroDef;
2424
use rustc_ast::visit::{VisitorResult, try_visit};
2525
use rustc_data_structures::fx::FxHashSet;
2626
use rustc_data_structures::intern::Interned;
27+
use rustc_errors::MultiSpan;
2728
use rustc_hir::def::{DefKind, Res};
2829
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId};
2930
use rustc_hir::intravisit::{self, Visitor};
@@ -38,7 +39,7 @@ use rustc_middle::ty::{
3839
use rustc_middle::{bug, span_bug};
3940
use rustc_session::lint;
4041
use rustc_span::hygiene::Transparency;
41-
use rustc_span::{Ident, Span, kw, sym};
42+
use rustc_span::{Ident, Span, Symbol, kw, sym};
4243
use tracing::debug;
4344
use {rustc_attr_parsing as attr, rustc_hir as hir};
4445

@@ -921,31 +922,95 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
921922
&mut self,
922923
hir_id: hir::HirId, // ID of the field use
923924
use_ctxt: Span, // syntax context of the field name at the use site
924-
span: Span, // span of the field pattern, e.g., `x: 0`
925925
def: ty::AdtDef<'tcx>, // definition of the struct or enum
926926
field: &'tcx ty::FieldDef,
927-
in_update_syntax: bool,
928-
) {
927+
) -> bool {
929928
if def.is_enum() {
930-
return;
929+
return true;
931930
}
932931

933932
// definition of the field
934933
let ident = Ident::new(kw::Empty, use_ctxt);
935934
let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1;
936-
if !field.vis.is_accessible_from(def_id, self.tcx) {
937-
self.tcx.dcx().emit_err(FieldIsPrivate {
938-
span,
939-
field_name: field.name,
940-
variant_descr: def.variant_descr(),
941-
def_path_str: self.tcx.def_path_str(def.did()),
942-
label: if in_update_syntax {
943-
FieldIsPrivateLabel::IsUpdateSyntax { span, field_name: field.name }
944-
} else {
945-
FieldIsPrivateLabel::Other { span }
946-
},
947-
});
935+
!field.vis.is_accessible_from(def_id, self.tcx)
936+
}
937+
938+
// Checks that a field in a struct constructor (expression or pattern) is accessible.
939+
fn emit_unreachable_field_error(
940+
&mut self,
941+
fields: Vec<(Symbol, Span, bool /* field is present */)>,
942+
def: ty::AdtDef<'tcx>, // definition of the struct or enum
943+
update_syntax: Option<Span>,
944+
struct_span: Span,
945+
) {
946+
if def.is_enum() || fields.is_empty() {
947+
return;
948948
}
949+
950+
// error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private
951+
// --> $DIR/visibility.rs:18:13
952+
// |
953+
// LL | let _x = Alpha {
954+
// | ----- in this type # from `def`
955+
// LL | beta: 0,
956+
// | ^^^^^^^ private field # `fields.2` is `true`
957+
// LL | ..
958+
// | ^^ field `gamma` is private # `fields.2` is `false`
959+
960+
// Get the list of all private fields for the main message.
961+
let field_names: Vec<_> = fields.iter().map(|(name, _, _)| name).collect();
962+
let field_names = match &field_names[..] {
963+
[] => return,
964+
[name] => format!("`{name}`"),
965+
[fields @ .., last] => format!(
966+
"{} and `{last}`",
967+
fields.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join(", "),
968+
),
969+
};
970+
let span: MultiSpan = fields.iter().map(|(_, span, _)| *span).collect::<Vec<Span>>().into();
971+
972+
// Get the list of all private fields when pointing at the `..rest`.
973+
let rest_field_names: Vec<_> =
974+
fields.iter().filter(|(_, _, is_present)| !is_present).map(|(n, _, _)| n).collect();
975+
let rest_len = rest_field_names.len();
976+
let rest_field_names = match &rest_field_names[..] {
977+
[] => String::new(),
978+
[name] => format!("`{name}`"),
979+
[fields @ .., last] => format!(
980+
"{} and `{last}`",
981+
fields.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join(", "),
982+
),
983+
};
984+
// Get all the labels for each field or `..rest` in the primary MultiSpan.
985+
let labels = fields
986+
.iter()
987+
.filter(|(_, _, is_present)| *is_present)
988+
.map(|(_, span, _)| FieldIsPrivateLabel::Other { span: *span })
989+
.chain(update_syntax.iter().map(|span| FieldIsPrivateLabel::IsUpdateSyntax {
990+
span: *span,
991+
rest_field_names: rest_field_names.clone(),
992+
rest_len,
993+
}))
994+
.collect();
995+
996+
self.tcx.dcx().emit_err(FieldIsPrivate {
997+
span,
998+
struct_span: if self
999+
.tcx
1000+
.sess
1001+
.source_map()
1002+
.is_multiline(fields[0].1.between(struct_span))
1003+
{
1004+
Some(struct_span)
1005+
} else {
1006+
None
1007+
},
1008+
field_names: field_names.clone(),
1009+
variant_descr: def.variant_descr(),
1010+
def_path_str: self.tcx.def_path_str(def.did()),
1011+
labels,
1012+
len: fields.len(),
1013+
});
9491014
}
9501015

9511016
fn check_expanded_fields(
@@ -955,16 +1020,25 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
9551020
fields: &[hir::ExprField<'tcx>],
9561021
hir_id: hir::HirId,
9571022
span: Span,
1023+
struct_span: Span,
9581024
) {
1025+
let mut failed_fields = vec![];
9591026
for (vf_index, variant_field) in variant.fields.iter_enumerated() {
9601027
let field =
9611028
fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
9621029
let (hir_id, use_ctxt, span) = match field {
9631030
Some(field) => (field.hir_id, field.ident.span, field.span),
9641031
None => (hir_id, span, span),
9651032
};
966-
self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
1033+
if self.check_field(hir_id, use_ctxt, adt, variant_field) {
1034+
let name = match field {
1035+
Some(field) => field.ident.name,
1036+
None => variant_field.name,
1037+
};
1038+
failed_fields.push((name, span, field.is_some()));
1039+
}
9671040
}
1041+
self.emit_unreachable_field_error(failed_fields, adt, Some(span), struct_span);
9681042
}
9691043
}
9701044

@@ -990,24 +1064,35 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
9901064
// If the expression uses FRU we need to make sure all the unmentioned fields
9911065
// are checked for privacy (RFC 736). Rather than computing the set of
9921066
// unmentioned fields, just check them all.
993-
self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span);
1067+
self.check_expanded_fields(
1068+
adt,
1069+
variant,
1070+
fields,
1071+
base.hir_id,
1072+
base.span,
1073+
qpath.span(),
1074+
);
9941075
}
9951076
hir::StructTailExpr::DefaultFields(span) => {
996-
self.check_expanded_fields(adt, variant, fields, expr.hir_id, span);
1077+
self.check_expanded_fields(
1078+
adt,
1079+
variant,
1080+
fields,
1081+
expr.hir_id,
1082+
span,
1083+
qpath.span(),
1084+
);
9971085
}
9981086
hir::StructTailExpr::None => {
1087+
let mut failed_fields = vec![];
9991088
for field in fields {
1000-
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
1089+
let (hir_id, use_ctxt) = (field.hir_id, field.ident.span);
10011090
let index = self.typeck_results().field_index(field.hir_id);
1002-
self.check_field(
1003-
hir_id,
1004-
use_ctxt,
1005-
span,
1006-
adt,
1007-
&variant.fields[index],
1008-
false,
1009-
);
1091+
if self.check_field(hir_id, use_ctxt, adt, &variant.fields[index]) {
1092+
failed_fields.push((field.ident.name, field.ident.span, true));
1093+
}
10101094
}
1095+
self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span());
10111096
}
10121097
}
10131098
}
@@ -1020,11 +1105,15 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
10201105
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
10211106
let adt = self.typeck_results().pat_ty(pat).ty_adt_def().unwrap();
10221107
let variant = adt.variant_of_res(res);
1108+
let mut failed_fields = vec![];
10231109
for field in fields {
1024-
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
1110+
let (hir_id, use_ctxt) = (field.hir_id, field.ident.span);
10251111
let index = self.typeck_results().field_index(field.hir_id);
1026-
self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false);
1112+
if self.check_field(hir_id, use_ctxt, adt, &variant.fields[index]) {
1113+
failed_fields.push((field.ident.name, field.ident.span, true));
1114+
}
10271115
}
1116+
self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span());
10281117
}
10291118

10301119
intravisit::walk_pat(self, pat);

tests/ui/deprecation/deprecation-lint.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -739,8 +739,11 @@ LL | _)
739739
error[E0451]: field `i` of struct `this_crate::nested::DeprecatedStruct` is private
740740
--> $DIR/deprecation-lint.rs:280:13
741741
|
742+
LL | let _ = nested::DeprecatedStruct {
743+
| ------------------------ in this type
744+
LL |
742745
LL | i: 0
743-
| ^^^^ private field
746+
| ^ private field
744747

745748
error: aborting due to 123 previous errors
746749

tests/ui/error-codes/E0451.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ error[E0451]: field `b` of struct `Foo` is private
88
--> $DIR/E0451.rs:18:29
99
|
1010
LL | let f = bar::Foo{ a: 0, b: 0 };
11-
| ^^^^ private field
11+
| ^ private field
1212

1313
error: aborting due to 2 previous errors
1414

tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0451]: field `1` of struct `Box` is private
22
--> $DIR/issue-82772-match-box-as-struct.rs:4:15
33
|
44
LL | let Box { 1: _, .. }: Box<()>;
5-
| ^^^^ private field
5+
| ^ private field
66

77
error: aborting due to 1 previous error
88

tests/ui/privacy/private-struct-field-ctor.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `Foo` is private
22
--> $DIR/private-struct-field-ctor.rs:8:22
33
|
44
LL | let s = a::Foo { x: 1 };
5-
| ^^^^ private field
5+
| ^ private field
66

77
error: aborting due to 1 previous error
88

tests/ui/privacy/private-struct-field-pattern.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `Foo` is private
22
--> $DIR/private-struct-field-pattern.rs:15:15
33
|
44
LL | Foo { x: _ } => {}
5-
| ^^^^ private field
5+
| ^ private field
66

77
error: aborting due to 1 previous error
88

tests/ui/privacy/restricted/struct-literal-field.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `S` is private
22
--> $DIR/struct-literal-field.rs:18:9
33
|
44
LL | S { x: 0 };
5-
| ^^^^ private field
5+
| ^ private field
66

77
error: aborting due to 1 previous error
88

tests/ui/privacy/union-field-privacy-1.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0451]: field `c` of union `U` is private
22
--> $DIR/union-field-privacy-1.rs:12:20
33
|
44
LL | let u = m::U { c: 0 };
5-
| ^^^^ private field
5+
| ^ private field
66

77
error[E0451]: field `c` of union `U` is private
88
--> $DIR/union-field-privacy-1.rs:16:16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![feature(default_field_values)]
2+
pub mod foo {
3+
#[derive(Default)]
4+
pub struct Alpha {
5+
beta: u8 = 42,
6+
gamma: bool = true,
7+
}
8+
}
9+
10+
mod bar {
11+
use crate::foo::Alpha;
12+
fn baz() {
13+
let _x = Alpha { .. };
14+
//~^ ERROR fields `beta` and `gamma` of struct `Alpha` are private
15+
let _x = Alpha {
16+
beta: 0, //~ ERROR fields `beta` and `gamma` of struct `Alpha` are private
17+
gamma: false,
18+
};
19+
let _x = Alpha {
20+
beta: 0, //~ ERROR fields `beta` and `gamma` of struct `Alpha` are private
21+
..
22+
};
23+
let _x = Alpha { beta: 0, .. };
24+
//~^ ERROR fields `beta` and `gamma` of struct `Alpha` are private
25+
let _x = Alpha { beta: 0, ..Default::default() };
26+
//~^ ERROR fields `beta` and `gamma` of struct `Alpha` are private
27+
}
28+
}
29+
30+
pub mod baz {
31+
pub struct S {
32+
x: i32 = 1,
33+
}
34+
}
35+
fn main() {
36+
let _a = baz::S {
37+
.. //~ ERROR field `x` of struct `S` is private
38+
};
39+
let _b = baz::S {
40+
x: 0, //~ ERROR field `x` of struct `S` is private
41+
};
42+
}

0 commit comments

Comments
 (0)