@@ -952,16 +952,17 @@ fn check_for_loop_range<'a, 'tcx>(
952
952
let mut visitor = VarVisitor {
953
953
cx : cx,
954
954
var : canonical_id,
955
- indexed : HashMap :: new ( ) ,
955
+ indexed_mut : HashSet :: new ( ) ,
956
+ indexed_indirectly : HashMap :: new ( ) ,
956
957
indexed_directly : HashMap :: new ( ) ,
957
958
referenced : HashSet :: new ( ) ,
958
959
nonindex : false ,
960
+ prefer_mutable : false ,
959
961
} ;
960
962
walk_expr ( & mut visitor, body) ;
961
963
962
964
// linting condition: we only indexed one variable, and indexed it directly
963
- // (`indexed_directly` is subset of `indexed`)
964
- if visitor. indexed . len ( ) == 1 && visitor. indexed_directly . len ( ) == 1 {
965
+ if visitor. indexed_indirectly . is_empty ( ) && visitor. indexed_directly . len ( ) == 1 {
965
966
let ( indexed, indexed_extent) = visitor
966
967
. indexed_directly
967
968
. into_iter ( )
@@ -1009,6 +1010,12 @@ fn check_for_loop_range<'a, 'tcx>(
1009
1010
"" . to_owned ( )
1010
1011
} ;
1011
1012
1013
+ let ( ref_mut, method) = if visitor. indexed_mut . contains ( & indexed) {
1014
+ ( "mut " , "iter_mut" )
1015
+ } else {
1016
+ ( "" , "iter" )
1017
+ } ;
1018
+
1012
1019
if visitor. nonindex {
1013
1020
span_lint_and_then (
1014
1021
cx,
@@ -1021,16 +1028,16 @@ fn check_for_loop_range<'a, 'tcx>(
1021
1028
"consider using an iterator" . to_string ( ) ,
1022
1029
vec ! [
1023
1030
( pat. span, format!( "({}, <item>)" , ident. node) ) ,
1024
- ( arg. span, format!( "{}.iter ().enumerate(){}{}" , indexed, take, skip) ) ,
1031
+ ( arg. span, format!( "{}.{} ().enumerate(){}{}" , indexed, method , take, skip) ) ,
1025
1032
] ,
1026
1033
) ;
1027
1034
} ,
1028
1035
) ;
1029
1036
} else {
1030
1037
let repl = if starts_at_zero && take. is_empty ( ) {
1031
- format ! ( "&{}" , indexed)
1038
+ format ! ( "&{}{}" , ref_mut , indexed)
1032
1039
} else {
1033
- format ! ( "{}.iter (){}{}" , indexed, take, skip)
1040
+ format ! ( "{}.{} (){}{}" , indexed, method , take, skip)
1034
1041
} ;
1035
1042
1036
1043
span_lint_and_then (
@@ -1537,8 +1544,10 @@ struct VarVisitor<'a, 'tcx: 'a> {
1537
1544
cx : & ' a LateContext < ' a , ' tcx > ,
1538
1545
/// var name to look for as index
1539
1546
var : ast:: NodeId ,
1540
- /// indexed variables, the extend is `None` for global
1541
- indexed : HashMap < Name , Option < region:: Scope > > ,
1547
+ /// indexed variables that are used mutably
1548
+ indexed_mut : HashSet < Name > ,
1549
+ /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
1550
+ indexed_indirectly : HashMap < Name , Option < region:: Scope > > ,
1542
1551
/// subset of `indexed` of vars that are indexed directly: `v[i]`
1543
1552
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
1544
1553
indexed_directly : HashMap < Name , Option < region:: Scope > > ,
@@ -1548,20 +1557,21 @@ struct VarVisitor<'a, 'tcx: 'a> {
1548
1557
/// has the loop variable been used in expressions other than the index of
1549
1558
/// an index op?
1550
1559
nonindex : bool ,
1560
+ /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
1561
+ /// takes `&mut self`
1562
+ prefer_mutable : bool ,
1551
1563
}
1552
1564
1553
- impl < ' a , ' tcx > Visitor < ' tcx > for VarVisitor < ' a , ' tcx > {
1554
- fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
1565
+ impl < ' a , ' tcx > VarVisitor < ' a , ' tcx > {
1566
+ fn check ( & mut self , idx : & ' tcx Expr , seqexpr : & ' tcx Expr , expr : & ' tcx Expr ) -> bool {
1555
1567
if_chain ! {
1556
- // an index op
1557
- if let ExprIndex ( ref seqexpr, ref idx) = expr. node;
1558
1568
// the indexed container is referenced by a name
1559
1569
if let ExprPath ( ref seqpath) = seqexpr. node;
1560
1570
if let QPath :: Resolved ( None , ref seqvar) = * seqpath;
1561
1571
if seqvar. segments. len( ) == 1 ;
1562
1572
then {
1563
1573
let index_used_directly = same_var( self . cx, idx, self . var) ;
1564
- let index_used = index_used_directly || {
1574
+ let indexed_indirectly = {
1565
1575
let mut used_visitor = LocalUsedVisitor {
1566
1576
cx: self . cx,
1567
1577
local: self . var,
@@ -1571,7 +1581,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
1571
1581
used_visitor. used
1572
1582
} ;
1573
1583
1574
- if index_used {
1584
+ if indexed_indirectly || index_used_directly {
1585
+ if self . prefer_mutable {
1586
+ self . indexed_mut. insert( seqvar. segments[ 0 ] . name) ;
1587
+ }
1575
1588
let def = self . cx. tables. qpath_def( seqpath, seqexpr. hir_id) ;
1576
1589
match def {
1577
1590
Def :: Local ( node_id) | Def :: Upvar ( node_id, ..) => {
@@ -1580,24 +1593,48 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
1580
1593
let parent_id = self . cx. tcx. hir. get_parent( expr. id) ;
1581
1594
let parent_def_id = self . cx. tcx. hir. local_def_id( parent_id) ;
1582
1595
let extent = self . cx. tcx. region_scope_tree( parent_def_id) . var_scope( hir_id. local_id) ;
1583
- self . indexed. insert( seqvar. segments[ 0 ] . name, Some ( extent) ) ;
1596
+ if indexed_indirectly {
1597
+ self . indexed_indirectly. insert( seqvar. segments[ 0 ] . name, Some ( extent) ) ;
1598
+ }
1584
1599
if index_used_directly {
1585
1600
self . indexed_directly. insert( seqvar. segments[ 0 ] . name, Some ( extent) ) ;
1586
1601
}
1587
- return ; // no need to walk further *on the variable*
1602
+ return false ; // no need to walk further *on the variable*
1588
1603
}
1589
1604
Def :: Static ( ..) | Def :: Const ( ..) => {
1590
- self . indexed. insert( seqvar. segments[ 0 ] . name, None ) ;
1605
+ if indexed_indirectly {
1606
+ self . indexed_indirectly. insert( seqvar. segments[ 0 ] . name, None ) ;
1607
+ }
1591
1608
if index_used_directly {
1592
1609
self . indexed_directly. insert( seqvar. segments[ 0 ] . name, None ) ;
1593
1610
}
1594
- return ; // no need to walk further *on the variable*
1611
+ return false ; // no need to walk further *on the variable*
1595
1612
}
1596
1613
_ => ( ) ,
1597
1614
}
1598
1615
}
1599
1616
}
1600
1617
}
1618
+ true
1619
+ }
1620
+ }
1621
+
1622
+ impl < ' a , ' tcx > Visitor < ' tcx > for VarVisitor < ' a , ' tcx > {
1623
+ fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
1624
+ if_chain ! {
1625
+ // a range index op
1626
+ if let ExprMethodCall ( ref meth, _, ref args) = expr. node;
1627
+ if meth. name == "index" || meth. name == "index_mut" ;
1628
+ if !self . check( & args[ 1 ] , & args[ 0 ] , expr) ;
1629
+ then { return }
1630
+ }
1631
+
1632
+ if_chain ! {
1633
+ // an index op
1634
+ if let ExprIndex ( ref seqexpr, ref idx) = expr. node;
1635
+ if !self . check( idx, seqexpr, expr) ;
1636
+ then { return }
1637
+ }
1601
1638
1602
1639
if_chain ! {
1603
1640
// directly using a variable
@@ -1615,8 +1652,47 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
1615
1652
}
1616
1653
}
1617
1654
}
1618
-
1619
- walk_expr ( self , expr) ;
1655
+ let old = self . prefer_mutable ;
1656
+ match expr. node {
1657
+ ExprAssignOp ( _, ref lhs, ref rhs) |
1658
+ ExprAssign ( ref lhs, ref rhs) => {
1659
+ self . prefer_mutable = true ;
1660
+ self . visit_expr ( lhs) ;
1661
+ self . prefer_mutable = false ;
1662
+ self . visit_expr ( rhs) ;
1663
+ } ,
1664
+ ExprAddrOf ( mutbl, ref expr) => {
1665
+ if mutbl == MutMutable {
1666
+ self . prefer_mutable = true ;
1667
+ }
1668
+ self . visit_expr ( expr) ;
1669
+ } ,
1670
+ ExprCall ( ref f, ref args) => {
1671
+ for ( ty, expr) in self . cx . tables . expr_ty ( f) . fn_sig ( self . cx . tcx ) . inputs ( ) . skip_binder ( ) . iter ( ) . zip ( args) {
1672
+ self . prefer_mutable = false ;
1673
+ if let ty:: TyRef ( _, mutbl) = ty. sty {
1674
+ if mutbl. mutbl == MutMutable {
1675
+ self . prefer_mutable = true ;
1676
+ }
1677
+ }
1678
+ self . visit_expr ( expr) ;
1679
+ }
1680
+ } ,
1681
+ ExprMethodCall ( _, _, ref args) => {
1682
+ let def_id = self . cx . tables . type_dependent_defs ( ) [ expr. hir_id ] . def_id ( ) ;
1683
+ for ( ty, expr) in self . cx . tcx . fn_sig ( def_id) . inputs ( ) . skip_binder ( ) . iter ( ) . zip ( args) {
1684
+ self . prefer_mutable = false ;
1685
+ if let ty:: TyRef ( _, mutbl) = ty. sty {
1686
+ if mutbl. mutbl == MutMutable {
1687
+ self . prefer_mutable = true ;
1688
+ }
1689
+ }
1690
+ self . visit_expr ( expr) ;
1691
+ }
1692
+ } ,
1693
+ _ => walk_expr ( self , expr) ,
1694
+ }
1695
+ self . prefer_mutable = old;
1620
1696
}
1621
1697
fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
1622
1698
NestedVisitorMap :: None
0 commit comments