11use clippy_utils:: diagnostics:: span_lint_and_then;
2- use clippy_utils:: { expr_or_init, get_trait_def_id , path_def_id} ;
2+ use clippy_utils:: { expr_or_init, fn_def_id_with_node_args , path_def_id} ;
33use rustc_ast:: BinOpKind ;
44use rustc_data_structures:: fx:: FxHashMap ;
55use rustc_hir as hir;
@@ -19,11 +19,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
1919
2020declare_clippy_lint ! {
2121 /// ### What it does
22- /// Checks that there isn't an infinite recursion in `PartialEq` trait
23- /// implementation .
22+ /// Checks that there isn't an infinite recursion in trait
23+ /// implementations .
2424 ///
2525 /// ### Why is this bad?
26- /// This is a hard to find infinite recursion which will crashing any code
26+ /// This is a hard to find infinite recursion that will crash any code.
2727 /// using it.
2828 ///
2929 /// ### Example
@@ -201,7 +201,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
201201 }
202202}
203203
204- #[ allow( clippy:: unnecessary_def_path) ]
205204fn check_to_string ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident , expr : & Expr < ' _ > ) {
206205 let args = cx
207206 . tcx
@@ -224,7 +223,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
224223 && let Some ( trait_) = impl_. of_trait
225224 && let Some ( trait_def_id) = trait_. trait_def_id ( )
226225 // The trait is `ToString`.
227- && Some ( trait_def_id ) == get_trait_def_id ( cx , & [ "alloc" , "string" , "ToString" ] )
226+ && cx . tcx . is_diagnostic_item ( sym :: ToString , trait_def_id )
228227 {
229228 let is_bad = match expr. kind {
230229 ExprKind :: MethodCall ( segment, _receiver, & [ _arg] , _) if segment. ident . name == name. name => {
@@ -291,7 +290,6 @@ where
291290 self . map
292291 }
293292
294- #[ allow( clippy:: unnecessary_def_path) ]
295293 fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
296294 if self . found_default_call {
297295 return ;
@@ -303,7 +301,7 @@ where
303301 && is_default_method_on_current_ty ( self . cx . tcx , qpath, self . implemented_ty_id )
304302 && let Some ( method_def_id) = path_def_id ( self . cx , f)
305303 && let Some ( trait_def_id) = self . cx . tcx . trait_of_item ( method_def_id)
306- && Some ( trait_def_id ) == get_trait_def_id ( self . cx , & [ "core" , "default" , "Default" ] )
304+ && self . cx . tcx . is_diagnostic_item ( sym :: Default , trait_def_id )
307305 {
308306 self . found_default_call = true ;
309307 span_error ( self . cx , self . method_span , expr) ;
@@ -312,10 +310,9 @@ where
312310}
313311
314312impl UnconditionalRecursion {
315- #[ allow( clippy:: unnecessary_def_path) ]
316313 fn init_default_impl_for_type_if_needed ( & mut self , cx : & LateContext < ' _ > ) {
317314 if self . default_impl_for_type . is_empty ( )
318- && let Some ( default_trait_id) = get_trait_def_id ( cx , & [ "core" , "default" , " Default" ] )
315+ && let Some ( default_trait_id) = cx . tcx . get_diagnostic_item ( sym :: Default )
319316 {
320317 let impls = cx. tcx . trait_impls_of ( default_trait_id) ;
321318 for ( ty, impl_def_ids) in impls. non_blanket_impls ( ) {
@@ -394,6 +391,34 @@ impl UnconditionalRecursion {
394391 }
395392}
396393
394+ fn check_from ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , expr : & Expr < ' _ > ) {
395+ let Some ( sig) = cx
396+ . typeck_results ( )
397+ . liberated_fn_sigs ( )
398+ . get ( cx. tcx . local_def_id_to_hir_id ( method_def_id) )
399+ else {
400+ return ;
401+ } ;
402+
403+ // Check if we are calling `Into::into` where the node args match with our `From::from` signature:
404+ // From::from signature: fn(S1) -> S2
405+ // <S1 as Into<S2>>::into(s1), node_args=[S1, S2]
406+ // If they do match, then it must mean that it is the blanket impl,
407+ // which calls back into our `From::from` again (`Into` is not specializable).
408+ // rustc's unconditional_recursion already catches calling `From::from` directly
409+ if let Some ( ( fn_def_id, node_args) ) = fn_def_id_with_node_args ( cx, expr)
410+ && let [ s1, s2] = * * node_args
411+ && let ( Some ( s1) , Some ( s2) ) = ( s1. as_type ( ) , s2. as_type ( ) )
412+ && let Some ( trait_def_id) = cx. tcx . trait_of_item ( fn_def_id)
413+ && cx. tcx . is_diagnostic_item ( sym:: Into , trait_def_id)
414+ && get_impl_trait_def_id ( cx, method_def_id) == cx. tcx . get_diagnostic_item ( sym:: From )
415+ && s1 == sig. inputs ( ) [ 0 ]
416+ && s2 == sig. output ( )
417+ {
418+ span_error ( cx, method_span, expr) ;
419+ }
420+ }
421+
397422impl < ' tcx > LateLintPass < ' tcx > for UnconditionalRecursion {
398423 fn check_fn (
399424 & mut self ,
@@ -410,10 +435,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
410435 // Doesn't have a conditional return.
411436 && !has_conditional_return ( body, expr)
412437 {
413- if name. name == sym:: eq || name. name == sym:: ne {
414- check_partial_eq ( cx, method_span, method_def_id, name, expr) ;
415- } else if name. name == sym:: to_string {
416- check_to_string ( cx, method_span, method_def_id, name, expr) ;
438+ match name. name {
439+ sym:: eq | sym:: ne => check_partial_eq ( cx, method_span, method_def_id, name, expr) ,
440+ sym:: to_string => check_to_string ( cx, method_span, method_def_id, name, expr) ,
441+ sym:: from => check_from ( cx, method_span, method_def_id, expr) ,
442+ _ => { } ,
417443 }
418444 self . check_default_new ( cx, decl, body, method_span, method_def_id) ;
419445 }
0 commit comments