diff options
| author | Jason Newcomb <jsnewcomb@pm.me> | 2022-01-03 12:44:33 -0500 |
|---|---|---|
| committer | Jason Newcomb <jsnewcomb@pm.me> | 2022-01-21 09:50:11 -0500 |
| commit | 15c068ed0f4d231beabb22975cbca7fd33996ce5 (patch) | |
| tree | ab92cf4fb0201fe5d09127d3667735dc26dcccac | |
| parent | fff8e78f6d6699a8a05acb94fa0e6dd461d0bd86 (diff) | |
| download | rust-15c068ed0f4d231beabb22975cbca7fd33996ce5.tar.gz rust-15c068ed0f4d231beabb22975cbca7fd33996ce5.zip | |
Fix `needless_borrow` causing mutable borrows to be moved
| -rw-r--r-- | clippy_lints/src/dereference.rs | 79 | ||||
| -rw-r--r-- | tests/ui/needless_borrow.fixed | 17 | ||||
| -rw-r--r-- | tests/ui/needless_borrow.rs | 17 | ||||
| -rw-r--r-- | tests/ui/needless_borrow.stderr | 26 |
4 files changed, 114 insertions, 25 deletions
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index bf077a212fd..9645f1a03be 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -10,11 +10,10 @@ use rustc_hir::{ Pat, PatKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; -use std::iter; declare_clippy_lint! { /// ### What it does @@ -226,40 +225,58 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { let mut iter = find_adjustments(cx.tcx, typeck, expr).iter(); if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| { if !matches!(adjust.kind, Adjust::Deref(_)) { - Some((i, adjust)) + Some((i, Some(adjust))) } else if !adjust.target.is_ref() { - // Add one to the number of references found. - Some((i + 1, adjust)) + // Include the current deref. + Some((i + 1, None)) } else { None } }) { - // Found two consecutive derefs. At least one can be removed. if i > 1 { - let target_mut = iter::once(adjust) - .chain(iter) - .find_map(|adjust| match adjust.kind { - Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()), - _ => None, - }) - // This default should never happen. Auto-deref always reborrows. - .unwrap_or(Mutability::Not); - self.state = Some(( - // Subtract one for the current borrow expression, and one to cover the last - // reference which can't be removed (it's either reborrowed, or needed for - // auto-deref to happen). - State::DerefedBorrow { + // If the next adjustment is a mutable borrow, then check to see if the compiler will + // insert a re-borrow here. If not, leave an extra borrow here to avoid attempting to + // move the a mutable reference. + let (i, target_mut) = if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) = + adjust.or_else(|| iter.next()).map(|a| &a.kind) + { + if matches!(mutability, AutoBorrowMutability::Mut { .. }) + && !is_auto_reborrow_position(parent, expr.hir_id) + { + (i - 1, Mutability::Mut) + } else { + (i, mutability.into()) + } + } else { + ( + i, + iter.find_map(|adjust| match adjust.kind { + Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()), + _ => None, + }) + // This default should never happen. Auto-deref always reborrows. + .unwrap_or(Mutability::Not), + ) + }; + + if i > 1 { + self.state = Some(( + // Subtract one for the current borrow expression, and one to cover the last + // reference which can't be removed (it's either reborrowed, or needed for + // auto-deref to happen). + State::DerefedBorrow { count: // Truncation here would require more than a `u32::MAX` level reference. The compiler // does not support this. #[allow(clippy::cast_possible_truncation)] { i as u32 - 2 } }, - StateData { - span: expr.span, - target_mut, - }, - )); + StateData { + span: expr.span, + target_mut, + }, + )); + } } } }, @@ -456,6 +473,20 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, } } +/// Checks if the given expression is in a position which can be auto-reborrowed. +/// Note: This is only correct assuming auto-deref is already occurring. +fn is_auto_reborrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool { + match parent { + Some(Node::Expr(parent)) => match parent.kind { + ExprKind::MethodCall(..) => true, + ExprKind::Call(callee, _) => callee.hir_id != child_id, + _ => false, + }, + Some(Node::Local(_)) => true, + _ => false, + } +} + /// Adjustments are sometimes made in the parent block rather than the expression itself. fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 9e37fb92559..2a87b520ce5 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -45,6 +45,23 @@ fn main() { let b = &mut b; x(b); } + + // Issue #8191 + let mut x = 5; + let mut x = &mut x; + + mut_ref(x); + mut_ref(x); + let y: &mut i32 = x; + let y: &mut i32 = x; + + let y = match 0 { + // Don't lint. Removing the borrow would move 'x' + 0 => &mut x, + _ => &mut *x, + }; + + *x = 5; } #[allow(clippy::needless_borrowed_reference)] diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 093277784be..4a98350711f 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -45,6 +45,23 @@ fn main() { let b = &mut b; x(&b); } + + // Issue #8191 + let mut x = 5; + let mut x = &mut x; + + mut_ref(&mut x); + mut_ref(&mut &mut x); + let y: &mut i32 = &mut x; + let y: &mut i32 = &mut &mut x; + + let y = match 0 { + // Don't lint. Removing the borrow would move 'x' + 0 => &mut x, + _ => &mut *x, + }; + + *x = 5; } #[allow(clippy::needless_borrowed_reference)] diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 03a5b3d260e..2211f957982 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -60,5 +60,29 @@ error: this expression borrows a reference (`&mut i32`) that is immediately dere LL | x(&b); | ^^ help: change this to: `b` -error: aborting due to 10 previous errors +error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:53:13 + | +LL | mut_ref(&mut x); + | ^^^^^^ help: change this to: `x` + +error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:54:13 + | +LL | mut_ref(&mut &mut x); + | ^^^^^^^^^^^ help: change this to: `x` + +error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:55:23 + | +LL | let y: &mut i32 = &mut x; + | ^^^^^^ help: change this to: `x` + +error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:56:23 + | +LL | let y: &mut i32 = &mut &mut x; + | ^^^^^^^^^^^ help: change this to: `x` + +error: aborting due to 14 previous errors |
