Skip to content

Commit 9b2011e

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 737f0a6 commit 9b2011e

File tree

7 files changed

+129
-2
lines changed

7 files changed

+129
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ Released 2018-09-13
11901190
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
11911191
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
11921192
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
1193+
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
11931194
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
11941195
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
11951196
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments

README.md

Lines changed: 1 addition & 1 deletion
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 318 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 319 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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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,49 @@ 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 let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind {
163+
if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() {
164+
// span between BinOp LHS and RHS
165+
let binop_span = lhs.span.between(rhs.span);
166+
// if RHS is a UnOp
167+
if let ExprKind::Unary(op, ref un_rhs) = rhs.kind {
168+
// from UnOp operator to UnOp operand
169+
let unop_operand_span = rhs.span.until(un_rhs.span);
170+
if let (Some(binop_snippet), Some(unop_operand_snippet)) =
171+
(snippet_opt(cx, binop_span), snippet_opt(cx, unop_operand_span))
172+
{
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+
let unop_str = UnOp::to_string(op);
177+
let eqop_span = lhs.span.between(un_rhs.span);
178+
span_note_and_lint(
179+
cx,
180+
SUSPICIOUS_UNARY_OP_FORMATTING,
181+
eqop_span,
182+
&format!(
183+
"by not having a space between `{binop}` and `{unop}` it looks like \
184+
`{binop}{unop}` is a single operator",
185+
binop = binop_str,
186+
unop = unop_str
187+
),
188+
eqop_span,
189+
&format!(
190+
"to remove this lint, put a space between `{binop}` and `{unop}` \
191+
or remove the space after `{binop}`",
192+
binop = binop_str,
193+
unop = unop_str
194+
),
195+
);
196+
}
197+
}
198+
}
199+
}
200+
}
201+
}
202+
136203
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
137204
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
138205
if_chain! {

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
739739
formatting::POSSIBLE_MISSING_COMMA,
740740
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
741741
formatting::SUSPICIOUS_ELSE_FORMATTING,
742+
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
742743
functions::NOT_UNSAFE_PTR_ARG_DEREF,
743744
functions::TOO_MANY_ARGUMENTS,
744745
get_last_with_len::GET_LAST_WITH_LEN,
@@ -946,6 +947,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
946947
excessive_precision::EXCESSIVE_PRECISION,
947948
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
948949
formatting::SUSPICIOUS_ELSE_FORMATTING,
950+
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
949951
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
950952
inherent_to_string::INHERENT_TO_STRING,
951953
len_zero::LEN_WITHOUT_IS_EMPTY,

src/lintlist/mod.rs

Lines changed: 8 additions & 1 deletion
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; 318] = [
9+
pub const ALL_LINTS: [Lint; 319] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1792,6 +1792,13 @@ pub const ALL_LINTS: [Lint; 318] = [
17921792
deprecation: None,
17931793
module: "suspicious_trait_impl",
17941794
},
1795+
Lint {
1796+
name: "suspicious_unary_op_formatting",
1797+
group: "style",
1798+
desc: "suspicious formatting of unary `-` or `!` on the RHS of a BinOp",
1799+
deprecation: None,
1800+
module: "formatting",
1801+
},
17951802
Lint {
17961803
name: "temporary_assignment",
17971804
group: "complexity",
Lines changed: 23 additions & 0 deletions
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+
} else if a >=- 30 {
10+
}
11+
12+
let b = true;
13+
let c = false;
14+
15+
if b &&! c {
16+
}
17+
18+
// those are ok:
19+
if a >-30 {
20+
} else if a < -30 {
21+
} else if b && !c {
22+
}
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
= note: to remove this lint, put a space between `>` and `-` or 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:16
12+
|
13+
LL | } else if a >=- 30 {
14+
| ^^^^^
15+
|
16+
= note: to remove this lint, put a space between `>=` and `-` or 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:15:9
20+
|
21+
LL | if b &&! c {
22+
| ^^^^^
23+
|
24+
= note: to remove this lint, put a space between `&&` and `!` or remove the space after `&&`
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)