@@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
41
41
let hir:: ItemKind :: Impl ( data) = item. kind else { return } ;
42
42
let Some ( trait_ref) = data. of_trait else { return } ;
43
43
let Res :: Def ( DefKind :: Trait , def_id) = trait_ref. path . res else { return } ;
44
- if Some ( def_id) != cx. tcx . get_diagnostic_item ( sym:: Default ) {
44
+ let Some ( default_def_id) = cx. tcx . get_diagnostic_item ( sym:: Default ) else { return } ;
45
+ if Some ( def_id) != Some ( default_def_id) {
45
46
return ;
46
47
}
47
48
if cx. tcx . has_attr ( def_id, sym:: automatically_derived) {
@@ -137,6 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
137
138
// - `val` is `0`
138
139
// - `val` is `false`
139
140
fn check_expr ( tcx : TyCtxt < ' _ > , kind : hir:: ExprKind < ' _ > ) -> bool {
141
+ let Some ( default_def_id) = tcx. get_diagnostic_item ( sym:: Default ) else {
142
+ return false ;
143
+ } ;
140
144
match kind {
141
145
hir:: ExprKind :: Lit ( spanned_lit) => match spanned_lit. node {
142
146
LitKind :: Int ( val, _) if val == 0 => true , // field: 0,
@@ -155,15 +159,84 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
155
159
hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
156
160
if let Res :: Def (
157
161
DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) ,
158
- def_id,
159
- ) = path. res
160
- && let def_id = tcx. parent ( def_id) // From Ctor to variant
161
- && tcx. is_lang_item ( def_id, hir:: LangItem :: OptionNone ) =>
162
+ ctor_def_id,
163
+ ) = path. res =>
162
164
{
163
165
// FIXME: We should use a better check where we explore existing
164
- // `impl Default for def_id` of the found type and see compare them
165
- // against what we have here. For now, special case `Option::None`.
166
- true
166
+ // `impl Default for def_id` of the found type when `def_id` is not
167
+ // local and see compare them against what we have here. For now,
168
+ // we special case `Option::None` and only check unit variants of
169
+ // local `Default` impls.
170
+ let var_def_id = tcx. parent ( ctor_def_id) ; // From Ctor to variant
171
+
172
+ // We explicitly check for `Option::<T>::None`. If `Option` was
173
+ // local, it would be accounted by the logic further down, but
174
+ // because the analysis uses purely the HIR, that doesn't work
175
+ // accross crates.
176
+ //
177
+ // field: None,
178
+ let mut found =
179
+ tcx. is_lang_item ( var_def_id, hir:: LangItem :: OptionNone ) ;
180
+
181
+ // Look at the local `impl Default for ty` of the field's `ty`.
182
+ let ty_def_id = tcx. parent ( var_def_id) ; // From variant to enum
183
+ let ty = tcx. type_of ( ty_def_id) . instantiate_identity ( ) ;
184
+ tcx. for_each_relevant_impl ( default_def_id, ty, |impl_did| {
185
+ let hir = tcx. hir ( ) ;
186
+ let Some ( hir:: Node :: Item ( impl_item) ) =
187
+ hir. get_if_local ( impl_did)
188
+ else {
189
+ return ;
190
+ } ;
191
+ let hir:: ItemKind :: Impl ( impl_item) = impl_item. kind else {
192
+ return ;
193
+ } ;
194
+ for assoc in impl_item. items {
195
+ let hir:: AssocItemKind :: Fn { has_self : false } = assoc. kind
196
+ else {
197
+ continue ;
198
+ } ;
199
+ if assoc. ident . name != kw:: Default {
200
+ continue ;
201
+ }
202
+ let assoc = hir. impl_item ( assoc. id ) ;
203
+ let hir:: ImplItemKind :: Fn ( _ty, body) = assoc. kind else {
204
+ continue ;
205
+ } ;
206
+ let body = hir. body ( body) ;
207
+ let hir:: ExprKind :: Block (
208
+ hir:: Block { stmts : [ ] , expr : Some ( expr) , .. } ,
209
+ None ,
210
+ ) = body. value . kind
211
+ else {
212
+ continue ;
213
+ } ;
214
+ // Look at a specific implementation of `Default::default()`
215
+ // for their content and see if they are requivalent to what
216
+ // the user wrote in their manual `impl` for a given field.
217
+ match expr. kind {
218
+ hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
219
+ if let Res :: Def (
220
+ DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) ,
221
+ orig_def_id,
222
+ ) = path. res =>
223
+ {
224
+ // We found
225
+ //
226
+ // field: Foo::Unit,
227
+ //
228
+ // and
229
+ //
230
+ // impl Default for Foo {
231
+ // fn default() -> Foo { Foo::Unit }
232
+ // }
233
+ found |= orig_def_id == ctor_def_id
234
+ }
235
+ _ => { }
236
+ }
237
+ }
238
+ } ) ;
239
+ found
167
240
}
168
241
_ => false ,
169
242
}
0 commit comments