Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions clippy_lints/src/replace_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, Pl
use rustc_lint::{LateContext, LateLintPass, impl_lint_pass};
use rustc_middle::hir::place::ProjectionKind;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty;
use rustc_middle::ty::{self, TypeVisitableExt as _};
use rustc_span::{Symbol, sym};

declare_clippy_lint! {
Expand Down Expand Up @@ -121,7 +121,15 @@ impl LateLintPass<'_> for ReplaceBox {
&& let Some(rhs_inner) = get_box_new_payload(cx, rhs)
{
span_lint_and_then(cx, REPLACE_BOX, expr.span, "creating a new box", |diag| {
let mut app = Applicability::MachineApplicable;
// Replacing the box in place (`*b = ..`) keeps any borrow already stored in it
// alive across the assignment, while `b = Box::new(..)` drops the old box first.
// When the boxed type carries a lifetime, that difference can change
// borrow-check results, so don't offer this as a machine-applicable rewrite.
let mut app = if inner_ty.has_type_flags(ty::TypeFlags::HAS_RE_ERASED) {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
let suggestion = format!(
"{} = {}",
Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref(),
Expand Down
37 changes: 37 additions & 0 deletions tests/ui/replace_box_unfixable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//@no-rustfix: the suggestion can change borrow-check results for boxed types carrying a lifetime
#![warn(clippy::replace_box)]

// Replacing the box in place keeps any borrow already stored in it alive across the assignment, so
// for a boxed type that carries a lifetime the suggestion is only `MaybeIncorrect`, not applied.

fn mut_ref() {
let mut a = 300;
let mut b = Box::new(&mut a);
**b += 1;
let mut c = 900;
b = Box::new(&mut c);
//~^ replace_box
**b += 1;
}

fn shared_ref() {
let x = 1;
let mut b = Box::new(&x);
let y = 2;
b = Box::new(&y);
//~^ replace_box
let _ = **b;
}

struct Wrapper<'a>(&'a u32);

fn lifetime_struct() {
let x = 1;
let mut b = Box::new(Wrapper(&x));
let y = 2;
b = Box::new(Wrapper(&y));
//~^ replace_box
let _ = *b.0;
}

fn main() {}
28 changes: 28 additions & 0 deletions tests/ui/replace_box_unfixable.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error: creating a new box
--> tests/ui/replace_box_unfixable.rs:12:5
|
LL | b = Box::new(&mut c);
| ^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = &mut c`
|
= note: this creates a needless allocation
= note: `-D clippy::replace-box` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::replace_box)]`

error: creating a new box
--> tests/ui/replace_box_unfixable.rs:21:5
|
LL | b = Box::new(&y);
| ^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = &y`
|
= note: this creates a needless allocation

error: creating a new box
--> tests/ui/replace_box_unfixable.rs:32:5
|
LL | b = Box::new(Wrapper(&y));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = Wrapper(&y)`
|
= note: this creates a needless allocation

error: aborting due to 3 previous errors