1
1
use crate :: consts:: constant;
2
2
use crate :: reexport:: Name ;
3
3
use crate :: utils:: paths;
4
+ use crate :: utils:: sugg:: Sugg ;
4
5
use crate :: utils:: usage:: { is_unused, mutated_variables} ;
5
6
use crate :: utils:: {
6
7
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 ,
10
12
} ;
11
- use crate :: utils:: { is_type_diagnostic_item, qpath_res, sugg} ;
12
13
use if_chain:: if_chain;
13
14
use rustc_ast:: ast;
14
15
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -17,7 +18,7 @@ use rustc_hir::def::{DefKind, Res};
17
18
use rustc_hir:: intravisit:: { walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap , Visitor } ;
18
19
use rustc_hir:: {
19
20
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 ,
21
22
} ;
22
23
use rustc_infer:: infer:: TyCtxtInferExt ;
23
24
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -27,7 +28,7 @@ use rustc_middle::middle::region;
27
28
use rustc_middle:: ty:: { self , Ty , TyS } ;
28
29
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
29
30
use rustc_span:: source_map:: Span ;
30
- use rustc_span:: symbol:: Symbol ;
31
+ use rustc_span:: symbol:: { Ident , Symbol } ;
31
32
use rustc_typeck:: expr_use_visitor:: { ConsumeMode , Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
32
33
use std:: iter:: { once, Iterator } ;
33
34
use std:: mem;
@@ -2358,6 +2359,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
2358
2359
const NEEDLESS_COLLECT_MSG : & str = "avoid using `collect()` when not needed" ;
2359
2360
2360
2361
fn check_needless_collect < ' tcx > ( expr : & ' tcx Expr < ' _ > , cx : & LateContext < ' tcx > ) {
2362
+ // Check for direct, immediate usage
2361
2363
if_chain ! {
2362
2364
if let ExprKind :: MethodCall ( ref method, _, ref args, _) = expr. kind;
2363
2365
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>) {
2423
2425
}
2424
2426
}
2425
2427
}
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
+ }
2426
2521
}
2427
2522
2428
2523
fn shorten_span ( expr : & Expr < ' _ > , target_fn_name : Symbol ) -> Span {
0 commit comments