@@ -23,13 +23,14 @@ use crate::type_error_struct;
2323
2424use super :: suggest_call_constructor;
2525use crate :: errors:: { AddressOfTemporaryTaken , ReturnStmtOutsideOfFnBody , StructExprNonExhaustive } ;
26+ use itertools:: { Either , Itertools } ;
2627use rustc_ast as ast;
2728use rustc_data_structures:: fx:: FxHashMap ;
2829use 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+ } ;
3334use rustc_hir as hir;
3435use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
3536use 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 (
0 commit comments