@@ -42,83 +42,43 @@ pub(crate) fn check<'tcx>(
42
42
43
43
match op {
44
44
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
+ }
62
52
} ,
63
53
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
+ }
73
58
} ,
74
59
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
+ }
92
67
} ,
93
68
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
+ }
103
73
} ,
104
74
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
+ }
122
82
} ,
123
83
BinOpKind :: Rem => check_remainder ( cx, left, right, expr. span , left. span ) ,
124
84
_ => ( ) ,
@@ -138,43 +98,70 @@ enum Parens {
138
98
Unneeded ,
139
99
}
140
100
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.
142
107
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
143
108
/// 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.
145
114
///
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 {
149
118
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 ;
156
163
} ,
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
- } ;
176
164
}
177
-
178
165
Parens :: Needed
179
166
}
180
167
@@ -199,7 +186,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
199
186
}
200
187
}
201
188
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 {
203
190
if let Some ( Constant :: Int ( v) ) = ConstEvalCtxt :: new ( cx) . eval_simple ( e) . map ( Constant :: peel_refs) {
204
191
let check = match * cx. typeck_results ( ) . expr_ty ( e) . peel_refs ( ) . kind ( ) {
205
192
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
212
199
1 => v == 1 ,
213
200
_ => unreachable ! ( ) ,
214
201
} {
215
- span_ineffective_operation ( cx, span, arg, parens, is_erased) ;
216
202
return true ;
217
203
}
218
204
}
0 commit comments