@@ -23,13 +23,14 @@ use crate::type_error_struct;
23
23
24
24
use super :: suggest_call_constructor;
25
25
use crate :: errors:: { AddressOfTemporaryTaken , ReturnStmtOutsideOfFnBody , StructExprNonExhaustive } ;
26
+ use itertools:: { Either , Itertools } ;
26
27
use rustc_ast as ast;
27
28
use rustc_data_structures:: fx:: FxHashMap ;
28
29
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
+ } ;
33
34
use rustc_hir as hir;
34
35
use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
35
36
use rustc_hir:: def_id:: DefId ;
@@ -1672,12 +1673,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1672
1673
} ;
1673
1674
self . typeck_results . borrow_mut ( ) . fru_field_types_mut ( ) . insert ( expr_id, fru_tys) ;
1674
1675
} 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 ( ) ;
1678
1684
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) ;
1681
1691
} else {
1682
1692
self . report_missing_fields (
1683
1693
adt_ty,
@@ -1801,21 +1811,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1801
1811
/// Report an error for a struct field expression when there are invisible fields.
1802
1812
///
1803
1813
/// ```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
1805
1815
/// --> src/main.rs:8:5
1806
1816
/// |
1807
1817
/// 8 | foo::Foo {};
1808
1818
/// | ^^^^^^^^
1809
1819
///
1810
1820
/// error: aborting due to previous error
1811
1821
/// ```
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 (
1814
1844
span,
1815
1845
& 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" ,
1817
1847
) ,
1818
1848
) ;
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 ( ) ;
1819
1873
}
1820
1874
1821
1875
fn report_unknown_field (
0 commit comments