Skip to main content

rustc_lint/
default_could_be_derived.rs

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