@@ -7,7 +7,12 @@ use rustc_middle::ty::{self, Ty, UintTy};
7
7
8
8
use super :: CAST_SIGN_LOSS ;
9
9
10
- const METHODS_RET_POSITIVE : & [ & str ] = & [ "abs" , "checked_abs" , "rem_euclid" , "checked_rem_euclid" ] ;
10
+ /// A list of methods that can never return a negative value.
11
+ /// Includes methods that panic rather than returning a negative value.
12
+ ///
13
+ /// Methods that can overflow and return a negative value must not be included in this list,
14
+ /// because checking for negative return values from those functions can be useful.
15
+ const METHODS_RET_POSITIVE : & [ & str ] = & [ "checked_abs" , "rem_euclid" , "checked_rem_euclid" ] ;
11
16
12
17
pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cast_op : & Expr < ' _ > , cast_from : Ty < ' _ > , cast_to : Ty < ' _ > ) {
13
18
if should_lint ( cx, cast_op, cast_from, cast_to) {
@@ -27,13 +32,15 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
27
32
return false ;
28
33
}
29
34
30
- // Don't lint if `cast_op` is known to be positive.
35
+ // Don't lint if `cast_op` is known to be positive, ignoring overflow .
31
36
if let Sign :: ZeroOrPositive = expr_sign ( cx, cast_op, cast_from) {
32
37
return false ;
33
38
}
34
39
35
40
let ( mut uncertain_count, mut negative_count) = ( 0 , 0 ) ;
36
- // Peel off possible binary expressions, e.g. x * x * y => [x, x, y]
41
+ // Peel off possible binary expressions, for example:
42
+ // x * x * y => [x, x, y]
43
+ // a % b => [a]
37
44
let Some ( exprs) = exprs_with_selected_binop_peeled ( cast_op) else {
38
45
// Assume cast sign lose if we cannot determine the sign of `cast_op`
39
46
return true ;
@@ -47,8 +54,9 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
47
54
} ;
48
55
}
49
56
50
- // Lint if there are odd number of uncertain or negative results
51
- uncertain_count % 2 == 1 || negative_count % 2 == 1
57
+ // Lint if there are any uncertain results (because they could be negative or positive),
58
+ // or an odd number of negative results.
59
+ uncertain_count > 0 || negative_count % 2 == 1
52
60
} ,
53
61
54
62
( false , true ) => !cast_to. is_signed ( ) ,
@@ -87,6 +95,12 @@ fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
87
95
{
88
96
method_name = inner_path. ident . name . as_str ( ) ;
89
97
}
98
+ if method_name == "expect"
99
+ && let Some ( arglist) = method_chain_args ( expr, & [ "expect" ] )
100
+ && let ExprKind :: MethodCall ( inner_path, ..) = & arglist[ 0 ] . 0 . kind
101
+ {
102
+ method_name = inner_path. ident . name . as_str ( ) ;
103
+ }
90
104
91
105
if method_name == "pow"
92
106
&& let [ arg] = args
@@ -100,53 +114,69 @@ fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
100
114
Sign :: Uncertain
101
115
}
102
116
103
- /// Return the sign of the `pow` call's result.
117
+ /// Return the sign of the `pow` call's result, ignoring overflow.
118
+ ///
119
+ /// If the base is positive, the result is always positive.
120
+ /// If the base is negative, and the exponent is a even number, the result is always positive,
121
+ /// otherwise if the exponent is an odd number, the result is always negative.
104
122
///
105
- /// If the caller is a positive number, the result is always positive,
106
- /// If the `power_of` is a even number, the result is always positive as well,
107
- /// Otherwise a [`Sign::Uncertain`] will be returned.
123
+ /// If either value can't be evaluated, [`Sign::Uncertain`] will be returned.
108
124
fn pow_call_result_sign( cx : & LateContext < ' _ > , caller : & Expr < ' _ > , power_of : & Expr < ' _ > ) -> Sign {
109
125
let caller_ty = cx. typeck_results ( ) . expr_ty ( caller) ;
110
- if let Some ( caller_val) = get_const_int_eval ( cx, caller, caller_ty)
111
- && caller_val >= 0
112
- {
126
+ let Some ( caller_val) = get_const_int_eval ( cx, caller, caller_ty) else {
127
+ return Sign :: Uncertain ;
128
+ }
129
+ // Non-negative values raised to non-negative exponents are always non-negative, ignoring overflow.
130
+ // (Rust's integer pow() function takes an unsigned exponent.)
131
+ if caller_val >= 0 {
113
132
return Sign :: ZeroOrPositive ;
114
133
}
115
134
116
- if let Some ( Constant :: Int ( n) ) = constant ( cx, cx. typeck_results ( ) , power_of)
117
- && clip ( cx. tcx , n, UintTy :: U32 ) % 2 == 0
118
- {
135
+ let Some ( Constant :: Int ( n) ) = constant ( cx, cx. typeck_results ( ) , power_of) else {
136
+ return Sign :: Uncertain ;
137
+ }
138
+ // A negative value raised to an even exponent is non-negative, and an odd exponent
139
+ // is negative, ignoring overflow.
140
+ if clip ( cx. tcx , n, UintTy :: U32 ) % 2 == 0 0 {
119
141
return Sign :: ZeroOrPositive ;
142
+ } else {
143
+ return Sign :: Negative ;
120
144
}
121
-
122
- Sign :: Uncertain
123
145
}
124
146
125
147
/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
126
- /// which the result could always be positive under certain condition .
148
+ /// which the result could always be positive under certain conditions, ignoring overflow .
127
149
///
128
- /// Other operators such as `+`/`-` causing the result's sign hard to determine, which we will
129
- /// return `None`
130
- fn exprs_with_selected_binop_peeled < ' a > ( expr : & ' a Expr < ' _ > ) -> Option < Vec < & ' a Expr < ' a > > > {
150
+ /// Expressions using other operators are preserved, so we can try to evaluate them later.
151
+ fn exprs_with_selected_binop_peeled < ' a > ( expr : & ' a Expr < ' _ > ) -> Vec < & ' a Expr < ' a > > {
131
152
#[ inline]
132
- fn collect_operands < ' a > ( expr : & ' a Expr < ' a > , operands : & mut Vec < & ' a Expr < ' a > > ) -> Option < ( ) > {
153
+ fn collect_operands < ' a > ( expr : & ' a Expr < ' a > , operands : & mut Vec < & ' a Expr < ' a > > ) {
133
154
match expr. kind {
134
155
ExprKind :: Binary ( op, lhs, rhs) => {
135
- if matches ! ( op. node, BinOpKind :: Mul | BinOpKind :: Div | BinOpKind :: Rem ) {
156
+ if matches ! ( op. node, BinOpKind :: Mul | BinOpKind :: Div ) {
157
+ // For binary operators which both contribute to the sign of the result,
158
+ // collect all their operands, recursively. This ignores overflow.
159
+ collect_operands ( lhs, operands) ;
160
+ collect_operands ( rhs, operands) ;
161
+ } else if matches ! ( op. node, BinOpKind :: Rem ) {
162
+ // For binary operators where the left hand side determines the sign of the result,
163
+ // only collect that side, recursively. Overflow panics, so this always holds.
164
+ //
165
+ // > Given remainder = dividend % divisor, the remainder will have the same sign as the dividend
166
+ // https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators
136
167
collect_operands ( lhs, operands) ;
137
- operands. push ( rhs) ;
138
168
} else {
139
- // Things are complicated when there are other binary ops exist ,
140
- // abort checking by returning `None` for now .
141
- return None ;
169
+ // The sign of the result of other binary operators depends on the values of the operands ,
170
+ // so try to evaluate the expression .
171
+ operands . push ( expr ) ;
142
172
}
143
173
} ,
174
+ // For other expressions, including unary operators and constants, try to evaluate the expression.
144
175
_ => operands. push ( expr) ,
145
176
}
146
- Some ( ( ) )
147
177
}
148
178
149
179
let mut res = vec ! [ ] ;
150
- collect_operands ( expr, & mut res) ? ;
151
- Some ( res)
180
+ collect_operands ( expr, & mut res) ;
181
+ res
152
182
}
0 commit comments