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 ;
109use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
10+ use rustc_span:: sym;
1111
1212declare_clippy_lint ! {
1313 /// ### What it does
@@ -70,7 +70,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
7070 && let ( ty, _, Mutability :: Mut ) = peel_mid_ty_refs_is_mutable ( ty)
7171 && match_type ( cx, ty, & paths:: PEEKABLE )
7272 {
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 ) ;
7476
7577 if idx + 1 == block. stmts . len ( ) && block. expr . is_none ( ) {
7678 return ;
@@ -103,20 +105,26 @@ struct PeekableVisitor<'a, 'tcx> {
103105 cx : & ' a LateContext < ' tcx > ,
104106 expected_hir_id : HirId ,
105107 found_peek_call : bool ,
108+ visited : & ' a mut Vec < HirId > ,
106109}
107110
108111impl < ' 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 {
110113 Self {
111114 cx,
112115 expected_hir_id,
113116 found_peek_call : false ,
117+ visited,
114118 }
115119 }
116120}
117121
118122impl < ' tcx > Visitor < ' _ > for PeekableVisitor < ' _ , ' tcx > {
119123 fn visit_expr ( & mut self , ex : & ' _ Expr < ' _ > ) {
124+ if self . found_peek_call {
125+ return ;
126+ }
127+
120128 if path_to_local_id ( ex, self . expected_hir_id ) {
121129 for ( _, node) in self . cx . tcx . hir ( ) . parent_iter ( ex. hir_id ) {
122130 match node {
@@ -138,54 +146,57 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
138146 return ;
139147 }
140148
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 ;
153154 }
154155 } ,
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)
167167 {
168168 self . found_peek_call = true ;
169169 return ;
170170 }
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 ;
171181 } ,
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+ _ => {
174186 self . found_peek_call = true ;
175187 return ;
176188 } ,
177- _ => { } ,
178189 }
179190 } ,
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) {
185195 self . found_peek_call = true ;
186196 return ;
187197 }
188198
199+ self . visited . push ( pat. hir_id ) ;
189200 break ;
190201 } ,
191202 Node :: Stmt ( stmt) => match stmt. kind {
@@ -206,3 +217,14 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
206217 walk_expr ( self , ex) ;
207218 }
208219}
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