@@ -1943,8 +1943,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19431943 }
19441944
19451945 fn report_privacy_error ( & mut self , privacy_error : & PrivacyError < ' ra > ) {
1946- let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } =
1947- * privacy_error;
1946+ let PrivacyError {
1947+ ident,
1948+ binding,
1949+ outermost_res,
1950+ parent_scope,
1951+ single_nested,
1952+ dedup_span,
1953+ ref source,
1954+ } = * privacy_error;
19481955
19491956 let res = binding. res ( ) ;
19501957 let ctor_fields_span = self . ctor_fields_span ( binding) ;
@@ -1960,6 +1967,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19601967 let mut err =
19611968 self . dcx ( ) . create_err ( errors:: IsPrivate { span : ident. span , ident_descr, ident } ) ;
19621969
1970+ self . mention_default_field_values ( source, ident, & mut err) ;
1971+
19631972 let mut not_publicly_reexported = false ;
19641973 if let Some ( ( this_res, outer_ident) ) = outermost_res {
19651974 let import_suggestions = self . lookup_import_candidates (
@@ -2141,6 +2150,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
21412150 err. emit ( ) ;
21422151 }
21432152
2153+ /// When a private field is being set that has a default field value, we suggest using `..` and
2154+ /// setting the value of that field implicitly with its default.
2155+ ///
2156+ /// If we encounter code like
2157+ /// ```text
2158+ /// struct Priv;
2159+ /// pub struct S {
2160+ /// pub field: Priv = Priv,
2161+ /// }
2162+ /// ```
2163+ /// which is used from a place where `Priv` isn't accessible
2164+ /// ```text
2165+ /// let _ = S { field: m::Priv1 {} };
2166+ /// // ^^^^^ private struct
2167+ /// ```
2168+ /// we will suggest instead using the `default_field_values` syntax instead:
2169+ /// ```text
2170+ /// let _ = S { .. };
2171+ /// ```
2172+ fn mention_default_field_values (
2173+ & self ,
2174+ source : & Option < ast:: Expr > ,
2175+ ident : Ident ,
2176+ err : & mut Diag < ' _ > ,
2177+ ) {
2178+ let Some ( expr) = source else { return } ;
2179+ let ast:: ExprKind :: Struct ( struct_expr) = & expr. kind else { return } ;
2180+ // We don't have to handle type-relative paths because they're forbidden in ADT
2181+ // expressions, but that would change with `#[feature(more_qualified_paths)]`.
2182+ let Some ( Res :: Def ( _, def_id) ) =
2183+ self . partial_res_map [ & struct_expr. path . segments . iter ( ) . last ( ) . unwrap ( ) . id ] . full_res ( )
2184+ else {
2185+ return ;
2186+ } ;
2187+ let Some ( default_fields) = self . field_defaults ( def_id) else { return } ;
2188+ if struct_expr. fields . is_empty ( ) {
2189+ return ;
2190+ }
2191+ let last_span = struct_expr. fields . iter ( ) . last ( ) . unwrap ( ) . span ;
2192+ let mut iter = struct_expr. fields . iter ( ) . peekable ( ) ;
2193+ let mut prev: Option < Span > = None ;
2194+ while let Some ( field) = iter. next ( ) {
2195+ if field. expr . span . overlaps ( ident. span ) {
2196+ err. span_label ( field. ident . span , "while setting this field" ) ;
2197+ if default_fields. contains ( & field. ident . name ) {
2198+ let sugg = if last_span == field. span {
2199+ vec ! [ ( field. span, ".." . to_string( ) ) ]
2200+ } else {
2201+ vec ! [
2202+ (
2203+ // Account for trailing commas and ensure we remove them.
2204+ match ( prev, iter. peek( ) ) {
2205+ ( _, Some ( next) ) => field. span. with_hi( next. span. lo( ) ) ,
2206+ ( Some ( prev) , _) => field. span. with_lo( prev. hi( ) ) ,
2207+ ( None , None ) => field. span,
2208+ } ,
2209+ String :: new( ) ,
2210+ ) ,
2211+ ( last_span. shrink_to_hi( ) , ", .." . to_string( ) ) ,
2212+ ]
2213+ } ;
2214+ err. multipart_suggestion_verbose (
2215+ format ! (
2216+ "the type `{ident}` of field `{}` is private, but you can construct \
2217+ the default value defined for it in `{}` using `..` in the struct \
2218+ initializer expression",
2219+ field. ident,
2220+ self . tcx. item_name( def_id) ,
2221+ ) ,
2222+ sugg,
2223+ Applicability :: MachineApplicable ,
2224+ ) ;
2225+ break ;
2226+ }
2227+ }
2228+ prev = Some ( field. span ) ;
2229+ }
2230+ }
2231+
21442232 pub ( crate ) fn find_similarly_named_module_or_crate (
21452233 & self ,
21462234 ident : Symbol ,
0 commit comments