Skip to content

Commit 45dc752

Browse files
committed
fix(mut_mutex_lock): don't lint on indexing and field accesses
1 parent f15abd4 commit 45dc752

3 files changed

Lines changed: 106 additions & 4 deletions

File tree

clippy_lints/src/methods/mut_mutex_lock.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,32 @@ use clippy_utils::expr_custom_deref_adjustment;
33
use clippy_utils::res::MaybeDef;
44
use clippy_utils::ty::peel_and_count_ty_refs;
55
use rustc_errors::Applicability;
6-
use rustc_hir::{Expr, Mutability};
6+
use rustc_hir::{Expr, ExprKind, Mutability};
77
use rustc_lint::LateContext;
88
use rustc_span::{Span, sym};
99

1010
use super::MUT_MUTEX_LOCK;
1111

1212
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
13-
if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut))
14-
// NOTE: the reason we don't use `expr_ty_adjusted` here is that a call to `Mutex::lock` by itself
15-
// adjusts the receiver to be `&Mutex`
13+
// We don't want to lint on indexing and field accesses, as both of those would take exclusive
14+
// access to only part of a value -- which would conflict with any immutable reborrow over
15+
// the whole value
16+
if !matches!(recv.kind, ExprKind::Field(..) | ExprKind::Index(..))
17+
// Make sure that we have a mutable access to `Mutex`:
18+
//
19+
// 1. Check that there are no custom deref adjustments turning the access immutable (e.g.
20+
// `Arc<Mutex>` derefs to `&Mutex`)
21+
// TODO: add tests for this
22+
&& matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut))
23+
// 2. Check that the receiver is behind one or more `&mut`s -- we want to have mutable
24+
// access, while not outright owning it -- if the latter were the case, then changing
25+
// `.lock()` to `.get_mut()`, could easily result in a conflict with other existing
26+
// immutable borrows.
27+
//
28+
// NOTE: `mutbl` being `Some` is enough to determine that there was at least one layer of
29+
// references, so no need to check `n`
30+
// NOTE: the reason we don't use `expr_ty_adjusted` here is that a call
31+
// to `Mutex::lock` by itself adjusts the receiver to be `&Mutex`
1632
&& let (recv_ty, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv))
1733
&& recv_ty.is_diag_item(cx, sym::Mutex)
1834
{

tests/ui/mut_mutex_lock.fixed

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,47 @@ fn mut_ref_ref_mutex_lock() {
3737
*guard += 1;
3838
}
3939

40+
mod issue16253 {
41+
use std::sync::{Arc, Mutex};
42+
43+
// Do not lint, even if the mutex is owned
44+
fn dont_lint_owned(m: Mutex<i32>) {
45+
m.lock();
46+
}
47+
48+
struct Wrapper {
49+
arc: Arc<Mutex<i32>>,
50+
ref_: &'static Mutex<i32>,
51+
ref_mut: &'static mut Mutex<i32>,
52+
owned: Mutex<i32>,
53+
}
54+
55+
// Do not lint, even if the projection chain would theoretically allow mutable access
56+
fn field(w: Wrapper, ref_w: &Wrapper, refmut_w: &mut Wrapper) {
57+
w.arc.lock();
58+
w.ref_.lock();
59+
w.ref_mut.lock();
60+
w.owned.lock();
61+
ref_w.arc.lock();
62+
ref_w.ref_.lock();
63+
ref_w.ref_mut.lock();
64+
ref_w.owned.lock();
65+
refmut_w.arc.lock();
66+
refmut_w.ref_.lock();
67+
refmut_w.ref_mut.lock();
68+
refmut_w.owned.lock();
69+
}
70+
71+
// Do not lint, even if the `.index()` could've been turned into `.index_mut()`
72+
fn index(mutexes: &mut [Mutex<u32>]) {
73+
// even though `[0]` is _currently_ an `.index(0)`, it can be turned into `.index_mut()` to
74+
// enable mutable access: `&mut [Mutex] -> &mut Mutex`
75+
mutexes[0].lock().unwrap();
76+
77+
// `exes` is `&[Mutex] = &Mutex`, so we can't get to `&mut Mutex` no matter what
78+
let exes: &_ = mutexes;
79+
exes[0].lock().unwrap();
80+
}
81+
}
82+
4083
fn main() {}

tests/ui/mut_mutex_lock.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,47 @@ fn mut_ref_ref_mutex_lock() {
3737
*guard += 1;
3838
}
3939

40+
mod issue16253 {
41+
use std::sync::{Arc, Mutex};
42+
43+
// Do not lint, even if the mutex is owned
44+
fn dont_lint_owned(m: Mutex<i32>) {
45+
m.lock();
46+
}
47+
48+
struct Wrapper {
49+
arc: Arc<Mutex<i32>>,
50+
ref_: &'static Mutex<i32>,
51+
ref_mut: &'static mut Mutex<i32>,
52+
owned: Mutex<i32>,
53+
}
54+
55+
// Do not lint, even if the projection chain would theoretically allow mutable access
56+
fn field(w: Wrapper, ref_w: &Wrapper, refmut_w: &mut Wrapper) {
57+
w.arc.lock();
58+
w.ref_.lock();
59+
w.ref_mut.lock();
60+
w.owned.lock();
61+
ref_w.arc.lock();
62+
ref_w.ref_.lock();
63+
ref_w.ref_mut.lock();
64+
ref_w.owned.lock();
65+
refmut_w.arc.lock();
66+
refmut_w.ref_.lock();
67+
refmut_w.ref_mut.lock();
68+
refmut_w.owned.lock();
69+
}
70+
71+
// Do not lint, even if the `.index()` could've been turned into `.index_mut()`
72+
fn index(mutexes: &mut [Mutex<u32>]) {
73+
// even though `[0]` is _currently_ an `.index(0)`, it can be turned into `.index_mut()` to
74+
// enable mutable access: `&mut [Mutex] -> &mut Mutex`
75+
mutexes[0].lock().unwrap();
76+
77+
// `exes` is `&[Mutex] = &Mutex`, so we can't get to `&mut Mutex` no matter what
78+
let exes: &_ = mutexes;
79+
exes[0].lock().unwrap();
80+
}
81+
}
82+
4083
fn main() {}

0 commit comments

Comments
 (0)