Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions clippy_lints/src/loops/explicit_counter_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::Range;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::{EMPTY, Sugg};
use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped};
use clippy_utils::{get_enclosing_block, is_integer_literal, is_integer_literal_untyped};
use rustc_ast::{Label, RangeLimits};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr};
Expand Down Expand Up @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
continue;
}

let is_zero = is_integer_const(cx, initializer, 0);
let is_zero = is_integer_literal(initializer, 0);
let mut applicability = Applicability::MaybeIncorrect;
let span = expr.span.with_hi(arg.span.hi());
let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name));
Expand Down Expand Up @@ -88,7 +88,7 @@ pub(super) fn check<'tcx>(
if pat_snippet == "_"
&& let Some(range) = Range::hir(cx, arg)
&& range.limits == RangeLimits::HalfOpen
&& range.start.is_some_and(|start| is_integer_const(cx, start, 0))
&& range.start.is_some_and(|start| is_integer_literal(start, 0))
&& let Some(end) = range.end
{
let end = snippet_with_applicability(cx, end.span, "..", &mut applicability);
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/loops/needless_range_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::ty::has_iter_method;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, peel_hir_expr_while, sugg};
use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_literal, peel_hir_expr_while, sugg};
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::Applicability;
Expand Down Expand Up @@ -83,7 +83,7 @@ pub(super) fn check<'tcx>(
return;
}

let starts_at_zero = is_integer_const(cx, start, 0);
let starts_at_zero = is_integer_literal(start, 0);

let skip = if starts_at_zero {
String::new()
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/loops/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clippy_utils::res::MaybeResPath;
use clippy_utils::ty::{has_iter_method, implements_trait};
use clippy_utils::{get_parent_expr, is_integer_const, sugg};
use clippy_utils::{get_parent_expr, is_integer_literal, sugg};
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_local};
Expand Down Expand Up @@ -70,7 +70,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> {
match parent.kind {
ExprKind::AssignOp(op, lhs, rhs) if lhs.hir_id == expr.hir_id => {
*state = if op.node == AssignOpKind::AddAssign
&& is_integer_const(self.cx, rhs, 1)
&& is_integer_literal(rhs, 1)
&& *state == IncrementVisitorVarState::Initial
&& self.depth == 0
{
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/manual_option_as_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let Some(range) = clippy_utils::higher::Range::hir(cx, range) else {
return false;
};
range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0))
range.end.is_some_and(|e| clippy_utils::is_integer_literal(e, 0))
},
_ => false,
},
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/methods/range_zip_with_len.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability};
use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, sym};
use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_literal, sym};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath};
use rustc_lint::LateContext;
Expand All @@ -12,7 +12,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator)
// range expression in `.zip()` call: `0..x.len()`
&& let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(cx, zip_arg)
&& is_integer_const(cx, start, 0)
&& is_integer_literal(start, 0)
// `.len()` call
&& let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind
&& len_path.ident.name == sym::len
Expand Down
23 changes: 7 additions & 16 deletions clippy_lints/src/operators/modulo_one.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
use clippy_utils::consts::{FullInt, eval_int};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{is_integer_const, unsext};
use rustc_hir::{BinOpKind, Expr};
use rustc_lint::LateContext;
use rustc_middle::ty;

use super::MODULO_ONE;

pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: &Expr<'_>) {
if op == BinOpKind::Rem {
if is_integer_const(cx, right, 1) {
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
}

if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind()
&& is_integer_const(cx, right, unsext(cx.tcx, -1, *ity))
{
span_lint(
cx,
MODULO_ONE,
expr.span,
"any number modulo -1 will panic/overflow or result in 0",
);
}
let msg = match eval_int(cx, right) {
Some(FullInt::S(-1)) => "any number modulo -1 will panic/overflow or result in 0",
Some(FullInt::U(1)) => "any number modulo 1 will be 0",
_ => return,
};
span_lint(cx, MODULO_ONE, expr.span, msg);
}
}
18 changes: 10 additions & 8 deletions clippy_lints/src/ranges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use clippy_utils::res::MaybeResPath;
use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{fn_def_id, get_expr_use_site, get_parent_expr, higher, is_in_const_context, is_integer_const, sym};
use clippy_utils::{
fn_def_id, get_expr_use_site, get_parent_expr, higher, is_in_const_context, is_integer_literal, sym,
};
use rustc_ast::Mutability;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
Expand Down Expand Up @@ -510,7 +512,7 @@ fn check_range_switch<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
kind: RangeLimits,
predicate: impl for<'hir> FnOnce(&LateContext<'_>, &Expr<'hir>) -> Option<&'hir Expr<'hir>>,
predicate: impl for<'hir> FnOnce(&Expr<'hir>) -> Option<&'hir Expr<'hir>>,
lint: &'static Lint,
msg: &'static str,
operator: &str,
Expand All @@ -524,7 +526,7 @@ fn check_range_switch<'tcx>(
} = range
&& span.can_be_used_for_suggestions()
&& limits == kind
&& let Some(y) = predicate(cx, end)
&& let Some(y) = predicate(end)
&& can_switch_ranges(cx, span.ctxt(), expr, kind, cx.typeck_results().expr_ty(y))
{
span_lint_and_then(cx, lint, span, msg, |diag| {
Expand Down Expand Up @@ -632,7 +634,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
}
}

fn y_plus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
fn y_plus_one<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Binary(
Spanned {
Expand All @@ -641,9 +643,9 @@ fn y_plus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Exp
lhs,
rhs,
) => {
if is_integer_const(cx, lhs, 1) {
if is_integer_literal(lhs, 1) {
Some(rhs)
} else if is_integer_const(cx, rhs, 1) {
} else if is_integer_literal(rhs, 1) {
Some(lhs)
} else {
None
Expand All @@ -653,15 +655,15 @@ fn y_plus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Exp
}
}

fn y_minus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
fn y_minus_one<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Binary(
Spanned {
node: BinOpKind::Sub, ..
},
lhs,
rhs,
) if is_integer_const(cx, rhs, 1) => Some(lhs),
) if is_integer_literal(rhs, 1) => Some(lhs),
_ => None,
}
}
8 changes: 4 additions & 4 deletions clippy_lints/src/transmute/transmuting_null.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt, eval_int};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::{is_integer_const, sym};
use clippy_utils::sym;
use rustc_hir::{ConstBlock, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
Expand All @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
// Catching:
// `std::mem::transmute(0 as *const i32)`
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind
&& is_integer_const(cx, inner_expr, 0)
&& eval_int(cx, inner_expr).is_some_and(FullInt::is_zero)
{
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
Expand All @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
if let ExprKind::Call(func1, [arg1]) = arg.kind
&& (func1.basic_res().is_diag_item(cx, sym::ptr_without_provenance)
|| func1.basic_res().is_diag_item(cx, sym::ptr_without_provenance_mut))
&& is_integer_const(cx, arg1, 0)
&& eval_int(cx, arg1).is_some_and(FullInt::is_zero)
{
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
Expand Down
25 changes: 25 additions & 0 deletions clippy_utils/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,12 @@ pub enum FullInt {
U(u128),
}

impl FullInt {
pub fn is_zero(self) -> bool {
matches!(self, Self::S(0) | Self::U(0))
}
}

impl PartialEq for FullInt {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
Expand Down Expand Up @@ -491,6 +497,25 @@ impl Ord for FullInt {
}
}

/// Evaluates an expression if it's a builtin integer type.
pub fn eval_int(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<FullInt> {
match e.kind {
ExprKind::Lit(lit) if let LitKind::Int(val, _) = lit.node => Some(FullInt::U(val.0)),
ExprKind::Unary(UnOp::Neg, e)
if let ExprKind::Lit(lit) = e.kind
&& let LitKind::Int(val, _) = lit.node =>
{
Some(FullInt::S(val.0.cast_signed().wrapping_neg()))
},
_ if let ty = cx.typeck_results().expr_ty(e)
&& let ty::Int(_) | ty::Uint(_) = *ty.kind() =>
{
ConstEvalCtxt::new(cx).eval(e).and_then(|x| x.int_value(cx.tcx, ty))
},
_ => None,
}
}

/// The context required to evaluate a constant expression.
///
/// This is currently limited to constant folding and reading the value of named constants.
Expand Down
18 changes: 1 addition & 17 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ use source::{SpanRangeExt, walk_span_to_context};
use visitors::{Visitable, for_each_unconsumed_temporary};

use crate::ast_utils::unordered_over;
use crate::consts::{ConstEvalCtxt, Constant};
use crate::consts::ConstEvalCtxt;
use crate::higher::Range;
use crate::msrvs::Msrv;
use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
Expand Down Expand Up @@ -1381,24 +1381,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
false
}

/// Checks whether the given expression is a constant integer of the given value.
/// unlike `is_integer_literal`, this version does const folding
pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
if is_integer_literal(e, value) {
return true;
}
let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
if let Some(Constant::Int(v)) =
ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
{
return value == v;
}
false
}

/// Checks whether the given expression is a constant literal of the given value.
pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
// FIXME: use constant folding
if let ExprKind::Lit(spanned) = expr.kind
&& let LitKind::Int(v, _) = spanned.node
{
Expand Down
4 changes: 1 addition & 3 deletions tests/ui/range_plus_minus_one.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ fn main() {
let _ = (f() + 1)..(f() + 1);

const ONE: usize = 1;
// integer consts are linted, too
for _ in 1..=ONE {}
//~^ range_plus_one
for _ in 1..ONE + ONE {}

let mut vec: Vec<()> = std::vec::Vec::new();
vec.drain(..);
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/range_plus_minus_one.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ fn main() {
let _ = (f() + 1)..(f() + 1);

const ONE: usize = 1;
// integer consts are linted, too
for _ in 1..ONE + ONE {}
//~^ range_plus_one

let mut vec: Vec<()> = std::vec::Vec::new();
vec.drain(..);
Expand Down
28 changes: 11 additions & 17 deletions tests/ui/range_plus_minus_one.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -26,61 +26,55 @@ LL | for _ in 0..(1 + f()) {}
| ^^^^^^^^^^^^ help: use: `0..=f()`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:60:14
|
LL | for _ in 1..ONE + ONE {}
| ^^^^^^^^^^^^ help: use: `1..=ONE`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:70:6
--> tests/ui/range_plus_minus_one.rs:68:6
|
LL | (1..10 + 1).for_each(|_| {});
| ^^^^^^^^^ help: use: `1..=10`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:75:6
--> tests/ui/range_plus_minus_one.rs:73:6
|
LL | (1..10 + 1).into_iter().for_each(|_| {});
| ^^^^^^^^^ help: use: `1..=10`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:80:18
--> tests/ui/range_plus_minus_one.rs:78:18
|
LL | let _ = (1..10 + 1).start_bound();
| ^^^^^^^^^ help: use: `1..=10`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:86:16
--> tests/ui/range_plus_minus_one.rs:84:16
|
LL | let _ = &a[1..1 + 1];
| ^^^^^^^^ help: use: `1..=1`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:90:15
--> tests/ui/range_plus_minus_one.rs:88:15
|
LL | vec.drain(2..3 + 1);
| ^^^^^^^^ help: use: `2..=3`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:94:14
--> tests/ui/range_plus_minus_one.rs:92:14
|
LL | take_arg(10..20 + 1);
| ^^^^^^^^^^ help: use: `10..=20`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:98:16
--> tests/ui/range_plus_minus_one.rs:96:16
|
LL | take_arg({ 10..20 + 1 });
| ^^^^^^^^^^ help: use: `10..=20`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:112:7
--> tests/ui/range_plus_minus_one.rs:110:7
|
LL | a[0..2 + 1][0] = 1;
| ^^^^^^^^ help: use: `0..=2`

error: an exclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:180:6
--> tests/ui/range_plus_minus_one.rs:178:6
|
LL | (1..=n - 1).sum()
| ^^^^^^^^^ help: use: `1..n`
Expand All @@ -89,10 +83,10 @@ LL | (1..=n - 1).sum()
= help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]`

error: an inclusive range would be more readable
--> tests/ui/range_plus_minus_one.rs:192:14
--> tests/ui/range_plus_minus_one.rs:190:14
|
LL | for _ in test!(x)..test!(x) + 1 {
| ^^^^^^^^^^^^^^^^^^^^^^ help: use: `test!(x)..=test!(x)`

error: aborting due to 15 previous errors
error: aborting due to 14 previous errors