1
1
use clippy_utils:: consts:: { self , Constant } ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
- use clippy_utils:: source:: snippet_with_context;
3
+ use clippy_utils:: get_parent_expr;
4
+ use clippy_utils:: source:: { snippet, snippet_with_context} ;
4
5
use clippy_utils:: sugg:: has_enclosing_paren;
5
6
use rustc_ast:: util:: parser:: ExprPrecedence ;
6
7
use rustc_errors:: Applicability ;
7
8
use rustc_hir:: { BinOpKind , Expr , ExprKind , UnOp } ;
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
9
10
use rustc_session:: declare_lint_pass;
10
- use rustc_span:: Span ;
11
11
12
12
declare_clippy_lint ! {
13
13
/// ### What it does
@@ -33,22 +33,35 @@ declare_clippy_lint! {
33
33
34
34
declare_lint_pass ! ( NegMultiply => [ NEG_MULTIPLY ] ) ;
35
35
36
+ fn is_in_parens_with_postfix ( cx : & LateContext < ' _ > , mul_expr : & Expr < ' _ > ) -> bool {
37
+ if let Some ( parent) = get_parent_expr ( cx, mul_expr) {
38
+ let mult_snippet = snippet ( cx, mul_expr. span , "" ) ;
39
+ if has_enclosing_paren ( & mult_snippet)
40
+ && let ExprKind :: MethodCall ( _, _, _, _) = parent. kind
41
+ {
42
+ return true ;
43
+ }
44
+ }
45
+
46
+ false
47
+ }
48
+
36
49
impl < ' tcx > LateLintPass < ' tcx > for NegMultiply {
37
50
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) {
38
51
if let ExprKind :: Binary ( ref op, left, right) = e. kind
39
52
&& BinOpKind :: Mul == op. node
40
53
{
41
54
match ( & left. kind , & right. kind ) {
42
55
( & ExprKind :: Unary ( ..) , & ExprKind :: Unary ( ..) ) => { } ,
43
- ( & ExprKind :: Unary ( UnOp :: Neg , lit) , _) => check_mul ( cx, e. span , lit, right) ,
44
- ( _, & ExprKind :: Unary ( UnOp :: Neg , lit) ) => check_mul ( cx, e. span , lit, left) ,
56
+ ( & ExprKind :: Unary ( UnOp :: Neg , lit) , _) => check_mul ( cx, e, lit, right) ,
57
+ ( _, & ExprKind :: Unary ( UnOp :: Neg , lit) ) => check_mul ( cx, e, lit, left) ,
45
58
_ => { } ,
46
59
}
47
60
}
48
61
}
49
62
}
50
63
51
- fn check_mul ( cx : & LateContext < ' _ > , span : Span , lit : & Expr < ' _ > , exp : & Expr < ' _ > ) {
64
+ fn check_mul ( cx : & LateContext < ' _ > , mul_expr : & Expr < ' _ > , lit : & Expr < ' _ > , exp : & Expr < ' _ > ) {
52
65
const F16_ONE : u16 = 1.0_f16 . to_bits ( ) ;
53
66
const F128_ONE : u128 = 1.0_f128 . to_bits ( ) ;
54
67
if let ExprKind :: Lit ( l) = lit. kind
@@ -63,16 +76,27 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
63
76
&& cx. typeck_results ( ) . expr_ty ( exp) . is_numeric ( )
64
77
{
65
78
let mut applicability = Applicability :: MachineApplicable ;
66
- let ( snip, from_macro) = snippet_with_context ( cx, exp. span , span. ctxt ( ) , ".." , & mut applicability) ;
67
- let suggestion = if !from_macro && cx. precedence ( exp) < ExprPrecedence :: Prefix && !has_enclosing_paren ( & snip) {
79
+ let ( snip, from_macro) = snippet_with_context ( cx, exp. span , mul_expr. span . ctxt ( ) , ".." , & mut applicability) ;
80
+
81
+ let needs_parens_for_postfix = is_in_parens_with_postfix ( cx, mul_expr) ;
82
+
83
+ let suggestion = if needs_parens_for_postfix {
84
+ // Special case: when the multiplication is in parentheses followed by a method call
85
+ // we need to preserve the grouping but negate the inner expression.
86
+ // Consider this expression: `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)`
87
+ // We need to end up with: `(-(a.delta - 0.5).abs()).total_cmp(&1.0)`
88
+ // Otherwise, without the parentheses we would try to negate an Ordering:
89
+ // `-(a.delta - 0.5).abs().total_cmp(&1.0)`
90
+ format ! ( "(-{snip})" )
91
+ } else if !from_macro && cx. precedence ( exp) < ExprPrecedence :: Prefix && !has_enclosing_paren ( & snip) {
68
92
format ! ( "-({snip})" )
69
93
} else {
70
94
format ! ( "-{snip}" )
71
95
} ;
72
96
span_lint_and_sugg (
73
97
cx,
74
98
NEG_MULTIPLY ,
75
- span,
99
+ mul_expr . span ,
76
100
"this multiplication by -1 can be written more succinctly" ,
77
101
"consider using" ,
78
102
suggestion,
0 commit comments