Skip to content

Commit c1afad3

Browse files
committed
Lint precedence possible ambiguity between closure and method call
`|x: String| {x}.clone()` applies `clone()` to {x}, while `|x: String| -> String {x}.clone()` applies `.clone()` to the closure because a closure with an explicit return type requires a block. This extends the `precedence` lint to suggest using parentheses around the closure definition when the closure would be cloned, as in `(|x: String| -> String {x}).clone()`.
1 parent 57f4f5d commit c1afad3

File tree

4 files changed

+104
-16
lines changed

4 files changed

+104
-16
lines changed

clippy_lints/src/precedence.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::span_lint_and_sugg;
1+
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
22
use clippy_utils::source::snippet_with_applicability;
33
use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub};
44
use rustc_ast::ast::{BinOpKind, Expr, ExprKind};
@@ -10,7 +10,8 @@ use rustc_span::source_map::Spanned;
1010
declare_clippy_lint! {
1111
/// ### What it does
1212
/// Checks for operations where precedence may be unclear and suggests to add parentheses.
13-
/// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses
13+
/// It catches a mixed usage of arithmetic and bit shifting/combining operators,
14+
/// as well as method calls applied to closures.
1415
///
1516
/// ### Why is this bad?
1617
/// Not everyone knows the precedence of those operators by
@@ -109,6 +110,19 @@ impl EarlyLintPass for Precedence {
109110
},
110111
_ => (),
111112
}
113+
} else if let ExprKind::MethodCall(method_call) = &expr.kind
114+
&& let ExprKind::Closure(closure) = &method_call.receiver.kind
115+
{
116+
span_lint_and_then(cx, PRECEDENCE, expr.span, "precedence might not be obvious", |diag| {
117+
diag.multipart_suggestion(
118+
"consider parenthesizing the closure",
119+
vec![
120+
(closure.fn_decl_span.shrink_to_lo(), String::from("(")),
121+
(closure.body.span.shrink_to_hi(), String::from(")")),
122+
],
123+
Applicability::MachineApplicable,
124+
);
125+
});
112126
}
113127
}
114128
}

tests/ui/precedence.fixed

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
#![warn(clippy::precedence)]
2-
#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
3-
#![allow(clippy::identity_op)]
4-
#![allow(clippy::eq_op)]
2+
#![allow(
3+
unused_must_use,
4+
clippy::no_effect,
5+
clippy::unnecessary_operation,
6+
clippy::clone_on_copy,
7+
clippy::identity_op,
8+
clippy::eq_op
9+
)]
510

611
macro_rules! trip {
712
($a:expr) => {
@@ -35,3 +40,24 @@ fn main() {
3540
let b = 3;
3641
trip!(b * 8);
3742
}
43+
44+
struct W(u8);
45+
impl Clone for W {
46+
fn clone(&self) -> Self {
47+
W(1)
48+
}
49+
}
50+
51+
fn closure_method_call() {
52+
// Do not lint when the method call is applied to the block, both inside the closure
53+
let f = |x: W| { x }.clone();
54+
assert!(matches!(f(W(0)), W(1)));
55+
56+
let f = (|x: W| -> _ { x }).clone();
57+
assert!(matches!(f(W(0)), W(0)));
58+
//~^^ precedence
59+
60+
let f = (move |x: W| -> _ { x }).clone();
61+
assert!(matches!(f(W(0)), W(0)));
62+
//~^^ precedence
63+
}

tests/ui/precedence.rs

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
#![warn(clippy::precedence)]
2-
#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
3-
#![allow(clippy::identity_op)]
4-
#![allow(clippy::eq_op)]
2+
#![allow(
3+
unused_must_use,
4+
clippy::no_effect,
5+
clippy::unnecessary_operation,
6+
clippy::clone_on_copy,
7+
clippy::identity_op,
8+
clippy::eq_op
9+
)]
510

611
macro_rules! trip {
712
($a:expr) => {
@@ -35,3 +40,24 @@ fn main() {
3540
let b = 3;
3641
trip!(b * 8);
3742
}
43+
44+
struct W(u8);
45+
impl Clone for W {
46+
fn clone(&self) -> Self {
47+
W(1)
48+
}
49+
}
50+
51+
fn closure_method_call() {
52+
// Do not lint when the method call is applied to the block, both inside the closure
53+
let f = |x: W| { x }.clone();
54+
assert!(matches!(f(W(0)), W(1)));
55+
56+
let f = |x: W| -> _ { x }.clone();
57+
assert!(matches!(f(W(0)), W(0)));
58+
//~^^ precedence
59+
60+
let f = move |x: W| -> _ { x }.clone();
61+
assert!(matches!(f(W(0)), W(0)));
62+
//~^^ precedence
63+
}

tests/ui/precedence.stderr

+30-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: operator precedence might not be obvious
2-
--> tests/ui/precedence.rs:16:5
2+
--> tests/ui/precedence.rs:21:5
33
|
44
LL | 1 << 2 + 3;
55
| ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)`
@@ -8,40 +8,62 @@ LL | 1 << 2 + 3;
88
= help: to override `-D warnings` add `#[allow(clippy::precedence)]`
99

1010
error: operator precedence might not be obvious
11-
--> tests/ui/precedence.rs:18:5
11+
--> tests/ui/precedence.rs:23:5
1212
|
1313
LL | 1 + 2 << 3;
1414
| ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3`
1515

1616
error: operator precedence might not be obvious
17-
--> tests/ui/precedence.rs:20:5
17+
--> tests/ui/precedence.rs:25:5
1818
|
1919
LL | 4 >> 1 + 1;
2020
| ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)`
2121

2222
error: operator precedence might not be obvious
23-
--> tests/ui/precedence.rs:22:5
23+
--> tests/ui/precedence.rs:27:5
2424
|
2525
LL | 1 + 3 >> 2;
2626
| ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2`
2727

2828
error: operator precedence might not be obvious
29-
--> tests/ui/precedence.rs:24:5
29+
--> tests/ui/precedence.rs:29:5
3030
|
3131
LL | 1 ^ 1 - 1;
3232
| ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)`
3333

3434
error: operator precedence might not be obvious
35-
--> tests/ui/precedence.rs:26:5
35+
--> tests/ui/precedence.rs:31:5
3636
|
3737
LL | 3 | 2 - 1;
3838
| ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)`
3939

4040
error: operator precedence might not be obvious
41-
--> tests/ui/precedence.rs:28:5
41+
--> tests/ui/precedence.rs:33:5
4242
|
4343
LL | 3 & 5 - 2;
4444
| ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
4545

46-
error: aborting due to 7 previous errors
46+
error: precedence might not be obvious
47+
--> tests/ui/precedence.rs:56:13
48+
|
49+
LL | let f = |x: W| -> _ { x }.clone();
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
51+
|
52+
help: consider parenthesizing the closure
53+
|
54+
LL | let f = (|x: W| -> _ { x }).clone();
55+
| + +
56+
57+
error: precedence might not be obvious
58+
--> tests/ui/precedence.rs:60:13
59+
|
60+
LL | let f = move |x: W| -> _ { x }.clone();
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
|
63+
help: consider parenthesizing the closure
64+
|
65+
LL | let f = (move |x: W| -> _ { x }).clone();
66+
| + +
67+
68+
error: aborting due to 9 previous errors
4769

0 commit comments

Comments
 (0)