From 1e7ab0bbd772d4dab2a66a72af20f8b207c252ab Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 20 Jun 2022 16:29:05 +0900 Subject: [PATCH 1/3] point at private fields in struct literal --- Cargo.lock | 1 + compiler/rustc_typeck/Cargo.toml | 1 + compiler/rustc_typeck/src/check/expr.rs | 80 ++++++++++++++++--- src/test/ui/issues/issue-76077.rs | 2 +- src/test/ui/issues/issue-76077.stderr | 8 +- src/test/ui/privacy/issue-79593.rs | 2 +- src/test/ui/privacy/issue-79593.stderr | 8 +- ...7872-missing-inaccessible-field-literal.rs | 2 +- ...-missing-inaccessible-field-literal.stderr | 8 +- ...issing-private-fields-in-struct-literal.rs | 18 +++++ ...ng-private-fields-in-struct-literal.stderr | 23 ++++++ 11 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/typeck/missing-private-fields-in-struct-literal.rs create mode 100644 src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr diff --git a/Cargo.lock b/Cargo.lock index df6f46f26cf0d..cc0f4cc53e656 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4590,6 +4590,7 @@ dependencies = [ name = "rustc_typeck" version = "0.0.0" dependencies = [ + "itertools", "rustc_arena", "rustc_ast", "rustc_attr", diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index c08023ee6a70a..b3dd695508094 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" +itertools = "0.10.1" rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index dc9d76160c4e9..0a017de80f248 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -23,13 +23,14 @@ use crate::type_error_struct; use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; +use itertools::{Either, Itertools}; 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, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; 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); } 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() + && 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 { self.report_missing_fields( 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. /// /// ```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 {}; @@ -1809,13 +1819,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// 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 field_names = |fields: Vec, len: usize| match &fields + .iter() + .map(|field| field.to_string()) + .collect::>()[..] + { + _ if len > 6 => String::new(), + [name] => format!("`{name}` "), + [names @ .., last] => { + let names = names.iter().map(|name| format!("`{name}`")).collect::>(); + format!("{} and `{last}` ", names.join(", ")) + } + [] => unreachable!(), + }; + + 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)>, + 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( diff --git a/src/test/ui/issues/issue-76077.rs b/src/test/ui/issues/issue-76077.rs index 1ecd37de2e14a..2d29093b01b02 100644 --- a/src/test/ui/issues/issue-76077.rs +++ b/src/test/ui/issues/issue-76077.rs @@ -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 } diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr index d834ec5e0edd2..c70a928f6475a 100644 --- a/src/test/ui/issues/issue-76077.stderr +++ b/src/test/ui/issues/issue-76077.stderr @@ -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 | 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 diff --git a/src/test/ui/privacy/issue-79593.rs b/src/test/ui/privacy/issue-79593.rs index b94278bfdd221..39c222f7c3414 100644 --- a/src/test/ui/privacy/issue-79593.rs +++ b/src/test/ui/privacy/issue-79593.rs @@ -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() { diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr index b8c7d4f23a28f..435d4cbf73595 100644 --- a/src/test/ui/privacy/issue-79593.stderr +++ b/src/test/ui/privacy/issue-79593.stderr @@ -10,11 +10,17 @@ 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: 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` --> $DIR/issue-79593.rs:23:5 diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs index 3176144133760..326e958aaa94f 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs @@ -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 } diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr index 81b73c00e8600..2ade7aea57b3d 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -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 | 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 diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs b/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs new file mode 100644 index 0000000000000..9f1560bfb8dfe --- /dev/null +++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs @@ -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: (), + }; +} diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr new file mode 100644 index 0000000000000..eb5f460f868e1 --- /dev/null +++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr @@ -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 + From f847261478de5fa72d7e1d2ec3341e31a794fcaf Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 22 Jun 2022 12:01:41 +0900 Subject: [PATCH 2/3] stop pointing at definitions of missing fields --- Cargo.lock | 1 - compiler/rustc_typeck/Cargo.toml | 1 - compiler/rustc_typeck/src/check/expr.rs | 69 ++++++++----------- src/test/ui/issues/issue-76077.stderr | 6 +- src/test/ui/privacy/issue-79593.stderr | 6 +- ...-missing-inaccessible-field-literal.stderr | 6 +- ...ng-private-fields-in-struct-literal.stderr | 10 +-- 7 files changed, 33 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc0f4cc53e656..df6f46f26cf0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4590,7 +4590,6 @@ dependencies = [ name = "rustc_typeck" version = "0.0.0" dependencies = [ - "itertools", "rustc_arena", "rustc_ast", "rustc_attr", diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index b3dd695508094..c08023ee6a70a 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -10,7 +10,6 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" -itertools = "0.10.1" rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0a017de80f248..e5048fc513217 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -23,13 +23,12 @@ use crate::type_error_struct; use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; -use itertools::{Either, Itertools}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, - EmissionGuarantee, ErrorGuaranteed, MultiSpan, + EmissionGuarantee, ErrorGuaranteed, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -1682,11 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect(); - if !private_fields.is_empty() - && tcx - .visibility(variant.def_id) - .is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) - { + if !private_fields.is_empty() { self.report_private_fields(adt_ty, span, private_fields, ast_fields); } else { self.report_missing_fields( @@ -1826,12 +1821,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { private_fields: Vec<&ty::FieldDef>, used_fields: &'tcx [hir::ExprField<'tcx>], ) { - let field_names = |fields: Vec, len: usize| match &fields + let mut err = self.tcx.sess.struct_span_err( + span, + &format!( + "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); + let remaining_private_fields_len = remaining_private_fields.len(); + let names = match &remaining_private_fields .iter() - .map(|field| field.to_string()) + .map(|(name, _, _)| name.to_string()) .collect::>()[..] { - _ if len > 6 => String::new(), + _ if remaining_private_fields_len > 6 => String::new(), [name] => format!("`{name}` "), [names @ .., last] => { let names = names.iter().map(|name| format!("`{name}`")).collect::>(); @@ -1839,36 +1853,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } [] => unreachable!(), }; - - let mut err = self.tcx.sess.struct_span_err( - span, - &format!( - "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.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); + err.note(format!( + "... and other private field{s} {names}that were not provided", + s = pluralize!(remaining_private_fields_len), + )); err.emit(); } diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr index c70a928f6475a..57f7abe3931a4 100644 --- a/src/test/ui/issues/issue-76077.stderr +++ b/src/test/ui/issues/issue-76077.stderr @@ -4,11 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields 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, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ... and other private field `you_cant_use_this_field` that were not provided error: aborting due to previous error diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr index 435d4cbf73595..d878e1c023ff1 100644 --- a/src/test/ui/privacy/issue-79593.stderr +++ b/src/test/ui/privacy/issue-79593.stderr @@ -16,11 +16,7 @@ error: cannot construct `Pub` with struct literal syntax due to private fields LL | foo::Pub {}; | ^^^^^^^^ | -note: missing field `private` is private - --> $DIR/issue-79593.rs:2:22 - | -LL | pub struct Pub { private: () } - | ^^^^^^^^^^^ + = note: ... and other private field `private` that were not provided error[E0063]: missing field `y` in initializer of `Enum` --> $DIR/issue-79593.rs:23:5 diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr index 2ade7aea57b3d..fa1c661ef244e 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -4,11 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields 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, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ... and other private field `you_cant_use_this_field` that were not provided error: aborting due to previous error diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr index eb5f460f868e1..234110f31f79c 100644 --- a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr +++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr @@ -9,15 +9,7 @@ LL | a: (), 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: (), - | ^^^^^ + = note: ... and other private fields `c`, `d` and `e` that were not provided error: aborting due to previous error From eb86daa1383d5330a18aa4e78270a6ca5b4ea469 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 22 Jun 2022 14:56:40 +0900 Subject: [PATCH 3/3] add "was" to pluralize macro and use it --- compiler/rustc_lint_defs/src/lib.rs | 3 ++ compiler/rustc_typeck/src/check/expr.rs | 39 ++++++++++--------- src/test/ui/issues/issue-76077.stderr | 2 +- src/test/ui/privacy/issue-79593.stderr | 2 +- ...-missing-inaccessible-field-literal.stderr | 2 +- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index cb1c6f4098767..1cd19c7eaab35 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -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" } }; diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index e5048fc513217..c69de6434539a 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1839,25 +1839,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }) .partition(|field| field.2); - let remaining_private_fields_len = remaining_private_fields.len(); - let names = match &remaining_private_fields - .iter() - .map(|(name, _, _)| name.to_string()) - .collect::>()[..] - { - _ if remaining_private_fields_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - let names = names.iter().map(|name| format!("`{name}`")).collect::>(); - format!("{} and `{last}` ", names.join(", ")) - } - [] => unreachable!(), - }; err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); - err.note(format!( - "... and other private field{s} {names}that were not provided", - s = pluralize!(remaining_private_fields_len), - )); + 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::>()[..] + { + _ if remaining_private_fields_len > 6 => String::new(), + [name] => format!("`{name}` "), + [names @ .., last] => { + let names = names.iter().map(|name| format!("`{name}`")).collect::>(); + 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(); } diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr index 57f7abe3931a4..197ca8d5a7b25 100644 --- a/src/test/ui/issues/issue-76077.stderr +++ b/src/test/ui/issues/issue-76077.stderr @@ -4,7 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields LL | foo::Foo {}; | ^^^^^^^^ | - = note: ... and other private field `you_cant_use_this_field` that were not provided + = note: ... and other private field `you_cant_use_this_field` that was not provided error: aborting due to previous error diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr index d878e1c023ff1..21ba760ad0bcc 100644 --- a/src/test/ui/privacy/issue-79593.stderr +++ b/src/test/ui/privacy/issue-79593.stderr @@ -16,7 +16,7 @@ error: cannot construct `Pub` with struct literal syntax due to private fields LL | foo::Pub {}; | ^^^^^^^^ | - = note: ... and other private field `private` that were not provided + = 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 diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr index fa1c661ef244e..f0bd3e0ddf768 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -4,7 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields LL | foo::Foo {}; | ^^^^^^^^ | - = note: ... and other private field `you_cant_use_this_field` that were not provided + = note: ... and other private field `you_cant_use_this_field` that was not provided error: aborting due to previous error