11use clippy_utils:: diagnostics:: span_lint_and_help;
22use 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} ;
44use rustc_ast:: Mutability ;
55use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
66use rustc_hir:: lang_items:: LangItem ;
77use rustc_hir:: { Block , Expr , ExprKind , HirId , Local , Node , PatKind , PathSegment , StmtKind } ;
88use 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 ;
1111
1212declare_clippy_lint ! {
1313 /// ### What it does
@@ -42,12 +42,7 @@ declare_clippy_lint! {
4242 "creating a peekable iterator without using any of its methods"
4343}
4444
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 ] ) ;
5146
5247impl < ' tcx > LateLintPass < ' tcx > for UnusedPeekable {
5348 fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
@@ -62,15 +57,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
6257 for ( idx, stmt) in block. stmts . iter ( ) . enumerate ( ) {
6358 if !stmt. span . from_expansion ( )
6459 && 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
6761 && let Some ( init) = local. init
6862 && !init. span . from_expansion ( )
6963 && let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( init)
7064 && let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
7165 && match_type ( cx, ty, & paths:: PEEKABLE )
7266 {
73- let mut vis = PeekableVisitor :: new ( cx, local . pat . hir_id ) ;
67+ let mut vis = PeekableVisitor :: new ( cx, binding ) ;
7468
7569 if idx + 1 == block. stmts . len ( ) && block. expr . is_none ( ) {
7670 return ;
@@ -117,6 +111,10 @@ impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
117111
118112impl < ' tcx > Visitor < ' _ > for PeekableVisitor < ' _ , ' tcx > {
119113 fn visit_expr ( & mut self , ex : & ' _ Expr < ' _ > ) {
114+ if self . found_peek_call {
115+ return ;
116+ }
117+
120118 if path_to_local_id ( ex, self . expected_hir_id ) {
121119 for ( _, node) in self . cx . tcx . hir ( ) . parent_iter ( ex. hir_id ) {
122120 match node {
@@ -138,50 +136,58 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
138136 return ;
139137 }
140138
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 ;
153144 }
154145 } ,
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 )
167168 {
168169 self . found_peek_call = true ;
169170 return ;
170171 }
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 ( _) => {
171181 } ,
172- // Don't bother if moved into a struct
173- ExprKind :: Struct ( .. ) => {
182+ ExprKind :: AddrOf ( _ , Mutability :: Not , _ ) => return ,
183+ _ => {
174184 self . found_peek_call = true ;
175185 return ;
176186 } ,
177- _ => { } ,
178187 }
179188 } ,
180189 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) {
185191 self . found_peek_call = true ;
186192 return ;
187193 }
@@ -206,3 +212,14 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
206212 walk_expr ( self , ex) ;
207213 }
208214}
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