about summary refs log tree commit diff
path: root/compiler/rustc_resolve/src/diagnostics.rs
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-01-21 21:07:20 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-08-10 19:15:18 +0000
commit464a6b1b4af28f7b2d1adf051bad3f182e23b88e (patch)
tree33f8b9d8e302ee705b99b74923c63c20bc96b97e /compiler/rustc_resolve/src/diagnostics.rs
parent18eeac04fc5c2a4c4a8020dbdf1c652077ad0e4e (diff)
downloadrust-464a6b1b4af28f7b2d1adf051bad3f182e23b88e.tar.gz
rust-464a6b1b4af28f7b2d1adf051bad3f182e23b88e.zip
Detect struct construction with private field in field with default
When trying to construct a struct that has a public field of a private type, suggest using `..` if that field has a default value.

```
error[E0603]: struct `Priv1` is private
  --> $DIR/non-exhaustive-ctor.rs:25:39
   |
LL |     let _ = S { field: (), field1: m::Priv1 {} };
   |                            ------     ^^^^^ private struct
   |                            |
   |                            while setting this field
   |
note: the struct `Priv1` is defined here
  --> $DIR/non-exhaustive-ctor.rs:14:4
   |
LL |    struct Priv1 {}
   |    ^^^^^^^^^^^^
help: the field `field1` you're trying to set has a default value, you can use `..` to use it
   |
LL |     let _ = S { field: (), .. };
   |                            ~~
```
Diffstat (limited to 'compiler/rustc_resolve/src/diagnostics.rs')
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs60
1 files changed, 58 insertions, 2 deletions
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 66f79585d92..40ea90bf484 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1943,8 +1943,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
     }
 
     fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) {
-        let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } =
-            *privacy_error;
+        let PrivacyError {
+            ident,
+            binding,
+            outermost_res,
+            parent_scope,
+            single_nested,
+            dedup_span,
+            ref source,
+        } = *privacy_error;
 
         let res = binding.res();
         let ctor_fields_span = self.ctor_fields_span(binding);
@@ -1960,6 +1967,55 @@ 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);
+            }
+        }
+
         let mut not_publicly_reexported = false;
         if let Some((this_res, outer_ident)) = outermost_res {
             let import_suggestions = self.lookup_import_candidates(