Skip to content

Commit e3267b1

Browse files
Fix mutaby used async function argument in closure for needless_pass_by_ref_mut
1 parent b788add commit e3267b1

File tree

3 files changed

+118
-21
lines changed

3 files changed

+118
-21
lines changed

clippy_lints/src/needless_pass_by_ref_mut.rs

+66-19
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ use clippy_utils::source::snippet;
44
use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
55
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
66
use rustc_errors::Applicability;
7-
use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
7+
use rustc_hir::intravisit::{walk_fn, walk_qpath, FnKind, Visitor};
88
use rustc_hir::{
9-
Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
9+
Body, BodyId, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node,
10+
PatKind, QPath,
1011
};
1112
use rustc_hir_typeck::expr_use_visitor as euv;
12-
use rustc_infer::infer::TyCtxtInferExt;
13+
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
1314
use rustc_lint::{LateContext, LateLintPass};
1415
use rustc_middle::hir::map::associated_body;
1516
use rustc_middle::hir::nested_filter::OnlyBodies;
@@ -95,6 +96,30 @@ fn should_skip<'tcx>(
9596
is_from_proc_macro(cx, &input)
9697
}
9798

99+
fn check_closures<'tcx>(
100+
ctx: &mut MutablyUsedVariablesCtxt<'tcx>,
101+
cx: &LateContext<'tcx>,
102+
infcx: &InferCtxt<'tcx>,
103+
checked_closures: &mut FxHashSet<LocalDefId>,
104+
closures: FxHashSet<LocalDefId>,
105+
) {
106+
let hir = cx.tcx.hir();
107+
for closure in closures {
108+
if !checked_closures.insert(closure) {
109+
continue;
110+
}
111+
ctx.prev_bind = None;
112+
ctx.prev_move_to_closure.clear();
113+
if let Some(body) = hir
114+
.find_by_def_id(closure)
115+
.and_then(associated_body)
116+
.map(|(_, body_id)| hir.body(body_id))
117+
{
118+
euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body);
119+
}
120+
}
121+
}
122+
98123
impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
99124
fn check_fn(
100125
&mut self,
@@ -161,25 +186,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
161186
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
162187
if is_async {
163188
let mut checked_closures = FxHashSet::default();
189+
190+
// We retrieve all the closures declared in the async function because they will
191+
// not be found by `euv::Delegate`.
192+
let mut closures_retriever = ClosuresRetriever {
193+
cx,
194+
closures: FxHashSet::default(),
195+
};
196+
walk_fn(&mut closures_retriever, kind, decl, body.id(), fn_def_id);
197+
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures_retriever.closures);
198+
164199
while !ctx.async_closures.is_empty() {
165200
let closures = ctx.async_closures.clone();
166201
ctx.async_closures.clear();
167-
let hir = cx.tcx.hir();
168-
for closure in closures {
169-
if !checked_closures.insert(closure) {
170-
continue;
171-
}
172-
ctx.prev_bind = None;
173-
ctx.prev_move_to_closure.clear();
174-
if let Some(body) = hir
175-
.find_by_def_id(closure)
176-
.and_then(associated_body)
177-
.map(|(_, body_id)| hir.body(body_id))
178-
{
179-
euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results())
180-
.consume_body(body);
181-
}
182-
}
202+
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures);
183203
}
184204
}
185205
ctx
@@ -439,3 +459,30 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
439459
}
440460
}
441461
}
462+
463+
struct ClosuresRetriever<'a, 'tcx> {
464+
cx: &'a LateContext<'tcx>,
465+
closures: FxHashSet<LocalDefId>,
466+
}
467+
468+
impl<'a, 'tcx> Visitor<'tcx> for ClosuresRetriever<'a, 'tcx> {
469+
type NestedFilter = OnlyBodies;
470+
471+
fn nested_visit_map(&mut self) -> Self::Map {
472+
self.cx.tcx.hir()
473+
}
474+
475+
fn visit_fn(
476+
&mut self,
477+
kind: FnKind<'tcx>,
478+
decl: &'tcx FnDecl<'tcx>,
479+
body_id: BodyId,
480+
_span: Span,
481+
fn_def_id: LocalDefId,
482+
) {
483+
if matches!(kind, FnKind::Closure) {
484+
self.closures.insert(fn_def_id);
485+
}
486+
walk_fn(self, kind, decl, body_id, fn_def_id);
487+
}
488+
}

tests/ui/needless_pass_by_ref_mut.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(clippy::if_same_then_else, clippy::no_effect)]
1+
#![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)]
22
#![feature(lint_reasons)]
33
//@no-rustfix
44
use std::ptr::NonNull;
@@ -231,6 +231,32 @@ async fn async_vec2(b: &mut Vec<bool>) {
231231
b.push(true);
232232
}
233233

234+
// Should not warn.
235+
pub async fn closure(n: &mut usize) -> impl '_ + FnMut() {
236+
|| {
237+
*n += 1;
238+
}
239+
}
240+
241+
// Should warn.
242+
pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
243+
//~^ ERROR: this argument is a mutable reference, but not used mutably
244+
|| *n + 1
245+
}
246+
247+
// Should not warn.
248+
pub async fn closure3(n: &mut usize) {
249+
(|| *n += 1)();
250+
}
251+
252+
// Should warn.
253+
pub async fn closure4(n: &mut usize) {
254+
//~^ ERROR: this argument is a mutable reference, but not used mutably
255+
(|| {
256+
let _x = *n + 1;
257+
})();
258+
}
259+
234260
fn main() {
235261
let mut u = 0;
236262
let mut v = vec![0];

tests/ui/needless_pass_by_ref_mut.stderr

+25-1
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,29 @@ error: this argument is a mutable reference, but not used mutably
107107
LL | async fn inner_async3(x: &mut i32, y: &mut u32) {
108108
| ^^^^^^^^ help: consider changing to: `&i32`
109109

110-
error: aborting due to 17 previous errors
110+
error: this argument is a mutable reference, but not used mutably
111+
--> $DIR/needless_pass_by_ref_mut.rs:235:25
112+
|
113+
LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() {
114+
| ^^^^^^^^^^ help: consider changing to: `&usize`
115+
|
116+
= warning: changing this function will impact semver compatibility
117+
118+
error: this argument is a mutable reference, but not used mutably
119+
--> $DIR/needless_pass_by_ref_mut.rs:242:20
120+
|
121+
LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
122+
| ^^^^^^^^^^ help: consider changing to: `&usize`
123+
|
124+
= warning: changing this function will impact semver compatibility
125+
126+
error: this argument is a mutable reference, but not used mutably
127+
--> $DIR/needless_pass_by_ref_mut.rs:253:26
128+
|
129+
LL | pub async fn closure4(n: &mut usize) {
130+
| ^^^^^^^^^^ help: consider changing to: `&usize`
131+
|
132+
= warning: changing this function will impact semver compatibility
133+
134+
error: aborting due to 20 previous errors
111135

0 commit comments

Comments
 (0)