From 3474d6ae5107a33d0ec74bc1ea5ee01c90ac0024 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Aug 2024 00:36:21 -0400 Subject: [PATCH] Require `ConstEvalCtxt` to be constructed. --- clippy_lints/src/assertions_on_constants.rs | 4 +- clippy_lints/src/casts/cast_nan_to_int.rs | 4 +- .../src/casts/cast_possible_truncation.rs | 4 +- clippy_lints/src/casts/cast_sign_loss.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 30 +-- clippy_lints/src/if_not_else.rs | 4 +- clippy_lints/src/implicit_saturating_add.rs | 8 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/indexing_slicing.rs | 9 +- .../src/invalid_upcast_comparisons.rs | 4 +- .../src/loops/while_immutable_condition.rs | 4 +- clippy_lints/src/manual_clamp.rs | 7 +- clippy_lints/src/manual_float_methods.rs | 7 +- clippy_lints/src/manual_rem_euclid.rs | 4 +- clippy_lints/src/manual_rotate.rs | 4 +- clippy_lints/src/manual_strip.rs | 4 +- clippy_lints/src/matches/manual_unwrap_or.rs | 4 +- clippy_lints/src/matches/overlapping_arms.rs | 12 +- .../src/methods/is_digit_ascii_radix.rs | 4 +- clippy_lints/src/methods/is_empty.rs | 4 +- clippy_lints/src/methods/iter_nth_zero.rs | 4 +- clippy_lints/src/methods/iter_skip_zero.rs | 4 +- .../src/methods/iterator_step_by_zero.rs | 4 +- clippy_lints/src/methods/mod.rs | 6 +- clippy_lints/src/methods/repeat_once.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/unnecessary_min_or_max.rs | 13 +- clippy_lints/src/minmax.rs | 20 +- .../operators/absurd_extreme_comparisons.rs | 4 +- .../src/operators/arithmetic_side_effects.rs | 10 +- clippy_lints/src/operators/bit_mask.rs | 4 +- .../src/operators/const_comparisons.rs | 5 +- clippy_lints/src/operators/duration_subsec.rs | 4 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/operators/float_cmp.rs | 8 +- clippy_lints/src/operators/identity_op.rs | 13 +- .../src/operators/modulo_arithmetic.rs | 16 +- .../src/operators/numeric_arithmetic.rs | 4 +- clippy_lints/src/ranges.rs | 11 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/repeat_vec_with_capacity.rs | 4 +- .../src/transmute/transmute_null_to_fn.rs | 7 +- .../src/transmute/transmuting_null.rs | 4 +- .../interning_defined_symbol.rs | 6 +- .../src/utils/internal_lints/invalid_paths.rs | 8 +- clippy_lints/src/vec.rs | 4 +- clippy_lints/src/zero_div_zero.rs | 7 +- clippy_utils/src/consts.rs | 187 +++++++++--------- clippy_utils/src/eager_or_lazy.rs | 16 +- clippy_utils/src/higher.rs | 4 +- clippy_utils/src/hir_utils.rs | 12 +- clippy_utils/src/lib.rs | 10 +- 52 files changed, 269 insertions(+), 277 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index ed4cdce8cb88..7eaac80f969f 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_inside_always_const_context; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return; }; - let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { + let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { return; }; diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs index 5bc8692c289f..464eabe5d9ab 100644 --- a/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/clippy_lints/src/casts/cast_nan_to_int.rs @@ -1,6 +1,6 @@ use super::CAST_NAN_TO_INT; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, } fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - match constant(cx, cx.typeck_results(), e) { + match ConstEvalCtxt::new(cx).eval(e) { // FIXME(f16_f128): add these types when nan checks are available on all platforms Some(Constant::F64(n)) => n.is_nan(), Some(Constant::F32(n)) => n.is_nan(), diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 7c5acd1a678d..102fe25fc67b 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; use clippy_utils::source::snippet; @@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) { Some(c) } else { None diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 8bbd41b0db1e..9daf237344a4 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,7 +1,7 @@ use std::convert::Infallible; use std::ops::ControlFlow; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{method_chain_args, sext}; @@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>( ) -> Option { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Int(ity) = *ty.kind() { return Some(sext(cx.tcx, n, ity)); @@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>( ) -> Option { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Uint(_ity) = *ty.kind() { return Some(n); diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 68bdf88d0a7e..16778a35bb75 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,5 +1,5 @@ use clippy_utils::consts::Constant::{Int, F32, F64}; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal, @@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = constant(cx, cx.typeck_results(), base) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { rhs, ) = receiver.kind { - let recv = match ( - constant(cx, cx.typeck_results(), lhs), - constant(cx, cx.typeck_results(), rhs), - ) { + let ecx = ConstEvalCtxt::new(cx); + let recv = match (ecx.eval(lhs), ecx.eval(rhs)) { (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs, (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs, _ => return, @@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = constant(cx, cx.typeck_results(), receiver) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { @@ -251,7 +249,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } // Check argument - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( SUBOPTIMAL_FLOPS, @@ -291,7 +289,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { if value == Int(2) { if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { @@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { ) = &add_rhs.kind && lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi" - && let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1) - && let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lvalue) = ecx.eval(largs_1) + && let Some(rvalue) = ecx.eval(rargs_1) && Int(2) == lvalue && Int(2) == rvalue { @@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && cx.typeck_results().expr_ty(lhs).is_floating_point() - && let Some(value) = constant(cx, cx.typeck_results(), rhs) + && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && cx.typeck_results().expr_ty(self_arg).is_floating_point() @@ -552,7 +551,7 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match constant_simple(cx, cx.typeck_results(), expr) { + match ConstEvalCtxt::new(cx).eval_simple(expr) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, @@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { mul_lhs, mul_rhs, ) = &div_lhs.kind - && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) - && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(rvalue) = ecx.eval(div_rhs) + && let Some(lvalue) = ecx.eval(mul_rhs) { // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 2f6daeeb90d9..0ebd8d0c237b 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ //! lint on if branches that could be swapped so no `!` operation is necessary //! on the condition -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_else_clause; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -49,7 +49,7 @@ declare_clippy_lint! { declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) { return Constant::Int(0) == value; } false diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index f225c6e7f049..dd5908553e59 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; @@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { - let tr = cx.typeck_results(); - if let Some(Constant::Int(c)) = constant(cx, tr, r) { + let ecx = ConstEvalCtxt::new(cx); + if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); }; - if let Some(Constant::Int(c)) = constant(cx, tr, l) { + if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 526b4e1fba0e..2f9661c9ea38 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; @@ -246,7 +246,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { && let parent_id = cx.tcx.parent_hir_id(expr.hir_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind - && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) + && let Some(Constant::Int(index_value)) = ConstEvalCtxt::new(cx).eval(index_expr) && let Ok(index_value) = index_value.try_into() && index_value < max_suggested_slice diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 3b65901c165a..3ac50b8f1fba 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,7 +1,7 @@ //! lint on indexing and slicing operations use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; use clippy_utils::{higher, is_from_proc_macro}; @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { return; } // Index is a constant uint. - if let Some(constant) = constant(cx, cx.typeck_results(), index) { + if let Some(constant) = ConstEvalCtxt::new(cx).eval(index) { // only `usize` index is legal in rust array index // leave other type to rustc if let Constant::Int(off) = constant @@ -215,14 +215,15 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option, Option) { - let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr)); + let ecx = ConstEvalCtxt::new(cx); + let s = range.start.map(|expr| ecx.eval(expr)); let start = match s { Some(Some(Constant::Int(x))) => Some(x), Some(_) => None, None => Some(0), }; - let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr)); + let e = range.end.map(|expr| ecx.eval(expr)); let end = match e { Some(Some(Constant::Int(x))) => { if range.limits == RangeLimits::Closed { diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 30f2285bdd23..1929fbded3b5 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; @@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) { + if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index e7b3a2c4973c..cc1bd5929d0f 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -1,5 +1,5 @@ use super::WHILE_IMMUTABLE_CONDITION; -use clippy_utils::consts::constant; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use rustc_hir::def::{DefKind, Res}; @@ -10,7 +10,7 @@ use rustc_lint::LateContext; use std::ops::ControlFlow; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - if constant(cx, cx.typeck_results(), cond).is_some() { + if ConstEvalCtxt::new(cx).eval(cond).is_some() { // A pure constant condition (e.g., `while false`) is not linted. return; } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index a79ad018a042..4e0b12f8d13f 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::sugg::Sugg; @@ -122,8 +122,9 @@ impl<'tcx> ClampSuggestion<'tcx> { if max_type != min_type { return false; } - if let Some(max) = constant(cx, cx.typeck_results(), self.params.max) - && let Some(min) = constant(cx, cx.typeck_results(), self.params.min) + let ecx = ConstEvalCtxt::new(cx); + if let Some(max) = ecx.eval(self.params.max) + && let Some(min) = ecx.eval(self.params.min) && let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max) { ord != Ordering::Greater diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 03416ba96de7..6bdc79129a9b 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{is_from_proc_macro, path_to_local}; @@ -95,8 +95,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { || cx.tcx.features().declared(sym!(const_float_classify)) ) && let [first, second, const_1, const_2] = exprs - && let Some(const_1) = constant(cx, cx.typeck_results(), const_1) - && let Some(const_2) = constant(cx, cx.typeck_results(), const_2) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(const_1) = ecx.eval(const_1) + && let Some(const_2) = ecx.eval(const_2) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 78a750f0dcd0..c5eb1e1384f7 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::{in_constant, path_to_local}; @@ -117,7 +117,7 @@ fn check_for_either_unsigned_int_constant<'a>( } fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option { - let int_const = constant_full_int(cx, cx.typeck_results(), expr)?; + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index a517a4d50752..07537fc65c08 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg; use rustc_errors::Applicability; @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = constant(cx, cx.typeck_results(), r)?; + let const_expr = ConstEvalCtxt::new(cx).eval(r)?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 686ecccf8294..aedb3822089b 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; @@ -145,7 +145,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E // Returns the length of the `expr` if it's a constant string or char. fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - let value = constant(cx, cx.typeck_results(), expr)?; + let value = ConstEvalCtxt::new(cx).eval(expr)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 85a08f81c2f3..2d4c8daf5cb6 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::constant_simple; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -69,7 +69,7 @@ fn check_and_lint<'tcx>( && let Some(ty_name) = find_type_name(cx, ty) && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span) && let Some(indent) = indent_of(cx, expr.span) - && constant_simple(cx, cx.typeck_results(), else_expr).is_some() + && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() { lint(cx, expr, let_expr, ty_name, or_body_snippet, indent); } diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index c7aa497220f3..1ca9524c42cf 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt}; +use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -34,21 +34,21 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, + Some(lhs) => ConstEvalCtxt::new(cx).eval(lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }, }; let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, + Some(rhs) => ConstEvalCtxt::new(cx).eval(rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? }, }; - let lhs_val = lhs_const.int_value(cx, ty)?; - let rhs_val = rhs_const.int_value(cx, ty)?; + let lhs_val = lhs_const.int_value(cx.tcx, ty)?; + let rhs_val = rhs_const.int_value(cx.tcx, ty)?; let rhs_bound = match range_end { RangeEnd::Included => EndBound::Included(rhs_val), RangeEnd::Excluded => EndBound::Excluded(rhs_val), @@ -60,7 +60,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) } if let PatKind::Lit(value) = pat.kind { - let value = constant_full_int(cx, cx.typeck_results(), value)?; + let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 210e4ae0a7bc..22d896433f07 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -2,7 +2,7 @@ use super::IS_DIGIT_ASCII_RADIX; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index d921b7ea14f5..cc82f6cfd638 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::constant_is_empty; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::{find_binding_init, path_to_local}; use rustc_hir::{Expr, HirId}; @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if !receiver.span.eq_ctxt(init_expr.span) { return; } - if let Some(init_is_empty) = constant_is_empty(cx, init_expr) { + if let Some(init_is_empty) = ConstEvalCtxt::new(cx).eval_is_empty(init_expr) { span_lint( cx, CONST_IS_EMPTY, diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 262a57ab591a..9ff6eaa34871 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 6b696b42a693..39e440e784f6 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| { + && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { if let Constant::Int(arg) = constant { Some(arg) } else { diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index b631cd00cda4..9b358235a40d 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir as hir; @@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { if is_trait_method(cx, expr, sym::Iterator) { - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) { + if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( cx, ITERATOR_STEP_BY_ZERO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4c5300315816..ca33cf71046b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -133,7 +133,7 @@ mod zst_offset; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; @@ -4905,13 +4905,13 @@ impl Methods { str_split::check(cx, expr, recv, arg); }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index bb4cdd2a6fa1..7837517ed5d8 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 4f42fb73547a..12cabd43cb1b 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,5 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; @@ -301,7 +301,7 @@ fn parse_iter_usage<'tcx>( }; }, ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 78851d4122f1..86c0a6322b66 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::consts::{constant, constant_with_source, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; use clippy_utils::source::snippet; use rustc_errors::Applicability; @@ -20,10 +20,9 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) { let typeck_results = cx.typeck_results(); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = - constant_with_source(cx, typeck_results, recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = - constant_with_source(cx, typeck_results, arg) + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results); + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; @@ -78,9 +77,9 @@ enum Extrema { fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?; + let cv = ConstEvalCtxt::new(cx).eval(expr)?; - match (cv.int_value(cx, ty)?, ty.kind()) { + match (cv.int_value(cx.tcx, ty)?, ty.kind()) { (FullInt::S(i), &ty::Int(ity)) if i == i128::MIN >> (128 - ity.bit_width()?) => Some(Extrema::Minimum), (FullInt::S(i), &ty::Int(ity)) if i == i128::MAX >> (128 - ity.bit_width()?) => Some(Extrema::Maximum), (FullInt::U(i), &ty::Uint(uty)) if i == u128::MAX >> (128 - uty.bit_width()?) => Some(Extrema::Maximum), diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index c3fbca1d560e..e95864c6db8b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir::{Expr, ExprKind}; @@ -106,15 +106,11 @@ fn fetch_const<'a, 'tcx>( if args.next().is_some() { return None; } - constant_simple(cx, cx.typeck_results(), first_arg).map_or_else( - || constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)), - |c| { - if constant_simple(cx, cx.typeck_results(), second_arg).is_none() { - // otherwise ignore - Some((m, c, second_arg)) - } else { - None - } - }, - ) + let ecx = ConstEvalCtxt::new(cx); + match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + (Some(c), None) => Some((m, c, second_arg)), + (None, Some(c)) => Some((m, c, first_arg)), + // otherwise ignore + _ => None, + } } diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 9769da6d3e9b..a0de5ea711ca 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -3,7 +3,7 @@ use rustc_lint::LateContext; use rustc_middle::ty; use clippy_utils::comparisons::{normalize_comparison, Rel}; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::ty::is_isize_or_usize; @@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>( fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?; + let cv = ConstEvalCtxt::new(cx).eval(expr)?; let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index a7e381be743d..bc71a4790b9d 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,6 +1,6 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; @@ -162,7 +162,7 @@ impl ArithmeticSideEffects { { return Some(n.get()); } - if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) { return Some(n); } None @@ -200,7 +200,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if constant_simple(cx, cx.typeck_results(), expr).is_some() { + if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { return; } if !matches!( @@ -280,7 +280,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if constant_simple(cx, cx.typeck_results(), receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty(receiver); @@ -308,7 +308,7 @@ impl ArithmeticSideEffects { let hir::UnOp::Neg = un_op else { return; }; - if constant(cx, cx.typeck_results(), un_expr).is_some() { + if ConstEvalCtxt::new(cx).eval(un_expr).is_some() { return; } let ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index 545e680ce0de..4414056a467c 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: } fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option { - match constant(cx, cx.typeck_results(), lit)? { + match ConstEvalCtxt::new(cx).eval(lit)? { Constant::Int(n) => Some(n), _ => None, } diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 1f9f3b3c4730..c13175243962 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::layout::HasTyCtxt; @@ -26,7 +26,8 @@ fn comparison_to_const<'tcx>( if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { - match (constant(cx, typeck, left), constant(cx, typeck, right)) { + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + match (ecx.eval(left), ecx.eval(right)) { (Some(_), Some(_)) => None, (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))), diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index ca3112ce5c46..e3029f8438e5 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) { let suggested_fn = match (method_path.ident.as_str(), divisor) { ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index fc2129b91036..24bfe2b050bb 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::same_type_and_consts; @@ -39,7 +39,7 @@ fn check_op<'tcx>( other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.param_env, tck).eval_simple(op) == Some(Constant::Int(0)) { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 0e5b440c50f2..df6e67455965 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_with_source, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; @@ -17,12 +17,14 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { - let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { + let typeck = cx.typeck_results(); + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + let left_is_local = match ecx.eval_with_source(left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) { + let right_is_local = match ecx.eval_with_source(right) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 0879dcd9bcdf..830be50c8ba4 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{clip, peel_hir_expr_refs, unsext}; @@ -184,14 +184,13 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Exp && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code && !(cmp == BinOpKind::Shl - && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) - && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) + && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0)) + && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1))) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { - let lhs_const = constant_full_int(cx, cx.typeck_results(), left); - let rhs_const = constant_full_int(cx, cx.typeck_results(), right); - if match (lhs_const, rhs_const) { + let ecx = ConstEvalCtxt::new(cx); + if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -201,7 +200,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool { - if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index d65fffac5a82..c83bdda347a6 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; @@ -42,15 +42,11 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }; if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne { - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) { - return true; - } - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) { - return true; - } + let ecx = ConstEvalCtxt::new(cx); + matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + } else { + false } - - false } struct OperandInfo { @@ -60,7 +56,7 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - match constant(cx, cx.typeck_results(), operand) { + match ConstEvalCtxt::new(cx).eval(operand) { Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index ea933168cfd5..565294bb40a8 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,5 @@ use super::FLOAT_ARITHMETIC; -use clippy_utils::consts::constant_simple; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::LateContext; @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 829fb58bc652..25cfe830c778 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; use clippy_utils::sugg::Sugg; @@ -319,7 +319,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> _ => return None, }; if let Some(id) = path_to_local(l) { - if let Some(c) = constant(cx, cx.typeck_results(), r) { + if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, expr: r, @@ -331,7 +331,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> }); } } else if let Some(id) = path_to_local(r) { - if let Some(c) = constant(cx, cx.typeck_results(), l) { + if let Some(c) = ConstEvalCtxt::new(cx).eval(l) { return Some(RangeBounds { val: c, expr: l, @@ -451,8 +451,9 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { }) = higher::Range::hir(expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() - && let Some(start_idx) = constant(cx, cx.typeck_results(), start) - && let Some(end_idx) = constant(cx, cx.typeck_results(), end) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(start_idx) = ecx.eval(start) + && let Some(end_idx) = ecx.eval(end) && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx) && is_empty_range(limits, ordering) { diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 8cacb646f514..95014b230431 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::snippet_opt; use clippy_utils::{def_path_def_ids, path_def_id, paths}; @@ -148,7 +148,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { - constant(cx, cx.typeck_results(), e).and_then(|c| match c { + ConstEvalCtxt::new(cx).eval(e).and_then(|c| match c { Constant::Str(s) => Some(s), _ => None, }) diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index 792d8fc88f0b..678681ea4252 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; @@ -69,7 +69,7 @@ fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) { && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr) && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) && !len_expr.span.from_expansion() - && let Some(Constant::Int(2..)) = constant(cx, cx.typeck_results(), expr_or_init(cx, len_expr)) + && let Some(Constant::Int(2..)) = ConstEvalCtxt::new(cx).eval(expr_or_init(cx, len_expr)) { emit_lint( cx, diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index b26365e34ab9..7acf3be51fb7 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -33,10 +33,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // transmute over constants that resolve to `null`. ExprKind::Path(ref _qpath) - if matches!( - constant(cx, cx.typeck_results(), casts_peeled), - Some(Constant::RawPtr(0)) - ) => + if matches!(ConstEvalCtxt::new(cx).eval(casts_peeled), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); true diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 471bd44b5d5e..544014bd32b3 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. if let ExprKind::Path(ref _qpath) = arg.kind - && let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg) + && let Some(Constant::RawPtr(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index b017a6bf665c..7c2e23995c1d 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && match_def_path(cx, *def_id, &paths::SYMBOL_INTERN) - && let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg) + && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) && let value = Symbol::intern(&arg).as_u32() && let Some(&def_id) = self.symbol_map.get(&value) { @@ -199,7 +199,7 @@ impl InterningDefinedSymbol { }); } // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) { let value = Symbol::intern(&s).as_u32(); // ...which matches a symbol constant if let Some(&def_id) = self.symbol_map.get(&value) { diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 980437259c3e..0ffcb433481e 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; @@ -32,9 +32,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if mod_name.as_str() == "paths" && let hir::ItemKind::Const(.., body_id) = item.kind - && let body = cx.tcx.hir().body(body_id) - && let typeck_results = cx.tcx.typeck_body(body_id) - && let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value) + && let Some(Constant::Vec(path)) = + ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(item.owner_id), cx.tcx.typeck(item.owner_id)) + .eval_simple(cx.tcx.hir().body(body_id).value) && let Some(path) = path .iter() .map(|x| { diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index a831234906bf..228db14d1b7c 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_copy; @@ -159,7 +159,7 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) { + if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { return; diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 60d8a13d3599..5eb207a0aedb 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -36,8 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. - && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left) - && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lhs_value) = ecx.eval_simple(left) + && let Some(rhs_value) = ecx.eval_simple(right) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index d465daa06e27..e843b68e0a28 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -14,14 +14,13 @@ use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; -use rustc_middle::ty::{ - self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy, -}; +use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::symbol::Ident; use rustc_span::{sym, SyntaxContext}; use rustc_target::abi::Size; +use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::iter; @@ -265,10 +264,10 @@ impl<'tcx> Constant<'tcx> { } /// Returns the integer value or `None` if `self` or `val_type` is not integer type. - pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option { + pub fn int_value(&self, tcx: TyCtxt<'_>, val_type: Ty<'_>) -> Option { if let Constant::Int(const_int) = *self { match *val_type.kind() { - ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Int(ity) => Some(FullInt::S(sext(tcx, const_int, ity))), ty::Uint(_) => Some(FullInt::U(const_int)), _ => None, } @@ -324,6 +323,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan } /// The source of a constant value. +#[derive(Clone, Copy)] pub enum ConstantSource { /// The value is determined solely from the expression. Local, @@ -333,54 +333,11 @@ pub enum ConstantSource { CoreConstant, } impl ConstantSource { - pub fn is_local(&self) -> bool { + pub fn is_local(self) -> bool { matches!(self, Self::Local) } } -/// Attempts to check whether the expression is a constant representing an empty slice, str, array, -/// etc… -pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option { - ConstEvalLateContext::new(lcx.tcx, lcx.param_env, lcx.typeck_results()).expr_is_empty(e) -} - -/// Attempts to evaluate the expression as a constant. -pub fn constant<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &'tcx TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option> { - ConstEvalLateContext::new(lcx.tcx, lcx.param_env, typeck_results).expr(e) -} - -/// Attempts to evaluate the expression as a constant. -pub fn constant_with_source<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &'tcx TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<(Constant<'tcx>, ConstantSource)> { - let mut ctxt = ConstEvalLateContext::new(lcx.tcx, lcx.param_env, typeck_results); - let res = ctxt.expr(e); - res.map(|x| (x, ctxt.source)) -} - -/// Attempts to evaluate an expression only if its value is not dependent on other items. -pub fn constant_simple<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &'tcx TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option> { - constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c)) -} - -pub fn constant_full_int<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &'tcx TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option { - constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e)) -} - #[derive(Copy, Clone, Debug, Eq)] pub enum FullInt { S(i128), @@ -419,45 +376,87 @@ impl Ord for FullInt { } } -pub struct ConstEvalLateContext<'tcx> { +/// The context required to evaluate a constant expression. +/// +/// This is currently limited to constant folding and reading the value of named constants. +pub struct ConstEvalCtxt<'tcx> { tcx: TyCtxt<'tcx>, - typeck_results: &'tcx TypeckResults<'tcx>, param_env: ParamEnv<'tcx>, - source: ConstantSource, - args: GenericArgsRef<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + source: Cell, } -impl<'tcx> ConstEvalLateContext<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>) -> Self { +impl<'tcx> ConstEvalCtxt<'tcx> { + /// Creates the evaluation context from the lint context. This requires the lint context to be + /// in a body (i.e. `cx.enclosing_body.is_some()`). + pub fn new(cx: &LateContext<'tcx>) -> Self { + Self { + tcx: cx.tcx, + param_env: cx.param_env, + typeck: cx.typeck_results(), + source: Cell::new(ConstantSource::Local), + } + } + + /// Creates an evaluation context. + pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self { Self { tcx, - typeck_results, param_env, - source: ConstantSource::Local, - args: List::empty(), + typeck, + source: Cell::new(ConstantSource::Local), + } + } + + /// Attempts to evaluate the expression and returns both the value and whether it's dependant on + /// other items. + pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + self.source.set(ConstantSource::Local); + self.expr(e).map(|c| (c, self.source.get())) + } + + /// Attempts to evaluate the expression. + pub fn eval(&self, e: &Expr<'_>) -> Option> { + self.expr(e) + } + + /// Attempts to evaluate the expression without accessing other items. + pub fn eval_simple(&self, e: &Expr<'_>) -> Option> { + match self.eval_with_source(e) { + Some((x, ConstantSource::Local)) => Some(x), + _ => None, + } + } + + /// Attempts to evaluate the expression as an integer without accessing other items. + pub fn eval_full_int(&self, e: &Expr<'_>) -> Option { + match self.eval_with_source(e) { + Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), + _ => None, } } /// Simple constant folding: Insert an expression, get a constant or none. - pub fn expr(&mut self, e: &Expr<'_>) -> Option> { + fn expr(&self, e: &Expr<'_>) -> Option> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.typeck_results.qpath_res(qpath, e.hir_id()).opt_def_id() - { + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { self.tcx.crate_name(def_id.krate) == sym::core } else { false }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |self_, result| { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { let result = mir_to_const(self_.tcx, result)?; // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source = if is_core_crate && !matches!(self_.source, ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }; + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); Some(result) }) }, @@ -466,21 +465,21 @@ impl<'tcx> ConstEvalLateContext<'tcx> { if is_direct_expn_of(e.span, "cfg").is_some() { None } else { - Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e))) + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) } }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Repeat(value, _) => { - let n = match self.typeck_results.expr_ty(e).kind() { + let n = match self.typeck.expr_ty(e).kind() { ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, _ => span_bug!(e.span, "typeck error"), }; self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) }, ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op { - UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), - UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), + UnOp::Not => self.constant_not(&o, self.typeck.expr_ty(e)), + UnOp::Neg => self.constant_negate(&o, self.typeck.expr_ty(e)), UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), @@ -488,8 +487,9 @@ impl<'tcx> ConstEvalLateContext<'tcx> { ExprKind::Call(callee, args) => { // We only handle a few const functions for now. if args.is_empty() - && let ExprKind::Path(qpath) = &callee.kind - && let Some(did) = self.typeck_results.qpath_res(qpath, callee.hir_id).opt_def_id() + && let ExprKind::Path(qpath @ QPath::TypeRelative(_, name)) = &callee.kind + && let Res::Def(DefKind::AssocFn, did) = self.typeck.qpath_res(qpath, callee.hir_id) + && name.ident.as_str() == "max_value" { match self.tcx.get_diagnostic_name(did) { Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)), @@ -524,20 +524,20 @@ impl<'tcx> ConstEvalLateContext<'tcx> { /// Simple constant folding to determine if an expression is an empty slice, str, array, … /// `None` will be returned if the constness cannot be determined, or if the resolution /// leaves the local crate. - pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option { + pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.tcx.hir().body(body).value), - ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.eval_is_empty(e), ExprKind::Path(ref qpath) => { if !self - .typeck_results + .typeck .qpath_res(qpath, e.hir_id) .opt_def_id() .is_some_and(DefId::is_local) { return None; } - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |self_, result| { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { mir_is_empty(self_.tcx, result) }) }, @@ -554,7 +554,7 @@ impl<'tcx> ConstEvalLateContext<'tcx> { }, ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), ExprKind::Repeat(..) => { - if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { + if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() { Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0) } else { span_bug!(e.span, "typeck error"); @@ -607,16 +607,16 @@ impl<'tcx> ConstEvalLateContext<'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&mut self, vec: &[Expr<'_>]) -> Option>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option>> { vec.iter().map(|elem| self.expr(elem)).collect::>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option + fn fetch_path_and_apply(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option where - F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option, + F: FnOnce(&Self, mir::Const<'tcx>) -> Option, { - let res = self.typeck_results.qpath_res(qpath, id); + let res = self.typeck.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { // Check if this constant is based on `cfg!(..)`, @@ -636,12 +636,7 @@ impl<'tcx> ConstEvalLateContext<'tcx> { return None; } - let args = self.typeck_results.node_args(id); - let args = if self.args.is_empty() { - args - } else { - EarlyBinder::bind(args).instantiate(self.tcx, self.args) - }; + let args = self.typeck.node_args(id); let result = self .tcx .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) @@ -653,7 +648,7 @@ impl<'tcx> ConstEvalLateContext<'tcx> { } } - fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option> { let lhs = self.expr(lhs); let index = self.expr(index); @@ -683,7 +678,7 @@ impl<'tcx> ConstEvalLateContext<'tcx> { } /// A block can only yield a constant if it has exactly one constant expression. - fn block(&mut self, block: &Block<'_>) -> Option> { + fn block(&self, block: &Block<'_>) -> Option> { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -702,11 +697,11 @@ impl<'tcx> ConstEvalLateContext<'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source = ConstantSource::Constant; + self.source.set(ConstantSource::Constant); } } else { // Unable to access the source. Assume a non-local dependency. - self.source = ConstantSource::Constant; + self.source.set(ConstantSource::Constant); } } @@ -716,7 +711,7 @@ impl<'tcx> ConstEvalLateContext<'tcx> { } } - fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option> { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -728,11 +723,11 @@ impl<'tcx> ConstEvalLateContext<'tcx> { } } - fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { + (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck.expr_ty_opt(left)?.kind() { ty::Int(ity) => { let (ty_min_value, _) = ity.min_max()?; let bits = ity.bits(); diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 6c40029a9de7..a6dd12a28c5b 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::consts::{constant, FullInt}; +use crate::consts::{ConstEvalCtxt, FullInt}; use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; @@ -206,7 +206,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if constant(self.cx, self.cx.typeck_results(), right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { self.eagerness |= NoChange; }, @@ -232,7 +232,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && constant(self.cx, self.cx.typeck_results(), right).is_none() => + && ConstEvalCtxt::new(self.cx).eval(right).is_none() => { self.eagerness |= NoChange; }, @@ -240,9 +240,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) && let right_ty = self.cx.typeck_results().expr_ty(right) - && let left = constant(self.cx, self.cx.typeck_results(), left) - && let right = constant(self.cx, self.cx.typeck_results(), right) - .and_then(|c| c.int_value(self.cx, right_ty)) + && let ecx = ConstEvalCtxt::new(self.cx) + && let left = ecx.eval(left) + && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -261,8 +261,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && (constant(self.cx, self.cx.typeck_results(), left).is_none() - || constant(self.cx, self.cx.typeck_results(), right).is_none()) => + && let ecx = ConstEvalCtxt::new(self.cx) + && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 33c88efa0cd3..8970b4d12298 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -2,7 +2,7 @@ #![deny(clippy::missing_docs_in_private_items)] -use crate::consts::{constant_simple, Constant}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; @@ -471,7 +471,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.first()?; - return match constant_simple(cx, cx.typeck_results(), arg) { + return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 28178a61a932..f325e4eaf154 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::constant_simple; +use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt}; use crate::tokenize_with_text; @@ -255,8 +255,8 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_lhs, left), - constant_simple(self.inner.cx, typeck_rhs, right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right), ) && l == r { @@ -714,9 +714,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { - let simple_const = self - .maybe_typeck_results - .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e)); + let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e) + }); // const hashing may result in the same hash as some unrelated node, so add a sort of // discriminant depending on which path we're choosing next diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b190f2c62c64..06b599d71227 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -126,7 +126,7 @@ use rustc_span::{sym, Span}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{constant, mir_to_const, Constant}; +use crate::consts::{mir_to_const, ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1579,7 +1579,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) && let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) - && let Some(start_const) = constant(cx, cx.typeck_results(), start) + && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { start_const == min_const } else { @@ -1592,7 +1592,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) && let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) - && let Some(end_const) = constant(cx, cx.typeck_results(), end) + && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { end_const == max_const } else { @@ -1623,7 +1623,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool return true; } let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); - if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { + if let Some(Constant::Int(v)) = + ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e) + { return value == v; } false