about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-01-22 20:37:26 +0100
committerGitHub <noreply@github.com>2025-01-22 20:37:26 +0100
commit3b3687920337d20d41f2f44b97eed6ea30120100 (patch)
tree9725a6607ff3844707ab7df4d86601fc25abbd40
parent5fab5429c4a48d606e1722a68eecbd40662daa75 (diff)
parent922e6bb2d4a5828cc12499c4f3dca685c3fd9002 (diff)
downloadrust-3b3687920337d20d41f2f44b97eed6ea30120100.tar.gz
rust-3b3687920337d20d41f2f44b97eed6ea30120100.zip
Rollup merge of #135794 - estebank:non-exhaustive-dfv-ctor, r=jieyouxu
Detect missing fields with default values and suggest `..`

When a struct ctor use has missing fields, if all those missing fields have defaults, suggest `..`:

```
error[E0063]: missing fields `field1` and `field2` in initializer of `S`
  --> $DIR/non-exhaustive-ctor.rs:16:13
   |
LL |     let _ = S { field: () };
   |             ^ missing `field1` and `field2`
   |
help: all remaining fields have default values, you can use those values with `..`
   |
LL |     let _ = S { field: (), .. };
   |                          ++++
```
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs30
-rw-r--r--tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr86
-rw-r--r--tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed28
-rw-r--r--tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr25
-rw-r--r--tests/ui/structs/default-field-values/non-exhaustive-ctor.rs28
5 files changed, 197 insertions, 0 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 01fed72d5a2..bdd436302f4 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2349,6 +2349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.report_missing_fields(
                     adt_ty,
                     path_span,
+                    expr.span,
                     remaining_fields,
                     variant,
                     hir_fields,
@@ -2386,6 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         adt_ty: Ty<'tcx>,
         span: Span,
+        full_span: Span,
         remaining_fields: UnordMap<Ident, (FieldIdx, &ty::FieldDef)>,
         variant: &'tcx ty::VariantDef,
         hir_fields: &'tcx [hir::ExprField<'tcx>],
@@ -2425,6 +2427,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
 
