1
1
use clippy_utils:: consts:: { constant, Constant } ;
2
2
use clippy_utils:: diagnostics:: span_lint;
3
- use clippy_utils:: { clip , method_chain_args, sext} ;
3
+ use clippy_utils:: { method_chain_args, sext} ;
4
4
use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
5
5
use rustc_lint:: LateContext ;
6
- use rustc_middle:: ty:: { self , Ty , UintTy } ;
6
+ use rustc_middle:: ty:: { self , Ty } ;
7
7
8
8
use super :: CAST_SIGN_LOSS ;
9
9
@@ -22,10 +22,7 @@ const METHODS_RET_POSITIVE: &[&str] = &[
22
22
"wrapping_rem_euclid" ,
23
23
] ;
24
24
25
- /// A list of methods that act like `pow()`, and can never return:
26
- /// - a negative value from a non-negative base
27
- /// - a negative value from a negative base and even exponent
28
- /// - a non-negative value from a negative base and odd exponent
25
+ /// A list of methods that act like `pow()`. See `pow_call_result_sign()` for details.
29
26
///
30
27
/// Methods that can overflow and return a negative value must not be included in this list,
31
28
/// because casting their return values can still result in sign loss.
@@ -34,7 +31,13 @@ const METHODS_POW: &[&str] = &["pow", "saturating_pow", "checked_pow"];
34
31
/// A list of methods that act like `unwrap()`, and don't change the sign of the inner value.
35
32
const METHODS_UNWRAP : & [ & str ] = & [ "unwrap" , "unwrap_unchecked" , "expect" , "into_ok" ] ;
36
33
37
- pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cast_op : & Expr < ' _ > , cast_from : Ty < ' _ > , cast_to : Ty < ' _ > ) {
34
+ pub ( super ) fn check < ' cx > (
35
+ cx : & LateContext < ' cx > ,
36
+ expr : & Expr < ' _ > ,
37
+ cast_op : & Expr < ' _ > ,
38
+ cast_from : Ty < ' cx > ,
39
+ cast_to : Ty < ' _ > ,
40
+ ) {
38
41
if should_lint ( cx, cast_op, cast_from, cast_to) {
39
42
span_lint (
40
43
cx,
@@ -45,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c
45
48
}
46
49
}
47
50
48
- fn should_lint ( cx : & LateContext < ' _ > , cast_op : & Expr < ' _ > , cast_from : Ty < ' _ > , cast_to : Ty < ' _ > ) -> bool {
51
+ fn should_lint < ' cx > ( cx : & LateContext < ' cx > , cast_op : & Expr < ' _ > , cast_from : Ty < ' cx > , cast_to : Ty < ' _ > ) -> bool {
49
52
match ( cast_from. is_integral ( ) , cast_to. is_integral ( ) ) {
50
53
( true , true ) => {
51
54
if !cast_from. is_signed ( ) || cast_to. is_signed ( ) {
@@ -82,7 +85,9 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
82
85
}
83
86
}
84
87
85
- fn get_const_int_eval ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , ty : Ty < ' _ > ) -> Option < i128 > {
88
+ fn get_const_int_eval < ' cx > ( cx : & LateContext < ' cx > , expr : & Expr < ' _ > , ty : impl Into < Option < Ty < ' cx > > > ) -> Option < i128 > {
89
+ let ty = ty. into ( ) . unwrap_or_else ( || cx. typeck_results ( ) . expr_ty ( expr) ) ;
90
+
86
91
if let Constant :: Int ( n) = constant ( cx, cx. typeck_results ( ) , expr) ?
87
92
&& let ty:: Int ( ity) = * ty. kind ( )
88
93
{
@@ -97,7 +102,7 @@ enum Sign {
97
102
Uncertain ,
98
103
}
99
104
100
- fn expr_sign ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , ty : Ty < ' _ > ) -> Sign {
105
+ fn expr_sign < ' cx > ( cx : & LateContext < ' cx > , expr : & Expr < ' _ > , ty : impl Into < Option < Ty < ' cx > > > ) -> Sign {
101
106
// Try evaluate this expr first to see if it's positive
102
107
if let Some ( val) = get_const_int_eval ( cx, expr, ty) {
103
108
return if val >= 0 { Sign :: ZeroOrPositive } else { Sign :: Negative } ;
@@ -112,6 +117,8 @@ fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
112
117
&& let Some ( arglist) = method_chain_args ( expr, & [ found_name] )
113
118
&& let ExprKind :: MethodCall ( inner_path, ..) = & arglist[ 0 ] . 0 . kind
114
119
{
120
+ // The original type has changed, but we can't use `ty` here anyway, because it has been
121
+ // moved.
115
122
method_name = inner_path. ident . name . as_str ( ) ;
116
123
}
117
124
@@ -130,31 +137,31 @@ fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
130
137
/// Return the sign of the `pow` call's result, ignoring overflow.
131
138
///
132
139
/// If the base is positive, the result is always positive.
133
- /// If the base is negative, and the exponent is a even number, the result is always positive,
134
- /// otherwise if the exponent is an odd number, the result is always negative.
140
+ /// If the exponent is a even number, the result is always positive,
141
+ /// Otherwise, if the base is negative, and the exponent is an odd number, the result is always
142
+ /// negative.
135
143
///
136
- /// If either value can't be evaluated, [`Sign::Uncertain`] will be returned .
144
+ /// Otherwise, returns [`Sign::Uncertain`].
137
145
fn pow_call_result_sign ( cx : & LateContext < ' _ > , base : & Expr < ' _ > , exponent : & Expr < ' _ > ) -> Sign {
138
- let base_ty = cx. typeck_results ( ) . expr_ty ( base) ;
139
- let Some ( base_val) = get_const_int_eval ( cx, base, base_ty) else {
140
- return Sign :: Uncertain ;
141
- } ;
142
- // Non-negative bases raised to non-negative exponents are always non-negative, ignoring overflow.
143
- // (Rust's integer pow() function takes an unsigned exponent.)
144
- if base_val >= 0 {
145
- return Sign :: ZeroOrPositive ;
146
- }
146
+ let base_sign = expr_sign ( cx, base, None ) ;
147
+ let exponent_val = get_const_int_eval ( cx, exponent, None ) ;
148
+ let exponent_is_even = exponent_val. map ( |val| val % 2 == 0 ) ;
149
+
150
+ match ( base_sign, exponent_is_even) {
151
+ // Non-negative bases always return non-negative results, ignoring overflow.
152
+ // This is because Rust's integer pow() functions take an unsigned exponent.
153
+ ( Sign :: ZeroOrPositive , _) => Sign :: ZeroOrPositive ,
154
+
155
+ // Any base raised to an even exponent is non-negative.
156
+ // This is true even if we don't know the value of the base.
157
+ ( _, Some ( true ) ) => Sign :: ZeroOrPositive ,
147
158
148
- let Some ( Constant :: Int ( n) ) = constant ( cx, cx. typeck_results ( ) , exponent) else {
149
- return Sign :: Uncertain ;
150
- } ;
159
+ // A negative base raised to an odd exponent is non-negative.
160
+ ( Sign :: Negative , Some ( false ) ) => Sign :: Negative ,
151
161
152
- // A negative value raised to an even exponent is non-negative, and an odd exponent
153
- // is negative, ignoring overflow.
154
- if clip ( cx. tcx , n, UintTy :: U32 ) % 2 == 0 {
155
- return Sign :: ZeroOrPositive ;
156
- } else {
157
- return Sign :: Negative ;
162
+ // Negative or unknown base to an unknown exponent, or an unknown base to an odd exponent.
163
+ ( Sign :: Negative | Sign :: Uncertain , None ) => Sign :: Uncertain ,
164
+ ( Sign :: Uncertain , Some ( false ) ) => Sign :: Uncertain ,
158
165
}
159
166
}
160
167
0 commit comments