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_with_node_args , path_def_id} ;
3
3
use rustc_ast:: BinOpKind ;
4
4
use rustc_data_structures:: fx:: FxHashMap ;
5
5
use rustc_hir as hir;
@@ -19,11 +19,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
19
19
20
20
declare_clippy_lint ! {
21
21
/// ### 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 .
24
24
///
25
25
/// ### 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.
27
27
/// using it.
28
28
///
29
29
/// ### Example
@@ -201,7 +201,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
201
201
}
202
202
}
203
203
204
- #[ allow( clippy:: unnecessary_def_path) ]
205
204
fn check_to_string ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident , expr : & Expr < ' _ > ) {
206
205
let args = cx
207
206
. tcx
@@ -224,7 +223,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
224
223
&& let Some ( trait_) = impl_. of_trait
225
224
&& let Some ( trait_def_id) = trait_. trait_def_id ( )
226
225
// 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 )
228
227
{
229
228
let is_bad = match expr. kind {
230
229
ExprKind :: MethodCall ( segment, _receiver, & [ _arg] , _) if segment. ident . name == name. name => {
@@ -291,7 +290,6 @@ where
291
290
self . map
292
291
}
293
292
294
- #[ allow( clippy:: unnecessary_def_path) ]
295
293
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
296
294
if self . found_default_call {
297
295
return ;
@@ -303,7 +301,7 @@ where
303
301
&& is_default_method_on_current_ty ( self . cx . tcx , qpath, self . implemented_ty_id )
304
302
&& let Some ( method_def_id) = path_def_id ( self . cx , f)
305
303
&& 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 )
307
305
{
308
306
self . found_default_call = true ;
309
307
span_error ( self . cx , self . method_span , expr) ;
@@ -312,10 +310,9 @@ where
312
310
}
313
311
314
312
impl UnconditionalRecursion {
315
- #[ allow( clippy:: unnecessary_def_path) ]
316
313
fn init_default_impl_for_type_if_needed ( & mut self , cx : & LateContext < ' _ > ) {
317
314
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 )
319
316
{
320
317
let impls = cx. tcx . trait_impls_of ( default_trait_id) ;
321
318
for ( ty, impl_def_ids) in impls. non_blanket_impls ( ) {
@@ -394,6 +391,34 @@ impl UnconditionalRecursion {
394
391
}
395
392
}
396
393
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
+
397
422
impl < ' tcx > LateLintPass < ' tcx > for UnconditionalRecursion {
398
423
fn check_fn (
399
424
& mut self ,
@@ -410,10 +435,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
410
435
// Doesn't have a conditional return.
411
436
&& !has_conditional_return ( body, expr)
412
437
{
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
+ _ => { } ,
417
443
}
418
444
self . check_default_new ( cx, decl, body, method_span, method_def_id) ;
419
445
}
0 commit comments