Skip to content

Commit e6755c5

Browse files
committed
document
1 parent 57b8d0d commit e6755c5

File tree

1 file changed

+28
-5
lines changed

1 file changed

+28
-5
lines changed

clippy_lints/src/methods/mut_mutex_lock.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,61 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, name_s
1919
let index_mut_trait = cx.tcx.lang_items().index_mut_trait();
2020
let impls_deref_mut = |ty| deref_mut_trait.is_some_and(|trait_id| implements_trait(cx, ty, trait_id, &[]));
2121
let impls_index_mut = |ty, idx| index_mut_trait.is_some_and(|trait_id| implements_trait(cx, ty, trait_id, &[idx]));
22+
23+
// The mutex was accessed either directly (`mutex.lock()`), or through a series of
24+
// deref/field/indexing projections. Since the final `.lock()` call only requires `&Mutex`,
25+
// those might be immutable, and so we need to manually check whether mutable projections
26+
// would've been possible. For that, we'll repeatedly peel off projections and check each
27+
// intermediary receiver.
2228
let mut r = recv;
2329
loop {
30+
// Check that the (deref) adjustments could be made mutable
2431
if (typeck.expr_adjustments(r))
2532
.iter()
2633
.map_while(|a| match a.kind {
2734
Adjust::Deref(x) => Some((a.target, x)),
2835
_ => None,
2936
})
3037
.try_fold(typeck.expr_ty(r), |ty, (target, deref)| match deref {
38+
// There has been an overloaded deref, most likely an immutable one, as `.lock()` didn't require a
39+
// mutable one -- we need to check if a mutable deref would've been possible, i.e. if
40+
// `ty: DerefMut<Target = target>` (we don't need to check the `Target` part, as `Deref` and `DerefMut`
41+
// impls necessarily have the same one)
3142
Some(_) => impls_deref_mut(ty).then_some(target),
43+
// There has been a simple deref; if it happened on a `&T`, then we know it will can't be changed to
44+
// provide mutable access
3245
None => (ty.ref_mutability() != Some(Mutability::Not)).then_some(target),
3346
})
3447
.is_none()
3548
{
3649
return;
3750
}
51+
52+
// Peel off one projection
3853
match r.kind {
39-
ExprKind::Field(base, _) => r = base,
40-
ExprKind::Index(base, idx, _) => {
41-
if impls_index_mut(typeck.expr_ty_adjusted(base), typeck.expr_ty_adjusted(idx).into()) {
54+
// In order to be able to make this `*` mean `.deref_mut()`, `base: DerefMut` needs to hold
55+
ExprKind::Unary(UnOp::Deref, base) => {
56+
if impls_deref_mut(typeck.expr_ty_adjusted(base)) {
4257
r = base;
4358
} else {
4459
return;
4560
}
4661
},
47-
ExprKind::Unary(UnOp::Deref, base) => {
48-
if impls_deref_mut(typeck.expr_ty_adjusted(base)) {
62+
// In order to be able to make this `[idx]` mean `.index_mut(idx)`, `base: IndexMut<idx>` needs to hold
63+
ExprKind::Index(base, idx, _) => {
64+
// NOTE: the reason we do need to take into account `idx` here is that it's a _generic_ of
65+
// `IndexMut`, not an associated type of the impl
66+
if impls_index_mut(typeck.expr_ty_adjusted(base), typeck.expr_ty_adjusted(idx).into()) {
4967
r = base;
5068
} else {
5169
return;
5270
}
5371
},
72+
// A field projection by itself can't be mutable/immutable -- we'll only need to check
73+
// that the field type is not a `&T`, and we'll do that in the next iteration of the
74+
// loop, during adjustment checking
75+
ExprKind::Field(base, _) => r = base,
76+
// No more projections to check
5477
_ => break,
5578
}
5679
}

0 commit comments

Comments
 (0)