11use super :: NEEDLESS_COLLECT ;
22use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
3- use clippy_utils:: higher;
43use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
54use clippy_utils:: sugg:: Sugg ;
65use clippy_utils:: ty:: { is_type_diagnostic_item, make_normalized_projection, make_projection} ;
76use clippy_utils:: {
87 can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id,
98 CaptureKind ,
109} ;
10+ use clippy_utils:: { fn_def_id, higher} ;
1111use rustc_data_structures:: fx:: FxHashMap ;
1212use rustc_errors:: { Applicability , MultiSpan } ;
1313use rustc_hir:: intravisit:: { walk_block, walk_expr, Visitor } ;
@@ -16,7 +16,7 @@ use rustc_hir::{
1616} ;
1717use rustc_lint:: LateContext ;
1818use 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 } ;
2020use rustc_span:: symbol:: Ident ;
2121use rustc_span:: { sym, Span , Symbol } ;
2222
@@ -32,6 +32,8 @@ pub(super) fn check<'tcx>(
3232 if let Some ( parent) = get_parent_node ( cx. tcx , collect_expr. hir_id ) {
3333 match parent {
3434 Node :: Expr ( parent) => {
35+ check_collect_into_intoiterator ( cx, parent, collect_expr, call_span, iter_expr) ;
36+
3537 if let ExprKind :: MethodCall ( name, _, args @ ( [ ] | [ _] ) , _) = parent. kind {
3638 let mut app = Applicability :: MachineApplicable ;
3739 let name = name. ident . as_str ( ) ;
@@ -134,6 +136,68 @@ pub(super) fn check<'tcx>(
134136 }
135137}
136138
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+
137201/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
138202fn is_is_empty_sig ( cx : & LateContext < ' _ > , call_id : HirId ) -> bool {
139203 cx. typeck_results ( ) . type_dependent_def_id ( call_id) . map_or ( false , |id| {
0 commit comments