11use crate :: consts:: constant;
22use crate :: reexport:: Name ;
33use crate :: utils:: paths;
4+ use crate :: utils:: sugg:: Sugg ;
45use crate :: utils:: usage:: { is_unused, mutated_variables} ;
56use crate :: utils:: {
67 get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
7- is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
8- multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
9- span_lint_and_sugg, span_lint_and_then, SpanlessEq ,
8+ is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_path,
9+ match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt,
10+ snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
11+ SpanlessEq ,
1012} ;
11- use crate :: utils:: { is_type_diagnostic_item, qpath_res, sugg} ;
1213use if_chain:: if_chain;
1314use rustc_ast:: ast;
1415use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -17,7 +18,7 @@ use rustc_hir::def::{DefKind, Res};
1718use rustc_hir:: intravisit:: { walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap , Visitor } ;
1819use rustc_hir:: {
1920 def_id, BinOpKind , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , GenericArg , HirId , InlineAsmOperand ,
20- LoopSource , MatchSource , Mutability , Node , Pat , PatKind , QPath , Stmt , StmtKind ,
21+ Local , LoopSource , MatchSource , Mutability , Node , Pat , PatKind , QPath , Stmt , StmtKind ,
2122} ;
2223use rustc_infer:: infer:: TyCtxtInferExt ;
2324use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -27,7 +28,7 @@ use rustc_middle::middle::region;
2728use rustc_middle:: ty:: { self , Ty , TyS } ;
2829use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
2930use rustc_span:: source_map:: Span ;
30- use rustc_span:: symbol:: Symbol ;
31+ use rustc_span:: symbol:: { Ident , Symbol } ;
3132use rustc_typeck:: expr_use_visitor:: { ConsumeMode , Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
3233use std:: iter:: { once, Iterator } ;
3334use std:: mem;
@@ -2358,6 +2359,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
23582359const NEEDLESS_COLLECT_MSG : & str = "avoid using `collect()` when not needed" ;
23592360
23602361fn check_needless_collect < ' tcx > ( expr : & ' tcx Expr < ' _ > , cx : & LateContext < ' tcx > ) {
2362+ // Check for direct, immediate usage
23612363 if_chain ! {
23622364 if let ExprKind :: MethodCall ( ref method, _, ref args, _) = expr. kind;
23632365 if let ExprKind :: MethodCall ( ref chain_method, _, _, _) = args[ 0 ] . kind;
@@ -2423,6 +2425,99 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24232425 }
24242426 }
24252427 }
2428+ // Check for collecting it and then turning it back into an iterator later
2429+ if let ExprKind :: Block ( ref block, _) = expr. kind {
2430+ for ref stmt in block. stmts {
2431+ if_chain ! {
2432+ // TODO also work for assignments to an existing variable
2433+ if let StmtKind :: Local (
2434+ Local { pat: Pat { kind: PatKind :: Binding ( _, _, ident, .. ) , .. } ,
2435+ init: Some ( ref init_expr) , .. }
2436+ ) = stmt. kind;
2437+ if let ExprKind :: MethodCall ( ref method_name, _, & [ ref iter_source] , ..) = init_expr. kind;
2438+ if method_name. ident. name == sym!( collect) && match_trait_method( cx, & init_expr, & paths:: ITERATOR ) ;
2439+ if let Some ( ref generic_args) = method_name. args;
2440+ if let Some ( GenericArg :: Type ( ref ty) ) = generic_args. args. get( 0 ) ;
2441+ if let ty = cx. typeck_results( ) . node_type( ty. hir_id) ;
2442+ if is_type_diagnostic_item( cx, ty, sym!( vec_type) ) ||
2443+ is_type_diagnostic_item( cx, ty, sym!( vecdeque_type) ) ||
2444+ match_type( cx, ty, & paths:: LINKED_LIST ) ;
2445+ if let Some ( iter_calls) = detect_iter_and_into_iters( block, * ident) ;
2446+ if iter_calls. len( ) == 1 ;
2447+ then {
2448+ // Suggest replacing iter_call with iter_replacement, and removing stmt
2449+ span_lint_and_then(
2450+ cx,
2451+ NEEDLESS_COLLECT ,
2452+ stmt. span,
2453+ NEEDLESS_COLLECT_MSG ,
2454+ |diag| {
2455+ let iter_replacement = Sugg :: hir( cx, iter_source, ".." ) . to_string( ) ;
2456+ diag. multipart_suggestion(
2457+ "Use the original Iterator instead of collecting it and then producing a new one" ,
2458+ vec![
2459+ ( stmt. span, String :: new( ) ) ,
2460+ ( iter_calls[ 0 ] . span, iter_replacement)
2461+ ] ,
2462+ Applicability :: MaybeIncorrect ,
2463+ ) ;
2464+ } ,
2465+ ) ;
2466+ }
2467+ }
2468+ }
2469+ }
2470+ }
2471+
2472+ struct IntoIterVisitor < ' tcx > {
2473+ iters : Vec < & ' tcx Expr < ' tcx > > ,
2474+ seen_other : bool ,
2475+ target : String ,
2476+ }
2477+ impl < ' tcx > Visitor < ' tcx > for IntoIterVisitor < ' tcx > {
2478+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
2479+ match & expr. kind {
2480+ ExprKind :: MethodCall (
2481+ method_name,
2482+ _,
2483+ & [ Expr {
2484+ kind : ExprKind :: Path ( QPath :: Resolved ( _, ref path) ) ,
2485+ ..
2486+ } ] ,
2487+ _,
2488+ ) if match_path ( path, & [ & self . target ] ) => {
2489+ // TODO Check what method is being called, if it's called on target, and act
2490+ // accordingly
2491+ if method_name. ident . name == sym ! ( into_iter) {
2492+ self . iters . push ( expr) ;
2493+ } else {
2494+ self . seen_other = true ;
2495+ }
2496+ } ,
2497+ _ => walk_expr ( self , expr) ,
2498+ }
2499+ }
2500+
2501+ type Map = Map < ' tcx > ;
2502+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
2503+ NestedVisitorMap :: None
2504+ }
2505+ }
2506+
2507+ /// Detect the occurences of calls to `iter` or `into_iter` for the
2508+ /// given identifier
2509+ fn detect_iter_and_into_iters < ' tcx > ( block : & ' tcx Block < ' tcx > , identifier : Ident ) -> Option < Vec < & ' tcx Expr < ' tcx > > > {
2510+ let mut visitor = IntoIterVisitor {
2511+ iters : Vec :: new ( ) ,
2512+ target : identifier. name . to_ident_string ( ) ,
2513+ seen_other : false ,
2514+ } ;
2515+ visitor. visit_block ( block) ;
2516+ if visitor. seen_other {
2517+ None
2518+ } else {
2519+ Some ( visitor. iters )
2520+ }
24262521}
24272522
24282523fn shorten_span ( expr : & Expr < ' _ > , target_fn_name : Symbol ) -> Span {
0 commit comments