@@ -7,7 +7,12 @@ use rustc_middle::ty::{self, Ty, UintTy};
77
88use super :: CAST_SIGN_LOSS ;
99
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" ] ;
1116
1217pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cast_op : & Expr < ' _ > , cast_from : Ty < ' _ > , cast_to : Ty < ' _ > ) {
1318 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
2732 return false ;
2833 }
2934
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 .
3136 if let Sign :: ZeroOrPositive = expr_sign ( cx, cast_op, cast_from) {
3237 return false ;
3338 }
3439
3540 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]
3744 let Some ( exprs) = exprs_with_selected_binop_peeled ( cast_op) else {
3845 // Assume cast sign lose if we cannot determine the sign of `cast_op`
3946 return true ;
@@ -47,8 +54,9 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
4754 } ;
4855 }
4956
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
5260 } ,
5361
5462 ( false , true ) => !cast_to. is_signed ( ) ,
@@ -87,6 +95,12 @@ fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
8795 {
8896 method_name = inner_path. ident . name . as_str ( ) ;
8997 }
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+ }
90104
91105 if method_name == "pow"
92106 && let [ arg] = args
@@ -100,53 +114,69 @@ fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
100114 Sign :: Uncertain
101115}
102116
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.
104122///
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.
108124fn pow_call_result_sign( cx : & LateContext < ' _ > , caller : & Expr < ' _ > , power_of : & Expr < ' _ > ) -> Sign {
109125 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 {
113132 return Sign :: ZeroOrPositive ;
114133 }
115134
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 {
119141 return Sign :: ZeroOrPositive ;
142+ } else {
143+ return Sign :: Negative ;
120144 }
121-
122- Sign :: Uncertain
123145}
124146
125147/// 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 .
127149///
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 > > {
131152 #[ 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 > > ) {
133154 match expr. kind {
134155 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
136167 collect_operands ( lhs, operands) ;
137- operands. push ( rhs) ;
138168 } 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 ) ;
142172 }
143173 } ,
174+ // For other expressions, including unary operators and constants, try to evaluate the expression.
144175 _ => operands. push ( expr) ,
145176 }
146- Some ( ( ) )
147177 }
148178
149179 let mut res = vec ! [ ] ;
150- collect_operands ( expr, & mut res) ? ;
151- Some ( res)
180+ collect_operands ( expr, & mut res) ;
181+ res
152182}
0 commit comments