Skip to content

Commit 5fb312e

Browse files
committed
Auto merge of #11624 - GuillaumeGomez:needless_pass_by_ref_mut-unsafe-fn-block, r=blyxyas
Don't emit `needless_pass_by_ref_mut` if the variable is used in an unsafe block or function Fixes #11586. Fixes #11180. As suggested in the two issues above, this lint should not be emitted if this an unsafe function or if the argument is used in an unsafe block. changelog: [`needless_pass_by_ref_mut`]: Don't emit if the variable is used in an unsafe block or function
2 parents 2640d5c + 80a092c commit 5fb312e

File tree

2 files changed

+83
-6
lines changed

2 files changed

+83
-6
lines changed

clippy_lints/src/needless_pass_by_ref_mut.rs

+71-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
77
use rustc_errors::Applicability;
88
use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
99
use rustc_hir::{
10-
Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
10+
BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node,
11+
PatKind, QPath,
1112
};
1213
use rustc_hir_typeck::expr_use_visitor as euv;
1314
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -139,13 +140,23 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
139140
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
140141
let is_async = match kind {
141142
FnKind::ItemFn(.., header) => {
143+
if header.is_unsafe() {
144+
// We don't check unsafe functions.
145+
return;
146+
}
142147
let attrs = cx.tcx.hir().attrs(hir_id);
143148
if header.abi != Abi::Rust || requires_exact_signature(attrs) {
144149
return;
145150
}
146151
header.is_async()
147152
},
148-
FnKind::Method(.., sig) => sig.header.is_async(),
153+
FnKind::Method(.., sig) => {
154+
if sig.header.is_unsafe() {
155+
// We don't check unsafe functions.
156+
return;
157+
}
158+
sig.header.is_async()
159+
},
149160
FnKind::Closure => return,
150161
};
151162

@@ -304,10 +315,27 @@ impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
304315
}
305316
self.aliases.insert(alias, target);
306317
}
318+
319+
// The goal here is to find if the current scope is unsafe or not. It stops when it finds
320+
// a function or an unsafe block.
321+
fn is_in_unsafe_block(&self, item: HirId) -> bool {
322+
let hir = self.tcx.hir();
323+
for (parent, node) in hir.parent_iter(item) {
324+
if let Some(fn_sig) = hir.fn_sig_by_hir_id(parent) {
325+
return fn_sig.header.is_unsafe();
326+
} else if let Node::Block(block) = node {
327+
if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) {
328+
return true;
329+
}
330+
}
331+
}
332+
false
333+
}
307334
}
308335

309336
impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
310-
fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
337+
#[allow(clippy::if_same_then_else)]
338+
fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
311339
if let euv::Place {
312340
base:
313341
euv::PlaceBase::Local(vid)
@@ -327,13 +355,18 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
327355
&& matches!(base_ty.ref_mutability(), Some(Mutability::Mut))
328356
{
329357
self.add_mutably_used_var(*vid);
358+
} else if self.is_in_unsafe_block(id) {
359+
// If we are in an unsafe block, any operation on this variable must not be warned
360+
// upon!
361+
self.add_mutably_used_var(*vid);
330362
}
331363
self.prev_bind = None;
332364
self.prev_move_to_closure.remove(vid);
333365
}
334366
}
335367

336-
fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) {
368+
#[allow(clippy::if_same_then_else)]
369+
fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) {
337370
self.prev_bind = None;
338371
if let euv::Place {
339372
base:
@@ -355,6 +388,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
355388
|| (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut))
356389
{
357390
self.add_mutably_used_var(*vid);
391+
} else if self.is_in_unsafe_block(id) {
392+
// If we are in an unsafe block, any operation on this variable must not be warned
393+
// upon!
394+
self.add_mutably_used_var(*vid);
358395
}
359396
} else if borrow == ty::ImmBorrow {
360397
// If there is an `async block`, it'll contain a call to a closure which we need to
@@ -397,7 +434,21 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
397434
}
398435
}
399436

400-
fn copy(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
437+
fn copy(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
438+
if let euv::Place {
439+
base:
440+
euv::PlaceBase::Local(vid)
441+
| euv::PlaceBase::Upvar(UpvarId {
442+
var_path: UpvarPath { hir_id: vid },
443+
..
444+
}),
445+
..
446+
} = &cmt.place
447+
{
448+
if self.is_in_unsafe_block(id) {
449+
self.add_mutably_used_var(*vid);
450+
}
451+
}
401452
self.prev_bind = None;
402453
}
403454

@@ -427,8 +478,22 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
427478
}
428479
}
429480

430-
fn bind(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
481+
fn bind(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
431482
self.prev_bind = Some(id);
483+
if let euv::Place {
484+
base:
485+
euv::PlaceBase::Local(vid)
486+
| euv::PlaceBase::Upvar(UpvarId {
487+
var_path: UpvarPath { hir_id: vid },
488+
..
489+
}),
490+
..
491+
} = &cmt.place
492+
{
493+
if self.is_in_unsafe_block(id) {
494+
self.add_mutably_used_var(*vid);
495+
}
496+
}
432497
}
433498
}
434499

tests/ui/needless_pass_by_ref_mut.rs

+12
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,18 @@ async fn _f(v: &mut Vec<()>) {
276276
_ = || || x;
277277
}
278278

279+
struct Data<T: ?Sized> {
280+
value: T,
281+
}
282+
// Unsafe functions should not warn.
283+
unsafe fn get_mut_unchecked<T>(ptr: &mut NonNull<Data<T>>) -> &mut T {
284+
&mut (*ptr.as_ptr()).value
285+
}
286+
// Unsafe blocks should not warn.
287+
fn get_mut_unchecked2<T>(ptr: &mut NonNull<Data<T>>) -> &mut T {
288+
unsafe { &mut (*ptr.as_ptr()).value }
289+
}
290+
279291
fn main() {
280292
let mut u = 0;
281293
let mut v = vec![0];

0 commit comments

Comments
 (0)