Fix unused-mut false positive for Box

This commit is contained in:
hkalbasi 2023-06-04 15:56:01 +03:30
parent 71f3e4b08c
commit 0408af6453
2 changed files with 83 additions and 12 deletions

View file

@ -78,7 +78,7 @@ pub fn borrowck_query(
.map(|body| { .map(|body| {
let body = body?; let body = body?;
Ok(BorrowckResult { Ok(BorrowckResult {
mutability_of_locals: mutability_of_locals(&body), mutability_of_locals: mutability_of_locals(db, &body),
moved_out_of_ref: moved_out_of_ref(db, &body), moved_out_of_ref: moved_out_of_ref(db, &body),
mir_body: body, mir_body: body,
}) })
@ -186,10 +186,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
result result
} }
fn is_place_direct(lvalue: &Place) -> bool { #[derive(Debug, Clone, Copy, PartialEq, Eq)]
!lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
}
enum ProjectionCase { enum ProjectionCase {
/// Projection is a local /// Projection is a local
Direct, Direct,
@ -199,12 +196,14 @@ enum ProjectionCase {
Indirect, Indirect,
} }
fn place_case(lvalue: &Place) -> ProjectionCase { fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
let mut is_part_of = false; let mut is_part_of = false;
for proj in lvalue.projection.iter().rev() { let mut ty = body.locals[lvalue.local].ty.clone();
for proj in lvalue.projection.iter() {
match proj { match proj {
ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
ProjectionElem::ConstantIndex { .. } ProjectionElem::Deref // It's direct in case of `Box<T>`
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_) | ProjectionElem::Field(_)
| ProjectionElem::TupleOrClosureField(_) | ProjectionElem::TupleOrClosureField(_)
@ -213,6 +212,23 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
} }
ProjectionElem::OpaqueCast(_) => (), ProjectionElem::OpaqueCast(_) => (),
} }
ty = proj.projected_ty(
ty,
db,
|c, subst, f| {
let (def, _) = db.lookup_intern_closure(c.into());
let infer = db.infer(def);
let (captures, _) = infer.closure_info(&c);
let parent_subst = ClosureSubst(subst).parent_subst();
captures
.get(f)
.expect("broken closure field")
.ty
.clone()
.substitute(Interner, parent_subst)
},
body.owner.module(db.upcast()).krate(),
);
} }
if is_part_of { if is_part_of {
ProjectionCase::DirectPart ProjectionCase::DirectPart
@ -300,7 +316,10 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
result result
} }
fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> { fn mutability_of_locals(
db: &dyn HirDatabase,
body: &MirBody,
) -> ArenaMap<LocalId, MutabilityReason> {
let mut result: ArenaMap<LocalId, MutabilityReason> = let mut result: ArenaMap<LocalId, MutabilityReason> =
body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect(); body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
let mut push_mut_span = |local, span| match &mut result[local] { let mut push_mut_span = |local, span| match &mut result[local] {
@ -313,7 +332,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
for statement in &block.statements { for statement in &block.statements {
match &statement.kind { match &statement.kind {
StatementKind::Assign(place, value) => { StatementKind::Assign(place, value) => {
match place_case(place) { match place_case(db, body, place) {
ProjectionCase::Direct => { ProjectionCase::Direct => {
if ever_init_map.get(place.local).copied().unwrap_or_default() { if ever_init_map.get(place.local).copied().unwrap_or_default() {
push_mut_span(place.local, statement.span); push_mut_span(place.local, statement.span);
@ -328,7 +347,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
ProjectionCase::Indirect => (), ProjectionCase::Indirect => (),
} }
if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value { if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
if is_place_direct(p) { if place_case(db, body, p) != ProjectionCase::Indirect {
push_mut_span(p.local, statement.span); push_mut_span(p.local, statement.span);
} }
} }

View file

@ -993,6 +993,58 @@ fn f() {
); );
} }
#[test]
fn boxes() {
check_diagnostics(
r#"
//- minicore: coerce_unsized, deref_mut, slice
use core::ops::{Deref, DerefMut};
use core::{marker::Unsize, ops::CoerceUnsized};
#[lang = "owned_box"]
pub struct Box<T: ?Sized> {
inner: *mut T,
}
impl<T> Box<T> {
fn new(t: T) -> Self {
#[rustc_box]
Box::new(t)
}
}
impl<T: ?Sized> Deref for Box<T> {
type Target = T;
fn deref(&self) -> &T {
&**self
}
}
impl<T: ?Sized> DerefMut for Box<T> {
fn deref_mut(&mut self) -> &mut T {
&mut **self
}
}
fn f() {
let x = Box::new(5);
x = Box::new(7);
//^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
let x = Box::new(5);
*x = 7;
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
let mut y = Box::new(5);
//^^^^^ 💡 weak: variable does not need to be mutable
*x = *y;
//^^^^^^^ 💡 error: cannot mutate immutable variable `x`
let x = Box::new(5);
let closure = || *x = 2;
//^ 💡 error: cannot mutate immutable variable `x`
}
"#,
);
}
#[test] #[test]
fn allow_unused_mut_for_identifiers_starting_with_underline() { fn allow_unused_mut_for_identifiers_starting_with_underline() {
check_diagnostics( check_diagnostics(