@@ -2,7 +2,7 @@ use rustc_ast::LitKind;
22use rustc_errors:: Applicability ;
33use rustc_hir as hir;
44use rustc_hir:: def:: { CtorKind , CtorOf , DefKind , Res } ;
5- use rustc_middle:: ty:: TyCtxt ;
5+ use rustc_middle:: ty:: Ty ;
66use rustc_session:: { declare_lint, declare_lint_pass} ;
77use rustc_span:: symbol:: { kw, sym} ;
88
@@ -135,6 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
135135 // We have a manual `impl Default for Ty {}` item, where `Ty` has no type parameters.
136136
137137 for assoc in data. items {
138+ // We look for the user's `fn default() -> Self` associated fn of the `Default` impl.
139+
138140 let hir:: AssocItemKind :: Fn { has_self : false } = assoc. kind else { continue } ;
139141 if assoc. ident . name != kw:: Default {
140142 continue ;
@@ -148,6 +150,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
148150 continue ;
149151 } ;
150152
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()`.
151155 match expr. kind {
152156 hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
153157 if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , def_id) =
@@ -211,9 +215,10 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
211215 //
212216 // We suggest #[derive(Default)] if
213217 // - `val` is `Default::default()`
218+ // - `val` matches the `Default::default()` body for that type
214219 // - `val` is `0`
215220 // - `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 ) ) {
217222 cx. tcx . node_span_lint (
218223 DEFAULT_COULD_BE_DERIVED ,
219224 item. hir_id ( ) ,
@@ -241,7 +246,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
241246 path. res
242247 {
243248 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) ) {
245250 // We have a struct literal
246251 //
247252 // struct Foo(Type);
@@ -254,6 +259,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
254259 //
255260 // We suggest #[derive(Default)] if
256261 // - `val` is `Default::default()`
262+ // - `val` matches the `Default::default()` body for that type
257263 // - `val` is `0`
258264 // - `val` is `false`
259265 cx. tcx . node_span_lint (
@@ -319,97 +325,77 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
319325 }
320326}
321327
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 {
324335 return false ;
325336 } ;
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 {
327371 hir:: ExprKind :: Lit ( spanned_lit) => match spanned_lit. node {
328372 LitKind :: Int ( val, _) if val == 0 => true , // field: 0,
329373 LitKind :: Bool ( false ) => true , // field: false,
330374 _ => false ,
331375 } ,
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)
339387 }
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)
413399 }
414400 _ => false ,
415401 }
0 commit comments