Skip to content

Commit 15c068e

Browse files
committed
Fix needless_borrow causing mutable borrows to be moved
1 parent fff8e78 commit 15c068e

File tree

4 files changed

+114
-25
lines changed

4 files changed

+114
-25
lines changed

clippy_lints/src/dereference.rs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ use rustc_hir::{
1010
Pat, PatKind, UnOp,
1111
};
1212
use rustc_lint::{LateContext, LateLintPass};
13-
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
13+
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
1414
use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
1515
use rustc_session::{declare_tool_lint, impl_lint_pass};
1616
use rustc_span::{symbol::sym, Span};
17-
use std::iter;
1817

1918
declare_clippy_lint! {
2019
/// ### What it does
@@ -226,40 +225,58 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
226225
let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
227226
if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| {
228227
if !matches!(adjust.kind, Adjust::Deref(_)) {
229-
Some((i, adjust))
228+
Some((i, Some(adjust)))
230229
} else if !adjust.target.is_ref() {
231-
// Add one to the number of references found.
232-
Some((i + 1, adjust))
230+
// Include the current deref.
231+
Some((i + 1, None))
233232
} else {
234233
None
235234
}
236235
}) {
237-
// Found two consecutive derefs. At least one can be removed.
238236
if i > 1 {
239-
let target_mut = iter::once(adjust)
240-
.chain(iter)
241-
.find_map(|adjust| match adjust.kind {
242-
Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
243-
_ => None,
244-
})
245-
// This default should never happen. Auto-deref always reborrows.
246-
.unwrap_or(Mutability::Not);
247-
self.state = Some((
248-
// Subtract one for the current borrow expression, and one to cover the last
249-
// reference which can't be removed (it's either reborrowed, or needed for
250-
// auto-deref to happen).
251-
State::DerefedBorrow {
237+
// If the next adjustment is a mutable borrow, then check to see if the compiler will
238+
// insert a re-borrow here. If not, leave an extra borrow here to avoid attempting to
239+
// move the a mutable reference.
240+
let (i, target_mut) = if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
241+
adjust.or_else(|| iter.next()).map(|a| &a.kind)
242+
{
243+
if matches!(mutability, AutoBorrowMutability::Mut { .. })
244+
&& !is_auto_reborrow_position(parent, expr.hir_id)
245+
{
246+
(i - 1, Mutability::Mut)
247+
} else {
248+
(i, mutability.into())
249+
}
250+
} else {
251+
(
252+
i,
253+
iter.find_map(|adjust| match adjust.kind {
254+
Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
255+
_ => None,
256+
})
257+
// This default should never happen. Auto-deref always reborrows.
258+
.unwrap_or(Mutability::Not),
259+
)
260+
};
261+
262+
if i > 1 {
263+
self.state = Some((
264+
// Subtract one for the current borrow expression, and one to cover the last
265+
// reference which can't be removed (it's either reborrowed, or needed for
266+
// auto-deref to happen).
267+
State::DerefedBorrow {
252268
count:
253269
// Truncation here would require more than a `u32::MAX` level reference. The compiler
254270
// does not support this.
255271
#[allow(clippy::cast_possible_truncation)]
256272
{ i as u32 - 2 }
257273
},
258-
StateData {
259-
span: expr.span,
260-
target_mut,
261-
},
262-
));
274+
StateData {
275+
span: expr.span,
276+
target_mut,
277+
},
278+
));
279+
}
263280
}
264281
}
265282
},
@@ -456,6 +473,20 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
456473
}
457474
}
458475

476+
/// Checks if the given expression is in a position which can be auto-reborrowed.
477+
/// Note: This is only correct assuming auto-deref is already occurring.
478+
fn is_auto_reborrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
479+
match parent {
480+
Some(Node::Expr(parent)) => match parent.kind {
481+
ExprKind::MethodCall(..) => true,
482+
ExprKind::Call(callee, _) => callee.hir_id != child_id,
483+
_ => false,
484+
},
485+
Some(Node::Local(_)) => true,
486+
_ => false,
487+
}
488+
}
489+
459490
/// Adjustments are sometimes made in the parent block rather than the expression itself.
460491
fn find_adjustments<'tcx>(
461492
tcx: TyCtxt<'tcx>,

tests/ui/needless_borrow.fixed

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ fn main() {
4545
let b = &mut b;
4646
x(b);
4747
}
48+
49+
// Issue #8191
50+
let mut x = 5;
51+
let mut x = &mut x;
52+
53+
mut_ref(x);
54+
mut_ref(x);
55+
let y: &mut i32 = x;
56+
let y: &mut i32 = x;
57+
58+
let y = match 0 {
59+
// Don't lint. Removing the borrow would move 'x'
60+
0 => &mut x,
61+
_ => &mut *x,
62+
};
63+
64+
*x = 5;
4865
}
4966

5067
#[allow(clippy::needless_borrowed_reference)]

tests/ui/needless_borrow.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ fn main() {
4545
let b = &mut b;
4646
x(&b);
4747
}
48+
49+
// Issue #8191
50+
let mut x = 5;
51+
let mut x = &mut x;
52+
53+
mut_ref(&mut x);
54+
mut_ref(&mut &mut x);
55+
let y: &mut i32 = &mut x;
56+
let y: &mut i32 = &mut &mut x;
57+
58+
let y = match 0 {
59+
// Don't lint. Removing the borrow would move 'x'
60+
0 => &mut x,
61+
_ => &mut *x,
62+
};
63+
64+
*x = 5;
4865
}
4966

5067
#[allow(clippy::needless_borrowed_reference)]

tests/ui/needless_borrow.stderr

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,29 @@ error: this expression borrows a reference (`&mut i32`) that is immediately dere
6060
LL | x(&b);
6161
| ^^ help: change this to: `b`
6262

63-
error: aborting due to 10 previous errors
63+
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
64+
--> $DIR/needless_borrow.rs:53:13
65+
|
66+
LL | mut_ref(&mut x);
67+
| ^^^^^^ help: change this to: `x`
68+
69+
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
70+
--> $DIR/needless_borrow.rs:54:13
71+
|
72+
LL | mut_ref(&mut &mut x);
73+
| ^^^^^^^^^^^ help: change this to: `x`
74+
75+
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
76+
--> $DIR/needless_borrow.rs:55:23
77+
|
78+
LL | let y: &mut i32 = &mut x;
79+
| ^^^^^^ help: change this to: `x`
80+
81+
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
82+
--> $DIR/needless_borrow.rs:56:23
83+
|
84+
LL | let y: &mut i32 = &mut &mut x;
85+
| ^^^^^^^^^^^ help: change this to: `x`
86+
87+
error: aborting due to 14 previous errors
6488

0 commit comments

Comments
 (0)