@@ -22,6 +22,28 @@ declare_clippy_lint! {
22
22
"suspicious formatting of `*=`, `-=` or `!=`"
23
23
}
24
24
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
+
25
47
declare_clippy_lint ! {
26
48
/// **What it does:** Checks for formatting of `else`. It lints if the `else`
27
49
/// is followed immediately by a newline or the `else` seems to be missing.
@@ -80,6 +102,7 @@ declare_clippy_lint! {
80
102
81
103
declare_lint_pass ! ( Formatting => [
82
104
SUSPICIOUS_ASSIGNMENT_FORMATTING ,
105
+ SUSPICIOUS_UNARY_OP_FORMATTING ,
83
106
SUSPICIOUS_ELSE_FORMATTING ,
84
107
POSSIBLE_MISSING_COMMA
85
108
] ) ;
@@ -99,6 +122,7 @@ impl EarlyLintPass for Formatting {
99
122
100
123
fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , expr : & Expr ) {
101
124
check_assign ( cx, expr) ;
125
+ check_unop ( cx, expr) ;
102
126
check_else ( cx, expr) ;
103
127
check_array ( cx, expr) ;
104
128
}
@@ -133,6 +157,49 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
133
157
}
134
158
}
135
159
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
+
136
203
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
137
204
fn check_else ( cx : & EarlyContext < ' _ > , expr : & Expr ) {
138
205
if_chain ! {
0 commit comments