11use clippy_utils:: consts:: { self , Constant } ;
22use 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} ;
45use clippy_utils:: sugg:: has_enclosing_paren;
56use rustc_ast:: util:: parser:: ExprPrecedence ;
67use rustc_errors:: Applicability ;
78use rustc_hir:: { BinOpKind , Expr , ExprKind , UnOp } ;
89use rustc_lint:: { LateContext , LateLintPass } ;
910use rustc_session:: declare_lint_pass;
10- use rustc_span:: Span ;
1111
1212declare_clippy_lint ! {
1313 /// ### What it does
@@ -33,22 +33,35 @@ declare_clippy_lint! {
3333
3434declare_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+
3649impl < ' 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,
0 commit comments