1
1
mod empty_loop;
2
2
mod explicit_counter_loop;
3
- mod for_loop_arg;
3
+ mod explicit_into_iter_loop;
4
+ mod explicit_iter_loop;
4
5
mod for_loop_over_map_kv;
5
6
mod for_loop_range;
7
+ mod for_loops_over_fallibles;
6
8
mod for_mut_range_bound;
7
9
mod for_single_element_loop;
8
10
mod infinite_loop;
11
+ mod iter_next_loop;
9
12
mod manual_flatten;
10
13
mod manual_memcpy;
11
14
mod needless_collect;
@@ -15,11 +18,13 @@ mod utils;
15
18
mod while_let_loop;
16
19
mod while_let_on_iterator;
17
20
18
- use crate :: utils:: higher;
19
- use rustc_hir:: { Expr , ExprKind , LoopSource , Pat } ;
21
+ use crate :: utils:: { higher, is_type_diagnostic_item , match_trait_method , match_type , paths } ;
22
+ use rustc_hir:: { Expr , ExprKind , LoopSource , Mutability , Pat } ;
20
23
use rustc_lint:: { LateContext , LateLintPass } ;
24
+ use rustc_middle:: ty:: { self , Ty , TyS } ;
21
25
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
22
26
use rustc_span:: source_map:: Span ;
27
+ use rustc_span:: symbol:: sym;
23
28
use utils:: { get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor , InitializeVisitor } ;
24
29
25
30
declare_clippy_lint ! {
@@ -588,10 +593,77 @@ fn check_for_loop<'tcx>(
588
593
for_loop_range:: check_for_loop_range ( cx, pat, arg, body, expr) ;
589
594
explicit_counter_loop:: check_for_loop_explicit_counter ( cx, pat, arg, body, expr) ;
590
595
}
591
- for_loop_arg :: check_for_loop_arg ( cx, pat, arg, expr) ;
596
+ check_for_loop_arg ( cx, pat, arg, expr) ;
592
597
for_loop_over_map_kv:: check_for_loop_over_map_kv ( cx, pat, arg, body, expr) ;
593
598
for_mut_range_bound:: check_for_mut_range_bound ( cx, arg, body) ;
594
599
for_single_element_loop:: check_for_single_element_loop ( cx, pat, arg, body, expr) ;
595
600
same_item_push:: detect_same_item_push ( cx, pat, arg, body, expr) ;
596
601
manual_flatten:: check_manual_flatten ( cx, pat, arg, body, span) ;
597
602
}
603
+
604
+ fn check_for_loop_arg ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > , arg : & Expr < ' _ > , expr : & Expr < ' _ > ) {
605
+ let mut next_loop_linted = false ; // whether or not ITER_NEXT_LOOP lint was used
606
+ if let ExprKind :: MethodCall ( ref method, _, ref args, _) = arg. kind {
607
+ // just the receiver, no arguments
608
+ if args. len ( ) == 1 {
609
+ let method_name = & * method. ident . as_str ( ) ;
610
+ // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
611
+ if method_name == "iter" || method_name == "iter_mut" {
612
+ if is_ref_iterable_type ( cx, & args[ 0 ] ) {
613
+ explicit_iter_loop:: lint_iter_method ( cx, args, arg, method_name) ;
614
+ }
615
+ } else if method_name == "into_iter" && match_trait_method ( cx, arg, & paths:: INTO_ITERATOR ) {
616
+ let receiver_ty = cx. typeck_results ( ) . expr_ty ( & args[ 0 ] ) ;
617
+ let receiver_ty_adjusted = cx. typeck_results ( ) . expr_ty_adjusted ( & args[ 0 ] ) ;
618
+ if TyS :: same_type ( receiver_ty, receiver_ty_adjusted) {
619
+ explicit_into_iter_loop:: check_explicit_into_iter_loop ( cx, args, arg) ;
620
+ } else {
621
+ let ref_receiver_ty = cx. tcx . mk_ref (
622
+ cx. tcx . lifetimes . re_erased ,
623
+ ty:: TypeAndMut {
624
+ ty : receiver_ty,
625
+ mutbl : Mutability :: Not ,
626
+ } ,
627
+ ) ;
628
+ if TyS :: same_type ( receiver_ty_adjusted, ref_receiver_ty) {
629
+ explicit_iter_loop:: lint_iter_method ( cx, args, arg, method_name)
630
+ }
631
+ }
632
+ } else if method_name == "next" && match_trait_method ( cx, arg, & paths:: ITERATOR ) {
633
+ iter_next_loop:: lint ( cx, expr) ;
634
+ next_loop_linted = true ;
635
+ }
636
+ }
637
+ }
638
+ if !next_loop_linted {
639
+ for_loops_over_fallibles:: check_arg_type ( cx, pat, arg) ;
640
+ }
641
+ }
642
+
643
+ /// Returns `true` if the type of expr is one that provides `IntoIterator` impls
644
+ /// for `&T` and `&mut T`, such as `Vec`.
645
+ #[ rustfmt:: skip]
646
+ fn is_ref_iterable_type ( cx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> bool {
647
+ // no walk_ptrs_ty: calling iter() on a reference can make sense because it
648
+ // will allow further borrows afterwards
649
+ let ty = cx. typeck_results ( ) . expr_ty ( e) ;
650
+ is_iterable_array ( ty, cx) ||
651
+ is_type_diagnostic_item ( cx, ty, sym:: vec_type) ||
652
+ match_type ( cx, ty, & paths:: LINKED_LIST ) ||
653
+ is_type_diagnostic_item ( cx, ty, sym ! ( hashmap_type) ) ||
654
+ is_type_diagnostic_item ( cx, ty, sym ! ( hashset_type) ) ||
655
+ is_type_diagnostic_item ( cx, ty, sym ! ( vecdeque_type) ) ||
656
+ match_type ( cx, ty, & paths:: BINARY_HEAP ) ||
657
+ match_type ( cx, ty, & paths:: BTREEMAP ) ||
658
+ match_type ( cx, ty, & paths:: BTREESET )
659
+ }
660
+
661
+ fn is_iterable_array < ' tcx > ( ty : Ty < ' tcx > , cx : & LateContext < ' tcx > ) -> bool {
662
+ // IntoIterator is currently only implemented for array sizes <= 32 in rustc
663
+ match ty. kind ( ) {
664
+ ty:: Array ( _, n) => n
665
+ . try_eval_usize ( cx. tcx , cx. param_env )
666
+ . map_or ( false , |val| ( 0 ..=32 ) . contains ( & val) ) ,
667
+ _ => false ,
668
+ }
669
+ }
0 commit comments