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} ;
2
2
use if_chain:: if_chain;
3
3
use rustc:: lint:: { in_external_macro, EarlyContext , EarlyLintPass , LintArray , LintPass } ;
4
4
use rustc:: { declare_lint_pass, declare_tool_lint} ;
@@ -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,45 @@ 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_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
+
136
199
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
137
200
fn check_else ( cx : & EarlyContext < ' _ > , expr : & Expr ) {
138
201
if_chain ! {
0 commit comments