@@ -96,6 +96,7 @@ use rustc_hir::{
96
96
use rustc_lexer:: { tokenize, TokenKind } ;
97
97
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
98
98
use rustc_middle:: hir:: place:: PlaceBase ;
99
+ use rustc_middle:: mir:: ConstantKind ;
99
100
use rustc_middle:: ty as rustc_ty;
100
101
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
101
102
use rustc_middle:: ty:: binding:: BindingMode ;
@@ -114,7 +115,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
114
115
use rustc_span:: Span ;
115
116
use rustc_target:: abi:: Integer ;
116
117
117
- use crate :: consts:: { constant, Constant } ;
118
+ use crate :: consts:: { constant, miri_to_const , Constant } ;
118
119
use crate :: higher:: Range ;
119
120
use crate :: ty:: { can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param} ;
120
121
use crate :: visitors:: for_each_expr;
@@ -1492,22 +1493,66 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1492
1493
}
1493
1494
}
1494
1495
1495
- /// Checks whether the given `Range` is equivalent to a `RangeFull`.
1496
- /// Inclusive ranges are not considered because they already constitute a lint.
1497
- pub fn is_range_full ( cx : & LateContext < ' _ > , container : & Expr < ' _ > , range : Range < ' _ > ) -> bool {
1498
- range. start . map_or ( true , |e| is_integer_const ( cx, e, 0 ) )
1499
- && range. end . map_or ( true , |e| {
1500
- if range. limits == RangeLimits :: HalfOpen
1501
- && let ExprKind :: Path ( QPath :: Resolved ( None , container_path) ) = container. kind
1502
- && let ExprKind :: MethodCall ( name, self_arg, [ ] , _) = e. kind
1503
- && name. ident . name == sym:: len
1504
- && let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = self_arg. kind
1496
+ /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
1497
+ /// For the lower bound, this means that:
1498
+ /// - either there is none
1499
+ /// - or it is the smallest value that can be represented by the range's integer type
1500
+ /// For the upper bound, this means that:
1501
+ /// - either there is none
1502
+ /// - or it is the largest value that can be represented by the range's integer type and is
1503
+ /// inclusive
1504
+ /// - or it is a call to some container's `len` method and is exclusive, and the range is passed to
1505
+ /// a method call on that same container (e.g. `v.drain(..v.len())`)
1506
+ /// If the given `Expr` is not some kind of range, the function returns `false`.
1507
+ pub fn is_range_full ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , container_path : Option < & Path < ' _ > > ) -> bool {
1508
+ let ty = cx. typeck_results ( ) . expr_ty ( expr) ;
1509
+ if let Some ( Range { start, end, limits } ) = Range :: hir ( expr) {
1510
+ let start_is_none_or_min = start. map_or ( true , |start| {
1511
+ if let rustc_ty:: Adt ( _, subst) = ty. kind ( )
1512
+ && let bnd_ty = subst. type_at ( 0 )
1513
+ && let Some ( min_val) = bnd_ty. numeric_min_val ( cx. tcx )
1514
+ && let const_val = cx. tcx . valtree_to_const_val ( ( bnd_ty, min_val. to_valtree ( ) ) )
1515
+ && let min_const_kind = ConstantKind :: from_value ( const_val, bnd_ty)
1516
+ && let Some ( min_const) = miri_to_const ( cx. tcx , min_const_kind)
1517
+ && let Some ( ( start_const, _) ) = constant ( cx, cx. typeck_results ( ) , start)
1505
1518
{
1506
- container_path . res == path . res
1519
+ start_const == min_const
1507
1520
} else {
1508
1521
false
1509
1522
}
1510
- } )
1523
+ } ) ;
1524
+ let end_is_none_or_max = end. map_or ( true , |end| {
1525
+ match limits {
1526
+ RangeLimits :: Closed => {
1527
+ if let rustc_ty:: Adt ( _, subst) = ty. kind ( )
1528
+ && let bnd_ty = subst. type_at ( 0 )
1529
+ && let Some ( max_val) = bnd_ty. numeric_max_val ( cx. tcx )
1530
+ && let const_val = cx. tcx . valtree_to_const_val ( ( bnd_ty, max_val. to_valtree ( ) ) )
1531
+ && let max_const_kind = ConstantKind :: from_value ( const_val, bnd_ty)
1532
+ && let Some ( max_const) = miri_to_const ( cx. tcx , max_const_kind)
1533
+ && let Some ( ( end_const, _) ) = constant ( cx, cx. typeck_results ( ) , end)
1534
+ {
1535
+ end_const == max_const
1536
+ } else {
1537
+ false
1538
+ }
1539
+ } ,
1540
+ RangeLimits :: HalfOpen => {
1541
+ if let Some ( container_path) = container_path
1542
+ && let ExprKind :: MethodCall ( name, self_arg, [ ] , _) = end. kind
1543
+ && name. ident . name == sym:: len
1544
+ && let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = self_arg. kind
1545
+ {
1546
+ container_path. res == path. res
1547
+ } else {
1548
+ false
1549
+ }
1550
+ } ,
1551
+ }
1552
+ } ) ;
1553
+ return start_is_none_or_min && end_is_none_or_max;
1554
+ }
1555
+ false
1511
1556
}
1512
1557
1513
1558
/// Checks whether the given expression is a constant integer of the given value.
0 commit comments