@@ -96,6 +96,7 @@ use rustc_hir::{
9696use rustc_lexer:: { tokenize, TokenKind } ;
9797use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
9898use rustc_middle:: hir:: place:: PlaceBase ;
99+ use rustc_middle:: mir:: ConstantKind ;
99100use rustc_middle:: ty as rustc_ty;
100101use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
101102use rustc_middle:: ty:: binding:: BindingMode ;
@@ -114,7 +115,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
114115use rustc_span:: Span ;
115116use rustc_target:: abi:: Integer ;
116117
117- use crate :: consts:: { constant, Constant } ;
118+ use crate :: consts:: { constant, miri_to_const , Constant } ;
118119use crate :: higher:: Range ;
119120use crate :: ty:: { can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param} ;
120121use crate :: visitors:: for_each_expr;
@@ -1492,22 +1493,66 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
14921493 }
14931494}
14941495
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)
15051518 {
1506- container_path . res == path . res
1519+ start_const == min_const
15071520 } else {
15081521 false
15091522 }
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
15111556}
15121557
15131558/// Checks whether the given expression is a constant integer of the given value.
0 commit comments