rustc_lint/
default_could_be_derived.rs

1use rustc_data_structures::fx::FxHashMap;
2use rustc_errors::{Applicability, Diag};
3use rustc_hir as hir;
4use rustc_hir::attrs::AttributeKind;
5use rustc_hir::find_attr;
6use rustc_middle::ty;
7use rustc_middle::ty::TyCtxt;
8use rustc_session::{declare_lint, impl_lint_pass};
9use rustc_span::Symbol;
10use rustc_span::def_id::DefId;
11use rustc_span::symbol::sym;
12
13use crate::{LateContext, LateLintPass};
14
15declare_lint! {
16    /// The `default_overrides_default_fields` lint checks for manual `impl` blocks of the
17    /// `Default` trait of types with default field values.
18    ///
19    /// ### Example
20    ///
21    /// ```rust,compile_fail
22    /// #![feature(default_field_values)]
23    /// struct Foo {
24    ///     x: i32 = 101,
25    ///     y: NonDefault,
26    /// }
27    ///
28    /// struct NonDefault;
29    ///
30    /// #[deny(default_overrides_default_fields)]
31    /// impl Default for Foo {
32    ///     fn default() -> Foo {
33    ///         Foo { x: 100, y: NonDefault }
34    ///     }
35    /// }
36    /// ```
37    ///
38    /// {{produces}}
39    ///
40    /// ### Explanation
41    ///
42    /// Manually writing a `Default` implementation for a type that has
43    /// default field values runs the risk of diverging behavior between
44    /// `Type { .. }` and `<Type as Default>::default()`, which would be a
45    /// foot-gun for users of that type that would expect these to be
46    /// equivalent. If `Default` can't be derived due to some fields not
47    /// having a `Default` implementation, we encourage the use of `..` for
48    /// the fields that do have a default field value.
49    pub DEFAULT_OVERRIDES_DEFAULT_FIELDS,
50    Deny,
51    "detect `Default` impl that should use the type's default field values",
52    @feature_gate = default_field_values;
53}
54
55#[derive(Default)]
56pub(crate) struct DefaultCouldBeDerived;
57
58impl_lint_pass!(DefaultCouldBeDerived => [DEFAULT_OVERRIDES_DEFAULT_FIELDS]);
59
60impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
61    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
62        // Look for manual implementations of `Default`.
63        let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return };
64        let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
65        let parent = cx.tcx.parent(impl_item.owner_id.to_def_id());
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}