Skip to content

Commit 2493be2

Browse files
committed
Improve is_range_full implementation
Make this function work with signed integer types by extracting the underlying type and finding the min and max values. Change the signature to make it more consistent: - The range is now given as an `Expr` in order to extract the type - The container's path is now passed, and only as an `Option` so that the function can be called in the general case without a container
1 parent ee0de53 commit 2493be2

File tree

3 files changed

+65
-20
lines changed

3 files changed

+65
-20
lines changed

clippy_lints/src/methods/clear_with_drain.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::higher::Range;
32
use clippy_utils::is_range_full;
43
use clippy_utils::ty::is_type_diagnostic_item;
54
use rustc_errors::Applicability;
6-
use rustc_hir::Expr;
5+
use rustc_hir::{Expr, ExprKind, QPath};
76
use rustc_lint::LateContext;
87
use rustc_span::symbol::sym;
98
use rustc_span::Span;
@@ -12,7 +11,9 @@ use super::CLEAR_WITH_DRAIN;
1211

1312
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
1413
let ty = cx.typeck_results().expr_ty(recv);
15-
if is_type_diagnostic_item(cx, ty, sym::Vec) && let Some(range) = Range::hir(arg) && is_range_full(cx, recv, range)
14+
if is_type_diagnostic_item(cx, ty, sym::Vec)
15+
&& let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
16+
&& is_range_full(cx, arg, Some(container_path))
1617
{
1718
span_lint_and_sugg(
1819
cx,

clippy_lints/src/methods/iter_with_drain.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::higher::Range;
32
use clippy_utils::is_range_full;
43
use rustc_errors::Applicability;
5-
use rustc_hir::{Expr, ExprKind};
4+
use rustc_hir::{Expr, ExprKind, QPath};
65
use rustc_lint::LateContext;
76
use rustc_span::symbol::sym;
87
use rustc_span::Span;
@@ -14,8 +13,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
1413
&& let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
1514
&& let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
1615
&& matches!(ty_name, sym::Vec | sym::VecDeque)
17-
&& let Some(range) = Range::hir(arg)
18-
&& is_range_full(cx, recv, range)
16+
&& let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
17+
&& is_range_full(cx, arg, Some(container_path))
1918
{
2019
span_lint_and_sugg(
2120
cx,

clippy_utils/src/lib.rs

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ use rustc_hir::{
9696
use rustc_lexer::{tokenize, TokenKind};
9797
use rustc_lint::{LateContext, Level, Lint, LintContext};
9898
use rustc_middle::hir::place::PlaceBase;
99+
use rustc_middle::mir::ConstantKind;
99100
use rustc_middle::ty as rustc_ty;
100101
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
101102
use rustc_middle::ty::binding::BindingMode;
@@ -114,7 +115,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
114115
use rustc_span::Span;
115116
use rustc_target::abi::Integer;
116117

117-
use crate::consts::{constant, Constant};
118+
use crate::consts::{constant, miri_to_const, Constant};
118119
use crate::higher::Range;
119120
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
120121
use 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

Comments
 (0)