Skip to content

Commit 63ffbc4

Browse files
committed
New lint: suspicious_unary_op_formatting
Lints when, on the RHS of a BinOp, there is a UnOp without a space before the operator but with a space after (e.g. foo >- 1). Signed-off-by: Nikos Filippakis <nikolaos.filippakis@cern.ch>
1 parent 1dc7f5c commit 63ffbc4

File tree

7 files changed

+134
-3
lines changed

7 files changed

+134
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,7 @@ Released 2018-09-13
11911191
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
11921192
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
11931193
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
1194+
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
11941195
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
11951196
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
11961197
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 320 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 321 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/formatting.rs

+64-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::utils::{differing_macro_contexts, snippet_opt, span_note_and_lint};
1+
use crate::utils::{differing_macro_contexts, snippet_opt, span_help_and_lint, span_note_and_lint};
22
use if_chain::if_chain;
33
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintPass};
44
use rustc::{declare_lint_pass, declare_tool_lint};
@@ -22,6 +22,28 @@ declare_clippy_lint! {
2222
"suspicious formatting of `*=`, `-=` or `!=`"
2323
}
2424

25+
declare_clippy_lint! {
26+
/// **What it does:** Checks the formatting of a unary operator on the right hand side
27+
/// of a binary operator. It lints if there is no space between the binary and unary operators,
28+
/// but there is a space between the unary and its operand.
29+
///
30+
/// **Why is this bad?** This is either a typo in the binary operator or confusing.
31+
///
32+
/// **Known problems:** None.
33+
///
34+
/// **Example:**
35+
/// ```rust,ignore
36+
/// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
37+
/// }
38+
///
39+
/// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
40+
/// }
41+
/// ```
42+
pub SUSPICIOUS_UNARY_OP_FORMATTING,
43+
style,
44+
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
45+
}
46+
2547
declare_clippy_lint! {
2648
/// **What it does:** Checks for formatting of `else`. It lints if the `else`
2749
/// is followed immediately by a newline or the `else` seems to be missing.
@@ -80,6 +102,7 @@ declare_clippy_lint! {
80102

81103
declare_lint_pass!(Formatting => [
82104
SUSPICIOUS_ASSIGNMENT_FORMATTING,
105+
SUSPICIOUS_UNARY_OP_FORMATTING,
83106
SUSPICIOUS_ELSE_FORMATTING,
84107
POSSIBLE_MISSING_COMMA
85108
]);
@@ -99,6 +122,7 @@ impl EarlyLintPass for Formatting {
99122

100123
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
101124
check_assign(cx, expr);
125+
check_unop(cx, expr);
102126
check_else(cx, expr);
103127
check_array(cx, expr);
104128
}
@@ -133,6 +157,45 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
133157
}
134158
}
135159

160+
/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
161+
fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
162+
if_chain! {
163+
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
164+
if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
165+
// span between BinOp LHS and RHS
166+
let binop_span = lhs.span.between(rhs.span);
167+
// if RHS is a UnOp
168+
if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
169+
// from UnOp operator to UnOp operand
170+
let unop_operand_span = rhs.span.until(un_rhs.span);
171+
if let Some(binop_snippet) = snippet_opt(cx, binop_span);
172+
if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
173+
let binop_str = BinOpKind::to_string(&binop.node);
174+
// no space after BinOp operator and space after UnOp operator
175+
if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
176+
then {
177+
let unop_str = UnOp::to_string(op);
178+
let eqop_span = lhs.span.between(un_rhs.span);
179+
span_help_and_lint(
180+
cx,
181+
SUSPICIOUS_UNARY_OP_FORMATTING,
182+
eqop_span,
183+
&format!(
184+
"by not having a space between `{binop}` and `{unop}` it looks like \
185+
`{binop}{unop}` is a single operator",
186+
binop = binop_str,
187+
unop = unop_str
188+
),
189+
&format!(
190+
"put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
191+
binop = binop_str,
192+
unop = unop_str
193+
),
194+
);
195+
}
196+
}
197+
}
198+
136199
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
137200
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
138201
if_chain! {

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
743743
formatting::POSSIBLE_MISSING_COMMA,
744744
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
745745
formatting::SUSPICIOUS_ELSE_FORMATTING,
746+
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
746747
functions::NOT_UNSAFE_PTR_ARG_DEREF,
747748
functions::TOO_MANY_ARGUMENTS,
748749
get_last_with_len::GET_LAST_WITH_LEN,
@@ -953,6 +954,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
953954
excessive_precision::EXCESSIVE_PRECISION,
954955
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
955956
formatting::SUSPICIOUS_ELSE_FORMATTING,
957+
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
956958
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
957959
inherent_to_string::INHERENT_TO_STRING,
958960
len_zero::LEN_WITHOUT_IS_EMPTY,

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 320] = [
9+
pub const ALL_LINTS: [Lint; 321] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1799,6 +1799,13 @@ pub const ALL_LINTS: [Lint; 320] = [
17991799
deprecation: None,
18001800
module: "suspicious_trait_impl",
18011801
},
1802+
Lint {
1803+
name: "suspicious_unary_op_formatting",
1804+
group: "style",
1805+
desc: "suspicious formatting of unary `-` or `!` on the RHS of a BinOp",
1806+
deprecation: None,
1807+
module: "formatting",
1808+
},
18021809
Lint {
18031810
name: "temporary_assignment",
18041811
group: "complexity",
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![warn(clippy::suspicious_unary_op_formatting)]
2+
3+
#[rustfmt::skip]
4+
fn main() {
5+
// weird binary operator formatting:
6+
let a = 42;
7+
8+
if a >- 30 {}
9+
if a >=- 30 {}
10+
11+
let b = true;
12+
let c = false;
13+
14+
if b &&! c {}
15+
16+
if a >- 30 {}
17+
18+
// those are ok:
19+
if a >-30 {}
20+
if a < -30 {}
21+
if b && !c {}
22+
if a > - 30 {}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: by not having a space between `>` and `-` it looks like `>-` is a single operator
2+
--> $DIR/suspicious_unary_op_formatting.rs:8:9
3+
|
4+
LL | if a >- 30 {}
5+
| ^^^^
6+
|
7+
= note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings`
8+
= help: put a space between `>` and `-` and remove the space after `-`
9+
10+
error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator
11+
--> $DIR/suspicious_unary_op_formatting.rs:9:9
12+
|
13+
LL | if a >=- 30 {}
14+
| ^^^^^
15+
|
16+
= help: put a space between `>=` and `-` and remove the space after `-`
17+
18+
error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator
19+
--> $DIR/suspicious_unary_op_formatting.rs:14:9
20+
|
21+
LL | if b &&! c {}
22+
| ^^^^^
23+
|
24+
= help: put a space between `&&` and `!` and remove the space after `!`
25+
26+
error: by not having a space between `>` and `-` it looks like `>-` is a single operator
27+
--> $DIR/suspicious_unary_op_formatting.rs:16:9
28+
|
29+
LL | if a >- 30 {}
30+
| ^^^^^^
31+
|
32+
= help: put a space between `>` and `-` and remove the space after `-`
33+
34+
error: aborting due to 4 previous errors
35+

0 commit comments

Comments
 (0)