Skip to content

Commit a29495f

Browse files
Add regression test for mutaby used asyncfunction argument in returned closure for needless_pass_by_ref_mut
1 parent b788add commit a29495f

File tree

3 files changed

+112
-21
lines changed

3 files changed

+112
-21
lines changed

clippy_lints/src/needless_pass_by_ref_mut.rs

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ 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, PatKind, QPath,
1010
};
1111
use rustc_hir_typeck::expr_use_visitor as euv;
12-
use rustc_infer::infer::TyCtxtInferExt;
12+
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
1313
use rustc_lint::{LateContext, LateLintPass};
1414
use rustc_middle::hir::map::associated_body;
1515
use rustc_middle::hir::nested_filter::OnlyBodies;
@@ -95,6 +95,26 @@ fn should_skip<'tcx>(
9595
is_from_proc_macro(cx, &input)
9696
}
9797

98+
fn check_closures<'tcx>(ctx: &mut MutablyUsedVariablesCtxt<'tcx>, cx: &LateContext<'tcx>, infcx: &InferCtxt<'tcx>, checked_closures: &mut FxHashSet<LocalDefId>, closures: FxHashSet<LocalDefId>,
99+
) {
100+
let hir = cx.tcx.hir();
101+
for closure in closures {
102+
if !checked_closures.insert(closure) {
103+
continue;
104+
}
105+
ctx.prev_bind = None;
106+
ctx.prev_move_to_closure.clear();
107+
if let Some(body) = hir
108+
.find_by_def_id(closure)
109+
.and_then(associated_body)
110+
.map(|(_, body_id)| hir.body(body_id))
111+
{
112+
euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results())
113+
.consume_body(body);
114+
}
115+
}
116+
}
117+
98118
impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
99119
fn check_fn(
100120
&mut self,
@@ -161,25 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
161181
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
162182
if is_async {
163183
let mut checked_closures = FxHashSet::default();
184+
185+
// We retrieve all the closures declared in the async function because they will
186+
// not be found by `euv::Delegate`.
187+
let mut closures_retriever = ClosuresRetriever { cx, closures: FxHashSet::default() };
188+
walk_fn(&mut closures_retriever, kind, decl, body.id(), fn_def_id);
189+
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures_retriever.closures);
190+
164191
while !ctx.async_closures.is_empty() {
165192
let closures = ctx.async_closures.clone();
166193
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-
}
194+
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures);
183195
}
184196
}
185197
ctx
@@ -439,3 +451,30 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
439451
}
440452
}
441453
}
454+
455+
struct ClosuresRetriever<'a, 'tcx> {
456+
cx: &'a LateContext<'tcx>,
457+
closures: FxHashSet<LocalDefId>,
458+
}
459+
460+
impl<'a, 'tcx> Visitor<'tcx> for ClosuresRetriever<'a, 'tcx> {
461+
type NestedFilter = OnlyBodies;
462+
463+
fn nested_visit_map(&mut self) -> Self::Map {
464+
self.cx.tcx.hir()
465+
}
466+
467+
fn visit_fn(
468+
&mut self,
469+
kind: FnKind<'tcx>,
470+
decl: &'tcx FnDecl<'tcx>,
471+
body_id: BodyId,
472+
_span: Span,
473+
fn_def_id: LocalDefId,
474+
) {
475+
if matches!(kind, FnKind::Closure) {
476+
self.closures.insert(fn_def_id);
477+
}
478+
walk_fn(self, kind, decl, body_id, fn_def_id);
479+
}
480+
}

tests/ui/needless_pass_by_ref_mut.rs

Lines changed: 29 additions & 1 deletion
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,34 @@ 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+
|| {
245+
*n + 1
246+
}
247+
}
248+
249+
// Should not warn.
250+
pub async fn closure3(n: &mut usize) {
251+
(|| *n += 1)();
252+
}
253+
254+
// Should warn.
255+
pub async fn closure4(n: &mut usize) {
256+
//~^ ERROR: this argument is a mutable reference, but not used mutably
257+
(|| {
258+
let _x = *n + 1;
259+
})();
260+
}
261+
234262
fn main() {
235263
let mut u = 0;
236264
let mut v = vec![0];

tests/ui/needless_pass_by_ref_mut.stderr

Lines changed: 25 additions & 1 deletion
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:255: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)