1use rustc_data_structures::fx::FxHashMap;
2use rustc_errors::{Applicability, Diag};
3use rustc_hiras 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};
1011use crate::{LateContext, LateLintPass};
1213#[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.
47pub 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}5253pub 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 {
<[_]>::into_vec(::alloc::boxed::box_new([DEFAULT_OVERRIDES_DEFAULT_FIELDS]))
}
}
impl DefaultCouldBeDerived {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([DEFAULT_OVERRIDES_DEFAULT_FIELDS]))
}
}declare_lint_pass!(DefaultCouldBeDerived => [DEFAULT_OVERRIDES_DEFAULT_FIELDS]);
5455impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
56fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
57// Look for manual implementations of `Default`.
58let hir::ImplItemImplKind::Trait { trait_item_def_id, .. } = impl_item.impl_kind else {
59return;
60 };
61if !trait_item_def_id.is_ok_and(|id| cx.tcx.is_diagnostic_item(sym::default_fn, id)) {
62return;
63 }
64let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
65let impl_id = cx.tcx.local_parent(impl_item.owner_id.def_id);
66if cx.tcx.is_automatically_derived(impl_id.to_def_id()) {
67// We don't care about what `#[derive(Default)]` produces in this lint.
68return;
69 }
70let ty = cx.tcx.type_of(impl_id).instantiate_identity();
71let ty::Adt(def, _) = ty.kind() else { return };
7273// We now know we have a manually written definition of a `<Type as Default>::default()`.
7475let type_def_id = def.did();
76let body = cx.tcx.hir_body(body_id);
7778// FIXME: evaluate bodies with statements and evaluate bindings to see if they would be
79 // derivable.
80let hir::ExprKind::Block(hir::Block { stmts: _, expr: Some(expr), .. }, None) =
81body.value.kind
82else {
83return;
84 };
8586// 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.
89let orig_fields = match cx.tcx.hir_get_if_local(type_def_id) {
90Some(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 };
101102// We check `fn default()` body is a single ADT literal and get all the fields that are
103 // being set.
104let hir::ExprKind::Struct(_qpath, fields, tail) = expr.kind else { return };
105106// 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 // `..`.
123124if 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.
133return;
134 }
135136// 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.
139let any_default_field_given =
140fields.iter().any(|f| orig_fields.get(&f.ident.name).and_then(|f| f.default).is_some());
141142if !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.
145return;
146 }
147148let hir_id = cx.tcx.local_def_id_to_hir_id(impl_id);
149let span = cx.tcx.hir_span_with_body(hir_id);
150cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, span, |diag| {
151mk_lint(cx.tcx, diag, type_def_id, orig_fields, fields, span);
152 });
153 }
154}
155156fn mk_lint(
157 tcx: TyCtxt<'_>,
158 diag: &mut Diag<'_, ()>,
159 type_def_id: DefId,
160 orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
161 fields: &[hir::ExprField<'_>],
162 impl_span: Span,
163) {
164diag.primary_message("`Default` impl doesn't use the declared default field values");
165166// For each field in the struct expression
167 // - if the field in the type has a default value, it should be removed
168 // - elif the field is an expression that could be a default value, it should be used as the
169 // field's default value (FIXME: not done).
170 // - else, we wouldn't touch this field, it would remain in the manual impl
171let mut removed_all_fields = true;
172for field in fields {
173if orig_fields.get(&field.ident.name).and_then(|f| f.default).is_some() {
174 diag.span_label(field.expr.span, "this field has a default value");
175 } else {
176 removed_all_fields = false;
177 }
178 }
179180if removed_all_fields {
181let msg = "to avoid divergence in behavior between `Struct { .. }` and \
182 `<Struct as Default>::default()`, derive the `Default`";
183diag.multipart_suggestion_verbose(
184msg,
185<[_]>::into_vec(::alloc::boxed::box_new([(tcx.def_span(type_def_id).shrink_to_lo(),
"#[derive(Default)] ".to_string()),
(impl_span, String::new())]))vec![
186 (tcx.def_span(type_def_id).shrink_to_lo(), "#[derive(Default)] ".to_string()),
187 (impl_span, String::new()),
188 ],
189 Applicability::MachineApplicable,
190 );
191 } else {
192let msg = "use the default values in the `impl` with `Struct { mandatory_field, .. }` to \
193 avoid them diverging over time";
194diag.help(msg);
195 }
196}