Skip to content

Commit e74fe43

Browse files
committed
Check for both signed and unsigned constant expressions
1 parent c5e8487 commit e74fe43

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

clippy_lints/src/casts/cast_sign_loss.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
6666
// a % b => [a]
6767
let exprs = exprs_with_selected_binop_peeled(cast_op);
6868
for expr in exprs {
69-
let ty = cx.typeck_results().expr_ty(expr);
70-
match expr_sign(cx, expr, ty) {
69+
match expr_sign(cx, expr, None) {
7170
Sign::Negative => negative_count += 1,
7271
Sign::Uncertain => uncertain_count += 1,
7372
Sign::ZeroOrPositive => (),
@@ -85,7 +84,11 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
8584
}
8685
}
8786

88-
fn get_const_int_eval<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<Ty<'cx>>>) -> Option<i128> {
87+
fn get_const_signed_int_eval<'cx>(
88+
cx: &LateContext<'cx>,
89+
expr: &Expr<'_>,
90+
ty: impl Into<Option<Ty<'cx>>>,
91+
) -> Option<i128> {
8992
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
9093

9194
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
@@ -96,6 +99,22 @@ fn get_const_int_eval<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into
9699
None
97100
}
98101

102+
fn get_const_unsigned_int_eval<'cx>(
103+
cx: &LateContext<'cx>,
104+
expr: &Expr<'_>,
105+
ty: impl Into<Option<Ty<'cx>>>,
106+
) -> Option<u128> {
107+
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
108+
109+
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
110+
&& let ty::Uint(_ity) = *ty.kind()
111+
{
112+
return Some(n);
113+
}
114+
None
115+
}
116+
117+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
99118
enum Sign {
100119
ZeroOrPositive,
101120
Negative,
@@ -104,9 +123,12 @@ enum Sign {
104123

105124
fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<Ty<'cx>>>) -> Sign {
106125
// Try evaluate this expr first to see if it's positive
107-
if let Some(val) = get_const_int_eval(cx, expr, ty) {
126+
if let Some(val) = get_const_signed_int_eval(cx, expr, ty) {
108127
return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative };
109128
}
129+
if let Some(_val) = get_const_unsigned_int_eval(cx, expr, None) {
130+
return Sign::ZeroOrPositive;
131+
}
110132

111133
// Calling on methods that always return non-negative values.
112134
if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind {
@@ -144,12 +166,13 @@ fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<T
144166
/// Otherwise, returns [`Sign::Uncertain`].
145167
fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'_>) -> Sign {
146168
let base_sign = expr_sign(cx, base, None);
147-
let exponent_val = get_const_int_eval(cx, exponent, None);
169+
170+
// Rust's integer pow() functions take an unsigned exponent.
171+
let exponent_val = get_const_unsigned_int_eval(cx, exponent, None);
148172
let exponent_is_even = exponent_val.map(|val| val % 2 == 0);
149173

150174
match (base_sign, exponent_is_even) {
151175
// Non-negative bases always return non-negative results, ignoring overflow.
152-
// This is because Rust's integer pow() functions take an unsigned exponent.
153176
(Sign::ZeroOrPositive, _) => Sign::ZeroOrPositive,
154177

155178
// Any base raised to an even exponent is non-negative.

tests/ui/cast.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,5 +600,5 @@ error: casting `i32` to `u32` may lose the sign of the value
600600
LL | (a.abs() * b.pow(2) / c.abs()) as u32
601601
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
602602

603-
error: aborting due to 77 previous errors
603+
error: aborting due to 76 previous errors
604604

0 commit comments

Comments
 (0)