Skip to content

Commit 77d47e1

Browse files
committed
suspicious_map: accept mutation in map().count()
If the closure in the `map` call ends up mutating a variable, the call is assumed to no longer be suspicious. Given just a function name or path, there's no way to detect that there is interiour mutability, so the lint still fires in that case. However, it is now documented as a known problem. Fixes rust-lang#5253
1 parent 1ff81c1 commit 77d47e1

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

clippy_lints/src/methods/mod.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,8 @@ declare_clippy_lint! {
10841084
/// If the `map` call is intentional, this should be rewritten. Or, if you intend to
10851085
/// drive the iterator to completion, you can just use `for_each` instead.
10861086
///
1087-
/// **Known problems:** None
1087+
/// **Known problems:** Named functions which mutate other data internally are not
1088+
/// detected as non-suspicious.
10881089
///
10891090
/// **Example:**
10901091
///
@@ -1341,7 +1342,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
13411342
["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
13421343
["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
13431344
["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
1344-
["count", "map"] => lint_suspicious_map(cx, expr),
1345+
["count", "map"] => lint_suspicious_map(cx, expr, &arg_lists[1][1]),
13451346
["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
13461347
["unwrap_or", arith @ "checked_add"]
13471348
| ["unwrap_or", arith @ "checked_sub"]
@@ -3142,7 +3143,17 @@ fn is_maybe_uninit_ty_valid(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
31423143
}
31433144
}
31443145

3145-
fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
3146+
fn lint_suspicious_map<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'tcx>, map_arg: &'tcx hir::Expr<'_>) {
3147+
if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(map_arg.hir_id) {
3148+
let closure_body = cx.tcx.hir().body(body_id);
3149+
if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
3150+
// A variable is used mutably inside of the closure. Suppress the lint.
3151+
if !map_mutated_vars.is_empty() {
3152+
return;
3153+
}
3154+
}
3155+
}
3156+
31463157
span_lint_and_help(
31473158
cx,
31483159
SUSPICIOUS_MAP,

tests/ui/suspicious_map.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
#![warn(clippy::suspicious_map)]
2+
#![allow(clippy::redundant_closure)]
23

34
fn main() {
45
let _ = (0..3).map(|x| x + 2).count();
6+
7+
// This usage is OK because the `sum` side effect makes the `map` useful.
8+
let mut sum = 0;
9+
let _ = (0..3).map(|x| sum += x).count();
10+
11+
// The linter is blind to path-based arguments however.
12+
let mut ext_sum = 0;
13+
let ext_closure = |x| ext_sum += x;
14+
let _ = (0..3).map(ext_closure).count();
15+
16+
// The linter can see `FnMut` calls however.
17+
let mut ext_closure_inner = |x| ext_sum += x;
18+
let _ = (0..3).map(|x| ext_closure_inner(x)).count();
519
}

tests/ui/suspicious_map.stderr

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
error: this call to `map()` won't have an effect on the call to `count()`
2-
--> $DIR/suspicious_map.rs:4:13
2+
--> $DIR/suspicious_map.rs:5:13
33
|
44
LL | let _ = (0..3).map(|x| x + 2).count();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: `-D clippy::suspicious-map` implied by `-D warnings`
88
= help: make sure you did not confuse `map` with `filter` or `for_each`
99

10-
error: aborting due to previous error
10+
error: this call to `map()` won't have an effect on the call to `count()`
11+
--> $DIR/suspicious_map.rs:14:13
12+
|
13+
LL | let _ = (0..3).map(ext_closure).count();
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: make sure you did not confuse `map` with `filter` or `for_each`
17+
18+
error: aborting due to 2 previous errors
1119

0 commit comments

Comments
 (0)