]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / src / tools / clippy / clippy_lints / src / casts / cast_sign_loss.rs
CommitLineData
c620b35d
FG
1use std::convert::Infallible;
2use std::ops::ControlFlow;
3
17df50a5 4use clippy_utils::consts::{constant, Constant};
cdc7bbd5 5use clippy_utils::diagnostics::span_lint;
c620b35d
FG
6use clippy_utils::visitors::{for_each_expr, Descend};
7use clippy_utils::{method_chain_args, sext};
c0240ec0 8use rustc_hir::{BinOpKind, Expr, ExprKind};
f20569fa 9use rustc_lint::LateContext;
c620b35d 10use rustc_middle::ty::{self, Ty};
f20569fa 11
f20569fa
XL
12use 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.
19const 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.
33const 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.
36const METHODS_UNWRAP: &[&str] = &["unwrap", "unwrap_unchecked", "expect", "into_ok"];
37
38pub(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 55fn 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
84fn 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
99fn 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
115enum Sign {
116 ZeroOrPositive,
117 Negative,
118 Uncertain,
119}
120
c620b35d 121fn 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`].
165fn 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.
194fn 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.
225fn 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.
266fn 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.
315fn 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}