@@ -9,15 +9,14 @@ use rustc::middle::region::CodeExtent;
9
9
use rustc:: ty;
10
10
use rustc_const_eval:: EvalHint :: ExprTypeChecked ;
11
11
use rustc_const_eval:: eval_const_expr_partial;
12
- use std:: borrow:: Cow ;
13
12
use std:: collections:: HashMap ;
14
13
use syntax:: ast;
14
+ use utils:: sugg;
15
15
16
- use utils:: { snippet, span_lint, get_parent_expr, match_trait_method, match_type, in_external_macro,
17
- span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, unsugar_range ,
18
- walk_ptrs_ty, recover_for_loop } ;
16
+ use utils:: { snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg , in_external_macro,
17
+ span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher ,
18
+ walk_ptrs_ty} ;
19
19
use utils:: paths;
20
- use utils:: UnsugaredRange ;
21
20
22
21
/// **What it does:** This lint checks for looping over the range of `0..len` of some collection just to get the values by index.
23
22
///
@@ -224,7 +223,7 @@ impl LintPass for Pass {
224
223
225
224
impl LateLintPass for Pass {
226
225
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
227
- if let Some ( ( pat, arg, body) ) = recover_for_loop ( expr) {
226
+ if let Some ( ( pat, arg, body) ) = higher :: for_loop ( expr) {
228
227
check_for_loop ( cx, pat, arg, body, expr) ;
229
228
}
230
229
// check for `loop { if let {} else break }` that could be `while let`
@@ -333,7 +332,7 @@ fn check_for_loop(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &E
333
332
/// Check for looping over a range and then indexing a sequence with it.
334
333
/// The iteratee must be a range literal.
335
334
fn check_for_loop_range ( cx : & LateContext , pat : & Pat , arg : & Expr , body : & Expr , expr : & Expr ) {
336
- if let Some ( UnsugaredRange { start : Some ( ref start) , ref end, .. } ) = unsugar_range ( arg) {
335
+ if let Some ( higher :: Range { start : Some ( ref start) , ref end, limits } ) = higher :: range ( arg) {
337
336
// the var must be a single name
338
337
if let PatKind :: Binding ( _, ref ident, _) = pat. node {
339
338
let mut visitor = VarVisitor {
@@ -361,49 +360,58 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
361
360
362
361
let starts_at_zero = is_integer_literal ( start, 0 ) ;
363
362
364
- let skip: Cow < _ > = if starts_at_zero {
365
- "" . into ( )
363
+ let skip = if starts_at_zero {
364
+ "" . to_owned ( )
366
365
} else {
367
- format ! ( ".skip({})" , snippet( cx, start. span, ".." ) ) . into ( )
366
+ format ! ( ".skip({})" , snippet( cx, start. span, ".." ) )
368
367
} ;
369
368
370
- let take: Cow < _ > = if let Some ( ref end) = * end {
369
+ let take = if let Some ( ref end) = * end {
371
370
if is_len_call ( end, & indexed) {
372
- "" . into ( )
371
+ "" . to_owned ( )
373
372
} else {
374
- format ! ( ".take({})" , snippet( cx, end. span, ".." ) ) . into ( )
373
+ match limits {
374
+ ast:: RangeLimits :: Closed => {
375
+ let end = sugg:: Sugg :: hir ( cx, end, "<count>" ) ;
376
+ format ! ( ".take({})" , end + sugg:: ONE )
377
+ }
378
+ ast:: RangeLimits :: HalfOpen => {
379
+ format ! ( ".take({})" , snippet( cx, end. span, ".." ) )
380
+ }
381
+ }
375
382
}
376
383
} else {
377
- "" . into ( )
384
+ "" . to_owned ( )
378
385
} ;
379
386
380
387
if visitor. nonindex {
381
- span_lint ( cx,
382
- NEEDLESS_RANGE_LOOP ,
383
- expr. span ,
384
- & format ! ( "the loop variable `{}` is used to index `{}`. Consider using `for ({}, \
385
- item) in {}.iter().enumerate(){}{}` or similar iterators",
386
- ident. node,
387
- indexed,
388
- ident. node,
389
- indexed,
390
- take,
391
- skip) ) ;
388
+ span_lint_and_then ( cx,
389
+ NEEDLESS_RANGE_LOOP ,
390
+ expr. span ,
391
+ & format ! ( "the loop variable `{}` is used to index `{}`" , ident. node, indexed) ,
392
+ |db| {
393
+ multispan_sugg ( db, "consider using an iterator" . to_string ( ) , & [
394
+ ( pat. span , & format ! ( "({}, <item>)" , ident. node) ) ,
395
+ ( arg. span , & format ! ( "{}.iter().enumerate(){}{}" , indexed, take, skip) ) ,
396
+ ] ) ;
397
+ } ) ;
392
398
} else {
393
399
let repl = if starts_at_zero && take. is_empty ( ) {
394
400
format ! ( "&{}" , indexed)
395
401
} else {
396
402
format ! ( "{}.iter(){}{}" , indexed, take, skip)
397
403
} ;
398
404
399
- span_lint ( cx,
400
- NEEDLESS_RANGE_LOOP ,
401
- expr. span ,
402
- & format ! ( "the loop variable `{}` is only used to index `{}`. \
403
- Consider using `for item in {}` or similar iterators",
404
- ident. node,
405
- indexed,
406
- repl) ) ;
405
+ span_lint_and_then ( cx,
406
+ NEEDLESS_RANGE_LOOP ,
407
+ expr. span ,
408
+ & format ! ( "the loop variable `{}` is only used to index `{}`." , ident. node, indexed) ,
409
+ |db| {
410
+ multispan_sugg ( db, "consider using an iterator" . to_string ( ) , & [
411
+ ( pat. span , "<item>" ) ,
412
+ ( arg. span , & repl) ,
413
+ ] ) ;
414
+ } ) ;
407
415
}
408
416
}
409
417
}
@@ -427,7 +435,7 @@ fn is_len_call(expr: &Expr, var: &Name) -> bool {
427
435
428
436
fn check_for_loop_reverse_range ( cx : & LateContext , arg : & Expr , expr : & Expr ) {
429
437
// if this for loop is iterating over a two-sided range...
430
- if let Some ( UnsugaredRange { start : Some ( ref start) , end : Some ( ref end) , limits } ) = unsugar_range ( arg) {
438
+ if let Some ( higher :: Range { start : Some ( ref start) , end : Some ( ref end) , limits } ) = higher :: range ( arg) {
431
439
// ...and both sides are compile-time constant integers...
432
440
if let Ok ( start_idx) = eval_const_expr_partial ( cx. tcx , start, ExprTypeChecked , None ) {
433
441
if let Ok ( end_idx) = eval_const_expr_partial ( cx. tcx , end, ExprTypeChecked , None ) {
@@ -588,33 +596,34 @@ fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, ex
588
596
589
597
/// Check for the `FOR_KV_MAP` lint.
590
598
fn check_for_loop_over_map_kv ( cx : & LateContext , pat : & Pat , arg : & Expr , body : & Expr , expr : & Expr ) {
599
+ let pat_span = pat. span ;
600
+
591
601
if let PatKind :: Tuple ( ref pat, _) = pat. node {
592
602
if pat. len ( ) == 2 {
593
- let ( pat_span , kind) = match ( & pat[ 0 ] . node , & pat[ 1 ] . node ) {
594
- ( key, _) if pat_is_wild ( key, body) => ( & pat[ 1 ] . span , "values " ) ,
595
- ( _, value) if pat_is_wild ( value, body) => ( & pat[ 0 ] . span , "keys " ) ,
603
+ let ( new_pat_span , kind) = match ( & pat[ 0 ] . node , & pat[ 1 ] . node ) {
604
+ ( key, _) if pat_is_wild ( key, body) => ( pat[ 1 ] . span , "value " ) ,
605
+ ( _, value) if pat_is_wild ( value, body) => ( pat[ 0 ] . span , "key " ) ,
596
606
_ => return ,
597
607
} ;
598
608
599
- let arg_span = match arg. node {
600
- ExprAddrOf ( MutImmutable , ref expr) => expr . span ,
609
+ let ( arg_span, arg ) = match arg. node {
610
+ ExprAddrOf ( MutImmutable , ref expr) => ( arg . span , & * * expr ) ,
601
611
ExprAddrOf ( MutMutable , _) => return , // for _ in &mut _, there is no {values,keys}_mut method
602
- _ => arg. span ,
612
+ _ => ( arg. span , arg ) ,
603
613
} ;
604
614
605
615
let ty = walk_ptrs_ty ( cx. tcx . expr_ty ( arg) ) ;
606
616
if match_type ( cx, ty, & paths:: HASHMAP ) || match_type ( cx, ty, & paths:: BTREEMAP ) {
607
617
span_lint_and_then ( cx,
608
618
FOR_KV_MAP ,
609
619
expr. span ,
610
- & format ! ( "you seem to want to iterate on a map's {}" , kind) ,
620
+ & format ! ( "you seem to want to iterate on a map's {}s " , kind) ,
611
621
|db| {
612
- db. span_suggestion ( expr. span ,
613
- "use the corresponding method" ,
614
- format ! ( "for {} in {}.{}() {{ .. }}" ,
615
- snippet( cx, * pat_span, ".." ) ,
616
- snippet( cx, arg_span, ".." ) ,
617
- kind) ) ;
622
+ let map = sugg:: Sugg :: hir ( cx, arg, "map" ) ;
623
+ multispan_sugg ( db, "use the corresponding method" . into ( ) , & [
624
+ ( pat_span, & snippet ( cx, new_pat_span, kind) ) ,
625
+ ( arg_span, & format ! ( "{}.{}s()" , map. maybe_par( ) , kind) ) ,
626
+ ] ) ;
618
627
} ) ;
619
628
}
620
629
}
0 commit comments