1
1
use 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} ;
3
3
use rustc_ast:: BinOpKind ;
4
- use rustc_hir:: def:: Res ;
5
4
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
6
- use rustc_hir:: intravisit:: FnKind ;
5
+ use rustc_hir:: intravisit:: { walk_body , FnKind } ;
7
6
use rustc_hir:: { Body , Expr , ExprKind , FnDecl , Item , ItemKind , Node } ;
8
7
use rustc_lint:: { LateContext , LateLintPass } ;
9
8
use rustc_middle:: ty:: { self , Ty } ;
10
9
use rustc_session:: declare_lint_pass;
11
10
use rustc_span:: symbol:: Ident ;
12
11
use rustc_span:: { sym, Span } ;
12
+ use rustc_trait_selection:: traits:: error_reporting:: suggestions:: ReturnsVisitor ;
13
13
14
14
declare_clippy_lint ! {
15
15
/// ### What it does
@@ -52,12 +52,19 @@ fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
52
52
}
53
53
}
54
54
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
+ }
57
64
}
58
65
59
66
#[ 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 < ' _ > ) {
61
68
let args = cx
62
69
. tcx
63
70
. 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
90
97
} else {
91
98
BinOpKind :: Ne
92
99
} ;
93
- let expr = body. value . peel_blocks ( ) ;
94
100
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 )
100
117
&& let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
101
118
&& trait_id == trait_def_id
102
119
{
@@ -122,7 +139,7 @@ fn check_partial_eq(cx: &LateContext<'_>, body: &Body<'_>, method_span: Span, me
122
139
}
123
140
124
141
#[ 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 < ' _ > ) {
126
143
let args = cx
127
144
. tcx
128
145
. 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
146
163
// The trait is `ToString`.
147
164
&& Some ( trait_def_id) == get_trait_def_id ( cx, & [ "alloc" , "string" , "ToString" ] )
148
165
{
149
- let expr = expr_or_init ( cx, body. value . peel_blocks ( ) ) ;
150
166
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 )
155
169
&& let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
156
170
&& trait_id == trait_def_id
157
171
{
@@ -187,11 +201,15 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
187
201
method_def_id : LocalDefId ,
188
202
) {
189
203
// 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
+ {
191
209
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 ) ;
193
211
} 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 ) ;
195
213
}
196
214
}
197
215
}
0 commit comments