@@ -42,83 +42,43 @@ pub(crate) fn check<'tcx>(
4242
4343 match op {
4444 BinOpKind :: Add | BinOpKind :: BitOr | BinOpKind :: BitXor => {
45- let _ = check_op (
46- cx,
47- left,
48- 0 ,
49- expr. span ,
50- peeled_right_span,
51- needs_parenthesis ( cx, expr, right) ,
52- right_is_coerced_to_value,
53- ) || check_op (
54- cx,
55- right,
56- 0 ,
57- expr. span ,
58- peeled_left_span,
59- Parens :: Unneeded ,
60- left_is_coerced_to_value,
61- ) ;
45+ if is_redundant_op ( cx, left, 0 ) {
46+ let paren = needs_parenthesis ( cx, expr, right) ;
47+ span_ineffective_operation ( cx, expr. span , peeled_right_span, paren, right_is_coerced_to_value) ;
48+ } else if is_redundant_op ( cx, right, 0 ) {
49+ let paren = needs_parenthesis ( cx, expr, left) ;
50+ span_ineffective_operation ( cx, expr. span , peeled_left_span, paren, left_is_coerced_to_value) ;
51+ }
6252 } ,
6353 BinOpKind :: Shl | BinOpKind :: Shr | BinOpKind :: Sub => {
64- let _ = check_op (
65- cx,
66- right,
67- 0 ,
68- expr. span ,
69- peeled_left_span,
70- Parens :: Unneeded ,
71- left_is_coerced_to_value,
72- ) ;
54+ if is_redundant_op ( cx, right, 0 ) {
55+ let paren = needs_parenthesis ( cx, expr, left) ;
56+ span_ineffective_operation ( cx, expr. span , peeled_left_span, paren, left_is_coerced_to_value) ;
57+ }
7358 } ,
7459 BinOpKind :: Mul => {
75- let _ = check_op (
76- cx,
77- left,
78- 1 ,
79- expr. span ,
80- peeled_right_span,
81- needs_parenthesis ( cx, expr, right) ,
82- right_is_coerced_to_value,
83- ) || check_op (
84- cx,
85- right,
86- 1 ,
87- expr. span ,
88- peeled_left_span,
89- Parens :: Unneeded ,
90- left_is_coerced_to_value,
91- ) ;
60+ if is_redundant_op ( cx, left, 1 ) {
61+ let paren = needs_parenthesis ( cx, expr, right) ;
62+ span_ineffective_operation ( cx, expr. span , peeled_right_span, paren, right_is_coerced_to_value) ;
63+ } else if is_redundant_op ( cx, right, 1 ) {
64+ let paren = needs_parenthesis ( cx, expr, left) ;
65+ span_ineffective_operation ( cx, expr. span , peeled_left_span, paren, left_is_coerced_to_value) ;
66+ }
9267 } ,
9368 BinOpKind :: Div => {
94- let _ = check_op (
95- cx,
96- right,
97- 1 ,
98- expr. span ,
99- peeled_left_span,
100- Parens :: Unneeded ,
101- left_is_coerced_to_value,
102- ) ;
69+ if is_redundant_op ( cx, right, 1 ) {
70+ let paren = needs_parenthesis ( cx, expr, left) ;
71+ span_ineffective_operation ( cx, expr. span , peeled_left_span, paren, left_is_coerced_to_value) ;
72+ }
10373 } ,
10474 BinOpKind :: BitAnd => {
105- let _ = check_op (
106- cx,
107- left,
108- -1 ,
109- expr. span ,
110- peeled_right_span,
111- needs_parenthesis ( cx, expr, right) ,
112- right_is_coerced_to_value,
113- ) || check_op (
114- cx,
115- right,
116- -1 ,
117- expr. span ,
118- peeled_left_span,
119- Parens :: Unneeded ,
120- left_is_coerced_to_value,
121- ) ;
75+ if is_redundant_op ( cx, left, -1 ) {
76+ let paren = needs_parenthesis ( cx, expr, right) ;
77+ span_ineffective_operation ( cx, expr. span , peeled_right_span, paren, right_is_coerced_to_value) ;
78+ } else if is_redundant_op ( cx, right, -1 ) {
79+ let paren = needs_parenthesis ( cx, expr, left) ;
80+ span_ineffective_operation ( cx, expr. span , peeled_left_span, paren, left_is_coerced_to_value) ;
81+ }
12282 } ,
12383 BinOpKind :: Rem => check_remainder ( cx, left, right, expr. span , left. span ) ,
12484 _ => ( ) ,
@@ -138,43 +98,70 @@ enum Parens {
13898 Unneeded ,
13999}
140100
141- /// Checks if `left op right` needs parenthesis when reduced to `right`
101+ /// Checks if a binary expression needs parenthesis when reduced to just its
102+ /// right or left child.
103+ ///
104+ /// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently.
105+ /// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since
106+ /// the the cast expression will not apply to the same expression.
142107/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
143108/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
144- /// interpreted as a statement
109+ /// interpreted as a statement. The same behavior happens for `match`, `loop`,
110+ /// and blocks.
111+ /// e.g. `2 * (0 + { a })` can be reduced to `2 * { a }` without the need for parenthesis,
112+ /// but `1 * ({ a } + 4)` cannot be reduced to `{ a } + 4`, as a block at the start of a line
113+ /// will be interpreted as a statement instead of an expression.
145114///
146- /// See #8724
147- fn needs_parenthesis ( cx : & LateContext < ' _ > , binary : & Expr < ' _ > , right : & Expr < ' _ > ) -> Parens {
148- match right . kind {
115+ /// See #8724, #13470
116+ fn needs_parenthesis ( cx : & LateContext < ' _ > , binary : & Expr < ' _ > , child : & Expr < ' _ > ) -> Parens {
117+ match child . kind {
149118 ExprKind :: Binary ( _, lhs, _) | ExprKind :: Cast ( lhs, _) => {
150- // ensure we're checking against the leftmost expression of `right`
151- //
152- // ~~~ `lhs`
153- // 0 + {4} * 2
154- // ~~~~~~~ `right`
155- return needs_parenthesis ( cx, binary, lhs) ;
119+ // For casts and binary expressions, we want to add parenthesis if
120+ // the parent HIR node is an expression, or if the parent HIR node
121+ // is a Block or Stmt, and the new left hand side would need
122+ // parenthesis be treated as a statement rather than an expression.
123+ if let Some ( ( _, parent) ) = cx. tcx . hir ( ) . parent_iter ( binary. hir_id ) . next ( ) {
124+ match parent {
125+ Node :: Expr ( _) => return Parens :: Needed ,
126+ Node :: Block ( _) | Node :: Stmt ( _) => {
127+ // ensure we're checking against the leftmost expression of `child`
128+ //
129+ // ~~~~~~~~~~~ `binary`
130+ // ~~~ `lhs`
131+ // 0 + {4} * 2
132+ // ~~~~~~~ `child`
133+ return needs_parenthesis ( cx, binary, lhs) ;
134+ } ,
135+ _ => return Parens :: Unneeded ,
136+ }
137+ }
138+ } ,
139+ ExprKind :: If ( ..) | ExprKind :: Match ( ..) | ExprKind :: Block ( ..) | ExprKind :: Loop ( ..) => {
140+ // For if, match, block, and loop expressions, we want to add parenthesis if
141+ // the closest ancestor node that is not an expression is a block or statement.
142+ // This would mean that the rustfix suggestion will appear at the start of a line, which causes
143+ // these expressions to be interpreted as statements if they do not have parenthesis.
144+ let mut prev_id = binary. hir_id ;
145+ for ( _, parent) in cx. tcx . hir ( ) . parent_iter ( binary. hir_id ) {
146+ if let Node :: Expr ( expr) = parent
147+ && let ExprKind :: Binary ( _, lhs, _) | ExprKind :: Cast ( lhs, _) | ExprKind :: Unary ( _, lhs) = expr. kind
148+ && lhs. hir_id == prev_id
149+ {
150+ // keep going until we find a node that encompasses left of `binary`
151+ prev_id = expr. hir_id ;
152+ continue ;
153+ }
154+
155+ match parent {
156+ Node :: Block ( _) | Node :: Stmt ( _) => return Parens :: Needed ,
157+ _ => return Parens :: Unneeded ,
158+ } ;
159+ }
160+ } ,
161+ _ => {
162+ return Parens :: Unneeded ;
156163 } ,
157- ExprKind :: If ( ..) | ExprKind :: Match ( ..) | ExprKind :: Block ( ..) | ExprKind :: Loop ( ..) => { } ,
158- _ => return Parens :: Unneeded ,
159- }
160-
161- let mut prev_id = binary. hir_id ;
162- for ( _, node) in cx. tcx . hir ( ) . parent_iter ( binary. hir_id ) {
163- if let Node :: Expr ( expr) = node
164- && let ExprKind :: Binary ( _, lhs, _) | ExprKind :: Cast ( lhs, _) = expr. kind
165- && lhs. hir_id == prev_id
166- {
167- // keep going until we find a node that encompasses left of `binary`
168- prev_id = expr. hir_id ;
169- continue ;
170- }
171-
172- match node {
173- Node :: Block ( _) | Node :: Stmt ( _) => break ,
174- _ => return Parens :: Unneeded ,
175- } ;
176164 }
177-
178165 Parens :: Needed
179166}
180167
@@ -199,7 +186,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
199186 }
200187}
201188
202- fn check_op ( cx : & LateContext < ' _ > , e : & Expr < ' _ > , m : i8 , span : Span , arg : Span , parens : Parens , is_erased : bool ) -> bool {
189+ fn is_redundant_op ( cx : & LateContext < ' _ > , e : & Expr < ' _ > , m : i8 ) -> bool {
203190 if let Some ( Constant :: Int ( v) ) = ConstEvalCtxt :: new ( cx) . eval_simple ( e) . map ( Constant :: peel_refs) {
204191 let check = match * cx. typeck_results ( ) . expr_ty ( e) . peel_refs ( ) . kind ( ) {
205192 ty:: Int ( ity) => unsext ( cx. tcx , -1_i128 , ity) ,
@@ -212,7 +199,6 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
212199 1 => v == 1 ,
213200 _ => unreachable ! ( ) ,
214201 } {
215- span_ineffective_operation ( cx, span, arg, parens, is_erased) ;
216202 return true ;
217203 }
218204 }
0 commit comments