diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2025-06-27 22:38:29 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2025-08-10 19:15:20 +0000 |
| commit | 29d26f27a6d6c466ed143bdfa128f76a19226743 (patch) | |
| tree | 69aa62eeafec491359c415121bab25d00aaafe3f /compiler/rustc_resolve/src/diagnostics.rs | |
| parent | 464a6b1b4af28f7b2d1adf051bad3f182e23b88e (diff) | |
| download | rust-29d26f27a6d6c466ed143bdfa128f76a19226743.tar.gz rust-29d26f27a6d6c466ed143bdfa128f76a19226743.zip | |
review comments
Diffstat (limited to 'compiler/rustc_resolve/src/diagnostics.rs')
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 128 |
1 files changed, 80 insertions, 48 deletions
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 40ea90bf484..210ab72678c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1967,54 +1967,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut err = self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident }); - if let Some(expr) = source - && let ast::ExprKind::Struct(struct_expr) = &expr.kind - && let Some(Res::Def(_, def_id)) = self.partial_res_map - [&struct_expr.path.segments.iter().last().unwrap().id] - .full_res() - && let Some(default_fields) = self.field_defaults(def_id) - && !struct_expr.fields.is_empty() - { - let last_span = struct_expr.fields.iter().last().unwrap().span; - let mut iter = struct_expr.fields.iter().peekable(); - let mut prev: Option<Span> = None; - while let Some(field) = iter.next() { - if field.expr.span.overlaps(ident.span) { - err.span_label(field.ident.span, "while setting this field"); - if default_fields.contains(&field.ident.name) { - let sugg = if last_span == field.span { - vec![(field.span, "..".to_string())] - } else { - vec![ - ( - // Account for trailing commas and ensure we remove them. - match (prev, iter.peek()) { - (_, Some(next)) => field.span.with_hi(next.span.lo()), - (Some(prev), _) => field.span.with_lo(prev.hi()), - (None, None) => field.span, - }, - String::new(), - ), - (last_span.shrink_to_hi(), ", ..".to_string()), - ] - }; - err.multipart_suggestion_verbose( - format!( - "the type `{ident}` of field `{}` is private, but you can \ - construct the default value defined for it in `{}` using `..` in \ - the struct initializer expression", - field.ident, - self.tcx.item_name(def_id), - ), - sugg, - Applicability::MachineApplicable, - ); - break; - } - } - prev = Some(field.span); - } - } + self.mention_default_field_values(source, ident, &mut err); let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { @@ -2197,6 +2150,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err.emit(); } + /// When a private field is being set that has a default field value, we suggest using `..` and + /// setting the value of that field implicitly with its default. + /// + /// If we encounter code like + /// ```text + /// struct Priv; + /// pub struct S { + /// pub field: Priv = Priv, + /// } + /// ``` + /// which is used from a place where `Priv` isn't accessible + /// ```text + /// let _ = S { field: m::Priv1 {} }; + /// // ^^^^^ private struct + /// ``` + /// we will suggest instead using the `default_field_values` syntax instead: + /// ```text + /// let _ = S { .. }; + /// ``` + fn mention_default_field_values( + &self, + source: &Option<ast::Expr>, + ident: Ident, + err: &mut Diag<'_>, + ) { + let Some(expr) = source else { return }; + let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; + // We don't have to handle type-relative paths because they're forbidden in ADT + // expressions, but that would change with `#[feature(more_qualified_paths)]`. + let Some(Res::Def(_, def_id)) = + self.partial_res_map[&struct_expr.path.segments.iter().last().unwrap().id].full_res() + else { + return; + }; + let Some(default_fields) = self.field_defaults(def_id) else { return }; + if struct_expr.fields.is_empty() { + return; + } + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option<Span> = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion_verbose( + format!( + "the type `{ident}` of field `{}` is private, but you can construct \ + the default value defined for it in `{}` using `..` in the struct \ + initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } + pub(crate) fn find_similarly_named_module_or_crate( &self, ident: Symbol, |
