@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
55use clippy_utils:: source:: snippet_with_applicability;
66use clippy_utils:: { in_constant, is_integer_literal, SpanlessEq } ;
77use rustc_errors:: Applicability ;
8- use rustc_hir:: { BinOp , BinOpKind , Expr , ExprKind , QPath , TyKind } ;
8+ use rustc_hir:: { BinOpKind , Expr , ExprKind , QPath , TyKind } ;
99use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1010use rustc_middle:: lint:: in_external_macro;
1111use rustc_session:: impl_lint_pass;
@@ -50,61 +50,54 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
5050
5151impl < ' tcx > LateLintPass < ' tcx > for CheckedConversions {
5252 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 ,
6566 }
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+ } ,
8381 }
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+ ) ;
8495 }
8596 }
8697
8798 extract_msrv_attr ! ( LateContext ) ;
8899}
89100
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-
108101/// Contains the result of a tried conversion check
109102#[ derive( Clone , Debug ) ]
110103struct Conversion < ' a > {
@@ -121,6 +114,19 @@ enum ConversionType {
121114 FromUnsigned ,
122115}
123116
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+
124130impl < ' a > Conversion < ' a > {
125131 /// Combine multiple conversions if the are compatible
126132 pub fn combine ( self , other : Self , cx : & LateContext < ' _ > ) -> Option < Conversion < ' a > > {
@@ -188,29 +194,17 @@ impl ConversionType {
188194}
189195
190196/// 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)
197200 } else {
198201 None
199202 }
200203}
201204
202205/// 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) )
214208}
215209
216210/// Check for `expr >= 0`
@@ -309,15 +303,6 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
309303 }
310304}
311305
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-
321306// Constants
322307const UINTS : & [ & str ] = & [ "u8" , "u16" , "u32" , "u64" , "usize" ] ;
323308const SINTS : & [ & str ] = & [ "i8" , "i16" , "i32" , "i64" , "isize" ] ;
0 commit comments