@@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
4141 let hir:: ItemKind :: Impl ( data) = item. kind else { return } ;
4242 let Some ( trait_ref) = data. of_trait else { return } ;
4343 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) {
4546 return ;
4647 }
4748 if cx. tcx . has_attr ( def_id, sym:: automatically_derived) {
@@ -137,6 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
137138 // - `val` is `0`
138139 // - `val` is `false`
139140 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+ } ;
140144 match kind {
141145 hir:: ExprKind :: Lit ( spanned_lit) => match spanned_lit. node {
142146 LitKind :: Int ( val, _) if val == 0 => true , // field: 0,
@@ -155,15 +159,84 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
155159 hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) )
156160 if let Res :: Def (
157161 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 =>
162164 {
163165 // 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
167240 }
168241 _ => false ,
169242 }
0 commit comments