+        if remaining_fields.items().all(|(_, (_, field))| field.value.is_some())
+            && self.tcx.sess.is_nightly_build()
+        {
+            let msg = format!(
+                "all remaining fields have default values, {you_can} use those values with `..`",
+                you_can = if self.tcx.features().default_field_values() {
+                    "you can"
+                } else {
+                    "if you added `#![feature(default_field_values)]` to your crate you could"
+                },
+            );
+            if let Some(hir_field) = hir_fields.last() {
+                err.span_suggestion_verbose(
+                    hir_field.span.shrink_to_hi(),
+                    msg,
+                    ", ..".to_string(),
+                    Applicability::MachineApplicable,
+                );
+            } else if hir_fields.is_empty() {
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi().with_hi(full_span.hi()),
+                    msg,
+                    " { .. }".to_string(),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+
         if let Some(hir_field) = hir_fields.last() {
             self.suggest_fru_from_range_and_emit(hir_field, variant, args, err);
         } else {
diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr
new file mode 100644
index 00000000000..63793425657
--- /dev/null
+++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr
@@ -0,0 +1,86 @@
+error[E0658]: default values on fields are experimental
+  --> $DIR/non-exhaustive-ctor.rs:9:22
+   |
+LL |         pub field: () = (),
+   |                      ^^^^^
+   |
+   = note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
+   = help: add `#![feature(default_field_values)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: default values on fields are experimental
+  --> $DIR/non-exhaustive-ctor.rs:11:25
+   |
+LL |         pub field1: Priv = Priv,
+   |                         ^^^^^^^
+   |
+   = note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
+   = help: add `#![feature(default_field_values)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: default values on fields are experimental
+  --> $DIR/non-exhaustive-ctor.rs:13:25
+   |
+LL |         pub field2: Priv = Priv,
+   |                         ^^^^^^^
+   |
+   = note: see issue #132162 <https://github.com/rust-lang/rust/issues/132162> for more information
+   = help: add `#![feature(default_field_values)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0797]: base expression required after `..`
+  --> $DIR/non-exhaustive-ctor.rs:20:19
+   |
+LL |     let _ = S { .. }; // ok
+   |                   ^
+   |
+help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
+   |
+LL + #![feature(default_field_values)]
+   |
+help: add a base expression here
+   |
+LL |     let _ = S { ../* expr */ }; // ok
+   |                   ++++++++++
+
+error[E0797]: base expression required after `..`
+  --> $DIR/non-exhaustive-ctor.rs:22:30
+   |
+LL |     let _ = S { field: (), .. }; // ok
+   |                              ^
+   |
+help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
+   |
+LL + #![feature(default_field_values)]
+   |
+help: add a base expression here
+   |
+LL |     let _ = S { field: (), ../* expr */ }; // ok
+   |                              ++++++++++
+
+error[E0063]: missing fields `field`, `field1` and `field2` in initializer of `S`
+  --> $DIR/non-exhaustive-ctor.rs:24:13
+   |
+LL |     let _ = S { };
+   |             ^ missing `field`, `field1` and `field2`
+   |
+help: all remaining fields have default values, if you added `#![feature(default_field_values)]` to your crate you could use those values with `..`
+   |
+LL |     let _ = S { .. };
+   |               ~~~~~~
+
+error[E0063]: missing fields `field1` and `field2` in initializer of `S`
+  --> $DIR/non-exhaustive-ctor.rs:26:13
+   |
+LL |     let _ = S { field: () };
+   |             ^ missing `field1` and `field2`
+   |
+help: all remaining fields have default values, if you added `#![feature(default_field_values)]` to your crate you could use those values with `..`
+   |
+LL |     let _ = S { field: (), .. };
+   |                          ++++
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0063, E0658, E0797.
+For more information about an error, try `rustc --explain E0063`.
diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed
new file mode 100644
index 00000000000..7a371f993e8
--- /dev/null
+++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed
@@ -0,0 +1,28 @@
+//@ revisions: enabled disabled
+//@[enabled] run-rustfix
+#![allow(private_interfaces, dead_code)]
+#![cfg_attr(enabled, feature(default_field_values))]
+use m::S;
+
+mod m {
+    pub struct S {
+        pub field: () = (),
+        //[disabled]~^ ERROR default values on fields are experimental
+        pub field1: Priv = Priv,
+        //[disabled]~^ ERROR default values on fields are experimental
+        pub field2: Priv = Priv,
+        //[disabled]~^ ERROR default values on fields are experimental
+    }
+    struct Priv;
+}
+
+fn main() {
+    let _ = S { .. }; // ok
+    //[disabled]~^ ERROR base expression required after `..`
+    let _ = S { field: (), .. }; // ok
+    //[disabled]~^ ERROR base expression required after `..`
+    let _ = S { .. };
+    //~^ ERROR missing fields `field`, `field1` and `field2`
+    let _ = S { field: (), .. };
+    //~^ ERROR missing fields `field1` and `field2`
+}
diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr
new file mode 100644
index 00000000000..6d035ebdc47
--- /dev/null
+++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr
@@ -0,0 +1,25 @@
+error[E0063]: missing fields `field`, `field1` and `field2` in initializer of `S`
+  --> $DIR/non-exhaustive-ctor.rs:24:13
+   |
+LL |     let _ = S { };
+   |             ^ missing `field`, `field1` and `field2`
+   |
+help: all remaining fields have default values, you can use those values with `..`
+   |
+LL |     let _ = S { .. };
+   |               ~~~~~~
+
+error[E0063]: missing fields `field1` and `field2` in initializer of `S`
+  --> $DIR/non-exhaustive-ctor.rs:26:13
+   |
+LL |     let _ = S { field: () };
+   |             ^ missing `field1` and `field2`
+   |
+help: all remaining fields have default values, you can use those values with `..`
+   |
+LL |     let _ = S { field: (), .. };
+   |                          ++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0063`.
diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.rs b/tests/ui/structs/default-field-values/non-exhaustive-ctor.rs
new file mode 100644
index 00000000000..b60b219f8bc
--- /dev/null
+++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.rs
@@ -0,0 +1,28 @@
+//@ revisions: enabled disabled
+//@[enabled] run-rustfix
+#![allow(private_interfaces, dead_code)]
+#![cfg_attr(enabled, feature(default_field_values))]
+use m::S;
+
+mod m {
+    pub struct S {
+        pub field: () = (),
+        //[disabled]~^ ERROR default values on fields are experimental
+        pub field1: Priv = Priv,
+        //[disabled]~^ ERROR default values on fields are experimental
+        pub field2: Priv = Priv,
+        //[disabled]~^ ERROR default values on fields are experimental
+    }
+    struct Priv;
+}
+
+fn main() {
+    let _ = S { .. }; // ok
+    //[disabled]~^ ERROR base expression required after `..`
+    let _ = S { field: (), .. }; // ok
+    //[disabled]~^ ERROR base expression required after `..`
+    let _ = S { };
+    //~^ ERROR missing fields `field`, `field1` and `field2`
+    let _ = S { field: () };
+    //~^ ERROR missing fields `field1` and `field2`
+}