rustc_lint/default_could_be_derived.rs
1use rustc_attr_data_structures::{AttributeKind, find_attr};
2use rustc_data_structures::fx::FxHashMap;
3use rustc_errors::{Applicability, Diag};
4use rustc_hir as hir;
5use rustc_middle::ty;
6use rustc_middle::ty::TyCtxt;
7use rustc_session::{declare_lint, impl_lint_pass};
8use rustc_span::Symbol;
9use rustc_span::def_id::DefId;
10use rustc_span::symbol::sym;
11
12use crate::{LateContext, LateLintPass};
13
14declare_lint! {
15 /// The `default_overrides_default_fields` lint checks for manual `impl` blocks of the
16 /// `Default` trait of types with default field values.
17 ///
18 /// ### Example
19 ///
20 /// ```rust,compile_fail
21 /// #![feature(default_field_values)]
22 /// struct Foo {
23 /// x: i32 = 101,
24 /// y: NonDefault,
25 /// }
26 ///
27 /// struct NonDefault;
28 ///
29 /// #[deny(default_overrides_default_fields)]
30 /// impl Default for Foo {
31 /// fn default() -> Foo {
32 /// Foo { x: 100, y: NonDefault }
33 /// }
34 /// }
35 /// ```
36 ///
37 /// {{produces}}
38 ///
39 /// ### Explanation
40 ///
41 /// Manually writing a `Default` implementation for a type that has
42 /// default field values runs the risk of diverging behavior between
43 /// `Type { .. }` and `<Type as Default>::default()`, which would be a
44 /// foot-gun for users of that type that would expect these to be
45 /// equivalent. If `Default` can't be derived due to some fields not
46 /// having a `Default` implementation, we encourage the use of `..` for
47 /// the fields that do have a default field value.
48 pub DEFAULT_OVERRIDES_DEFAULT_FIELDS,
49 Deny,
50 "detect `Default` impl that should use the type's default field values",
51 @feature_gate = default_field_values;
52}
53
54#[derive(Default)]
55pub(crate) struct DefaultCouldBeDerived;
56
57impl_lint_pass!(DefaultCouldBeDerived => [DEFAULT_OVERRIDES_DEFAULT_FIELDS]);
58
59impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
60 fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
61 // Look for manual implementations of `Default`.
62 let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return };
63 let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
64 let assoc = cx.tcx.associated_item(impl_item.owner_id);
65 let parent = assoc.container_id(cx.tcx);
66 if find_attr!(cx.tcx.get_all_attrs(parent), AttributeKind::AutomaticallyDerived(..)) {
67 // We don't care about what `#[derive(Default)]` produces in this lint.
68 return;
69 }
70 let Some(trait_ref) = cx.tcx.impl_trait_ref(parent) else { return };
71 let trait_ref = trait_ref.instantiate_identity();
72 if trait_ref.def_id != default_def_id {
73 return;
74 }
75 let ty = trait_ref.self_ty();
76 let ty::Adt(def, _) = ty.kind() else { return };
77
78 // We now know we have a manually written definition of a `<Type as Default>::default()`.
79
80 let type_def_id = def.did();
81 let body = cx.tcx.hir_body(body_id);
82
83 // FIXME: evaluate bodies with statements and evaluate bindings to see if they would be
84 // derivable.
85 let hir::ExprKind::Block(hir::Block { stmts: _, expr: Some(expr), .. }, None) =
86 body.value.kind
87 else {
88 return;
89 };
90
91 // Keep a mapping of field name to `hir::FieldDef` for every field in the type. We'll use
92 // these to check for things like checking whether it has a default or using its span for
93 // suggestions.
94 let orig_fields = match cx.tcx.hir_get_if_local(type_def_id) {
95 Some(hir::Node::Item(hir::Item {
96 kind:
97 hir::ItemKind::Struct(
98 _,
99 _generics,
100 hir::VariantData::Struct { fields, recovered: _ },
101 ),
102 ..
103 })) => fields.iter().map(|f| (f.ident.name, f)).collect::<FxHashMap<_, _>>(),
104 _ => return,
105 };
106
107 // We check `fn default()` body is a single ADT literal and get all the fields that are
108 // being set.
109 let hir::ExprKind::Struct(_qpath, fields, tail) = expr.kind else { return };
110
111 // We have a struct literal
112 //
113 // struct Foo {
114 // field: Type,
115 // }
116 //
117 // impl Default for Foo {
118 // fn default() -> Foo {
119 // Foo {
120 // field: val,
121 // }
122 // }
123 // }
124 //
125 // We would suggest `#[derive(Default)]` if `field` has a default value, regardless of what
126 // it is; we don't want to encourage divergent behavior between `Default::default()` and
127 // `..`.
128
129 if let hir::StructTailExpr::Base(_) = tail {
130 // This is *very* niche. We'd only get here if someone wrote
131 // impl Default for Ty {
132 // fn default() -> Ty {
133 // Ty { ..something() }
134 // }
135 // }
136 // where `something()` would have to be a call or path.
137 // We have nothing meaningful to do with this.
138 return;
139 }
140
141 // At least one of the fields with a default value have been overridden in
142 // the `Default` implementation. We suggest removing it and relying on `..`
143 // instead.
144 let any_default_field_given =
145 fields.iter().any(|f| orig_fields.get(&f.ident.name).and_then(|f| f.default).is_some());
146
147 if !any_default_field_given {
148 // None of the default fields were actually provided explicitly, so the manual impl
149 // doesn't override them (the user used `..`), so there's no risk of divergent behavior.
150 return;
151 }
152
153 let Some(local) = parent.as_local() else { return };
154 let hir_id = cx.tcx.local_def_id_to_hir_id(local);
155 let hir::Node::Item(item) = cx.tcx.hir_node(hir_id) else { return };
156 cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, item.span, |diag| {
157 mk_lint(cx.tcx, diag, type_def_id, parent, orig_fields, fields);
158 });
159 }
160}
161
162fn mk_lint(
163 tcx: TyCtxt<'_>,
164 diag: &mut Diag<'_, ()>,
165 type_def_id: DefId,
166 impl_def_id: DefId,
167 orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
168 fields: &[hir::ExprField<'_>],
169) {
170 diag.primary_message("`Default` impl doesn't use the declared default field values");
171
172 // For each field in the struct expression
173 // - if the field in the type has a default value, it should be removed
174 // - elif the field is an expression that could be a default value, it should be used as the
175 // field's default value (FIXME: not done).
176 // - else, we wouldn't touch this field, it would remain in the manual impl
177 let mut removed_all_fields = true;
178 for field in fields {
179 if orig_fields.get(&field.ident.name).and_then(|f| f.default).is_some() {
180 diag.span_label(field.expr.span, "this field has a default value");
181 } else {
182 removed_all_fields = false;
183 }
184 }
185
186 if removed_all_fields {
187 let msg = "to avoid divergence in behavior between `Struct { .. }` and \
188 `<Struct as Default>::default()`, derive the `Default`";
189 if let Some(hir::Node::Item(impl_)) = tcx.hir_get_if_local(impl_def_id) {
190 diag.multipart_suggestion_verbose(
191 msg,
192 vec![
193 (tcx.def_span(type_def_id).shrink_to_lo(), "#[derive(Default)] ".to_string()),
194 (impl_.span, String::new()),
195 ],
196 Applicability::MachineApplicable,
197 );
198 } else {
199 diag.help(msg);
200 }
201 } else {
202 let msg = "use the default values in the `impl` with `Struct { mandatory_field, .. }` to \
203 avoid them diverging over time";
204 diag.help(msg);
205 }
206}