Skip to content

Commit 7eebd59

Browse files
committed
Lint structs with default field values and manual Default
1 parent 4db9d0c commit 7eebd59

4 files changed

+87
-8
lines changed

compiler/rustc_lint/src/default_could_be_derived.rs

+37-6
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,52 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
5858
hir::ItemKind::Struct(hir::VariantData::Struct { fields, recovered: _ }, _generics),
5959
..
6060
})) => {
61-
if fields.iter().all(|f| f.default.is_some()) {
61+
let fields_with_default_value: Vec<_> =
62+
fields.iter().filter_map(|f| f.default).collect();
63+
let fields_with_default_impl: Vec<_> = fields
64+
.iter()
65+
.filter_map(|f| match (f.ty.kind, f.default) {
66+
(hir::TyKind::Path(hir::QPath::Resolved(_, path)), None)
67+
if let Some(def_id) = path.res.opt_def_id()
68+
&& let DefKind::Struct | DefKind::Enum =
69+
cx.tcx.def_kind(def_id) =>
70+
{
71+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
72+
let mut count = 0;
73+
cx.tcx.for_each_relevant_impl(default_def_id, ty, |_| count += 1);
74+
if count > 0 { Some(f.ty.span) } else { None }
75+
}
76+
_ => None,
77+
})
78+
.collect();
79+
if !fields_with_default_value.is_empty()
80+
&& fields.len()
81+
== fields_with_default_value.len() + fields_with_default_impl.len()
82+
{
6283
cx.tcx.node_span_lint(
6384
DEFAULT_COULD_BE_DERIVED,
6485
item.hir_id(),
6586
item.span,
6687
|diag| {
6788
diag.primary_message("`impl Default` that could be derived");
89+
let msg = match (
90+
!fields_with_default_value.is_empty(),
91+
!fields_with_default_impl.is_empty(),
92+
) {
93+
(true, true) => "default values or a type that impls `Default`",
94+
(true, false) => "default values",
95+
(false, true) => "a type that impls `Default`",
96+
(false, false) => unreachable!(),
97+
};
6898
diag.span_label(
6999
cx.tcx.def_span(type_def_id),
70-
"all the fields in this struct have default values",
100+
format!("all the fields in this struct have {msg}"),
71101
);
72-
for field in &fields[..] {
73-
if let Some(anon) = field.default {
74-
diag.span_label(anon.span, "");
75-
}
102+
for anon in fields_with_default_value {
103+
diag.span_label(anon.span, "default value");
104+
}
105+
for field in fields_with_default_impl {
106+
diag.span_label(field, "implements `Default`");
76107
}
77108
diag.multipart_suggestion_verbose(
78109
"to avoid divergence in behavior between `Struct { .. }` and \

tests/ui/structs/manual-default-impl-could-be-derived.fixed

+8
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@
6060
}
6161

6262

63+
// Always lint against manual `Default` impl if all fields are defaulted.
64+
#[derive(PartialEq, Debug)]
65+
#[derive(Default)] struct I {
66+
x: i32 = 101,
67+
y: Option<i32>,
68+
}
69+
70+
6371
fn main() {
6472
// let _ = A::default();
6573
// let _ = B::default();

tests/ui/structs/manual-default-impl-could-be-derived.rs

+16
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@ impl Default for H { //~ ERROR
9494
}
9595
}
9696

97+
// Always lint against manual `Default` impl if all fields are defaulted.
98+
#[derive(PartialEq, Debug)]
99+
struct I {
100+
x: i32 = 101,
101+
y: Option<i32>,
102+
}
103+
104+
impl Default for I { //~ ERROR
105+
fn default() -> Self {
106+
I {
107+
x: 1,
108+
y: None,
109+
}
110+
}
111+
}
112+
97113
fn main() {
98114
// let _ = A::default();
99115
// let _ = B::default();

tests/ui/structs/manual-default-impl-could-be-derived.stderr

+26-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ error: `impl Default` that could be derived
7676
LL | struct H {
7777
| -------- all the fields in this struct have default values
7878
LL | x: i32 = 101,
79-
| ---
79+
| --- default value
8080
...
8181
LL | / impl Default for H {
8282
LL | | fn default() -> Self {
@@ -92,5 +92,29 @@ help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as De
9292
LL ~ #[derive(Default)] struct H {
9393
|
9494

95-
error: aborting due to 5 previous errors
95+
error: `impl Default` that could be derived
96+
--> $DIR/manual-default-impl-could-be-derived.rs:104:1
97+
|
98+
LL | struct I {
99+
| -------- all the fields in this struct have default values or a type that impls `Default`
100+
LL | x: i32 = 101,
101+
| --- default value
102+
LL | y: Option<i32>,
103+
| ----------- implements `Default`
104+
...
105+
LL | / impl Default for I {
106+
LL | | fn default() -> Self {
107+
LL | | I {
108+
LL | | x: 1,
109+
... |
110+
LL | | }
111+
LL | | }
112+
| |_^
113+
|
114+
help: to avoid divergence in behavior between `Struct { .. }` and `<Struct as Default>::default()`, derive the `Default`
115+
|
116+
LL ~ #[derive(Default)] struct I {
117+
|
118+
119+
error: aborting due to 6 previous errors
96120

0 commit comments

Comments
 (0)