1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
- use clippy_utils:: { get_trait_def_id, path_res} ;
2
+ use clippy_utils:: { expr_or_init , get_trait_def_id, path_res} ;
3
3
use rustc_ast:: BinOpKind ;
4
4
use rustc_hir:: def:: Res ;
5
5
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
@@ -8,6 +8,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl, Item, ItemKind, Node};
8
8
use rustc_lint:: { LateContext , LateLintPass } ;
9
9
use rustc_middle:: ty:: { self , Ty } ;
10
10
use rustc_session:: declare_lint_pass;
11
+ use rustc_span:: symbol:: Ident ;
11
12
use rustc_span:: { sym, Span } ;
12
13
13
14
declare_clippy_lint ! {
@@ -55,79 +56,142 @@ fn is_local(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
55
56
matches ! ( path_res( cx, expr) , Res :: Local ( _) )
56
57
}
57
58
59
+ #[ allow( clippy:: unnecessary_def_path) ]
60
+ fn check_partial_eq ( cx : & LateContext < ' _ > , body : & Body < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident ) {
61
+ let args = cx
62
+ . tcx
63
+ . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( method_def_id) . skip_binder ( ) )
64
+ . inputs ( ) ;
65
+ // That has two arguments.
66
+ if let [ self_arg, other_arg] = args
67
+ && let Some ( self_arg) = get_ty_def_id ( * self_arg)
68
+ && let Some ( other_arg) = get_ty_def_id ( * other_arg)
69
+ // The two arguments are of the same type.
70
+ && self_arg == other_arg
71
+ && let hir_id = cx. tcx . local_def_id_to_hir_id ( method_def_id)
72
+ && let Some ( (
73
+ _,
74
+ Node :: Item ( Item {
75
+ kind : ItemKind :: Impl ( impl_) ,
76
+ owner_id,
77
+ ..
78
+ } ) ,
79
+ ) ) = cx. tcx . hir ( ) . parent_iter ( hir_id) . next ( )
80
+ // We exclude `impl` blocks generated from rustc's proc macros.
81
+ && !cx. tcx . has_attr ( * owner_id, sym:: automatically_derived)
82
+ // It is a implementation of a trait.
83
+ && let Some ( trait_) = impl_. of_trait
84
+ && let Some ( trait_def_id) = trait_. trait_def_id ( )
85
+ // The trait is `PartialEq`.
86
+ && Some ( trait_def_id) == get_trait_def_id ( cx, & [ "core" , "cmp" , "PartialEq" ] )
87
+ {
88
+ let to_check_op = if name. name == sym:: eq {
89
+ BinOpKind :: Eq
90
+ } else {
91
+ BinOpKind :: Ne
92
+ } ;
93
+ let expr = body. value . peel_blocks ( ) ;
94
+ 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 )
100
+ && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
101
+ && trait_id == trait_def_id
102
+ {
103
+ true
104
+ } else {
105
+ false
106
+ }
107
+ } ,
108
+ _ => false ,
109
+ } ;
110
+ if is_bad {
111
+ span_lint_and_then (
112
+ cx,
113
+ UNCONDITIONAL_RECURSION ,
114
+ method_span,
115
+ "function cannot return without recursing" ,
116
+ |diag| {
117
+ diag. span_note ( expr. span , "recursive call site" ) ;
118
+ } ,
119
+ ) ;
120
+ }
121
+ }
122
+ }
123
+
124
+ #[ allow( clippy:: unnecessary_def_path) ]
125
+ fn check_to_string ( cx : & LateContext < ' _ > , body : & Body < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident ) {
126
+ let args = cx
127
+ . tcx
128
+ . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( method_def_id) . skip_binder ( ) )
129
+ . inputs ( ) ;
130
+ // That has one argument.
131
+ if let [ _self_arg] = args
132
+ && let hir_id = cx. tcx . local_def_id_to_hir_id ( method_def_id)
133
+ && let Some ( (
134
+ _,
135
+ Node :: Item ( Item {
136
+ kind : ItemKind :: Impl ( impl_) ,
137
+ owner_id,
138
+ ..
139
+ } ) ,
140
+ ) ) = cx. tcx . hir ( ) . parent_iter ( hir_id) . next ( )
141
+ // We exclude `impl` blocks generated from rustc's proc macros.
142
+ && !cx. tcx . has_attr ( * owner_id, sym:: automatically_derived)
143
+ // It is a implementation of a trait.
144
+ && let Some ( trait_) = impl_. of_trait
145
+ && let Some ( trait_def_id) = trait_. trait_def_id ( )
146
+ // The trait is `ToString`.
147
+ && Some ( trait_def_id) == get_trait_def_id ( cx, & [ "alloc" , "string" , "ToString" ] )
148
+ {
149
+ let expr = expr_or_init ( cx, body. value . peel_blocks ( ) ) ;
150
+ 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 )
155
+ && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
156
+ && trait_id == trait_def_id
157
+ {
158
+ true
159
+ } else {
160
+ false
161
+ }
162
+ } ,
163
+ _ => false ,
164
+ } ;
165
+ if is_bad {
166
+ span_lint_and_then (
167
+ cx,
168
+ UNCONDITIONAL_RECURSION ,
169
+ method_span,
170
+ "function cannot return without recursing" ,
171
+ |diag| {
172
+ diag. span_note ( expr. span , "recursive call site" ) ;
173
+ } ,
174
+ ) ;
175
+ }
176
+ }
177
+ }
178
+
58
179
impl < ' tcx > LateLintPass < ' tcx > for UnconditionalRecursion {
59
- #[ allow( clippy:: unnecessary_def_path) ]
60
180
fn check_fn (
61
181
& mut self ,
62
182
cx : & LateContext < ' tcx > ,
63
183
kind : FnKind < ' tcx > ,
64
184
_decl : & ' tcx FnDecl < ' tcx > ,
65
185
body : & ' tcx Body < ' tcx > ,
66
186
method_span : Span ,
67
- def_id : LocalDefId ,
187
+ method_def_id : LocalDefId ,
68
188
) {
69
189
// 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
- ) ;
190
+ if let FnKind :: Method ( name, _) = kind {
191
+ if name. name == sym:: eq || name. name == sym:: ne {
192
+ check_partial_eq ( cx, body, method_span, method_def_id, name) ;
193
+ } else if name. name == sym:: to_string {
194
+ check_to_string ( cx, body, method_span, method_def_id, name) ;
131
195
}
132
196
}
133
197
}
0 commit comments