@@ -1974,54 +1974,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19741974 let mut err =
19751975 self . dcx ( ) . create_err ( errors:: IsPrivate { span : ident. span , ident_descr, ident } ) ;
19761976
1977- if let Some ( expr) = source
1978- && let ast:: ExprKind :: Struct ( struct_expr) = & expr. kind
1979- && let Some ( Res :: Def ( _, def_id) ) = self . partial_res_map
1980- [ & struct_expr. path . segments . iter ( ) . last ( ) . unwrap ( ) . id ]
1981- . full_res ( )
1982- && let Some ( default_fields) = self . field_defaults ( def_id)
1983- && !struct_expr. fields . is_empty ( )
1984- {
1985- let last_span = struct_expr. fields . iter ( ) . last ( ) . unwrap ( ) . span ;
1986- let mut iter = struct_expr. fields . iter ( ) . peekable ( ) ;
1987- let mut prev: Option < Span > = None ;
1988- while let Some ( field) = iter. next ( ) {
1989- if field. expr . span . overlaps ( ident. span ) {
1990- err. span_label ( field. ident . span , "while setting this field" ) ;
1991- if default_fields. contains ( & field. ident . name ) {
1992- let sugg = if last_span == field. span {
1993- vec ! [ ( field. span, ".." . to_string( ) ) ]
1994- } else {
1995- vec ! [
1996- (
1997- // Account for trailing commas and ensure we remove them.
1998- match ( prev, iter. peek( ) ) {
1999- ( _, Some ( next) ) => field. span. with_hi( next. span. lo( ) ) ,
2000- ( Some ( prev) , _) => field. span. with_lo( prev. hi( ) ) ,
2001- ( None , None ) => field. span,
2002- } ,
2003- String :: new( ) ,
2004- ) ,
2005- ( last_span. shrink_to_hi( ) , ", .." . to_string( ) ) ,
2006- ]
2007- } ;
2008- err. multipart_suggestion_verbose (
2009- format ! (
2010- "the type `{ident}` of field `{}` is private, but you can \
2011- construct the default value defined for it in `{}` using `..` in \
2012- the struct initializer expression",
2013- field. ident,
2014- self . tcx. item_name( def_id) ,
2015- ) ,
2016- sugg,
2017- Applicability :: MachineApplicable ,
2018- ) ;
2019- break ;
2020- }
2021- }
2022- prev = Some ( field. span ) ;
2023- }
2024- }
1977+ self . mention_default_field_values ( source, ident, & mut err) ;
20251978
20261979 let mut not_publicly_reexported = false ;
20271980 if let Some ( ( this_res, outer_ident) ) = outermost_res {
@@ -2204,6 +2157,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
22042157 err. emit ( ) ;
22052158 }
22062159
2160+ /// When a private field is being set that has a default field value, we suggest using `..` and
2161+ /// setting the value of that field implicitly with its default.
2162+ ///
2163+ /// If we encounter code like
2164+ /// ```text
2165+ /// struct Priv;
2166+ /// pub struct S {
2167+ /// pub field: Priv = Priv,
2168+ /// }
2169+ /// ```
2170+ /// which is used from a place where `Priv` isn't accessible
2171+ /// ```text
2172+ /// let _ = S { field: m::Priv1 {} };
2173+ /// // ^^^^^ private struct
2174+ /// ```
2175+ /// we will suggest instead using the `default_field_values` syntax instead:
2176+ /// ```text
2177+ /// let _ = S { .. };
2178+ /// ```
2179+ fn mention_default_field_values (
2180+ & self ,
2181+ source : & Option < ast:: Expr > ,
2182+ ident : Ident ,
2183+ err : & mut Diag < ' _ > ,
2184+ ) {
2185+ let Some ( expr) = source else { return } ;
2186+ let ast:: ExprKind :: Struct ( struct_expr) = & expr. kind else { return } ;
2187+ // We don't have to handle type-relative paths because they're forbidden in ADT
2188+ // expressions, but that would change with `#[feature(more_qualified_paths)]`.
2189+ let Some ( Res :: Def ( _, def_id) ) =
2190+ self . partial_res_map [ & struct_expr. path . segments . iter ( ) . last ( ) . unwrap ( ) . id ] . full_res ( )
2191+ else {
2192+ return ;
2193+ } ;
2194+ let Some ( default_fields) = self . field_defaults ( def_id) else { return } ;
2195+ if struct_expr. fields . is_empty ( ) {
2196+ return ;
2197+ }
2198+ let last_span = struct_expr. fields . iter ( ) . last ( ) . unwrap ( ) . span ;
2199+ let mut iter = struct_expr. fields . iter ( ) . peekable ( ) ;
2200+ let mut prev: Option < Span > = None ;
2201+ while let Some ( field) = iter. next ( ) {
2202+ if field. expr . span . overlaps ( ident. span ) {
2203+ err. span_label ( field. ident . span , "while setting this field" ) ;
2204+ if default_fields. contains ( & field. ident . name ) {
2205+ let sugg = if last_span == field. span {
2206+ vec ! [ ( field. span, ".." . to_string( ) ) ]
2207+ } else {
2208+ vec ! [
2209+ (
2210+ // Account for trailing commas and ensure we remove them.
2211+ match ( prev, iter. peek( ) ) {
2212+ ( _, Some ( next) ) => field. span. with_hi( next. span. lo( ) ) ,
2213+ ( Some ( prev) , _) => field. span. with_lo( prev. hi( ) ) ,
2214+ ( None , None ) => field. span,
2215+ } ,
2216+ String :: new( ) ,
2217+ ) ,
2218+ ( last_span. shrink_to_hi( ) , ", .." . to_string( ) ) ,
2219+ ]
2220+ } ;
2221+ err. multipart_suggestion_verbose (
2222+ format ! (
2223+ "the type `{ident}` of field `{}` is private, but you can construct \
2224+ the default value defined for it in `{}` using `..` in the struct \
2225+ initializer expression",
2226+ field. ident,
2227+ self . tcx. item_name( def_id) ,
2228+ ) ,
2229+ sugg,
2230+ Applicability :: MachineApplicable ,
2231+ ) ;
2232+ break ;
2233+ }
2234+ }
2235+ prev = Some ( field. span ) ;
2236+ }
2237+ }
2238+
22072239 pub ( crate ) fn find_similarly_named_module_or_crate (
22082240 & self ,
22092241 ident : Symbol ,
0 commit comments