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
9
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
10
+ use rustc_span:: sym;
11
11
12
12
declare_clippy_lint ! {
13
13
/// ### What it does
@@ -70,7 +70,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
70
70
&& let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
71
71
&& match_type ( cx, ty, & paths:: PEEKABLE )
72
72
{
73
- let mut vis = PeekableVisitor :: new ( cx, local. pat . hir_id ) ;
73
+ self . visited . push ( local. pat . hir_id ) ;
74
+
75
+ let mut vis = PeekableVisitor :: new ( cx, local. pat . hir_id , & mut self . visited ) ;
74
76
75
77
if idx + 1 == block. stmts . len ( ) && block. expr . is_none ( ) {
76
78
return ;
@@ -103,20 +105,26 @@ struct PeekableVisitor<'a, 'tcx> {
103
105
cx : & ' a LateContext < ' tcx > ,
104
106
expected_hir_id : HirId ,
105
107
found_peek_call : bool ,
108
+ visited : & ' a mut Vec < HirId > ,
106
109
}
107
110
108
111
impl < ' a , ' tcx > PeekableVisitor < ' a , ' tcx > {
109
- fn new ( cx : & ' a LateContext < ' tcx > , expected_hir_id : HirId ) -> Self {
112
+ fn new ( cx : & ' a LateContext < ' tcx > , expected_hir_id : HirId , visited : & ' a mut Vec < HirId > ) -> Self {
110
113
Self {
111
114
cx,
112
115
expected_hir_id,
113
116
found_peek_call : false ,
117
+ visited,
114
118
}
115
119
}
116
120
}
117
121
118
122
impl < ' tcx > Visitor < ' _ > for PeekableVisitor < ' _ , ' tcx > {
119
123
fn visit_expr ( & mut self , ex : & ' _ Expr < ' _ > ) {
124
+ if self . found_peek_call {
125
+ return ;
126
+ }
127
+
120
128
if path_to_local_id ( ex, self . expected_hir_id ) {
121
129
for ( _, node) in self . cx . tcx . hir ( ) . parent_iter ( ex. hir_id ) {
122
130
match node {
@@ -138,54 +146,57 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
138
146
return ;
139
147
}
140
148
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
- }
149
+ if args. iter ( ) . any ( |arg| {
150
+ matches ! ( arg. kind, ExprKind :: Path ( _) ) && arg_is_mut_peekable ( self . cx , arg)
151
+ } ) {
152
+ self . found_peek_call = true ;
153
+ return ;
153
154
}
154
155
} ,
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 )
156
+ // Catch anything taking a Peekable mutably
157
+ ExprKind :: MethodCall (
158
+ PathSegment { ident : method_name, .. } ,
159
+ [ self_arg, remaining_args @ ..] ,
160
+ _,
161
+ ) => {
162
+ // `Peekable` methods
163
+ if matches ! (
164
+ method_name. name. as_str( ) ,
165
+ "peek" | "peek_mut" | "next_if" | "next_if_eq"
166
+ ) && arg_is_mut_peekable ( self . cx , self_arg)
167
167
{
168
168
self . found_peek_call = true ;
169
169
return ;
170
170
}
171
+
172
+ // foo.some_method() excluding Iterator methods
173
+ if remaining_args. iter ( ) . any ( |arg| arg_is_mut_peekable ( self . cx , arg) )
174
+ && !is_trait_method ( self . cx , expr, sym:: Iterator )
175
+ {
176
+ self . found_peek_call = true ;
177
+ return ;
178
+ }
179
+
180
+ return ;
171
181
} ,
172
- // Don't bother if moved into a struct
173
- ExprKind :: Struct ( ..) => {
182
+ ExprKind :: AddrOf ( _, Mutability :: Mut , _) | ExprKind :: Unary ( ..) | ExprKind :: DropTemps ( _) => {
183
+ } ,
184
+ ExprKind :: AddrOf ( _, Mutability :: Not , _) => return ,
185
+ _ => {
174
186
self . found_peek_call = true ;
175
187
return ;
176
188
} ,
177
- _ => { } ,
178
189
}
179
190
} ,
180
- 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
- {
191
+ Node :: Local ( Local {
192
+ init : Some ( init) , pat, ..
193
+ } ) => {
194
+ if arg_is_mut_peekable ( self . cx , init) {
185
195
self . found_peek_call = true ;
186
196
return ;
187
197
}
188
198
199
+ self . visited . push ( pat. hir_id ) ;
189
200
break ;
190
201
} ,
191
202
Node :: Stmt ( stmt) => match stmt. kind {
@@ -206,3 +217,14 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
206
217
walk_expr ( self , ex) ;
207
218
}
208
219
}
220
+
221
+ fn arg_is_mut_peekable ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > ) -> bool {
222
+ if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( arg)
223
+ && let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
224
+ && match_type ( cx, ty, & paths:: PEEKABLE )
225
+ {
226
+ true
227
+ } else {
228
+ false
229
+ }
230
+ }
0 commit comments