@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
5
5
use clippy_utils:: source:: snippet_with_applicability;
6
6
use clippy_utils:: { in_constant, is_integer_literal, SpanlessEq } ;
7
7
use rustc_errors:: Applicability ;
8
- use rustc_hir:: { BinOp , BinOpKind , Expr , ExprKind , QPath , TyKind } ;
8
+ use rustc_hir:: { BinOpKind , Expr , ExprKind , QPath , TyKind } ;
9
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
10
use rustc_middle:: lint:: in_external_macro;
11
11
use rustc_session:: impl_lint_pass;
@@ -50,61 +50,54 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
50
50
51
51
impl < ' tcx > LateLintPass < ' tcx > for CheckedConversions {
52
52
fn check_expr ( & mut self , cx : & LateContext < ' _ > , item : & Expr < ' _ > ) {
53
- if !self . msrv . meets ( msrvs:: TRY_FROM ) {
54
- return ;
55
- }
56
-
57
- let result = if !in_constant ( cx, item. hir_id )
58
- && !in_external_macro ( cx. sess ( ) , item. span )
59
- && let ExprKind :: Binary ( op, left, right) = & item. kind
60
- {
61
- match op. node {
62
- BinOpKind :: Ge | BinOpKind :: Le => single_check ( item) ,
63
- BinOpKind :: And => double_check ( cx, left, right) ,
64
- _ => None ,
53
+ if let ExprKind :: Binary ( op, lhs, rhs) = item. kind
54
+ && let ( lt1, gt1, op2) = match op. node {
55
+ BinOpKind :: Le => ( lhs, rhs, None ) ,
56
+ BinOpKind :: Ge => ( rhs, lhs, None ) ,
57
+ BinOpKind :: And
58
+ if let ExprKind :: Binary ( op1, lhs1, rhs1) = lhs. kind
59
+ && let ExprKind :: Binary ( op2, lhs2, rhs2) = rhs. kind
60
+ && let Some ( ( lt1, gt1) ) = read_le_ge ( op1. node , lhs1, rhs1)
61
+ && let Some ( ( lt2, gt2) ) = read_le_ge ( op2. node , lhs2, rhs2) =>
62
+ {
63
+ ( lt1, gt1, Some ( ( lt2, gt2) ) )
64
+ } ,
65
+ _ => return ,
65
66
}
66
- } else {
67
- None
68
- } ;
69
-
70
- if let Some ( cv) = result {
71
- if let Some ( to_type) = cv. to_type {
72
- let mut applicability = Applicability :: MachineApplicable ;
73
- let snippet = snippet_with_applicability ( cx, cv. expr_to_cast . span , "_" , & mut applicability) ;
74
- span_lint_and_sugg (
75
- cx,
76
- CHECKED_CONVERSIONS ,
77
- item. span ,
78
- "checked cast can be simplified" ,
79
- "try" ,
80
- format ! ( "{to_type}::try_from({snippet}).is_ok()" ) ,
81
- applicability,
82
- ) ;
67
+ && !in_external_macro ( cx. sess ( ) , item. span )
68
+ && !in_constant ( cx, item. hir_id )
69
+ && self . msrv . meets ( msrvs:: TRY_FROM )
70
+ && let Some ( cv) = match op2 {
71
+ // todo: check for case signed -> larger unsigned == only x >= 0
72
+ None => check_upper_bound ( lt1, gt1) . filter ( |cv| cv. cvt == ConversionType :: FromUnsigned ) ,
73
+ Some ( ( lt2, gt2) ) => {
74
+ let upper_lower = |lt1, gt1, lt2, gt2| {
75
+ check_upper_bound ( lt1, gt1)
76
+ . zip ( check_lower_bound ( lt2, gt2) )
77
+ . and_then ( |( l, r) | l. combine ( r, cx) )
78
+ } ;
79
+ upper_lower ( lt1, gt1, lt2, gt2) . or_else ( || upper_lower ( lt2, gt2, lt1, gt1) )
80
+ } ,
83
81
}
82
+ && let Some ( to_type) = cv. to_type
83
+ {
84
+ let mut applicability = Applicability :: MachineApplicable ;
85
+ let snippet = snippet_with_applicability ( cx, cv. expr_to_cast . span , "_" , & mut applicability) ;
86
+ span_lint_and_sugg (
87
+ cx,
88
+ CHECKED_CONVERSIONS ,
89
+ item. span ,
90
+ "checked cast can be simplified" ,
91
+ "try" ,
92
+ format ! ( "{to_type}::try_from({snippet}).is_ok()" ) ,
93
+ applicability,
94
+ ) ;
84
95
}
85
96
}
86
97
87
98
extract_msrv_attr ! ( LateContext ) ;
88
99
}
89
100
90
- /// Searches for a single check from unsigned to _ is done
91
- /// todo: check for case signed -> larger unsigned == only x >= 0
92
- fn single_check < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> Option < Conversion < ' tcx > > {
93
- check_upper_bound ( expr) . filter ( |cv| cv. cvt == ConversionType :: FromUnsigned )
94
- }
95
-
96
- /// Searches for a combination of upper & lower bound checks
97
- fn double_check < ' a > ( cx : & LateContext < ' _ > , left : & ' a Expr < ' _ > , right : & ' a Expr < ' _ > ) -> Option < Conversion < ' a > > {
98
- let upper_lower = |l, r| {
99
- let upper = check_upper_bound ( l) ;
100
- let lower = check_lower_bound ( r) ;
101
-
102
- upper. zip ( lower) . and_then ( |( l, r) | l. combine ( r, cx) )
103
- } ;
104
-
105
- upper_lower ( left, right) . or_else ( || upper_lower ( right, left) )
106
- }
107
-
108
101
/// Contains the result of a tried conversion check
109
102
#[ derive( Clone , Debug ) ]
110
103
struct Conversion < ' a > {
@@ -121,6 +114,19 @@ enum ConversionType {
121
114
FromUnsigned ,
122
115
}
123
116
117
+ /// Attempts to read either `<=` or `>=` with a normalized operand order.
118
+ fn read_le_ge < ' tcx > (
119
+ op : BinOpKind ,
120
+ lhs : & ' tcx Expr < ' tcx > ,
121
+ rhs : & ' tcx Expr < ' tcx > ,
122
+ ) -> Option < ( & ' tcx Expr < ' tcx > , & ' tcx Expr < ' tcx > ) > {
123
+ match op {
124
+ BinOpKind :: Le => Some ( ( lhs, rhs) ) ,
125
+ BinOpKind :: Ge => Some ( ( rhs, lhs) ) ,
126
+ _ => None ,
127
+ }
128
+ }
129
+
124
130
impl < ' a > Conversion < ' a > {
125
131
/// Combine multiple conversions if the are compatible
126
132
pub fn combine ( self , other : Self , cx : & LateContext < ' _ > ) -> Option < Conversion < ' a > > {
@@ -188,29 +194,17 @@ impl ConversionType {
188
194
}
189
195
190
196
/// Check for `expr <= (to_type::MAX as from_type)`
191
- fn check_upper_bound < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> Option < Conversion < ' tcx > > {
192
- if let ExprKind :: Binary ( ref op, left, right) = & expr. kind
193
- && let Some ( ( candidate, check) ) = normalize_le_ge ( op, left, right)
194
- && let Some ( ( from, to) ) = get_types_from_cast ( check, INTS , "max_value" , "MAX" )
195
- {
196
- Conversion :: try_new ( candidate, from, to)
197
+ fn check_upper_bound < ' tcx > ( lt : & ' tcx Expr < ' tcx > , gt : & ' tcx Expr < ' tcx > ) -> Option < Conversion < ' tcx > > {
198
+ if let Some ( ( from, to) ) = get_types_from_cast ( gt, INTS , "max_value" , "MAX" ) {
199
+ Conversion :: try_new ( lt, from, to)
197
200
} else {
198
201
None
199
202
}
200
203
}
201
204
202
205
/// Check for `expr >= 0|(to_type::MIN as from_type)`
203
- fn check_lower_bound < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> Option < Conversion < ' tcx > > {
204
- fn check_function < ' a > ( candidate : & ' a Expr < ' a > , check : & ' a Expr < ' a > ) -> Option < Conversion < ' a > > {
205
- ( check_lower_bound_zero ( candidate, check) ) . or_else ( || ( check_lower_bound_min ( candidate, check) ) )
206
- }
207
-
208
- // First of we need a binary containing the expression & the cast
209
- if let ExprKind :: Binary ( ref op, left, right) = & expr. kind {
210
- normalize_le_ge ( op, right, left) . and_then ( |( l, r) | check_function ( l, r) )
211
- } else {
212
- None
213
- }
206
+ fn check_lower_bound < ' tcx > ( lt : & ' tcx Expr < ' tcx > , gt : & ' tcx Expr < ' tcx > ) -> Option < Conversion < ' tcx > > {
207
+ check_lower_bound_zero ( gt, lt) . or_else ( || check_lower_bound_min ( gt, lt) )
214
208
}
215
209
216
210
/// Check for `expr >= 0`
@@ -309,15 +303,6 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
309
303
}
310
304
}
311
305
312
- /// Will return the expressions as if they were expr1 <= expr2
313
- fn normalize_le_ge < ' a > ( op : & BinOp , left : & ' a Expr < ' a > , right : & ' a Expr < ' a > ) -> Option < ( & ' a Expr < ' a > , & ' a Expr < ' a > ) > {
314
- match op. node {
315
- BinOpKind :: Le => Some ( ( left, right) ) ,
316
- BinOpKind :: Ge => Some ( ( right, left) ) ,
317
- _ => None ,
318
- }
319
- }
320
-
321
306
// Constants
322
307
const UINTS : & [ & str ] = & [ "u8" , "u16" , "u32" , "u64" , "usize" ] ;
323
308
const SINTS : & [ & str ] = & [ "i8" , "i16" , "i32" , "i64" , "isize" ] ;
0 commit comments