11use clippy_utils:: diagnostics:: span_lint_and_then;
2- use clippy_utils:: { expr_or_init, get_trait_def_id, path_res } ;
2+ use clippy_utils:: { expr_or_init, get_trait_def_id} ;
33use rustc_ast:: BinOpKind ;
4- use rustc_hir:: def:: Res ;
54use rustc_hir:: def_id:: { DefId , LocalDefId } ;
6- use rustc_hir:: intravisit:: FnKind ;
5+ use rustc_hir:: intravisit:: { walk_body , FnKind } ;
76use rustc_hir:: { Body , Expr , ExprKind , FnDecl , Item , ItemKind , Node } ;
87use rustc_lint:: { LateContext , LateLintPass } ;
98use rustc_middle:: ty:: { self , Ty } ;
109use rustc_session:: declare_lint_pass;
1110use rustc_span:: symbol:: Ident ;
1211use rustc_span:: { sym, Span } ;
12+ use rustc_trait_selection:: traits:: error_reporting:: suggestions:: ReturnsVisitor ;
1313
1414declare_clippy_lint ! {
1515 /// ### What it does
@@ -52,12 +52,19 @@ fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
5252 }
5353}
5454
55- fn is_local ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
56- matches ! ( path_res( cx, expr) , Res :: Local ( _) )
55+ fn has_conditional_return ( body : & Body < ' _ > , expr : & Expr < ' _ > ) -> bool {
56+ let mut visitor = ReturnsVisitor :: default ( ) ;
57+
58+ walk_body ( & mut visitor, body) ;
59+ match visitor. returns . as_slice ( ) {
60+ [ ] => false ,
61+ [ return_expr] => return_expr. hir_id != expr. hir_id ,
62+ _ => true ,
63+ }
5764}
5865
5966#[ allow( clippy:: unnecessary_def_path) ]
60- fn check_partial_eq ( cx : & LateContext < ' _ > , body : & Body < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident ) {
67+ fn check_partial_eq ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident , expr : & Expr < ' _ > ) {
6168 let args = cx
6269 . tcx
6370 . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( method_def_id) . skip_binder ( ) )
@@ -90,13 +97,23 @@ fn check_partial_eq(cx: &LateContext<'_>, body: &Body<'_>, method_span: Span, me
9097 } else {
9198 BinOpKind :: Ne
9299 } ;
93- let expr = body. value . peel_blocks ( ) ;
94100 let is_bad = match expr. kind {
95- ExprKind :: Binary ( op, left, right) if op. node == to_check_op => is_local ( cx, left) && is_local ( cx, right) ,
96- ExprKind :: MethodCall ( segment, receiver, & [ arg] , _) if segment. ident . name == name. name => {
97- if is_local ( cx, receiver)
98- && is_local ( cx, & arg)
99- && let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
101+ ExprKind :: Binary ( op, left, right) if op. node == to_check_op => {
102+ // Then we check if the left-hand element is of the same type as `self`.
103+ if let Some ( left_ty) = cx. typeck_results ( ) . expr_ty_opt ( left)
104+ && let Some ( left_id) = get_ty_def_id ( left_ty)
105+ && self_arg == left_id
106+ && let Some ( right_ty) = cx. typeck_results ( ) . expr_ty_opt ( right)
107+ && let Some ( right_id) = get_ty_def_id ( right_ty)
108+ && other_arg == right_id
109+ {
110+ true
111+ } else {
112+ false
113+ }
114+ } ,
115+ ExprKind :: MethodCall ( segment, _receiver, & [ _arg] , _) if segment. ident . name == name. name => {
116+ if let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
100117 && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
101118 && trait_id == trait_def_id
102119 {
@@ -122,7 +139,7 @@ fn check_partial_eq(cx: &LateContext<'_>, body: &Body<'_>, method_span: Span, me
122139}
123140
124141#[ allow( clippy:: unnecessary_def_path) ]
125- fn check_to_string ( cx : & LateContext < ' _ > , body : & Body < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident ) {
142+ fn check_to_string ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident , expr : & Expr < ' _ > ) {
126143 let args = cx
127144 . tcx
128145 . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( method_def_id) . skip_binder ( ) )
@@ -146,12 +163,9 @@ fn check_to_string(cx: &LateContext<'_>, body: &Body<'_>, method_span: Span, met
146163 // The trait is `ToString`.
147164 && Some ( trait_def_id) == get_trait_def_id ( cx, & [ "alloc" , "string" , "ToString" ] )
148165 {
149- let expr = expr_or_init ( cx, body. value . peel_blocks ( ) ) ;
150166 let is_bad = match expr. kind {
151- ExprKind :: MethodCall ( segment, receiver, & [ arg] , _) if segment. ident . name == name. name => {
152- if is_local ( cx, receiver)
153- && is_local ( cx, & arg)
154- && let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
167+ ExprKind :: MethodCall ( segment, _receiver, & [ _arg] , _) if segment. ident . name == name. name => {
168+ if let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
155169 && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
156170 && trait_id == trait_def_id
157171 {
@@ -187,11 +201,15 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
187201 method_def_id : LocalDefId ,
188202 ) {
189203 // If the function is a method...
190- if let FnKind :: Method ( name, _) = kind {
204+ if let FnKind :: Method ( name, _) = kind
205+ && let expr = expr_or_init ( cx, body. value ) . peel_blocks ( )
206+ // Doesn't have a conditional return.
207+ && !has_conditional_return ( body, expr)
208+ {
191209 if name. name == sym:: eq || name. name == sym:: ne {
192- check_partial_eq ( cx, body , method_span, method_def_id, name) ;
210+ check_partial_eq ( cx, method_span, method_def_id, name, expr ) ;
193211 } else if name. name == sym:: to_string {
194- check_to_string ( cx, body , method_span, method_def_id, name) ;
212+ check_to_string ( cx, method_span, method_def_id, name, expr ) ;
195213 }
196214 }
197215 }
0 commit comments