Skip to content

Commit cd679d6

Browse files
authored
FIX: NegMultiply should preserve parenthesis when method is called (#15179)
Hi, I noticed that the lint [neg_multiply](https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply) generates bad code when we call a method on the expression. Consider `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)`. Currently this would be changed by clippy to `-(a.delta - 0.5).abs() .total_cmp(&1.0)` - which does not compile because we are trying to negate an ordering enum - but what we really want is `(-(a.delta - 0.5).abs()).total_cmp(&1.0)`. This PR fixes this. changelog: [`neg_multiply`] does not remove parenthesis anymore if a method is being called on the affected expression NOTE: This is the first time I am contributing to clippy or the rust repo in general. So I am not sure whether my approach to fixing this issue is goo, if there are better solutions or if I missed something. Thanks & hope you have a good day, Dario
2 parents 2713c50 + e7fa536 commit cd679d6

File tree

4 files changed

+63
-9
lines changed

4 files changed

+63
-9
lines changed

clippy_lints/src/neg_multiply.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use clippy_utils::consts::{self, Constant};
22
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};
45
use clippy_utils::sugg::has_enclosing_paren;
56
use rustc_ast::util::parser::ExprPrecedence;
67
use rustc_errors::Applicability;
78
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
89
use rustc_lint::{LateContext, LateLintPass};
910
use rustc_session::declare_lint_pass;
10-
use rustc_span::Span;
1111

1212
declare_clippy_lint! {
1313
/// ### What it does
@@ -33,22 +33,35 @@ declare_clippy_lint! {
3333

3434
declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
3535

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+
3649
impl<'tcx> LateLintPass<'tcx> for NegMultiply {
3750
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
3851
if let ExprKind::Binary(ref op, left, right) = e.kind
3952
&& BinOpKind::Mul == op.node
4053
{
4154
match (&left.kind, &right.kind) {
4255
(&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),
4558
_ => {},
4659
}
4760
}
4861
}
4962
}
5063

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<'_>) {
5265
const F16_ONE: u16 = 1.0_f16.to_bits();
5366
const F128_ONE: u128 = 1.0_f128.to_bits();
5467
if let ExprKind::Lit(l) = lit.kind
@@ -63,16 +76,27 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
6376
&& cx.typeck_results().expr_ty(exp).is_numeric()
6477
{
6578
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) {
6892
format!("-({snip})")
6993
} else {
7094
format!("-{snip}")
7195
};
7296
span_lint_and_sugg(
7397
cx,
7498
NEG_MULTIPLY,
75-
span,
99+
mul_expr.span,
76100
"this multiplication by -1 can be written more succinctly",
77101
"consider using",
78102
suggestion,

tests/ui/neg_multiply.fixed

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,15 @@ fn float() {
8282

8383
-1.0 * -1.0; // should be ok
8484
}
85+
86+
struct Y {
87+
delta: f64,
88+
}
89+
90+
fn nested() {
91+
let a = Y { delta: 1.0 };
92+
let b = Y { delta: 1.0 };
93+
let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0);
94+
//~^ neg_multiply
95+
let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0);
96+
}

tests/ui/neg_multiply.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,15 @@ fn float() {
8282

8383
-1.0 * -1.0; // should be ok
8484
}
85+
86+
struct Y {
87+
delta: f64,
88+
}
89+
90+
fn nested() {
91+
let a = Y { delta: 1.0 };
92+
let b = Y { delta: 1.0 };
93+
let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0);
94+
//~^ neg_multiply
95+
let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0);
96+
}

tests/ui/neg_multiply.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,11 @@ error: this multiplication by -1 can be written more succinctly
9797
LL | (3.0_f32 as f64) * -1.0;
9898
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)`
9999

100-
error: aborting due to 16 previous errors
100+
error: this multiplication by -1 can be written more succinctly
101+
--> tests/ui/neg_multiply.rs:93:13
102+
|
103+
LL | let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0);
104+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-(a.delta - 0.5).abs())`
105+
106+
error: aborting due to 17 previous errors
101107

0 commit comments

Comments
 (0)