@@ -2429,7 +2429,6 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24292429 if let ExprKind :: Block ( ref block, _) = expr. kind {
24302430 for ref stmt in block. stmts {
24312431 if_chain ! {
2432- // TODO also work for assignments to an existing variable
24332432 if let StmtKind :: Local (
24342433 Local { pat: Pat { kind: PatKind :: Binding ( _, _, ident, .. ) , .. } ,
24352434 init: Some ( ref init_expr) , .. }
@@ -2446,21 +2445,22 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24462445 if iter_calls. len( ) == 1 ;
24472446 then {
24482447 // Suggest replacing iter_call with iter_replacement, and removing stmt
2448+ let iter_call = & iter_calls[ 0 ] ;
24492449 span_lint_and_then(
24502450 cx,
24512451 NEEDLESS_COLLECT ,
2452- stmt. span,
2452+ stmt. span. until ( iter_call . span ) ,
24532453 NEEDLESS_COLLECT_MSG ,
24542454 |diag| {
2455- let iter_replacement = Sugg :: hir( cx, iter_source, ".." ) . to_string ( ) ;
2455+ let iter_replacement = format! ( "{}{}" , Sugg :: hir( cx, iter_source, ".." ) , iter_call . get_iter_method ( cx ) ) ;
24562456 diag. multipart_suggestion(
2457- "Use the original Iterator instead of collecting it and then producing a new one" ,
2457+ iter_call . get_suggestion_text ( ) ,
24582458 vec![
24592459 ( stmt. span, String :: new( ) ) ,
2460- ( iter_calls [ 0 ] . span, iter_replacement)
2460+ ( iter_call . span, iter_replacement)
24612461 ] ,
2462- Applicability :: MaybeIncorrect ,
2463- ) ;
2462+ Applicability :: MachineApplicable , // MaybeIncorrect,
2463+ ) . emit ( ) ;
24642464 } ,
24652465 ) ;
24662466 }
@@ -2469,32 +2469,62 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24692469 }
24702470}
24712471
2472- struct IntoIterVisitor < ' tcx > {
2473- iters : Vec < & ' tcx Expr < ' tcx > > ,
2472+ struct IterFunction {
2473+ func : IterFunctionKind ,
2474+ span : Span ,
2475+ }
2476+ impl IterFunction {
2477+ fn get_iter_method ( & self , cx : & LateContext < ' _ > ) -> String {
2478+ match & self . func {
2479+ IterFunctionKind :: IntoIter => String :: new ( ) ,
2480+ IterFunctionKind :: Len => String :: from ( ".count()" ) ,
2481+ IterFunctionKind :: IsEmpty => String :: from ( ".next().is_none()" ) ,
2482+ IterFunctionKind :: Contains ( span) => format ! ( ".any(|x| x == {})" , snippet( cx, * span, ".." ) ) ,
2483+ }
2484+ }
2485+ fn get_suggestion_text ( & self ) -> & ' static str {
2486+ match & self . func {
2487+ IterFunctionKind :: IntoIter => "Use the original Iterator instead of collecting it and then producing a new one" ,
2488+ IterFunctionKind :: Len => "Take the original Iterator's count instead of collecting it and finding the length" ,
2489+ IterFunctionKind :: IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" ,
2490+ IterFunctionKind :: Contains ( _) => "Check if the original Iterator contains an element instead of collecting then checking" ,
2491+ }
2492+ }
2493+ }
2494+ enum IterFunctionKind {
2495+ IntoIter ,
2496+ Len ,
2497+ IsEmpty ,
2498+ Contains ( Span ) ,
2499+ }
2500+
2501+ struct IterFunctionVisitor {
2502+ uses : Vec < IterFunction > ,
24742503 seen_other : bool ,
24752504 target : String ,
24762505}
2477- impl < ' tcx > Visitor < ' tcx > for IntoIterVisitor < ' tcx > {
2506+ impl < ' tcx > Visitor < ' tcx > for IterFunctionVisitor {
24782507 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 ;
2508+ if_chain ! {
2509+ if let ExprKind :: MethodCall ( method_name, _, ref args, _) = & expr. kind;
2510+ if let Some ( Expr { kind: ExprKind :: Path ( QPath :: Resolved ( _, ref path) ) , .. } ) = args. get( 0 ) ;
2511+ if match_path( path, & [ & self . target] ) ;
2512+ then {
2513+ let into_iter = sym!( into_iter) ;
2514+ let len = sym!( len) ;
2515+ let is_empty = sym!( is_empty) ;
2516+ let contains = sym!( contains) ;
2517+ match method_name. ident. name {
2518+ name if name == into_iter => self . uses. push( IterFunction { func: IterFunctionKind :: IntoIter , span: expr. span } ) ,
2519+ name if name == len => self . uses. push( IterFunction { func: IterFunctionKind :: Len , span: expr. span } ) ,
2520+ name if name == is_empty => self . uses. push( IterFunction { func: IterFunctionKind :: IsEmpty , span: expr. span } ) ,
2521+ name if name == contains => self . uses. push( IterFunction { func: IterFunctionKind :: Contains ( args[ 1 ] . span) , span: expr. span } ) ,
2522+ _ => self . seen_other = true ,
24952523 }
2496- } ,
2497- _ => walk_expr ( self , expr) ,
2524+ }
2525+ else {
2526+ walk_expr( self , expr) ;
2527+ }
24982528 }
24992529 }
25002530
@@ -2506,17 +2536,17 @@ impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> {
25062536
25072537/// Detect the occurences of calls to `iter` or `into_iter` for the
25082538/// 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 ( ) ,
2539+ fn detect_iter_and_into_iters < ' tcx > ( block : & ' tcx Block < ' tcx > , identifier : Ident ) -> Option < Vec < IterFunction > > {
2540+ let mut visitor = IterFunctionVisitor {
2541+ uses : Vec :: new ( ) ,
25122542 target : identifier. name . to_ident_string ( ) ,
25132543 seen_other : false ,
25142544 } ;
25152545 visitor. visit_block ( block) ;
25162546 if visitor. seen_other {
25172547 None
25182548 } else {
2519- Some ( visitor. iters )
2549+ Some ( visitor. uses )
25202550 }
25212551}
25222552
0 commit comments