@@ -8,12 +8,13 @@ use clippy_utils::{
8
8
use if_chain:: if_chain;
9
9
use rustc_errors:: Applicability ;
10
10
use rustc_hir:: intravisit:: { walk_expr, ErasedMap , NestedVisitorMap , Visitor } ;
11
- use rustc_hir:: { def:: Res , Expr , ExprKind , HirId , Local , PatKind , QPath , UnOp } ;
11
+ use rustc_hir:: { def:: Res , Expr , ExprKind , HirId , Local , Mutability , PatKind , QPath , UnOp } ;
12
12
use rustc_lint:: LateContext ;
13
- use rustc_span:: { symbol:: sym, Span , Symbol } ;
13
+ use rustc_middle:: ty:: adjustment:: Adjust ;
14
+ use rustc_span:: { symbol:: sym, Symbol } ;
14
15
15
16
pub ( super ) fn check ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
16
- let ( scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain ! {
17
+ let ( scrutinee_expr, iter_expr_struct , iter_expr, some_pat, loop_expr) = if_chain ! {
17
18
if let Some ( higher:: WhileLet { if_then, let_pat, let_expr } ) = higher:: WhileLet :: hir( expr) ;
18
19
// check for `Some(..)` pattern
19
20
if let PatKind :: TupleStruct ( QPath :: Resolved ( None , pat_path) , some_pat, _) = let_pat. kind;
@@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
27
28
// get the loop containing the match expression
28
29
if !uses_iter( cx, & iter_expr_struct, if_then) ;
29
30
then {
30
- ( let_expr, iter_expr_struct, some_pat, expr)
31
+ ( let_expr, iter_expr_struct, iter_expr , some_pat, expr)
31
32
} else {
32
33
return ;
33
34
}
@@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
47
48
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
48
49
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
49
50
// afterwards a mutable borrow of a field isn't necessary.
50
- let by_ref = if !iter_expr. fields . is_empty ( ) || needs_mutable_borrow ( cx, & iter_expr, loop_expr) {
51
+ let by_ref = if cx. typeck_results ( ) . expr_ty ( iter_expr) . ref_mutability ( ) == Some ( Mutability :: Mut )
52
+ || !iter_expr_struct. can_move
53
+ || !iter_expr_struct. fields . is_empty ( )
54
+ || needs_mutable_borrow ( cx, & iter_expr_struct, loop_expr)
55
+ {
51
56
".by_ref()"
52
57
} else {
53
58
""
@@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
67
72
68
73
#[ derive( Debug ) ]
69
74
struct IterExpr {
70
- /// The span of the whole expression, not just the path and fields stored here.
71
- span : Span ,
72
75
/// The fields used, in order of child to parent.
73
76
fields : Vec < Symbol > ,
74
77
/// The path being used.
75
78
path : Res ,
79
+ /// Whether or not the iterator can be moved.
80
+ can_move : bool ,
76
81
}
77
82
78
83
/// Parses any expression to find out which field of which variable is used. Will return `None` if
79
84
/// the expression might have side effects.
80
85
fn try_parse_iter_expr ( cx : & LateContext < ' _ > , mut e : & Expr < ' _ > ) -> Option < IterExpr > {
81
- let span = e. span ;
82
86
let mut fields = Vec :: new ( ) ;
87
+ let mut can_move = true ;
83
88
loop {
89
+ if cx
90
+ . typeck_results ( )
91
+ . expr_adjustments ( e)
92
+ . iter ( )
93
+ . any ( |a| matches ! ( a. kind, Adjust :: Deref ( Some ( ..) ) ) )
94
+ {
95
+ // Custom deref impls need to borrow the whole value as it's captured by reference
96
+ can_move = false ;
97
+ fields. clear ( ) ;
98
+ }
84
99
match e. kind {
85
100
ExprKind :: Path ( ref path) => {
86
101
break Some ( IterExpr {
87
- span,
88
102
fields,
89
103
path : cx. qpath_res ( path, e. hir_id ) ,
104
+ can_move,
90
105
} ) ;
91
106
} ,
92
107
ExprKind :: Field ( base, name) => {
@@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp
99
114
// Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
100
115
// already been seen.
101
116
ExprKind :: Index ( base, idx) if !idx. can_have_side_effects ( ) => {
117
+ can_move = false ;
102
118
fields. clear ( ) ;
103
119
e = base;
104
120
} ,
105
121
ExprKind :: Unary ( UnOp :: Deref , base) => {
122
+ can_move = false ;
106
123
fields. clear ( ) ;
107
124
e = base;
108
125
} ,
0 commit comments