Skip to content

Commit

Permalink
Detect missing fields with default values and suggest ..
Browse files Browse the repository at this point in the history
When a struct definition has default field values, and the use struct ctor has missing field, 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 defaults, use `..`
   |
LL |     let _ = S { field: (), .. };
   |                          ++++
```
  • Loading branch information
estebank committed Jan 21, 2025
1 parent 6a64e3b commit 922e6bb
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 0 deletions.
30 changes: 30 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>],
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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`
}
Original file line number Diff line number Diff line change
@@ -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`.
28 changes: 28 additions & 0 deletions tests/ui/structs/default-field-values/non-exhaustive-ctor.rs
Original file line number Diff line number Diff line change
@@ -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`
}

0 comments on commit 922e6bb

Please sign in to comment.