diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index b813a18b221e..b10584fb9bd7 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -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}; @@ -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)); @@ -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); diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 83539b977ee4..71280e4b03f8 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -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; @@ -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() diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 68694d21e3d8..81e868b3b7ba 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -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}; @@ -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 { diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 2488873fb789..a851f1ea48db 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -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, }, diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index cf6347d466f2..5fec0c5f2cf7 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -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; @@ -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 diff --git a/clippy_lints/src/operators/modulo_one.rs b/clippy_lints/src/operators/modulo_one.rs index 2e6a071eb184..22ec8b1ee1ee 100644 --- a/clippy_lints/src/operators/modulo_one.rs +++ b/clippy_lints/src/operators/modulo_one.rs @@ -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); } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 93f01594351c..71965ee1e29f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -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; @@ -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, @@ -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| { @@ -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 { @@ -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 @@ -653,7 +655,7 @@ 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 { @@ -661,7 +663,7 @@ fn y_minus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Ex }, lhs, rhs, - ) if is_integer_const(cx, rhs, 1) => Some(lhs), + ) if is_integer_literal(rhs, 1) => Some(lhs), _ => None, } } diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index ed8a099eced0..c85a3155da8f 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -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; @@ -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; @@ -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; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 51db6b6b64c7..098be98f3672 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -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 @@ -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 { + 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. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index fc80378c18aa..19a79d2beaed 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -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}; @@ -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 { diff --git a/tests/ui/range_plus_minus_one.fixed b/tests/ui/range_plus_minus_one.fixed index e07a0e07368b..4c0cb8661197 100644 --- a/tests/ui/range_plus_minus_one.fixed +++ b/tests/ui/range_plus_minus_one.fixed @@ -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(..); diff --git a/tests/ui/range_plus_minus_one.rs b/tests/ui/range_plus_minus_one.rs index 3e6e4f629a51..c853d1f9387a 100644 --- a/tests/ui/range_plus_minus_one.rs +++ b/tests/ui/range_plus_minus_one.rs @@ -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(..); diff --git a/tests/ui/range_plus_minus_one.stderr b/tests/ui/range_plus_minus_one.stderr index 79c482aeaa6b..93d74b1d840c 100644 --- a/tests/ui/range_plus_minus_one.stderr +++ b/tests/ui/range_plus_minus_one.stderr @@ -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` @@ -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