@@ -56,8 +56,20 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
5656
5757impl < ' tcx > LateLintPass < ' tcx > for NeedlessForEach {
5858 fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' _ > ) {
59- if let StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) = stmt. kind
60- && let ExprKind :: MethodCall ( method_name, for_each_recv, [ for_each_arg] , _) = expr. kind
59+ if let StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) = stmt. kind {
60+ check_expr ( cx, expr, stmt. span ) ;
61+ }
62+ }
63+
64+ fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' _ > ) {
65+ if let Some ( expr) = block. expr {
66+ check_expr ( cx, expr, expr. span ) ;
67+ }
68+ }
69+ }
70+
71+ fn check_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , outer_span : Span ) {
72+ if let ExprKind :: MethodCall ( method_name, for_each_recv, [ for_each_arg] , _) = expr. kind
6173 && let ExprKind :: MethodCall ( _, iter_recv, [ ] , _) = for_each_recv. kind
6274 // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
6375 // `v.foo().iter().for_each()` must be skipped.
@@ -76,69 +88,74 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
7688 // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
7789 // and suggesting `for … in … { unsafe { } }` is a little ugly.
7890 && !matches ! ( body. value. kind, ExprKind :: Block ( Block { rules: BlockCheckMode :: UnsafeBlock ( _) , .. } , ..) )
91+ {
92+ let mut applicability = Applicability :: MachineApplicable ;
93+
94+ // If any closure parameter has an explicit type specified, applying the lint would necessarily
95+ // remove that specification, possibly breaking type inference
96+ if fn_decl
97+ . inputs
98+ . iter ( )
99+ . any ( |input| matches ! ( input. kind, TyKind :: Infer ( ..) ) )
79100 {
80- let mut applicability = Applicability :: MachineApplicable ;
81-
82- // If any closure parameter has an explicit type specified, applying the lint would necessarily
83- // remove that specification, possibly breaking type inference
84- if fn_decl
85- . inputs
86- . iter ( )
87- . any ( |input| matches ! ( input. kind, TyKind :: Infer ( ..) ) )
88- {
89- applicability = Applicability :: MaybeIncorrect ;
90- }
101+ applicability = Applicability :: MaybeIncorrect ;
102+ }
91103
92- let mut ret_collector = RetCollector :: default ( ) ;
93- ret_collector. visit_expr ( body. value ) ;
104+ let mut ret_collector = RetCollector :: default ( ) ;
105+ ret_collector. visit_expr ( body. value ) ;
94106
95- // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
96- if ret_collector. ret_in_loop {
97- return ;
98- }
107+ // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
108+ if ret_collector. ret_in_loop {
109+ return ;
110+ }
99111
100- let ret_suggs = if ret_collector. spans . is_empty ( ) {
101- None
112+ let ret_suggs = if ret_collector. spans . is_empty ( ) {
113+ None
114+ } else {
115+ applicability = Applicability :: MaybeIncorrect ;
116+ Some (
117+ ret_collector
118+ . spans
119+ . into_iter ( )
120+ . map ( |span| ( span, "continue" . to_string ( ) ) )
121+ . collect ( ) ,
122+ )
123+ } ;
124+
125+ let body_param_sugg = snippet_with_applicability ( cx, body. params [ 0 ] . pat . span , ".." , & mut applicability) ;
126+ let for_each_rev_sugg = snippet_with_applicability ( cx, for_each_recv. span , ".." , & mut applicability) ;
127+ let ( body_value_sugg, is_macro_call) =
128+ snippet_with_context ( cx, body. value . span , for_each_recv. span . ctxt ( ) , ".." , & mut applicability) ;
129+
130+ let sugg = format ! (
131+ "for {} in {} {}" ,
132+ body_param_sugg,
133+ for_each_rev_sugg,
134+ if is_macro_call {
135+ format!( "{{ {body_value_sugg}; }}" )
102136 } else {
103- applicability = Applicability :: MaybeIncorrect ;
104- Some (
105- ret_collector
106- . spans
107- . into_iter ( )
108- . map ( |span| ( span, "continue" . to_string ( ) ) )
109- . collect ( ) ,
110- )
111- } ;
112-
113- let body_param_sugg = snippet_with_applicability ( cx, body. params [ 0 ] . pat . span , ".." , & mut applicability) ;
114- let for_each_rev_sugg = snippet_with_applicability ( cx, for_each_recv. span , ".." , & mut applicability) ;
115- let ( body_value_sugg, is_macro_call) =
116- snippet_with_context ( cx, body. value . span , for_each_recv. span . ctxt ( ) , ".." , & mut applicability) ;
117-
118- let sugg = format ! (
119- "for {} in {} {}" ,
120- body_param_sugg,
121- for_each_rev_sugg,
122- if is_macro_call {
123- format!( "{{ {body_value_sugg}; }}" )
124- } else {
125- match body. value. kind {
126- ExprKind :: Block ( block, _) if is_let_desugar( block) => {
127- format!( "{{ {body_value_sugg} }}" )
128- } ,
129- ExprKind :: Block ( _, _) => body_value_sugg. to_string( ) ,
130- _ => format!( "{{ {body_value_sugg}; }}" ) ,
131- }
137+ match body. value. kind {
138+ ExprKind :: Block ( block, _) if is_let_desugar( block) => {
139+ format!( "{{ {body_value_sugg} }}" )
140+ } ,
141+ ExprKind :: Block ( _, _) => body_value_sugg. to_string( ) ,
142+ _ => format!( "{{ {body_value_sugg}; }}" ) ,
132143 }
133- ) ;
134-
135- span_lint_and_then ( cx, NEEDLESS_FOR_EACH , stmt. span , "needless use of `for_each`" , |diag| {
136- diag. span_suggestion ( stmt. span , "try" , sugg, applicability) ;
144+ }
145+ ) ;
146+
147+ span_lint_and_then (
148+ cx,
149+ NEEDLESS_FOR_EACH ,
150+ outer_span,
151+ "needless use of `for_each`" ,
152+ |diag| {
153+ diag. span_suggestion ( outer_span, "try" , sugg, applicability) ;
137154 if let Some ( ret_suggs) = ret_suggs {
138155 diag. multipart_suggestion ( "...and replace `return` with `continue`" , ret_suggs, applicability) ;
139156 }
140- } ) ;
141- }
157+ } ,
158+ ) ;
142159 }
143160}
144161
0 commit comments