@@ -8,6 +8,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl, Item, ItemKind, Node};
88use rustc_lint:: { LateContext , LateLintPass } ;
99use rustc_middle:: ty:: { self , Ty } ;
1010use rustc_session:: declare_lint_pass;
11+ use rustc_span:: symbol:: Ident ;
1112use rustc_span:: { sym, Span } ;
1213
1314declare_clippy_lint ! {
@@ -55,6 +56,76 @@ fn is_local(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
5556 matches ! ( path_res( cx, expr) , Res :: Local ( _) )
5657}
5758
59+ fn check_partial_eq (
60+ cx : & LateContext < ' _ > ,
61+ body : & Body < ' _ > ,
62+ method_span : Span ,
63+ method_def_id : LocalDefId ,
64+ name : Ident ,
65+ ) {
66+ let args = cx
67+ . tcx
68+ . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( method_def_id) . skip_binder ( ) )
69+ . inputs ( ) ;
70+ // That has two arguments.
71+ if let [ self_arg, other_arg] = args
72+ && let Some ( self_arg) = get_ty_def_id ( * self_arg)
73+ && let Some ( other_arg) = get_ty_def_id ( * other_arg)
74+ // The two arguments are of the same type.
75+ && self_arg == other_arg
76+ && let hir_id = cx. tcx . local_def_id_to_hir_id ( method_def_id)
77+ && let Some ( (
78+ _,
79+ Node :: Item ( Item {
80+ kind : ItemKind :: Impl ( impl_) ,
81+ owner_id,
82+ ..
83+ } ) ,
84+ ) ) = cx. tcx . hir ( ) . parent_iter ( hir_id) . next ( )
85+ // We exclude `impl` blocks generated from rustc's proc macros.
86+ && !cx. tcx . has_attr ( * owner_id, sym:: automatically_derived)
87+ // It is a implementation of a trait.
88+ && let Some ( trait_) = impl_. of_trait
89+ && let Some ( trait_def_id) = trait_. trait_def_id ( )
90+ // The trait is `PartialEq`.
91+ && Some ( trait_def_id) == get_trait_def_id ( cx, & [ "core" , "cmp" , "PartialEq" ] )
92+ {
93+ let to_check_op = if name. name == sym:: eq {
94+ BinOpKind :: Eq
95+ } else {
96+ BinOpKind :: Ne
97+ } ;
98+ let expr = body. value . peel_blocks ( ) ;
99+ let is_bad = match expr. kind {
100+ ExprKind :: Binary ( op, left, right) if op. node == to_check_op => is_local ( cx, left) && is_local ( cx, right) ,
101+ ExprKind :: MethodCall ( segment, receiver, & [ arg] , _) if segment. ident . name == name. name => {
102+ if is_local ( cx, receiver)
103+ && is_local ( cx, & arg)
104+ && let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
105+ && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
106+ && trait_id == trait_def_id
107+ {
108+ true
109+ } else {
110+ false
111+ }
112+ } ,
113+ _ => false ,
114+ } ;
115+ if is_bad {
116+ span_lint_and_then (
117+ cx,
118+ UNCONDITIONAL_RECURSION ,
119+ method_span,
120+ "function cannot return without recursing" ,
121+ |diag| {
122+ diag. span_note ( expr. span , "recursive call site" ) ;
123+ } ,
124+ ) ;
125+ }
126+ }
127+ }
128+
58129impl < ' tcx > LateLintPass < ' tcx > for UnconditionalRecursion {
59130 #[ allow( clippy:: unnecessary_def_path) ]
60131 fn check_fn (
@@ -64,70 +135,12 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
64135 _decl : & ' tcx FnDecl < ' tcx > ,
65136 body : & ' tcx Body < ' tcx > ,
66137 method_span : Span ,
67- def_id : LocalDefId ,
138+ method_def_id : LocalDefId ,
68139 ) {
69140 // If the function is a method...
70- if let FnKind :: Method ( name, _) = kind
71- // That has two arguments.
72- && let [ self_arg, other_arg] = cx
73- . tcx
74- . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( def_id) . skip_binder ( ) )
75- . inputs ( )
76- && let Some ( self_arg) = get_ty_def_id ( * self_arg)
77- && let Some ( other_arg) = get_ty_def_id ( * other_arg)
78- // The two arguments are of the same type.
79- && self_arg == other_arg
80- && let hir_id = cx. tcx . local_def_id_to_hir_id ( def_id)
81- && let Some ( (
82- _,
83- Node :: Item ( Item {
84- kind : ItemKind :: Impl ( impl_) ,
85- owner_id,
86- ..
87- } ) ,
88- ) ) = cx. tcx . hir ( ) . parent_iter ( hir_id) . next ( )
89- // We exclude `impl` blocks generated from rustc's proc macros.
90- && !cx. tcx . has_attr ( * owner_id, sym:: automatically_derived)
91- // It is a implementation of a trait.
92- && let Some ( trait_) = impl_. of_trait
93- && let Some ( trait_def_id) = trait_. trait_def_id ( )
94- // The trait is `PartialEq`.
95- && Some ( trait_def_id) == get_trait_def_id ( cx, & [ "core" , "cmp" , "PartialEq" ] )
96- {
97- let to_check_op = if name. name == sym:: eq {
98- BinOpKind :: Eq
99- } else {
100- BinOpKind :: Ne
101- } ;
102- let expr = body. value . peel_blocks ( ) ;
103- let is_bad = match expr. kind {
104- ExprKind :: Binary ( op, left, right) if op. node == to_check_op => {
105- is_local ( cx, left) && is_local ( cx, right)
106- } ,
107- ExprKind :: MethodCall ( segment, receiver, & [ arg] , _) if segment. ident . name == name. name => {
108- if is_local ( cx, receiver)
109- && is_local ( cx, & arg)
110- && let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
111- && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
112- && trait_id == trait_def_id
113- {
114- true
115- } else {
116- false
117- }
118- } ,
119- _ => false ,
120- } ;
121- if is_bad {
122- span_lint_and_then (
123- cx,
124- UNCONDITIONAL_RECURSION ,
125- method_span,
126- "function cannot return without recursing" ,
127- |diag| {
128- diag. span_note ( expr. span , "recursive call site" ) ;
129- } ,
130- ) ;
141+ if let FnKind :: Method ( name, _) = kind {
142+ if name. name == sym:: eq || name. name == sym:: ne {
143+ check_partial_eq ( cx, body, method_span, method_def_id, name) ;
131144 }
132145 }
133146 }
0 commit comments