@@ -2,7 +2,7 @@ use rustc_ast::LitKind;
2
2
use rustc_errors:: Applicability ;
3
3
use rustc_hir as hir;
4
4
use rustc_hir:: def:: { CtorKind , CtorOf , DefKind , Res } ;
5
- use rustc_middle:: ty:: TyCtxt ;
5
+ use rustc_middle:: ty:: Ty ;
6
6
use rustc_session:: { declare_lint, declare_lint_pass} ;
7
7
use rustc_span:: symbol:: { kw, sym} ;
8
8
@@ -135,6 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
135
135
// We have a manual `impl Default for Ty {}` item, where `Ty` has no type parameters.
136
136
137
137
for assoc in data. items {
138
+ // We look for the user's `fn default() -> Self` associated fn of the `Default` impl.
139
+
138
140
let hir:: AssocItemKind :: Fn { has_self : false } = assoc. kind else { continue } ;
139
141
if assoc. ident . name != kw:: Default {
140
142
continue ;
@@ -148,6 +150,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
148
150
continue ;
149
151
} ;
150
152
153
+ // We check `fn default()` body is a single ADT literal and all the fields are being
154
+ // set to something equivalent to the corresponding types' `Default::default()`.
151
155
match expr. kind {
152
156
hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
153
157
if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , def_id) =
@@ -211,9 +215,10 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
211
215
//
212
216
// We suggest #[derive(Default)] if
213
217
// - `val` is `Default::default()`
218
+ // - `val` matches the `Default::default()` body for that type
214
219
// - `val` is `0`
215
220
// - `val` is `false`
216
- if fields. iter ( ) . all ( |f| check_expr ( cx. tcx , f. expr . kind ) ) {
221
+ if fields. iter ( ) . all ( |f| check_expr ( cx, f. expr ) ) {
217
222
cx. tcx . node_span_lint (
218
223
DEFAULT_COULD_BE_DERIVED ,
219
224
item. hir_id ( ) ,
@@ -241,7 +246,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
241
246
path. res
242
247
{
243
248
let type_def_id = cx. tcx . parent ( ctor_def_id) ; // From Ctor to struct
244
- if args. iter ( ) . all ( |expr| check_expr ( cx. tcx , expr. kind ) ) {
249
+ if args. iter ( ) . all ( |expr| check_expr ( cx, expr) ) {
245
250
// We have a struct literal
246
251
//
247
252
// struct Foo(Type);
@@ -254,6 +259,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
254
259
//
255
260
// We suggest #[derive(Default)] if
256
261
// - `val` is `Default::default()`
262
+ // - `val` matches the `Default::default()` body for that type
257
263
// - `val` is `0`
258
264
// - `val` is `false`
259
265
cx. tcx . node_span_lint (
@@ -319,97 +325,77 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
319
325
}
320
326
}
321
327
322
- fn check_expr ( tcx : TyCtxt < ' _ > , kind : hir:: ExprKind < ' _ > ) -> bool {
323
- let Some ( default_def_id) = tcx. get_diagnostic_item ( sym:: Default ) else {
328
+ fn check_path < ' tcx > (
329
+ cx : & LateContext < ' tcx > ,
330
+ path : & hir:: QPath < ' _ > ,
331
+ hir_id : hir:: HirId ,
332
+ ty : Ty < ' tcx > ,
333
+ ) -> bool {
334
+ let Some ( default_def_id) = cx. tcx . get_diagnostic_item ( sym:: Default ) else {
324
335
return false ;
325
336
} ;
326
- match kind {
337
+ let res = cx. qpath_res ( & path, hir_id) ;
338
+ let Some ( def_id) = res. opt_def_id ( ) else { return false } ;
339
+ if cx. tcx . is_diagnostic_item ( sym:: default_fn, def_id) {
340
+ // We have `field: Default::default(),`. This is what the derive would do already.
341
+ return true ;
342
+ }
343
+ // For every `Default` impl for this type (there should be a single one), we see if it
344
+ // has a "canonical" `DefId` for a fn call with no arguments, or a path. If it does, we
345
+ // check that `DefId` with the `DefId` of this field's value if it is also a call/path.
346
+ // If there's a match, it means that the contents of that type's `Default` impl are the
347
+ // same to what the user wrote on *their* `Default` impl for this field.
348
+ let mut equivalents = vec ! [ ] ;
349
+ cx. tcx . for_each_relevant_impl ( default_def_id, ty, |impl_def_id| {
350
+ let equivalent = match impl_def_id. as_local ( ) {
351
+ None => cx. tcx . get_default_impl_equivalent ( impl_def_id) ,
352
+ Some ( local) => {
353
+ let def_kind = cx. tcx . def_kind ( impl_def_id) ;
354
+ cx. tcx . get_default_equivalent ( def_kind, local)
355
+ }
356
+ } ;
357
+ if let Some ( did) = equivalent {
358
+ equivalents. push ( did) ;
359
+ }
360
+ } ) ;
361
+ for did in equivalents {
362
+ if did == def_id {
363
+ return true ;
364
+ }
365
+ }
366
+ false
367
+ }
368
+
369
+ fn check_expr ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
370
+ match expr. kind {
327
371
hir:: ExprKind :: Lit ( spanned_lit) => match spanned_lit. node {
328
372
LitKind :: Int ( val, _) if val == 0 => true , // field: 0,
329
373
LitKind :: Bool ( false ) => true , // field: false,
330
374
_ => false ,
331
375
} ,
332
- hir:: ExprKind :: Call ( expr, [ ] )
333
- if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) ) = expr. kind
334
- && let Some ( def_id) = path. res . opt_def_id ( )
335
- && tcx. is_diagnostic_item ( sym:: default_fn, def_id) =>
336
- {
337
- // field: Default::default(),
338
- true
376
+ hir:: ExprKind :: Call ( hir:: Expr { kind : hir:: ExprKind :: Path ( path) , hir_id, .. } , [ ] ) => {
377
+ // `field: foo(),` or `field: Ty::assoc(),`
378
+ let Some ( ty) = cx
379
+ . tcx
380
+ . has_typeck_results ( expr. hir_id . owner . def_id )
381
+ . then ( || cx. tcx . typeck ( expr. hir_id . owner . def_id ) )
382
+ . and_then ( |typeck| typeck. expr_ty_adjusted_opt ( expr) )
383
+ else {
384
+ return false ;
385
+ } ;
386
+ check_path ( cx, & path, * hir_id, ty)
339
387
}
340
- hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
341
- if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , ctor_def_id) =
342
- path. res =>
343
- {
344
- // FIXME: We should use a better check where we explore existing
345
- // `impl Default for def_id` of the found type when `def_id` is not
346
- // local and see compare them against what we have here. For now,
347
- // we special case `Option::None` and only check unit variants of
348
- // local `Default` impls.
349
- let var_def_id = tcx. parent ( ctor_def_id) ; // From Ctor to variant
350
-
351
- // We explicitly check for `Option::<T>::None`. If `Option` was
352
- // local, it would be accounted by the logic further down, but
353
- // because the analysis uses purely the HIR, that doesn't work
354
- // accross crates.
355
- //
356
- // field: None,
357
- let mut found = tcx. is_lang_item ( var_def_id, hir:: LangItem :: OptionNone ) ;
358
-
359
- // Look at the local `impl Default for ty` of the field's `ty`.
360
- let ty_def_id = tcx. parent ( var_def_id) ; // From variant to enum
361
- let ty = tcx. type_of ( ty_def_id) . instantiate_identity ( ) ;
362
- tcx. for_each_relevant_impl ( default_def_id, ty, |impl_did| {
363
- let hir = tcx. hir ( ) ;
364
- let Some ( hir:: Node :: Item ( impl_item) ) = hir. get_if_local ( impl_did) else {
365
- return ;
366
- } ;
367
- let hir:: ItemKind :: Impl ( impl_item) = impl_item. kind else {
368
- return ;
369
- } ;
370
- for assoc in impl_item. items {
371
- let hir:: AssocItemKind :: Fn { has_self : false } = assoc. kind else {
372
- continue ;
373
- } ;
374
- if assoc. ident . name != kw:: Default {
375
- continue ;
376
- }
377
- let assoc = hir. impl_item ( assoc. id ) ;
378
- let hir:: ImplItemKind :: Fn ( _ty, body) = assoc. kind else {
379
- continue ;
380
- } ;
381
- let body = hir. body ( body) ;
382
- let hir:: ExprKind :: Block ( hir:: Block { stmts : [ ] , expr : Some ( expr) , .. } , None ) =
383
- body. value . kind
384
- else {
385
- continue ;
386
- } ;
387
- // Look at a specific implementation of `Default::default()`
388
- // for their content and see if they are requivalent to what
389
- // the user wrote in their manual `impl` for a given field.
390
- match expr. kind {
391
- hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
392
- if let Res :: Def (
393
- DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) ,
394
- orig_def_id,
395
- ) = path. res =>
396
- {
397
- // We found
398
- //
399
- // field: Foo::Unit,
400
- //
401
- // and
402
- //
403
- // impl Default for Foo {
404
- // fn default() -> Foo { Foo::Unit }
405
- // }
406
- found |= orig_def_id == ctor_def_id
407
- }
408
- _ => { }
409
- }
410
- }
411
- } ) ;
412
- found
388
+ hir:: ExprKind :: Path ( path) => {
389
+ // `field: qualified::Path,` or `field: <Ty as Trait>::Assoc,`
390
+ let Some ( ty) = cx
391
+ . tcx
392
+ . has_typeck_results ( expr. hir_id . owner . def_id )
393
+ . then ( || cx. tcx . typeck ( expr. hir_id . owner . def_id ) )
394
+ . and_then ( |typeck| typeck. expr_ty_adjusted_opt ( expr) )
395
+ else {
396
+ return false ;
397
+ } ;
398
+ check_path ( cx, & path, expr. hir_id , ty)
413
399
}
414
400
_ => false ,
415
401
}
0 commit comments