1
1
use clippy_utils:: diagnostics:: span_lint_and_help;
2
2
use clippy_utils:: ty:: { match_type, peel_mid_ty_refs_is_mutable} ;
3
- use clippy_utils:: { fn_def_id, path_to_local_id, paths, peel_ref_operators} ;
3
+ use clippy_utils:: { fn_def_id, is_trait_method , path_to_local_id, paths, peel_ref_operators} ;
4
4
use rustc_ast:: Mutability ;
5
5
use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
6
6
use rustc_hir:: lang_items:: LangItem ;
7
7
use rustc_hir:: { Block , Expr , ExprKind , HirId , Local , Node , PatKind , PathSegment , StmtKind } ;
8
8
use rustc_lint:: { LateContext , LateLintPass } ;
9
- use rustc_middle :: ty :: Ty ;
10
- use rustc_session :: { declare_tool_lint , impl_lint_pass } ;
9
+ use rustc_session :: { declare_lint_pass , declare_tool_lint } ;
10
+ use rustc_span :: sym ;
11
11
12
12
declare_clippy_lint ! {
13
13
/// ### What it does
@@ -42,12 +42,7 @@ declare_clippy_lint! {
42
42
"creating a peekable iterator without using any of its methods"
43
43
}
44
44
45
- #[ derive( Default ) ]
46
- pub struct UnusedPeekable {
47
- visited : Vec < HirId > ,
48
- }
49
-
50
- impl_lint_pass ! ( UnusedPeekable => [ UNUSED_PEEKABLE ] ) ;
45
+ declare_lint_pass ! ( UnusedPeekable => [ UNUSED_PEEKABLE ] ) ;
51
46
52
47
impl < ' tcx > LateLintPass < ' tcx > for UnusedPeekable {
53
48
fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
@@ -62,15 +57,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
62
57
for ( idx, stmt) in block. stmts . iter ( ) . enumerate ( ) {
63
58
if !stmt. span . from_expansion ( )
64
59
&& let StmtKind :: Local ( local) = stmt. kind
65
- && !self . visited . contains ( & local. pat . hir_id )
66
- && let PatKind :: Binding ( _, _, ident, _) = local. pat . kind
60
+ && let PatKind :: Binding ( _, binding, ident, _) = local. pat . kind
67
61
&& let Some ( init) = local. init
68
62
&& !init. span . from_expansion ( )
69
63
&& let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( init)
70
64
&& let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
71
65
&& match_type ( cx, ty, & paths:: PEEKABLE )
72
66
{
73
- let mut vis = PeekableVisitor :: new ( cx, local . pat . hir_id ) ;
67
+ let mut vis = PeekableVisitor :: new ( cx, binding ) ;
74
68
75
69
if idx + 1 == block. stmts . len ( ) && block. expr . is_none ( ) {
76
70
return ;
@@ -117,6 +111,10 @@ impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
117
111
118
112
impl < ' tcx > Visitor < ' _ > for PeekableVisitor < ' _ , ' tcx > {
119
113
fn visit_expr ( & mut self , ex : & ' _ Expr < ' _ > ) {
114
+ if self . found_peek_call {
115
+ return ;
116
+ }
117
+
120
118
if path_to_local_id ( ex, self . expected_hir_id ) {
121
119
for ( _, node) in self . cx . tcx . hir ( ) . parent_iter ( ex. hir_id ) {
122
120
match node {
@@ -138,50 +136,58 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
138
136
return ;
139
137
}
140
138
141
- for arg in args. iter ( ) . map ( |arg| peel_ref_operators ( self . cx , arg) ) {
142
- if let ExprKind :: Path ( _) = arg. kind
143
- && let Some ( ty) = self
144
- . cx
145
- . typeck_results ( )
146
- . expr_ty_opt ( arg)
147
- . map ( Ty :: peel_refs)
148
- && match_type ( self . cx , ty, & paths:: PEEKABLE )
149
- {
150
- self . found_peek_call = true ;
151
- return ;
152
- }
139
+ if args. iter ( ) . any ( |arg| {
140
+ matches ! ( arg. kind, ExprKind :: Path ( _) ) && arg_is_mut_peekable ( self . cx , arg)
141
+ } ) {
142
+ self . found_peek_call = true ;
143
+ return ;
153
144
}
154
145
} ,
155
- // Peekable::peek()
156
- ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , [ arg, ..] , _) => {
157
- let arg = peel_ref_operators ( self . cx , arg) ;
158
- let method_name = method_name. name . as_str ( ) ;
159
-
160
- if ( method_name == "peek"
161
- || method_name == "peek_mut"
162
- || method_name == "next_if"
163
- || method_name == "next_if_eq" )
164
- && let ExprKind :: Path ( _) = arg. kind
165
- && let Some ( ty) = self . cx . typeck_results ( ) . expr_ty_opt ( arg) . map ( Ty :: peel_refs)
166
- && match_type ( self . cx , ty, & paths:: PEEKABLE )
146
+ // Catch anything taking a Peekable mutably
147
+ ExprKind :: MethodCall (
148
+ PathSegment {
149
+ ident : method_name_ident,
150
+ ..
151
+ } ,
152
+ [ self_arg, remaining_args @ ..] ,
153
+ _,
154
+ ) => {
155
+ let method_name = method_name_ident. name . as_str ( ) ;
156
+
157
+ // `Peekable` methods
158
+ if matches ! ( method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq" )
159
+ && arg_is_mut_peekable ( self . cx , self_arg)
160
+ {
161
+ self . found_peek_call = true ;
162
+ return ;
163
+ }
164
+
165
+ // foo.some_method() excluding Iterator methods
166
+ if remaining_args. iter ( ) . any ( |arg| arg_is_mut_peekable ( self . cx , arg) )
167
+ && !is_trait_method ( self . cx , expr, sym:: Iterator )
167
168
{
168
169
self . found_peek_call = true ;
169
170
return ;
170
171
}
172
+
173
+ // foo.by_ref(), keep checking for `peek`
174
+ if method_name == "by_ref" {
175
+ continue ;
176
+ }
177
+
178
+ return ;
179
+ } ,
180
+ ExprKind :: AddrOf ( _, Mutability :: Mut , _) | ExprKind :: Unary ( ..) | ExprKind :: DropTemps ( _) => {
171
181
} ,
172
- // Don't bother if moved into a struct
173
- ExprKind :: Struct ( .. ) => {
182
+ ExprKind :: AddrOf ( _ , Mutability :: Not , _ ) => return ,
183
+ _ => {
174
184
self . found_peek_call = true ;
175
185
return ;
176
186
} ,
177
- _ => { } ,
178
187
}
179
188
} ,
180
189
Node :: Local ( Local { init : Some ( init) , .. } ) => {
181
- if let Some ( ty) = self . cx . typeck_results ( ) . expr_ty_opt ( init)
182
- && let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
183
- && match_type ( self . cx , ty, & paths:: PEEKABLE )
184
- {
190
+ if arg_is_mut_peekable ( self . cx , init) {
185
191
self . found_peek_call = true ;
186
192
return ;
187
193
}
@@ -206,3 +212,14 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
206
212
walk_expr ( self , ex) ;
207
213
}
208
214
}
215
+
216
+ fn arg_is_mut_peekable ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > ) -> bool {
217
+ if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( arg)
218
+ && let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
219
+ && match_type ( cx, ty, & paths:: PEEKABLE )
220
+ {
221
+ true
222
+ } else {
223
+ false
224
+ }
225
+ }
0 commit comments