1
1
use super :: NEEDLESS_COLLECT ;
2
2
use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
3
- use clippy_utils:: higher;
4
3
use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
5
4
use clippy_utils:: sugg:: Sugg ;
6
5
use clippy_utils:: ty:: { is_type_diagnostic_item, make_normalized_projection, make_projection} ;
7
6
use clippy_utils:: {
8
7
can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id,
9
8
CaptureKind ,
10
9
} ;
10
+ use clippy_utils:: { fn_def_id, higher} ;
11
11
use rustc_data_structures:: fx:: FxHashMap ;
12
12
use rustc_errors:: { Applicability , MultiSpan } ;
13
13
use rustc_hir:: intravisit:: { walk_block, walk_expr, Visitor } ;
@@ -16,7 +16,7 @@ use rustc_hir::{
16
16
} ;
17
17
use rustc_lint:: LateContext ;
18
18
use rustc_middle:: hir:: nested_filter;
19
- use rustc_middle:: ty:: { self , AssocKind , EarlyBinder , GenericArg , GenericArgKind , Ty } ;
19
+ use rustc_middle:: ty:: { self , AssocKind , Clause , EarlyBinder , GenericArg , GenericArgKind , PredicateKind , Ty } ;
20
20
use rustc_span:: symbol:: Ident ;
21
21
use rustc_span:: { sym, Span , Symbol } ;
22
22
@@ -32,6 +32,8 @@ pub(super) fn check<'tcx>(
32
32
if let Some ( parent) = get_parent_node ( cx. tcx , collect_expr. hir_id ) {
33
33
match parent {
34
34
Node :: Expr ( parent) => {
35
+ check_collect_into_intoiterator ( cx, parent, collect_expr, call_span, iter_expr) ;
36
+
35
37
if let ExprKind :: MethodCall ( name, _, args @ ( [ ] | [ _] ) , _) = parent. kind {
36
38
let mut app = Applicability :: MachineApplicable ;
37
39
let name = name. ident . as_str ( ) ;
@@ -134,6 +136,68 @@ pub(super) fn check<'tcx>(
134
136
}
135
137
}
136
138
139
+ /// checks for for collecting into a (generic) method or function argument
140
+ /// taking an `IntoIterator`
141
+ fn check_collect_into_intoiterator < ' tcx > (
142
+ cx : & LateContext < ' tcx > ,
143
+ parent : & ' tcx Expr < ' tcx > ,
144
+ collect_expr : & ' tcx Expr < ' tcx > ,
145
+ call_span : Span ,
146
+ iter_expr : & ' tcx Expr < ' tcx > ,
147
+ ) {
148
+ if let Some ( id) = fn_def_id ( cx, parent) {
149
+ let args = match parent. kind {
150
+ ExprKind :: Call ( _, args) | ExprKind :: MethodCall ( _, _, args, _) => args,
151
+ _ => & [ ] ,
152
+ } ;
153
+ // find the argument index of the `collect_expr` in the
154
+ // function / method call
155
+ if let Some ( arg_idx) = args. iter ( ) . position ( |e| e. hir_id == collect_expr. hir_id ) . map ( |i| {
156
+ if matches ! ( parent. kind, ExprKind :: MethodCall ( _, _, _, _) ) {
157
+ i + 1
158
+ } else {
159
+ i
160
+ }
161
+ } ) {
162
+ // extract the input types of the function/method call
163
+ // that contains `collect_expr`
164
+ let inputs = cx
165
+ . tcx
166
+ . liberate_late_bound_regions ( id, cx. tcx . fn_sig ( id) . subst_identity ( ) )
167
+ . inputs ( ) ;
168
+
169
+ // map IntoIterator generic bounds to their signature
170
+ // types and check whether the argument type is an
171
+ // `IntoIterator`
172
+ if cx
173
+ . tcx
174
+ . param_env ( id)
175
+ . caller_bounds ( )
176
+ . into_iter ( )
177
+ . filter_map ( |p| {
178
+ if let PredicateKind :: Clause ( Clause :: Trait ( t) ) = p. kind ( ) . skip_binder ( )
179
+ && cx. tcx . is_diagnostic_item ( sym:: IntoIterator , t. trait_ref . def_id ) {
180
+ Some ( t. self_ty ( ) )
181
+ } else {
182
+ None
183
+ }
184
+ } )
185
+ . any ( |ty| ty == inputs[ arg_idx] )
186
+ {
187
+ span_lint_and_sugg (
188
+ cx,
189
+ NEEDLESS_COLLECT ,
190
+ call_span. with_lo ( iter_expr. span . hi ( ) ) ,
191
+ NEEDLESS_COLLECT_MSG ,
192
+ "remove this call" ,
193
+ String :: new ( ) ,
194
+ Applicability :: MachineApplicable ,
195
+ ) ;
196
+ }
197
+ }
198
+ }
199
+ }
200
+
137
201
/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
138
202
fn is_is_empty_sig ( cx : & LateContext < ' _ > , call_id : HirId ) -> bool {
139
203
cx. typeck_results ( ) . type_dependent_def_id ( call_id) . map_or ( false , |id| {
0 commit comments