1+ use std:: borrow:: Cow ;
2+
13use super :: { EXPLICIT_COUNTER_LOOP , IncrementVisitor , InitializeVisitor , make_iterator_snippet} ;
2- use clippy_utils:: diagnostics:: { span_lint_and_sugg , span_lint_and_then} ;
4+ use clippy_utils:: diagnostics:: span_lint_and_then;
35use clippy_utils:: source:: snippet_with_applicability;
4- use clippy_utils:: { get_enclosing_block, is_integer_const} ;
5- use rustc_ast:: Label ;
6+ use clippy_utils:: sugg:: { EMPTY , Sugg } ;
7+ use clippy_utils:: { get_enclosing_block, is_integer_const, is_integer_literal_untyped} ;
8+ use rustc_ast:: { Label , RangeLimits } ;
69use rustc_errors:: Applicability ;
710use rustc_hir:: intravisit:: { walk_block, walk_expr} ;
811use rustc_hir:: { Expr , Pat } ;
912use rustc_lint:: LateContext ;
1013use rustc_middle:: ty:: { self , Ty , UintTy } ;
1114
12- // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
13- // incremented exactly once in the loop body, and initialized to zero
14- // at the start of the loop.
15+ // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be incremented exactly once in the
16+ // loop body.
1517pub ( super ) fn check < ' tcx > (
1618 cx : & LateContext < ' tcx > ,
1719 pat : & ' tcx Pat < ' _ > ,
@@ -31,55 +33,64 @@ pub(super) fn check<'tcx>(
3133 let mut initialize_visitor = InitializeVisitor :: new ( cx, expr, id) ;
3234 walk_block ( & mut initialize_visitor, block) ;
3335
34- if let Some ( ( name, ty, initializer) ) = initialize_visitor. get_result ( )
35- && is_integer_const ( cx, initializer, 0 )
36- {
36+ if let Some ( ( name, ty, initializer) ) = initialize_visitor. get_result ( ) {
37+ let is_zero = is_integer_const ( cx, initializer, 0 ) ;
3738 let mut applicability = Applicability :: MaybeIncorrect ;
3839 let span = expr. span . with_hi ( arg. span . hi ( ) ) ;
3940 let loop_label = label. map_or ( String :: new ( ) , |l| format ! ( "{}: " , l. ident. name) ) ;
40- let int_name = match ty. map ( Ty :: kind) {
41- // usize or inferred
42- Some ( ty:: Uint ( UintTy :: Usize ) ) | None => {
43- span_lint_and_sugg (
44- cx,
45- EXPLICIT_COUNTER_LOOP ,
46- span,
47- format ! ( "the variable `{name}` is used as a loop counter" ) ,
48- "consider using" ,
49- format ! (
50- "{loop_label}for ({name}, {}) in {}.enumerate()" ,
51- snippet_with_applicability( cx, pat. span, "item" , & mut applicability) ,
52- make_iterator_snippet( cx, arg, & mut applicability) ,
53- ) ,
54- applicability,
55- ) ;
56- return ;
57- } ,
58- Some ( ty:: Int ( int_ty) ) => int_ty. name_str ( ) ,
59- Some ( ty:: Uint ( uint_ty) ) => uint_ty. name_str ( ) ,
60- _ => return ,
61- } ;
6241
6342 span_lint_and_then (
6443 cx,
6544 EXPLICIT_COUNTER_LOOP ,
6645 span,
6746 format ! ( "the variable `{name}` is used as a loop counter" ) ,
6847 |diag| {
48+ let pat_snippet = snippet_with_applicability ( cx, pat. span , "item" , & mut applicability) ;
49+ let iter_snippet = make_iterator_snippet ( cx, arg, & mut applicability) ;
50+ let int_name = match ty. map ( Ty :: kind) {
51+ Some ( ty:: Uint ( UintTy :: Usize ) ) | None => {
52+ if is_zero {
53+ diag. span_suggestion (
54+ span,
55+ "consider using" ,
56+ format ! (
57+ "{loop_label}for ({name}, {pat_snippet}) in {iter_snippet}.enumerate()" ,
58+ ) ,
59+ applicability,
60+ ) ;
61+ return ;
62+ }
63+ None
64+ } ,
65+ Some ( ty:: Int ( int_ty) ) => Some ( int_ty. name_str ( ) ) ,
66+ Some ( ty:: Uint ( uint_ty) ) => Some ( uint_ty. name_str ( ) ) ,
67+ _ => None ,
68+ }
69+ . filter ( |_| is_integer_literal_untyped ( initializer) ) ;
70+
71+ let initializer = Sugg :: hir_from_snippet ( cx, initializer, |span| {
72+ let snippet = snippet_with_applicability ( cx, span, ".." , & mut applicability) ;
73+ if let Some ( int_name) = int_name {
74+ return Cow :: Owned ( format ! ( "{snippet}_{int_name}" ) ) ;
75+ }
76+ snippet
77+ } ) ;
78+
6979 diag. span_suggestion (
7080 span,
7181 "consider using" ,
7282 format ! (
73- "{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})" ,
74- snippet_with_applicability( cx, pat. span, "item" , & mut applicability) ,
75- make_iterator_snippet( cx, arg, & mut applicability) ,
83+ "{loop_label}for ({name}, {pat_snippet}) in ({}).zip({iter_snippet})" ,
84+ initializer. range( & EMPTY , RangeLimits :: HalfOpen )
7685 ) ,
7786 applicability,
7887 ) ;
7988
80- diag. note ( format ! (
81- "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
82- ) ) ;
89+ if is_zero && let Some ( int_name) = int_name {
90+ diag. note ( format ! (
91+ "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
92+ ) ) ;
93+ }
8394 } ,
8495 ) ;
8596 }
0 commit comments