1
1
use 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, get_trait_def_id, path_def_id} ;
3
+ use itertools:: Itertools ;
3
4
use rustc_ast:: BinOpKind ;
4
5
use rustc_data_structures:: fx:: FxHashMap ;
5
6
use rustc_hir as hir;
@@ -19,11 +20,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
19
20
20
21
declare_clippy_lint ! {
21
22
/// ### What it does
22
- /// Checks that there isn't an infinite recursion in `PartialEq` trait
23
- /// implementation .
23
+ /// Checks that there isn't an infinite recursion in trait
24
+ /// implementations .
24
25
///
25
26
/// ### Why is this bad?
26
- /// This is a hard to find infinite recursion which will crashing any code
27
+ /// This is a hard to find infinite recursion which will crash any code
27
28
/// using it.
28
29
///
29
30
/// ### Example
@@ -201,7 +202,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
201
202
}
202
203
}
203
204
204
- #[ allow( clippy:: unnecessary_def_path) ]
205
205
fn check_to_string ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident , expr : & Expr < ' _ > ) {
206
206
let args = cx
207
207
. tcx
@@ -224,7 +224,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
224
224
&& let Some ( trait_) = impl_. of_trait
225
225
&& let Some ( trait_def_id) = trait_. trait_def_id ( )
226
226
// The trait is `ToString`.
227
- && Some ( trait_def_id ) == get_trait_def_id ( cx , & [ "alloc" , "string" , "ToString" ] )
227
+ && cx . tcx . is_diagnostic_item ( sym :: ToString , trait_def_id )
228
228
{
229
229
let is_bad = match expr. kind {
230
230
ExprKind :: MethodCall ( segment, _receiver, & [ _arg] , _) if segment. ident . name == name. name => {
@@ -291,7 +291,6 @@ where
291
291
self . map
292
292
}
293
293
294
- #[ allow( clippy:: unnecessary_def_path) ]
295
294
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
296
295
if self . found_default_call {
297
296
return ;
@@ -303,7 +302,7 @@ where
303
302
&& is_default_method_on_current_ty ( self . cx . tcx , qpath, self . implemented_ty_id )
304
303
&& let Some ( method_def_id) = path_def_id ( self . cx , f)
305
304
&& 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" ] )
305
+ && self . cx . tcx . is_diagnostic_item ( sym :: Default , trait_def_id )
307
306
{
308
307
self . found_default_call = true ;
309
308
span_error ( self . cx , self . method_span , expr) ;
@@ -394,6 +393,39 @@ impl UnconditionalRecursion {
394
393
}
395
394
}
396
395
396
+ fn check_from ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , expr : & Expr < ' _ > ) {
397
+ let Some ( sig) = cx
398
+ . typeck_results ( )
399
+ . liberated_fn_sigs ( )
400
+ . get ( cx. tcx . local_def_id_to_hir_id ( method_def_id) )
401
+ else {
402
+ return ;
403
+ } ;
404
+ let typeck = cx. typeck_results ( ) ;
405
+
406
+ // Check if we are calling `Into::into` where the node args match with our `From::from` signature:
407
+ // From::from signature: fn(S1) -> S2
408
+ // <S1 as Into<S2>>::into(s1), node_args=[S1, S2]
409
+ // If they do match, then it must mean that it is the blanket impl,
410
+ // which calls back into our `From::from` again (`Into` is not specializable).
411
+ // rustc's unconditional_recursion already catches calling `From::from` directly
412
+ if let Some ( fn_def_id) = fn_def_id ( cx, expr)
413
+ && let node_args = match expr. kind {
414
+ ExprKind :: MethodCall ( ..) => typeck. node_args ( expr. hir_id ) ,
415
+ ExprKind :: Call ( callee, _) => typeck. node_args ( callee. hir_id ) ,
416
+ _ => return ,
417
+ }
418
+ && let Some ( ( s1, s2) ) = node_args. iter ( ) . filter_map ( ty:: GenericArg :: as_type) . collect_tuple ( )
419
+ && let Some ( trait_def_id) = cx. tcx . trait_of_item ( fn_def_id)
420
+ && cx. tcx . is_diagnostic_item ( sym:: Into , trait_def_id)
421
+ && get_impl_trait_def_id ( cx, method_def_id) == cx. tcx . get_diagnostic_item ( sym:: From )
422
+ && s1 == sig. inputs ( ) [ 0 ]
423
+ && s2 == sig. output ( )
424
+ {
425
+ span_error ( cx, method_span, expr) ;
426
+ }
427
+ }
428
+
397
429
impl < ' tcx > LateLintPass < ' tcx > for UnconditionalRecursion {
398
430
fn check_fn (
399
431
& mut self ,
@@ -410,10 +442,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
410
442
// Doesn't have a conditional return.
411
443
&& !has_conditional_return ( body, expr)
412
444
{
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) ;
445
+ match name. name {
446
+ sym:: eq | sym:: ne => check_partial_eq ( cx, method_span, method_def_id, name, expr) ,
447
+ sym:: to_string => check_to_string ( cx, method_span, method_def_id, name, expr) ,
448
+ sym:: from => check_from ( cx, method_span, method_def_id, expr) ,
449
+ _ => { } ,
417
450
}
418
451
self . check_default_new ( cx, decl, body, method_span, method_def_id) ;
419
452
}
0 commit comments