@@ -27,9 +27,10 @@ use syntax_pos::{BytePos, Symbol};
2727
2828use crate :: utils:: paths;
2929use 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 ,
3334} ;
3435
3536declare_clippy_lint ! {
@@ -1460,27 +1461,26 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
14601461 if visitor2. state == VarState :: Warn {
14611462 if let Some ( name) = visitor2. name {
14621463 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+
14631473 span_lint_and_sugg (
14641474 cx,
14651475 EXPLICIT_COUNTER_LOOP ,
1466- expr . span ,
1476+ for_span . with_hi ( arg . span . hi ( ) ) ,
14671477 & format ! ( "the variable `{}` is used as a loop counter." , name) ,
14681478 "consider using" ,
14691479 format ! (
14701480 "for ({}, {}) in {}.enumerate()" ,
14711481 name,
14721482 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) ,
14841484 ) ,
14851485 applicability,
14861486 ) ;
@@ -1490,6 +1490,39 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
14901490 }
14911491}
14921492
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+
14931526/// Checks for the `FOR_KV_MAP` lint.
14941527fn check_for_loop_over_map_kv < ' a , ' tcx > (
14951528 cx : & LateContext < ' a , ' tcx > ,
0 commit comments