@@ -27,9 +27,10 @@ use syntax_pos::{BytePos, Symbol};
27
27
28
28
use crate :: utils:: paths;
29
29
use crate :: utils:: {
30
- get_enclosing_block, get_parent_expr, has_iter_method, higher, is_integer_const, is_refutable, last_path_segment,
31
- match_trait_method, match_type, match_var, multispan_sugg, snippet, snippet_opt, snippet_with_applicability,
32
- span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then, SpanlessEq ,
30
+ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
31
+ is_integer_const, is_refutable, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg,
32
+ snippet, snippet_opt, snippet_with_applicability, span_help_and_lint, span_lint, span_lint_and_sugg,
33
+ span_lint_and_then, SpanlessEq ,
33
34
} ;
34
35
35
36
declare_clippy_lint ! {
@@ -1460,27 +1461,26 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
1460
1461
if visitor2. state == VarState :: Warn {
1461
1462
if let Some ( name) = visitor2. name {
1462
1463
let mut applicability = Applicability :: MachineApplicable ;
1464
+
1465
+ // for some reason this is the only way to get the `Span`
1466
+ // of the entire `for` loop
1467
+ let for_span = if let ExprKind :: Match ( _, arms, _) = & expr. kind {
1468
+ arms[ 0 ] . body . span
1469
+ } else {
1470
+ unreachable ! ( )
1471
+ } ;
1472
+
1463
1473
span_lint_and_sugg (
1464
1474
cx,
1465
1475
EXPLICIT_COUNTER_LOOP ,
1466
- expr . span ,
1476
+ for_span . with_hi ( arg . span . hi ( ) ) ,
1467
1477
& format ! ( "the variable `{}` is used as a loop counter." , name) ,
1468
1478
"consider using" ,
1469
1479
format ! (
1470
1480
"for ({}, {}) in {}.enumerate()" ,
1471
1481
name,
1472
1482
snippet_with_applicability( cx, pat. span, "item" , & mut applicability) ,
1473
- if higher:: range( cx, arg) . is_some( ) {
1474
- format!(
1475
- "({})" ,
1476
- snippet_with_applicability( cx, arg. span, "_" , & mut applicability)
1477
- )
1478
- } else {
1479
- format!(
1480
- "{}" ,
1481
- sugg:: Sugg :: hir_with_applicability( cx, arg, "_" , & mut applicability) . maybe_par( )
1482
- )
1483
- }
1483
+ make_iterator_snippet( cx, arg, & mut applicability) ,
1484
1484
) ,
1485
1485
applicability,
1486
1486
) ;
@@ -1490,6 +1490,39 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
1490
1490
}
1491
1491
}
1492
1492
1493
+ /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
1494
+ /// actual `Iterator` that the loop uses.
1495
+ fn make_iterator_snippet ( cx : & LateContext < ' _ , ' _ > , arg : & Expr , applic_ref : & mut Applicability ) -> String {
1496
+ let impls_iterator = get_trait_def_id ( cx, & paths:: ITERATOR )
1497
+ . map_or ( false , |id| implements_trait ( cx, cx. tables . expr_ty ( arg) , id, & [ ] ) ) ;
1498
+ if impls_iterator {
1499
+ format ! (
1500
+ "{}" ,
1501
+ sugg:: Sugg :: hir_with_applicability( cx, arg, "_" , applic_ref) . maybe_par( )
1502
+ )
1503
+ } else {
1504
+ // (&x).into_iter() ==> x.iter()
1505
+ // (&mut x).into_iter() ==> x.iter_mut()
1506
+ match & arg. kind {
1507
+ ExprKind :: AddrOf ( mutability, arg_inner) if has_iter_method ( cx, cx. tables . expr_ty ( & arg_inner) ) . is_some ( ) => {
1508
+ let meth_name = match mutability {
1509
+ MutMutable => "iter_mut" ,
1510
+ MutImmutable => "iter" ,
1511
+ } ;
1512
+ format ! (
1513
+ "{}.{}()" ,
1514
+ sugg:: Sugg :: hir_with_applicability( cx, & arg_inner, "_" , applic_ref) . maybe_par( ) ,
1515
+ meth_name,
1516
+ )
1517
+ } ,
1518
+ _ => format ! (
1519
+ "{}.into_iter()" ,
1520
+ sugg:: Sugg :: hir_with_applicability( cx, arg, "_" , applic_ref) . maybe_par( )
1521
+ ) ,
1522
+ }
1523
+ }
1524
+ }
1525
+
1493
1526
/// Checks for the `FOR_KV_MAP` lint.
1494
1527
fn check_for_loop_over_map_kv < ' a , ' tcx > (
1495
1528
cx : & LateContext < ' a , ' tcx > ,
0 commit comments