]>
Commit | Line | Data |
---|---|---|
c620b35d FG |
1 | use std::convert::Infallible; |
2 | use std::ops::ControlFlow; | |
3 | ||
17df50a5 | 4 | use clippy_utils::consts::{constant, Constant}; |
cdc7bbd5 | 5 | use clippy_utils::diagnostics::span_lint; |
c620b35d FG |
6 | use clippy_utils::visitors::{for_each_expr, Descend}; |
7 | use clippy_utils::{method_chain_args, sext}; | |
c0240ec0 | 8 | use rustc_hir::{BinOpKind, Expr, ExprKind}; |
f20569fa | 9 | use rustc_lint::LateContext; |
c620b35d | 10 | use rustc_middle::ty::{self, Ty}; |
f20569fa | 11 | |
f20569fa XL |
12 | use super::CAST_SIGN_LOSS; |
13 | ||
c620b35d FG |
14 | /// A list of methods that can never return a negative value. |
15 | /// Includes methods that panic rather than returning a negative value. | |
16 | /// | |
17 | /// Methods that can overflow and return a negative value must not be included in this list, | |
18 | /// because casting their return values can still result in sign loss. | |
19 | const METHODS_RET_POSITIVE: &[&str] = &[ | |
20 | "checked_abs", | |
21 | "saturating_abs", | |
22 | "isqrt", | |
23 | "checked_isqrt", | |
24 | "rem_euclid", | |
25 | "checked_rem_euclid", | |
26 | "wrapping_rem_euclid", | |
27 | ]; | |
28 | ||
29 | /// A list of methods that act like `pow()`. See `pow_call_result_sign()` for details. | |
30 | /// | |
31 | /// Methods that can overflow and return a negative value must not be included in this list, | |
32 | /// because casting their return values can still result in sign loss. | |
33 | const METHODS_POW: &[&str] = &["pow", "saturating_pow", "checked_pow"]; | |
c0240ec0 | 34 | |
c620b35d FG |
35 | /// A list of methods that act like `unwrap()`, and don't change the sign of the inner value. |
36 | const METHODS_UNWRAP: &[&str] = &["unwrap", "unwrap_unchecked", "expect", "into_ok"]; | |
37 | ||
38 | pub(super) fn check<'cx>( | |
39 | cx: &LateContext<'cx>, | |
40 | expr: &Expr<'_>, | |
41 | cast_op: &Expr<'_>, | |
42 | cast_from: Ty<'cx>, | |
43 | cast_to: Ty<'_>, | |
44 | ) { | |
f20569fa XL |
45 | if should_lint(cx, cast_op, cast_from, cast_to) { |
46 | span_lint( | |
47 | cx, | |
48 | CAST_SIGN_LOSS, | |
49 | expr.span, | |
e8be2606 | 50 | format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), |
f20569fa XL |
51 | ); |
52 | } | |
53 | } | |
54 | ||
c620b35d | 55 | fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx>, cast_to: Ty<'_>) -> bool { |
f20569fa XL |
56 | match (cast_from.is_integral(), cast_to.is_integral()) { |
57 | (true, true) => { | |
58 | if !cast_from.is_signed() || cast_to.is_signed() { | |
59 | return false; | |
60 | } | |
61 | ||
c620b35d | 62 | // Don't lint if `cast_op` is known to be positive, ignoring overflow. |
c0240ec0 | 63 | if let Sign::ZeroOrPositive = expr_sign(cx, cast_op, cast_from) { |
4b012472 | 64 | return false; |
f20569fa XL |
65 | } |
66 | ||
c620b35d FG |
67 | if let Sign::ZeroOrPositive = expr_muldiv_sign(cx, cast_op) { |
68 | return false; | |
69 | } | |
70 | ||
71 | if let Sign::ZeroOrPositive = expr_add_sign(cx, cast_op) { | |
72 | return false; | |
f20569fa XL |
73 | } |
74 | ||
c620b35d | 75 | true |
f20569fa XL |
76 | }, |
77 | ||
78 | (false, true) => !cast_to.is_signed(), | |
79 | ||
80 | (_, _) => false, | |
81 | } | |
82 | } | |
c0240ec0 | 83 | |
c620b35d FG |
84 | fn get_const_signed_int_eval<'cx>( |
85 | cx: &LateContext<'cx>, | |
86 | expr: &Expr<'_>, | |
87 | ty: impl Into<Option<Ty<'cx>>>, | |
88 | ) -> Option<i128> { | |
89 | let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); | |
90 | ||
c0240ec0 FG |
91 | if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? |
92 | && let ty::Int(ity) = *ty.kind() | |
93 | { | |
94 | return Some(sext(cx.tcx, n, ity)); | |
95 | } | |
96 | None | |
97 | } | |
98 | ||
c620b35d FG |
99 | fn get_const_unsigned_int_eval<'cx>( |
100 | cx: &LateContext<'cx>, | |
101 | expr: &Expr<'_>, | |
102 | ty: impl Into<Option<Ty<'cx>>>, | |
103 | ) -> Option<u128> { | |
104 | let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); | |
105 | ||
106 | if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? | |
107 | && let ty::Uint(_ity) = *ty.kind() | |
108 | { | |
109 | return Some(n); | |
110 | } | |
111 | None | |
112 | } | |
113 | ||
114 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
c0240ec0 FG |
115 | enum Sign { |
116 | ZeroOrPositive, | |
117 | Negative, | |
118 | Uncertain, | |
119 | } | |
120 | ||
c620b35d | 121 | fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: impl Into<Option<Ty<'cx>>>) -> Sign { |
c0240ec0 | 122 | // Try evaluate this expr first to see if it's positive |
c620b35d | 123 | if let Some(val) = get_const_signed_int_eval(cx, expr, ty) { |
c0240ec0 FG |
124 | return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative }; |
125 | } | |
c620b35d FG |
126 | if let Some(_val) = get_const_unsigned_int_eval(cx, expr, None) { |
127 | return Sign::ZeroOrPositive; | |
128 | } | |
129 | ||
c0240ec0 FG |
130 | // Calling on methods that always return non-negative values. |
131 | if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind { | |
132 | let mut method_name = path.ident.name.as_str(); | |
133 | ||
c620b35d FG |
134 | // Peel unwrap(), expect(), etc. |
135 | while let Some(&found_name) = METHODS_UNWRAP.iter().find(|&name| &method_name == name) | |
136 | && let Some(arglist) = method_chain_args(expr, &[found_name]) | |
137 | && let ExprKind::MethodCall(inner_path, recv, ..) = &arglist[0].0.kind | |
c0240ec0 | 138 | { |
c620b35d FG |
139 | // The original type has changed, but we can't use `ty` here anyway, because it has been |
140 | // moved. | |
c0240ec0 | 141 | method_name = inner_path.ident.name.as_str(); |
c620b35d | 142 | expr = recv; |
c0240ec0 FG |
143 | } |
144 | ||
c620b35d | 145 | if METHODS_POW.iter().any(|&name| method_name == name) |
c0240ec0 FG |
146 | && let [arg] = args |
147 | { | |
148 | return pow_call_result_sign(cx, caller, arg); | |
149 | } else if METHODS_RET_POSITIVE.iter().any(|&name| method_name == name) { | |
150 | return Sign::ZeroOrPositive; | |
151 | } | |
152 | } | |
153 | ||
154 | Sign::Uncertain | |
155 | } | |
156 | ||
c620b35d | 157 | /// Return the sign of the `pow` call's result, ignoring overflow. |
c0240ec0 | 158 | /// |
c620b35d FG |
159 | /// If the base is positive, the result is always positive. |
160 | /// If the exponent is a even number, the result is always positive, | |
161 | /// Otherwise, if the base is negative, and the exponent is an odd number, the result is always | |
162 | /// negative. | |
163 | /// | |
164 | /// Otherwise, returns [`Sign::Uncertain`]. | |
165 | fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'_>) -> Sign { | |
166 | let base_sign = expr_sign(cx, base, None); | |
167 | ||
168 | // Rust's integer pow() functions take an unsigned exponent. | |
169 | let exponent_val = get_const_unsigned_int_eval(cx, exponent, None); | |
170 | let exponent_is_even = exponent_val.map(|val| val % 2 == 0); | |
171 | ||
172 | match (base_sign, exponent_is_even) { | |
173 | // Non-negative bases always return non-negative results, ignoring overflow. | |
174 | (Sign::ZeroOrPositive, _) | | |
175 | // Any base raised to an even exponent is non-negative. | |
176 | // These both hold even if we don't know the value of the base. | |
177 | (_, Some(true)) | |
178 | => Sign::ZeroOrPositive, | |
179 | ||
180 | // A negative base raised to an odd exponent is non-negative. | |
181 | (Sign::Negative, Some(false)) => Sign::Negative, | |
182 | ||
183 | // Negative/unknown base to an unknown exponent, or unknown base to an odd exponent. | |
184 | // Could be negative or positive depending on the actual values. | |
185 | (Sign::Negative | Sign::Uncertain, None) | | |
186 | (Sign::Uncertain, Some(false)) => Sign::Uncertain, | |
c0240ec0 | 187 | } |
c620b35d | 188 | } |
c0240ec0 | 189 | |
c620b35d FG |
190 | /// Peels binary operators such as [`BinOpKind::Mul`] or [`BinOpKind::Rem`], |
191 | /// where the result could always be positive. See [`exprs_with_muldiv_binop_peeled()`] for details. | |
192 | /// | |
193 | /// Returns the sign of the list of peeled expressions. | |
194 | fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { | |
195 | let mut negative_count = 0; | |
196 | ||
197 | // Peel off possible binary expressions, for example: | |
198 | // x * x / y => [x, x, y] | |
199 | // a % b => [a] | |
200 | let exprs = exprs_with_muldiv_binop_peeled(expr); | |
201 | for expr in exprs { | |
202 | match expr_sign(cx, expr, None) { | |
203 | Sign::Negative => negative_count += 1, | |
204 | // A mul/div is: | |
205 | // - uncertain if there are any uncertain values (because they could be negative or positive), | |
206 | Sign::Uncertain => return Sign::Uncertain, | |
207 | Sign::ZeroOrPositive => (), | |
208 | }; | |
c0240ec0 FG |
209 | } |
210 | ||
c620b35d FG |
211 | // A mul/div is: |
212 | // - negative if there are an odd number of negative values, | |
213 | // - positive or zero otherwise. | |
214 | if negative_count % 2 == 1 { | |
215 | Sign::Negative | |
216 | } else { | |
217 | Sign::ZeroOrPositive | |
218 | } | |
219 | } | |
220 | ||
221 | /// Peels binary operators such as [`BinOpKind::Add`], where the result could always be positive. | |
222 | /// See [`exprs_with_add_binop_peeled()`] for details. | |
223 | /// | |
224 | /// Returns the sign of the list of peeled expressions. | |
225 | fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { | |
226 | let mut negative_count = 0; | |
227 | let mut positive_count = 0; | |
228 | ||
229 | // Peel off possible binary expressions, for example: | |
230 | // a + b + c => [a, b, c] | |
231 | let exprs = exprs_with_add_binop_peeled(expr); | |
232 | for expr in exprs { | |
233 | match expr_sign(cx, expr, None) { | |
234 | Sign::Negative => negative_count += 1, | |
235 | // A sum is: | |
236 | // - uncertain if there are any uncertain values (because they could be negative or positive), | |
237 | Sign::Uncertain => return Sign::Uncertain, | |
238 | Sign::ZeroOrPositive => positive_count += 1, | |
239 | }; | |
240 | } | |
241 | ||
242 | // A sum is: | |
243 | // - positive or zero if there are only positive (or zero) values, | |
244 | // - negative if there are only negative (or zero) values, or | |
245 | // - uncertain if there are both. | |
246 | // We could split Zero out into its own variant, but we don't yet. | |
247 | if negative_count == 0 { | |
248 | Sign::ZeroOrPositive | |
249 | } else if positive_count == 0 { | |
250 | Sign::Negative | |
251 | } else { | |
252 | Sign::Uncertain | |
253 | } | |
c0240ec0 FG |
254 | } |
255 | ||
256 | /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`], | |
c620b35d | 257 | /// where the result depends on: |
31ef2f64 | 258 | /// |
c620b35d FG |
259 | /// - the number of negative values in the entire expression, or |
260 | /// - the number of negative values on the left hand side of the expression. | |
31ef2f64 | 261 | /// |
c620b35d | 262 | /// Ignores overflow. |
c0240ec0 | 263 | /// |
c620b35d FG |
264 | /// |
265 | /// Expressions using other operators are preserved, so we can try to evaluate them later. | |
266 | fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { | |
267 | let mut res = vec![]; | |
268 | ||
269 | for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> { | |
270 | // We don't check for mul/div/rem methods here, but we could. | |
271 | if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind { | |
272 | if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) { | |
273 | // For binary operators where both sides contribute to the sign of the result, | |
274 | // collect all their operands, recursively. This ignores overflow. | |
275 | ControlFlow::Continue(Descend::Yes) | |
276 | } else if matches!(op.node, BinOpKind::Rem | BinOpKind::Shr) { | |
277 | // For binary operators where the left hand side determines the sign of the result, | |
278 | // only collect that side, recursively. Overflow panics, so this always holds. | |
279 | // | |
280 | // Large left shifts turn negatives into zeroes, so we can't use it here. | |
281 | // | |
282 | // > Given remainder = dividend % divisor, the remainder will have the same sign as the dividend | |
283 | // > ... | |
284 | // > Arithmetic right shift on signed integer types | |
285 | // https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators | |
286 | ||
287 | // We want to descend into the lhs, but skip the rhs. | |
288 | // That's tricky to do using for_each_expr(), so we just keep the lhs intact. | |
289 | res.push(lhs); | |
290 | ControlFlow::Continue(Descend::No) | |
291 | } else { | |
292 | // The sign of the result of other binary operators depends on the values of the operands, | |
293 | // so try to evaluate the expression. | |
294 | res.push(sub_expr); | |
295 | ControlFlow::Continue(Descend::No) | |
296 | } | |
297 | } else { | |
298 | // For other expressions, including unary operators and constants, try to evaluate the expression. | |
299 | res.push(sub_expr); | |
300 | ControlFlow::Continue(Descend::No) | |
c0240ec0 | 301 | } |
c620b35d | 302 | }); |
c0240ec0 | 303 | |
c620b35d FG |
304 | res |
305 | } | |
306 | ||
307 | /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on: | |
31ef2f64 | 308 | /// |
c620b35d FG |
309 | /// - all the expressions being positive, or |
310 | /// - all the expressions being negative. | |
31ef2f64 | 311 | /// |
c620b35d FG |
312 | /// Ignores overflow. |
313 | /// | |
314 | /// Expressions using other operators are preserved, so we can try to evaluate them later. | |
315 | fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { | |
c0240ec0 | 316 | let mut res = vec![]; |
c620b35d FG |
317 | |
318 | for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> { | |
319 | // We don't check for add methods here, but we could. | |
320 | if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind { | |
321 | if matches!(op.node, BinOpKind::Add) { | |
322 | // For binary operators where both sides contribute to the sign of the result, | |
323 | // collect all their operands, recursively. This ignores overflow. | |
324 | ControlFlow::Continue(Descend::Yes) | |
325 | } else { | |
326 | // The sign of the result of other binary operators depends on the values of the operands, | |
327 | // so try to evaluate the expression. | |
328 | res.push(sub_expr); | |
329 | ControlFlow::Continue(Descend::No) | |
330 | } | |
331 | } else { | |
332 | // For other expressions, including unary operators and constants, try to evaluate the expression. | |
333 | res.push(sub_expr); | |
334 | ControlFlow::Continue(Descend::No) | |
335 | } | |
336 | }); | |
337 | ||
338 | res | |
c0240ec0 | 339 | } |