@@ -8,12 +8,13 @@ use clippy_utils::{
88use if_chain:: if_chain;
99use rustc_errors:: Applicability ;
1010use 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 } ;
1212use 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 } ;
1415
1516pub ( 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 ! {
1718 if let Some ( higher:: WhileLet { if_then, let_pat, let_expr } ) = higher:: WhileLet :: hir( expr) ;
1819 // check for `Some(..)` pattern
1920 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<'_>) {
2728 // get the loop containing the match expression
2829 if !uses_iter( cx, & iter_expr_struct, if_then) ;
2930 then {
30- ( let_expr, iter_expr_struct, some_pat, expr)
31+ ( let_expr, iter_expr_struct, iter_expr , some_pat, expr)
3132 } else {
3233 return ;
3334 }
@@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
4748 // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
4849 // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
4950 // 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+ {
5156 ".by_ref()"
5257 } else {
5358 ""
@@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
6772
6873#[ derive( Debug ) ]
6974struct IterExpr {
70- /// The span of the whole expression, not just the path and fields stored here.
71- span : Span ,
7275 /// The fields used, in order of child to parent.
7376 fields : Vec < Symbol > ,
7477 /// The path being used.
7578 path : Res ,
79+ /// Whether or not the iterator can be moved.
80+ can_move : bool ,
7681}
7782
7883/// Parses any expression to find out which field of which variable is used. Will return `None` if
7984/// the expression might have side effects.
8085fn try_parse_iter_expr ( cx : & LateContext < ' _ > , mut e : & Expr < ' _ > ) -> Option < IterExpr > {
81- let span = e. span ;
8286 let mut fields = Vec :: new ( ) ;
87+ let mut can_move = true ;
8388 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+ }
8499 match e. kind {
85100 ExprKind :: Path ( ref path) => {
86101 break Some ( IterExpr {
87- span,
88102 fields,
89103 path : cx. qpath_res ( path, e. hir_id ) ,
104+ can_move,
90105 } ) ;
91106 } ,
92107 ExprKind :: Field ( base, name) => {
@@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp
99114 // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
100115 // already been seen.
101116 ExprKind :: Index ( base, idx) if !idx. can_have_side_effects ( ) => {
117+ can_move = false ;
102118 fields. clear ( ) ;
103119 e = base;
104120 } ,
105121 ExprKind :: Unary ( UnOp :: Deref , base) => {
122+ can_move = false ;
106123 fields. clear ( ) ;
107124 e = base;
108125 } ,
0 commit comments