Skip to content

Commit 86e6877

Browse files
committed
Detect missing fields with default values and suggest ..
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: (), .. }; | ++++ ```
1 parent 6a64e3b commit 86e6877

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+31
Original file line numberDiff line numberDiff line change
@@ -2349,6 +2349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23492349
self.report_missing_fields(
23502350
adt_ty,
23512351
path_span,
2352+
expr.span,
23522353
remaining_fields,
23532354
variant,
23542355
hir_fields,
@@ -2386,6 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23862387
&self,
23872388
adt_ty: Ty<'tcx>,
23882389
span: Span,
2390+
full_span: Span,
23892391
remaining_fields: UnordMap<Ident, (FieldIdx, &ty::FieldDef)>,
23902392
variant: &'tcx ty::VariantDef,
23912393
hir_fields: &'tcx [hir::ExprField<'tcx>],
@@ -2425,6 +2427,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24252427
);
24262428
err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
24272429

2430+
if remaining_fields.items().all(|(_, (_, field))| field.value.is_some()) {
2431+
let msg = format!(
2432+
"all remaining fields have default values, {you_can} use those values with `..`",
2433+
you_can = if self.tcx.features().default_field_values() {
2434+
"you can"
2435+
} else if self.tcx.sess.is_nightly_build() {
2436+
"if you added `#![feature(default_field_values)]` to your crate you could"
2437+
} else {
2438+
"if your crate were nightly-only, you could add \
2439+
`#![feature(default_field_values)]` to your crate and"
2440+
},
2441+
);
2442+
if let Some(hir_field) = hir_fields.last() {
2443+
err.span_suggestion_verbose(
2444+
hir_field.span.shrink_to_hi(),
2445+
msg,
2446+
", ..".to_string(),
2447+
Applicability::MachineApplicable,
2448+
);
2449+
} else if hir_fields.is_empty() {
2450+
err.span_suggestion_verbose(
2451+
span.shrink_to_hi().with_hi(full_span.hi()),
2452+
msg,
2453+
" { .. }".to_string(),
2454+
Applicability::MachineApplicable,
2455+
);
2456+
}
2457+
}
2458+
24282459
if let Some(hir_field) = hir_fields.last() {
24292460
self.suggest_fru_from_range_and_emit(hir_field, variant, args, err);
24302461
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![feature(default_field_values)]
2+
use m::S;
3+
4+
mod m {
5+
pub struct S {
6+
pub field: () = (),
7+
pub field1: Priv = Priv,
8+
pub field2: Priv = Priv,
9+
}
10+
struct Priv;
11+
}
12+
13+
fn main() {
14+
let _ = S { .. }; // ok
15+
let _ = S { field: (), .. }; // ok
16+
let _ = S { };
17+
//~^ ERROR missing fields `field`, `field1` and `field2`
18+
let _ = S { field: () };
19+
//~^ ERROR missing fields `field1` and `field2`
20+
let _ = S { field: (), field1: m::Priv };
21+
//~^ ERROR missing field `field2`
22+
//~| ERROR unit struct `Priv` is private
23+
let _ = S { field: (), field1: m::Priv, field2: m::Priv };
24+
//~^ ERROR unit struct `Priv` is private
25+
//~| ERROR unit struct `Priv` is private
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
error[E0603]: unit struct `Priv` is private
2+
--> $DIR/non-exhaustive-ctor.rs:20:39
3+
|
4+
LL | let _ = S { field: (), field1: m::Priv };
5+
| ^^^^ private unit struct
6+
|
7+
note: the unit struct `Priv` is defined here
8+
--> $DIR/non-exhaustive-ctor.rs:10:5
9+
|
10+
LL | struct Priv;
11+
| ^^^^^^^^^^^^
12+
13+
error[E0603]: unit struct `Priv` is private
14+
--> $DIR/non-exhaustive-ctor.rs:23:39
15+
|
16+
LL | let _ = S { field: (), field1: m::Priv, field2: m::Priv };
17+
| ^^^^ private unit struct
18+
|
19+
note: the unit struct `Priv` is defined here
20+
--> $DIR/non-exhaustive-ctor.rs:10:5
21+
|
22+
LL | struct Priv;
23+
| ^^^^^^^^^^^^
24+
25+
error[E0603]: unit struct `Priv` is private
26+
--> $DIR/non-exhaustive-ctor.rs:23:56
27+
|
28+
LL | let _ = S { field: (), field1: m::Priv, field2: m::Priv };
29+
| ^^^^ private unit struct
30+
|
31+
note: the unit struct `Priv` is defined here
32+
--> $DIR/non-exhaustive-ctor.rs:10:5
33+
|
34+
LL | struct Priv;
35+
| ^^^^^^^^^^^^
36+
37+
error[E0063]: missing fields `field`, `field1` and `field2` in initializer of `S`
38+
--> $DIR/non-exhaustive-ctor.rs:16:13
39+
|
40+
LL | let _ = S { };
41+
| ^ missing `field`, `field1` and `field2`
42+
|
43+
help: all remaining fields have default values, you can use those values with `..`
44+
|
45+
LL | let _ = S { .. };
46+
| ~~~~~~
47+
48+
error[E0063]: missing fields `field1` and `field2` in initializer of `S`
49+
--> $DIR/non-exhaustive-ctor.rs:18:13
50+
|
51+
LL | let _ = S { field: () };
52+
| ^ missing `field1` and `field2`
53+
|
54+
help: all remaining fields have default values, you can use those values with `..`
55+
|
56+
LL | let _ = S { field: (), .. };
57+
| ++++
58+
59+
error[E0063]: missing field `field2` in initializer of `S`
60+
--> $DIR/non-exhaustive-ctor.rs:20:13
61+
|
62+
LL | let _ = S { field: (), field1: m::Priv };
63+
| ^ missing `field2`
64+
|
65+
help: all remaining fields have default values, you can use those values with `..`
66+
|
67+
LL | let _ = S { field: (), field1: m::Priv, .. };
68+
| ++++
69+
70+
error: aborting due to 6 previous errors
71+
72+
Some errors have detailed explanations: E0063, E0603.
73+
For more information about an error, try `rustc --explain E0063`.

0 commit comments

Comments
 (0)