Auto merge of #13200 - Jarcho:constant_no_typeck, r=Alexendoo

Don't use `LateContext` in the constant evaluator

This also changes the interface to require explicitly creating the context. `constant` could be added back in, but the others are probably not worth it.

A couple of bugs have been fixed. The wrong `TypeckResults` was used once when evaluating a constant, and the wrong `ParamEnv` was used by some callers (there wasn't a way to use the correct one).

changelog: none
This commit is contained in:
bors 2024-08-08 14:42:42 +00:00
commit b1e87922c1
54 changed files with 395 additions and 386 deletions

View File

@ -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::diagnostics::span_lint_and_help;
use clippy_utils::is_inside_always_const_context; use clippy_utils::is_inside_always_const_context;
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; 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 { let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else {
return; 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; return;
}; };

View File

@ -1,6 +1,6 @@
use super::CAST_NAN_TO_INT; 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 clippy_utils::diagnostics::span_lint_and_note;
use rustc_hir::Expr; use rustc_hir::Expr;
use rustc_lint::LateContext; 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 { 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 // FIXME(f16_f128): add these types when nan checks are available on all platforms
Some(Constant::F64(n)) => n.is_nan(), Some(Constant::F64(n)) => n.is_nan(),
Some(Constant::F32(n)) => n.is_nan(), Some(Constant::F32(n)) => n.is_nan(),

View File

@ -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::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::expr_or_init; use clippy_utils::expr_or_init;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType;
use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
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) Some(c)
} else { } else {
None None

View File

@ -1,7 +1,7 @@
use std::convert::Infallible; use std::convert::Infallible;
use std::ops::ControlFlow; 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::diagnostics::span_lint;
use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
use clippy_utils::{method_chain_args, sext}; use clippy_utils::{method_chain_args, sext};
@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>(
) -> Option<i128> { ) -> Option<i128> {
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); 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() && let ty::Int(ity) = *ty.kind()
{ {
return Some(sext(cx.tcx, n, ity)); return Some(sext(cx.tcx, n, ity));
@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>(
) -> Option<u128> { ) -> Option<u128> {
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); 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() && let ty::Uint(_ity) = *ty.kind()
{ {
return Some(n); return Some(n);

View File

@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
.const_eval_poly(def_id.to_def_id()) .const_eval_poly(def_id.to_def_id())
.ok() .ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty)); .map(|val| rustc_middle::mir::Const::from_value(val, ty));
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
if let ty::Adt(adt, _) = ty.kind() { if let ty::Adt(adt, _) = ty.kind() {
if adt.is_enum() { if adt.is_enum() {
ty = adt.repr().discr_type().to_ty(cx.tcx); ty = adt.repr().discr_type().to_ty(cx.tcx);

View File

@ -1,5 +1,5 @@
use clippy_utils::consts::Constant::{Int, F32, F64}; 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::diagnostics::span_lint_and_sugg;
use clippy_utils::{ use clippy_utils::{
eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [
// Returns the specialized log method for a given base if base is constant // Returns the specialized log method for a given base if base is constant
// and is one of 2, 10 and e // and is one of 2, 10 and e
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { 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 { if F32(2.0) == value || F64(2.0) == value {
return Some("log2"); return Some("log2");
} else if F32(10.0) == value || F64(10.0) == value { } else if F32(10.0) == value || F64(10.0) == value {
@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
rhs, rhs,
) = receiver.kind ) = receiver.kind
{ {
let recv = match ( let ecx = ConstEvalCtxt::new(cx);
constant(cx, cx.typeck_results(), lhs), let recv = match (ecx.eval(lhs), ecx.eval(rhs)) {
constant(cx, cx.typeck_results(), rhs),
) {
(Some(value), _) if F32(1.0) == value || F64(1.0) == value => 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, (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs,
_ => return, _ => return,
@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
// Check receiver // 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 { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
Some("exp") Some("exp")
} else if F32(2.0) == value || F64(2.0) == value { } 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 // 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 { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
( (
SUBOPTIMAL_FLOPS, 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<'_>]) { 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 value == Int(2) {
if let Some(parent) = get_parent_expr(cx, expr) { if let Some(parent) = get_parent_expr(cx, expr) {
if let Some(grandparent) = get_parent_expr(cx, parent) { if let Some(grandparent) = get_parent_expr(cx, parent) {
@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
) = &add_rhs.kind ) = &add_rhs.kind
&& lmethod_name.as_str() == "powi" && lmethod_name.as_str() == "powi"
&& rmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"
&& let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1) && let ecx = ConstEvalCtxt::new(cx)
&& let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1) && let Some(lvalue) = ecx.eval(largs_1)
&& let Some(rvalue) = ecx.eval(rargs_1)
&& Int(2) == lvalue && Int(2) == lvalue
&& Int(2) == rvalue && Int(2) == rvalue
{ {
@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
rhs, rhs,
) = expr.kind ) = expr.kind
&& cx.typeck_results().expr_ty(lhs).is_floating_point() && 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) && (F32(1.0) == value || F64(1.0) == value)
&& let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
&& cx.typeck_results().expr_ty(self_arg).is_floating_point() && 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 /// Returns true iff expr is some zero literal
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { 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(Int(i)) => i == 0,
Some(F32(f)) => f == 0.0, Some(F32(f)) => f == 0.0,
Some(F64(f)) => f == 0.0, Some(F64(f)) => f == 0.0,
@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
mul_lhs, mul_lhs,
mul_rhs, mul_rhs,
) = &div_lhs.kind ) = &div_lhs.kind
&& let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) && let ecx = ConstEvalCtxt::new(cx)
&& let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs) && 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 // TODO: also check for constant values near PI/180 or 180/PI
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)

View File

@ -1,7 +1,7 @@
//! lint on if branches that could be swapped so no `!` operation is necessary //! lint on if branches that could be swapped so no `!` operation is necessary
//! on the condition //! 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::diagnostics::span_lint_and_help;
use clippy_utils::is_else_clause; use clippy_utils::is_else_clause;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
@ -49,7 +49,7 @@ declare_clippy_lint! {
declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { 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; return Constant::Int(0) == value;
} }
false false

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr; use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> {
fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { 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 { if let ExprKind::Binary(op, l, r) = expr.kind {
let tr = cx.typeck_results(); let ecx = ConstEvalCtxt::new(cx);
if let Some(Constant::Int(c)) = constant(cx, tr, r) { if let Some(Constant::Int(c)) = ecx.eval(r) {
return Some((c, op.node, l)); 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)); return Some((c, invert_op(op.node)?, r));
} }
} }

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLet; use clippy_utils::higher::IfLet;
use clippy_utils::ty::is_copy; 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 parent_id = cx.tcx.parent_hir_id(expr.hir_id)
&& let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id)
&& let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind && 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() && let Ok(index_value) = index_value.try_into()
&& index_value < max_suggested_slice && index_value < max_suggested_slice

View File

@ -1,7 +1,7 @@
//! lint on indexing and slicing operations //! lint on indexing and slicing operations
use clippy_config::Conf; 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::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
use clippy_utils::{higher, is_from_proc_macro}; use clippy_utils::{higher, is_from_proc_macro};
@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
return; return;
} }
// Index is a constant uint. // 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 // only `usize` index is legal in rust array index
// leave other type to rustc // leave other type to rustc
if let Constant::Int(off) = constant 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 /// 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. /// 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<u128>, Option<u128>) { fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) {
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 { let start = match s {
Some(Some(Constant::Int(x))) => Some(x), Some(Some(Constant::Int(x))) => Some(x),
Some(_) => None, Some(_) => None,
None => Some(0), 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 { let end = match e {
Some(Some(Constant::Int(x))) => { Some(Some(Constant::Int(x))) => {
if range.limits == RangeLimits::Closed { if range.limits == RangeLimits::Closed {

View File

@ -7,7 +7,7 @@ use rustc_span::Span;
use clippy_utils::comparisons; use clippy_utils::comparisons;
use clippy_utils::comparisons::Rel; 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::diagnostics::span_lint;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>(
invert: bool, invert: bool,
) { ) {
if let Some((lb, ub)) = lhs_bounds { 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 rel == Rel::Eq || rel == Rel::Ne {
if norm_rhs_val < lb || norm_rhs_val > ub { if norm_rhs_val < lb || norm_rhs_val > ub {
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);

View File

@ -1,5 +1,5 @@
use super::WHILE_IMMUTABLE_CONDITION; 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::diagnostics::span_lint_and_then;
use clippy_utils::usage::mutated_variables; use clippy_utils::usage::mutated_variables;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
@ -10,7 +10,7 @@ use rustc_lint::LateContext;
use std::ops::ControlFlow; use std::ops::ControlFlow;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { 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. // A pure constant condition (e.g., `while false`) is not linted.
return; return;
} }

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::higher::If; use clippy_utils::higher::If;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
@ -122,8 +122,9 @@ impl<'tcx> ClampSuggestion<'tcx> {
if max_type != min_type { if max_type != min_type {
return false; return false;
} }
if let Some(max) = constant(cx, cx.typeck_results(), self.params.max) let ecx = ConstEvalCtxt::new(cx);
&& let Some(min) = constant(cx, cx.typeck_results(), self.params.min) 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) && let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max)
{ {
ord != Ordering::Greater ord != Ordering::Greater

View File

@ -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::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::{is_from_proc_macro, path_to_local}; 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)) || cx.tcx.features().declared(sym!(const_float_classify))
) )
&& let [first, second, const_1, const_2] = exprs && let [first, second, const_1, const_2] = exprs
&& let Some(const_1) = constant(cx, cx.typeck_results(), const_1) && let ecx = ConstEvalCtxt::new(cx)
&& let Some(const_2) = constant(cx, cx.typeck_results(), const_2) && 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)) && 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 // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
// case somebody does that for some reason // case somebody does that for some reason

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::{is_in_const_context, path_to_local}; use clippy_utils::{is_in_const_context, 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<u128> { fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
let int_const = constant_full_int(cx, cx.typeck_results(), expr)?; let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?;
match int_const { match int_const {
FullInt::S(s) => s.try_into().ok(), FullInt::S(s) => s.try_into().ok(),
FullInt::U(u) => Some(u), FullInt::U(u) => Some(u),

View File

@ -1,6 +1,6 @@
use std::fmt::Display; 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::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg; use clippy_utils::sugg;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -66,7 +66,7 @@ fn parse_shift<'tcx>(
BinOpKind::Shr => ShiftDirection::Right, BinOpKind::Shr => ShiftDirection::Right,
_ => return None, _ => 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 { if let Constant::Int(shift) = const_expr {
return Some((dir, shift, l)); return Some((dir, shift, l));
} }

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables; use clippy_utils::usage::mutated_variables;
@ -147,7 +147,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. // Returns the length of the `expr` if it's a constant string or char.
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
let value = constant(cx, cx.typeck_results(), expr)?; let value = ConstEvalCtxt::new(cx).eval(expr)?;
match value { match value {
Constant::Str(value) => Some(value.len() as u128), Constant::Str(value) => Some(value.len() as u128),
Constant::Char(value) => Some(value.len_utf8() as u128), Constant::Char(value) => Some(value.len_utf8() as u128),

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item; 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(ty_name) = find_type_name(cx, ty)
&& let Some(or_body_snippet) = snippet_opt(cx, else_expr.span) && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
&& let Some(indent) = indent_of(cx, 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); lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
} }

View File

@ -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 clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering; use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@ -34,19 +34,19 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
if let Arm { pat, guard: None, .. } = *arm { if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
let lhs_const = if let Some(lhs) = lhs { let lhs_const = if let Some(lhs) = lhs {
constant(cx, cx.typeck_results(), lhs)? ConstEvalCtxt::new(cx).eval(lhs)?
} else { } else {
let min_val_const = ty.numeric_min_val(cx.tcx)?; let min_val_const = ty.numeric_min_val(cx.tcx)?;
mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
}; };
let rhs_const = if let Some(rhs) = rhs { let rhs_const = if let Some(rhs) = rhs {
constant(cx, cx.typeck_results(), rhs)? ConstEvalCtxt::new(cx).eval(rhs)?
} else { } else {
let max_val_const = ty.numeric_max_val(cx.tcx)?; let max_val_const = ty.numeric_max_val(cx.tcx)?;
mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, 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 lhs_val = lhs_const.int_value(cx.tcx, ty)?;
let rhs_val = rhs_const.int_value(cx, ty)?; let rhs_val = rhs_const.int_value(cx.tcx, ty)?;
let rhs_bound = match range_end { let rhs_bound = match range_end {
RangeEnd::Included => EndBound::Included(rhs_val), RangeEnd::Included => EndBound::Included(rhs_val),
RangeEnd::Excluded => EndBound::Excluded(rhs_val), RangeEnd::Excluded => EndBound::Excluded(rhs_val),
@ -58,7 +58,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
} }
if let PatKind::Lit(value) = pat.kind { 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 { return Some(SpannedRange {
span: pat.span, span: pat.span,
node: (value, EndBound::Included(value)), node: (value, EndBound::Included(value)),

View File

@ -2,7 +2,7 @@
use super::IS_DIGIT_ASCII_RADIX; use super::IS_DIGIT_ASCII_RADIX;
use clippy_config::msrvs::{self, Msrv}; 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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
return; 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 { let (num, replacement) = match radix_val {
FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),

View File

@ -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::diagnostics::span_lint;
use clippy_utils::{find_binding_init, path_to_local}; use clippy_utils::{find_binding_init, path_to_local};
use rustc_hir::{Expr, HirId}; 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) { if !receiver.span.eq_ctxt(init_expr.span) {
return; 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( span_lint(
cx, cx,
CONST_IS_EMPTY, CONST_IS_EMPTY,

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; 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)) 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() && let def_id = item.owner_id.to_def_id()
&& is_trait_method(cx, expr, sym::Iterator) && 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) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext)
{ {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;

View File

@ -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::diagnostics::span_lint_and_then;
use clippy_utils::{is_from_proc_macro, is_trait_method}; use clippy_utils::{is_from_proc_macro, is_trait_method};
use rustc_errors::Applicability; 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<'_>) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) {
if !expr.span.from_expansion() if !expr.span.from_expansion()
&& is_trait_method(cx, expr, sym::Iterator) && 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 { if let Constant::Int(arg) = constant {
Some(arg) Some(arg)
} else { } else {

View File

@ -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::diagnostics::span_lint;
use clippy_utils::is_trait_method; use clippy_utils::is_trait_method;
use rustc_hir as hir; 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<'_>) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
if is_trait_method(cx, expr, sym::Iterator) { 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( span_lint(
cx, cx,
ITERATOR_STEP_BY_ZERO, ITERATOR_STEP_BY_ZERO,

View File

@ -133,7 +133,7 @@ mod zst_offset;
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::macros::FormatArgsStorage; use clippy_utils::macros::FormatArgsStorage;
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
@ -4915,13 +4915,13 @@ impl Methods {
str_split::check(cx, expr, recv, arg); str_split::check(cx, expr, recv, arg);
}, },
("splitn" | "rsplitn", [count_arg, pat_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); suspicious_splitn::check(cx, name, expr, recv, count);
str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
} }
}, },
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { ("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); suspicious_splitn::check(cx, name, expr, recv, count);
} }
}, },

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_lang_item; use clippy_utils::ty::is_type_lang_item;
@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
recv: &'tcx Expr<'_>, recv: &'tcx Expr<'_>,
repeat_arg: &'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(); let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if ty.is_str() { if ty.is_str() {
span_lint_and_sugg( span_lint_and_sugg(

View File

@ -1,5 +1,5 @@
use clippy_config::msrvs::{self, Msrv}; 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::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::usage::local_used_after_expr; 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) => { ("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" { let span = if name.ident.as_str() == "nth" {
e.span e.span
} else if let Some((_, Node::Expr(next_expr))) = iter.next() } else if let Some((_, Node::Expr(next_expr))) = iter.next()

View File

@ -3,7 +3,7 @@ use std::cmp::Ordering;
use super::UNNECESSARY_MIN_OR_MAX; use super::UNNECESSARY_MIN_OR_MAX;
use clippy_utils::diagnostics::span_lint_and_sugg; 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 clippy_utils::source::snippet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -20,10 +20,9 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>, arg: &'tcx Expr<'_>,
) { ) {
let typeck_results = cx.typeck_results(); let typeck_results = cx.typeck_results();
if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results);
constant_with_source(cx, typeck_results, recv) if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
&& let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
constant_with_source(cx, typeck_results, arg)
{ {
let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
return; return;
@ -78,9 +77,9 @@ enum Extrema {
fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> { fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> {
let ty = cx.typeck_results().expr_ty(expr); 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::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::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), (FullInt::U(i), &ty::Uint(uty)) if i == u128::MAX >> (128 - uty.bit_width()?) => Some(Extrema::Maximum),

View File

@ -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::diagnostics::span_lint;
use clippy_utils::is_trait_method; use clippy_utils::is_trait_method;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@ -106,15 +106,11 @@ fn fetch_const<'a, 'tcx>(
if args.next().is_some() { if args.next().is_some() {
return None; return None;
} }
constant_simple(cx, cx.typeck_results(), first_arg).map_or_else( let ecx = ConstEvalCtxt::new(cx);
|| constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)), match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) {
|c| { (Some(c), None) => Some((m, c, second_arg)),
if constant_simple(cx, cx.typeck_results(), second_arg).is_none() { (None, Some(c)) => Some((m, c, first_arg)),
// otherwise ignore // otherwise ignore
Some((m, c, second_arg)) _ => None,
} else { }
None
}
},
)
} }

View File

@ -3,7 +3,7 @@ use rustc_lint::LateContext;
use rustc_middle::ty; use rustc_middle::ty;
use clippy_utils::comparisons::{normalize_comparison, Rel}; 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::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_isize_or_usize; 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<ExtremeExpr<'tcx>> { fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
let ty = cx.typeck_results().expr_ty(expr); 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) { let which = match (ty.kind(), cv) {
(&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,

View File

@ -1,6 +1,6 @@
use super::ARITHMETIC_SIDE_EFFECTS; use super::ARITHMETIC_SIDE_EFFECTS;
use clippy_config::Conf; 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::diagnostics::span_lint;
use clippy_utils::ty::is_type_diagnostic_item; 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}; 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()); 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); return Some(n);
} }
None None
@ -200,7 +200,7 @@ impl ArithmeticSideEffects {
lhs: &'tcx hir::Expr<'_>, lhs: &'tcx hir::Expr<'_>,
rhs: &'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; return;
} }
if !matches!( if !matches!(
@ -280,7 +280,7 @@ impl ArithmeticSideEffects {
let Some(arg) = args.first() else { let Some(arg) = args.first() else {
return; return;
}; };
if constant_simple(cx, cx.typeck_results(), receiver).is_some() { if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() {
return; return;
} }
let instance_ty = cx.typeck_results().expr_ty(receiver); let instance_ty = cx.typeck_results().expr_ty(receiver);
@ -308,7 +308,7 @@ impl ArithmeticSideEffects {
let hir::UnOp::Neg = un_op else { let hir::UnOp::Neg = un_op else {
return; return;
}; };
if constant(cx, cx.typeck_results(), un_expr).is_some() { if ConstEvalCtxt::new(cx).eval(un_expr).is_some() {
return; return;
} }
let ty = cx.typeck_results().expr_ty(expr).peel_refs(); let ty = cx.typeck_results().expr_ty(expr).peel_refs();

View File

@ -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::diagnostics::span_lint;
use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext; 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<u128> { fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> {
match constant(cx, cx.typeck_results(), lit)? { match ConstEvalCtxt::new(cx).eval(lit)? {
Constant::Int(n) => Some(n), Constant::Int(n) => Some(n),
_ => None, _ => None,
} }

View File

@ -2,7 +2,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{ConstEvalCtxt, Constant};
use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::layout::HasTyCtxt;
@ -20,13 +20,14 @@ use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
// Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42` // Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42`
fn comparison_to_const<'tcx>( fn comparison_to_const<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
typeck: &TypeckResults<'tcx>, typeck: &'tcx TypeckResults<'tcx>,
expr: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>,
) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> {
if let ExprKind::Binary(operator, left, right) = expr.kind if let ExprKind::Binary(operator, left, right) = expr.kind
&& let Ok(cmp_op) = CmpOp::try_from(operator.node) && 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(_), Some(_)) => None,
(_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), (_, 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))), (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))),

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(
if op == BinOpKind::Div if op == BinOpKind::Div
&& let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && 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) && 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) { let suggested_fn = match (method_path.ident.as_str(), divisor) {
("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",

View File

@ -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::diagnostics::span_lint;
use clippy_utils::ty::same_type_and_consts; use clippy_utils::ty::same_type_and_consts;
@ -34,12 +34,12 @@ fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>)
fn check_op<'tcx>( fn check_op<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
tck: &TypeckResults<'tcx>, tck: &'tcx TypeckResults<'tcx>,
op: &Expr<'tcx>, op: &Expr<'tcx>,
other: &Expr<'tcx>, other: &Expr<'tcx>,
parent: &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) { if different_types(tck, other, parent) {
return; return;
} }

View File

@ -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::diagnostics::span_lint_and_then;
use clippy_utils::get_item_name; use clippy_utils::get_item_name;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
@ -17,12 +17,14 @@ pub(crate) fn check<'tcx>(
right: &'tcx Expr<'_>, right: &'tcx Expr<'_>,
) { ) {
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { 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((c, s)) if !is_allowed(&c) => s.is_local(),
Some(_) => return, Some(_) => return,
None => true, 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((c, s)) if !is_allowed(&c) => s.is_local(),
Some(_) => return, Some(_) => return,
None => true, None => true,

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{clip, peel_hir_expr_refs, unsext}; 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() && cx.typeck_results().expr_ty(right).peel_refs().is_integral()
// `1 << 0` is a common pattern in bit manipulation code // `1 << 0` is a common pattern in bit manipulation code
&& !(cmp == BinOpKind::Shl && !(cmp == BinOpKind::Shl
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0))
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1)))
} }
fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { 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 ecx = ConstEvalCtxt::new(cx);
let rhs_const = constant_full_int(cx, cx.typeck_results(), right); if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) {
if match (lhs_const, rhs_const) {
(Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
_ => return, _ => 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 { 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() { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
ty::Uint(uty) => clip(cx.tcx, !0, uty), ty::Uint(uty) => clip(cx.tcx, !0, uty),

View File

@ -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::diagnostics::span_lint_and_then;
use clippy_utils::sext; use clippy_utils::sext;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; 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 op.node == BinOpKind::Eq || op.node == BinOpKind::Ne {
if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) { let ecx = ConstEvalCtxt::new(cx);
return true; matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0)))
} } else {
if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) { false
return true;
}
} }
false
} }
struct OperandInfo { struct OperandInfo {
@ -60,7 +56,7 @@ struct OperandInfo {
} }
fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> { fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
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() { Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() {
ty::Int(ity) => { ty::Int(ity) => {
let value = sext(cx.tcx, v, ity); let value = sext(cx.tcx, v, ity);

View File

@ -1,5 +1,5 @@
use super::FLOAT_ARITHMETIC; use super::FLOAT_ARITHMETIC;
use clippy_utils::consts::constant_simple; use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -55,7 +55,7 @@ impl Context {
return; return;
} }
let ty = cx.typeck_results().expr_ty(arg); 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"); span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.expr_id = Some(expr.hir_id); self.expr_id = Some(expr.hir_id);
} }

View File

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
@ -319,7 +319,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) ->
_ => return None, _ => return None,
}; };
if let Some(id) = path_to_local(l) { 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 { return Some(RangeBounds {
val: c, val: c,
expr: r, 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) { } 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 { return Some(RangeBounds {
val: c, val: c,
expr: l, expr: l,
@ -451,8 +451,9 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
}) = higher::Range::hir(expr) }) = higher::Range::hir(expr)
&& let ty = cx.typeck_results().expr_ty(start) && let ty = cx.typeck_results().expr_ty(start)
&& let ty::Int(_) | ty::Uint(_) = ty.kind() && let ty::Int(_) | ty::Uint(_) = ty.kind()
&& let Some(start_idx) = constant(cx, cx.typeck_results(), start) && let ecx = ConstEvalCtxt::new(cx)
&& let Some(end_idx) = constant(cx, cx.typeck_results(), end) && 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) && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx)
&& is_empty_range(limits, ordering) && is_empty_range(limits, ordering)
{ {

View File

@ -1,6 +1,6 @@
use std::fmt::Display; 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::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::{def_path_def_ids, path_def_id, paths}; use clippy_utils::{def_path_def_ids, path_def_id, paths};
@ -148,7 +148,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: &regex_syntax::Error, unescape
} }
fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> { fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
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), Constant::Str(s) => Some(s),
_ => None, _ => None,
}) })

View File

@ -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::diagnostics::span_lint_and_then;
use clippy_utils::higher::VecArgs; use clippy_utils::higher::VecArgs;
use clippy_utils::macros::matching_root_macro_call; 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) && 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)) && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
&& !len_expr.span.from_expansion() && !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( emit_lint(
cx, cx,

View File

@ -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::diagnostics::span_lint_and_then;
use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@ -33,10 +33,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
// Catching: // Catching:
// transmute over constants that resolve to `null`. // transmute over constants that resolve to `null`.
ExprKind::Path(ref _qpath) ExprKind::Path(ref _qpath)
if matches!( if matches!(ConstEvalCtxt::new(cx).eval(casts_peeled), Some(Constant::RawPtr(0))) =>
constant(cx, cx.typeck_results(), casts_peeled),
Some(Constant::RawPtr(0))
) =>
{ {
lint_expr(cx, expr); lint_expr(cx, expr);
true true

View File

@ -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::diagnostics::span_lint;
use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
use rustc_hir::{Expr, ExprKind}; 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`. // Catching transmute over constants that resolve to `null`.
if let ExprKind::Path(ref _qpath) = arg.kind 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); span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true; return true;

View File

@ -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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::match_type; use clippy_utils::ty::match_type;
@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
if let ExprKind::Call(func, [arg]) = &expr.kind if let ExprKind::Call(func, [arg]) = &expr.kind
&& let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind()
&& match_def_path(cx, *def_id, &paths::SYMBOL_INTERN) && 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 value = Symbol::intern(&arg).as_u32()
&& let Some(&def_id) = self.symbol_map.get(&value) && let Some(&def_id) = self.symbol_map.get(&value)
{ {
@ -199,7 +199,7 @@ impl InterningDefinedSymbol {
}); });
} }
// is a string constant // 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(); let value = Symbol::intern(&s).as_u32();
// ...which matches a symbol constant // ...which matches a symbol constant
if let Some(&def_id) = self.symbol_map.get(&value) { if let Some(&def_id) = self.symbol_map.get(&value) {

View File

@ -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::def_path_res;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir; 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()); let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
if mod_name.as_str() == "paths" if mod_name.as_str() == "paths"
&& let hir::ItemKind::Const(.., body_id) = item.kind && let hir::ItemKind::Const(.., body_id) = item.kind
&& let body = cx.tcx.hir().body(body_id) && let Some(Constant::Vec(path)) =
&& let typeck_results = cx.tcx.typeck_body(body_id) ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(item.owner_id), cx.tcx.typeck(item.owner_id))
&& let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value) .eval_simple(cx.tcx.hir().body(body_id).value)
&& let Some(path) = path && let Some(path) = path
.iter() .iter()
.map(|x| { .map(|x| {

View File

@ -3,7 +3,7 @@ use std::ops::ControlFlow;
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; 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::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_copy; use clippy_utils::ty::is_copy;
@ -159,7 +159,7 @@ impl UselessVec {
let snippet = match *vec_args { let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => { 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 // 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)) { if !is_copy(cx, cx.typeck_results().expr_ty(elem)) {
return; return;

View File

@ -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 clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; 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. // TODO - constant_simple does not fold many operations involving floats.
// That's probably fine for this lint - it's pretty unlikely that someone would // 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. // 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 ecx = ConstEvalCtxt::new(cx)
&& let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) && 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 // 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) == lhs_value || Constant::F64(0.0) == lhs_value)
&& (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value)

View File

@ -14,12 +14,13 @@ use rustc_lexer::tokenize;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::interpret::{alloc_range, Scalar};
use rustc_middle::mir::ConstValue; use rustc_middle::mir::ConstValue;
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
use rustc_middle::{bug, mir, span_bug}; use rustc_middle::{bug, mir, span_bug};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::Ident;
use rustc_span::{sym, SyntaxContext}; use rustc_span::{sym, SyntaxContext};
use rustc_target::abi::Size; use rustc_target::abi::Size;
use std::cell::Cell;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::iter; use std::iter;
@ -263,10 +264,10 @@ impl<'tcx> Constant<'tcx> {
} }
/// Returns the integer value or `None` if `self` or `val_type` is not integer type. /// 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<FullInt> { pub fn int_value(&self, tcx: TyCtxt<'_>, val_type: Ty<'_>) -> Option<FullInt> {
if let Constant::Int(const_int) = *self { if let Constant::Int(const_int) = *self {
match *val_type.kind() { 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)), ty::Uint(_) => Some(FullInt::U(const_int)),
_ => None, _ => None,
} }
@ -322,6 +323,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan
} }
/// The source of a constant value. /// The source of a constant value.
#[derive(Clone, Copy)]
pub enum ConstantSource { pub enum ConstantSource {
/// The value is determined solely from the expression. /// The value is determined solely from the expression.
Local, Local,
@ -331,54 +333,11 @@ pub enum ConstantSource {
CoreConstant, CoreConstant,
} }
impl ConstantSource { impl ConstantSource {
pub fn is_local(&self) -> bool { pub fn is_local(self) -> bool {
matches!(self, Self::Local) 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<bool> {
ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e)
}
/// Attempts to evaluate the expression as a constant.
pub fn constant<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<Constant<'tcx>> {
ConstEvalLateContext::new(lcx, typeck_results).expr(e)
}
/// Attempts to evaluate the expression as a constant.
pub fn constant_with_source<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<(Constant<'tcx>, ConstantSource)> {
let mut ctxt = ConstEvalLateContext::new(lcx, 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: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<Constant<'tcx>> {
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: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<FullInt> {
constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
}
#[derive(Copy, Clone, Debug, Eq)] #[derive(Copy, Clone, Debug, Eq)]
pub enum FullInt { pub enum FullInt {
S(i128), S(i128),
@ -417,44 +376,87 @@ impl Ord for FullInt {
} }
} }
pub struct ConstEvalLateContext<'a, 'tcx> { /// The context required to evaluate a constant expression.
lcx: &'a LateContext<'tcx>, ///
typeck_results: &'a ty::TypeckResults<'tcx>, /// This is currently limited to constant folding and reading the value of named constants.
param_env: ty::ParamEnv<'tcx>, pub struct ConstEvalCtxt<'tcx> {
source: ConstantSource, tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>, param_env: ParamEnv<'tcx>,
typeck: &'tcx TypeckResults<'tcx>,
source: Cell<ConstantSource>,
} }
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { impl<'tcx> ConstEvalCtxt<'tcx> {
pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { /// 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 { Self {
lcx, tcx: cx.tcx,
typeck_results, param_env: cx.param_env,
param_env: lcx.param_env, typeck: cx.typeck_results(),
source: ConstantSource::Local, source: Cell::new(ConstantSource::Local),
args: List::empty(), }
}
/// Creates an evaluation context.
pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self {
Self {
tcx,
param_env,
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<Constant<'tcx>> {
self.expr(e)
}
/// Attempts to evaluate the expression without accessing other items.
pub fn eval_simple(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
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<FullInt> {
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. /// Simple constant folding: Insert an expression, get a constant or none.
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> { fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind { match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
ExprKind::DropTemps(e) => self.expr(e), ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => {
let is_core_crate = if let Some(def_id) = self.lcx.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.lcx.tcx.crate_name(def_id.krate) == sym::core self.tcx.crate_name(def_id.krate) == sym::core
} else { } else {
false false
}; };
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
let result = mir_to_const(this.lcx, result)?; let result = mir_to_const(self_.tcx, result)?;
// If source is already Constant we wouldn't want to override it with CoreConstant // If source is already Constant we wouldn't want to override it with CoreConstant
this.source = if is_core_crate && !matches!(this.source, ConstantSource::Constant) { self_.source.set(
ConstantSource::CoreConstant if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
} else { ConstantSource::CoreConstant
ConstantSource::Constant } else {
}; ConstantSource::Constant
},
);
Some(result) Some(result)
}) })
}, },
@ -463,21 +465,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
if is_direct_expn_of(e.span, "cfg").is_some() { if is_direct_expn_of(e.span, "cfg").is_some() {
None None
} else { } 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::Array(vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(value, _) => { 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.lcx.tcx, self.lcx.param_env)?, ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
_ => span_bug!(e.span, "typeck error"), _ => span_bug!(e.span, "typeck error"),
}; };
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
}, },
ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op { 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::Not => self.constant_not(&o, self.typeck.expr_ty(e)),
UnOp::Neg => self.constant_negate(&o, self.typeck_results.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 }), UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
}), }),
ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
@ -486,21 +488,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
// We only handle a few const functions for now. // We only handle a few const functions for now.
if args.is_empty() if args.is_empty()
&& let ExprKind::Path(qpath) = &callee.kind && let ExprKind::Path(qpath) = &callee.kind
&& let res = self.typeck_results.qpath_res(qpath, callee.hir_id) && let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id()
&& let Some(def_id) = res.opt_def_id()
&& let def_path = self.lcx.get_def_path(def_id)
&& let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::<Vec<_>>()
&& let ["core", "num", int_impl, "max_value"] = *def_path
{ {
let value = match int_impl { match self.tcx.get_diagnostic_name(did) {
"<impl i8>" => i8::MAX as u128, Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)),
"<impl i16>" => i16::MAX as u128, Some(sym::i16_legacy_fn_max_value) => Some(Constant::Int(i16::MAX as u128)),
"<impl i32>" => i32::MAX as u128, Some(sym::i32_legacy_fn_max_value) => Some(Constant::Int(i32::MAX as u128)),
"<impl i64>" => i64::MAX as u128, Some(sym::i64_legacy_fn_max_value) => Some(Constant::Int(i64::MAX as u128)),
"<impl i128>" => i128::MAX as u128, Some(sym::i128_legacy_fn_max_value) => Some(Constant::Int(i128::MAX as u128)),
_ => return None, _ => None,
}; }
Some(Constant::Int(value))
} else { } else {
None None
} }
@ -512,9 +509,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
if let Some(Constant::Adt(constant)) = &self.expr(local_expr) if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
&& let ty::Adt(adt_def, _) = constant.ty().kind() && let ty::Adt(adt_def, _) = constant.ty().kind()
&& adt_def.is_struct() && adt_def.is_struct()
&& let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field) && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field)
{ {
mir_to_const(self.lcx, desired_field) mir_to_const(self.tcx, desired_field)
} else { } else {
result result
} }
@ -526,21 +523,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Simple constant folding to determine if an expression is an empty slice, str, array, … /// 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 /// `None` will be returned if the constness cannot be determined, or if the resolution
/// leaves the local crate. /// leaves the local crate.
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> { pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option<bool> {
match e.kind { match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value),
ExprKind::DropTemps(e) => self.expr_is_empty(e), ExprKind::DropTemps(e) => self.eval_is_empty(e),
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => {
if !self if !self
.typeck_results .typeck
.qpath_res(qpath, e.hir_id) .qpath_res(qpath, e.hir_id)
.opt_def_id() .opt_def_id()
.is_some_and(DefId::is_local) .is_some_and(DefId::is_local)
{ {
return None; return None;
} }
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
mir_is_empty(this.lcx, result) mir_is_empty(self_.tcx, result)
}) })
}, },
ExprKind::Lit(lit) => { ExprKind::Lit(lit) => {
@ -556,8 +553,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}, },
ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
ExprKind::Repeat(..) => { 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.lcx.tcx, self.lcx.param_env)? == 0) Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0)
} else { } else {
span_bug!(e.span, "typeck error"); span_bug!(e.span, "typeck error");
} }
@ -574,8 +571,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
Int(value) => { Int(value) => {
let value = !value; let value = !value;
match *ty.kind() { match *ty.kind() {
ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), ty::Int(ity) => Some(Int(unsext(self.tcx, value as i128, ity))),
ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), ty::Uint(ity) => Some(Int(clip(self.tcx, value, ity))),
_ => None, _ => None,
} }
}, },
@ -590,7 +587,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let ty::Int(ity) = *ty.kind() else { return None }; let ty::Int(ity) = *ty.kind() else { return None };
let (min, _) = ity.min_max()?; let (min, _) = ity.min_max()?;
// sign extend // sign extend
let value = sext(self.lcx.tcx, value, ity); let value = sext(self.tcx, value, ity);
// Applying unary - to the most negative value of any signed integer type panics. // Applying unary - to the most negative value of any signed integer type panics.
if value == min { if value == min {
@ -599,7 +596,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let value = value.checked_neg()?; let value = value.checked_neg()?;
// clear unused bits // clear unused bits
Some(Int(unsext(self.lcx.tcx, value, ity))) Some(Int(unsext(self.tcx, value, ity)))
}, },
F32(f) => Some(F32(-f)), F32(f) => Some(F32(-f)),
F64(f) => Some(F64(-f)), F64(f) => Some(F64(-f)),
@ -609,21 +606,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Create `Some(Vec![..])` of all constants, unless there is any /// Create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part. /// non-constant part.
fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> { fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>() vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
} }
/// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T> fn fetch_path_and_apply<T, F>(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
where where
F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option<T>, F: FnOnce(&Self, mir::Const<'tcx>) -> Option<T>,
{ {
let res = self.typeck_results.qpath_res(qpath, id); let res = self.typeck.qpath_res(qpath, id);
match res { match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
// Check if this constant is based on `cfg!(..)`, // Check if this constant is based on `cfg!(..)`,
// which is NOT constant for our purposes. // which is NOT constant for our purposes.
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) if let Some(node) = self.tcx.hir().get_if_local(def_id)
&& let Node::Item(Item { && let Node::Item(Item {
kind: ItemKind::Const(.., body_id), kind: ItemKind::Const(.., body_id),
.. ..
@ -632,20 +629,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
kind: ExprKind::Lit(_), kind: ExprKind::Lit(_),
span, span,
.. ..
}) = self.lcx.tcx.hir_node(body_id.hir_id) }) = self.tcx.hir_node(body_id.hir_id)
&& is_direct_expn_of(*span, "cfg").is_some() && is_direct_expn_of(*span, "cfg").is_some()
{ {
return None; return None;
} }
let args = self.typeck_results.node_args(id); let args = self.typeck.node_args(id);
let args = if self.args.is_empty() {
args
} else {
EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args)
};
let result = self let result = self
.lcx
.tcx .tcx
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
.ok() .ok()
@ -656,7 +647,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> { fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
let lhs = self.expr(lhs); let lhs = self.expr(lhs);
let index = self.expr(index); let index = self.expr(index);
@ -685,8 +676,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
/// A block can only yield a constant if it only has one constant expression. /// A block can only yield a constant if it has exactly one constant expression.
fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> { fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> {
if block.stmts.is_empty() if block.stmts.is_empty()
&& let Some(expr) = block.expr && let Some(expr) = block.expr
{ {
@ -696,7 +687,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
&& let expr_lo = expr_span.lo() && let expr_lo = expr_span.lo()
&& expr_lo >= span.lo && expr_lo >= span.lo
&& let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx) && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx)
&& let Some(src) = src.as_str() && let Some(src) = src.as_str()
{ {
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
@ -705,11 +696,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
.filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
.eq([OpenBrace]) .eq([OpenBrace])
{ {
self.source = ConstantSource::Constant; self.source.set(ConstantSource::Constant);
} }
} else { } else {
// Unable to access the source. Assume a non-local dependency. // Unable to access the source. Assume a non-local dependency.
self.source = ConstantSource::Constant; self.source.set(ConstantSource::Constant);
} }
} }
@ -719,7 +710,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> { fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
if let Some(Constant::Bool(b)) = self.expr(cond) { if let Some(Constant::Bool(b)) = self.expr(cond) {
if b { if b {
self.expr(then) self.expr(then)
@ -731,16 +722,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
let l = self.expr(left)?; let l = self.expr(left)?;
let r = self.expr(right); let r = self.expr(right);
match (l, r) { 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) => { ty::Int(ity) => {
let (ty_min_value, _) = ity.min_max()?; let (ty_min_value, _) = ity.min_max()?;
let bits = ity.bits(); let bits = ity.bits();
let l = sext(self.lcx.tcx, l, ity); let l = sext(self.tcx, l, ity);
let r = sext(self.lcx.tcx, r, ity); let r = sext(self.tcx, r, ity);
// Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
// the right-hand argument is -1 always panics, even with overflow-checks disabled // the right-hand argument is -1 always panics, even with overflow-checks disabled
@ -751,7 +742,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
return None; return None;
} }
let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
match op.node { match op.node {
// When +, * or binary - create a value greater than the maximum value, or less than // When +, * or binary - create a value greater than the maximum value, or less than
// the minimum value that can be stored, it panics. // the minimum value that can be stored, it panics.
@ -845,7 +836,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
let mir::Const::Val(val, _) = result else { let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts. // We only work on evaluated consts.
return None; return None;
@ -863,13 +854,13 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
_ => None, _ => None,
}, },
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; let data = val.try_get_slice_bytes_for_diagnostics(tcx)?;
String::from_utf8(data.to_owned()).ok().map(Constant::Str) String::from_utf8(data.to_owned()).ok().map(Constant::Str)
}, },
(_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
(ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let len = len.try_to_target_usize(lcx.tcx)?; let len = len.try_to_target_usize(tcx)?;
let ty::Float(flt) = sub_type.kind() else { let ty::Float(flt) = sub_type.kind() else {
return None; return None;
}; };
@ -877,7 +868,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
let mut res = Vec::new(); let mut res = Vec::new();
for idx in 0..len { for idx in 0..len {
let range = alloc_range(offset + size * idx, size); let range = alloc_range(offset + size * idx, size);
let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?;
res.push(match flt { res.push(match flt {
FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)),
FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)),
@ -891,7 +882,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
} }
} }
fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
let mir::Const::Val(val, _) = result else { let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts. // We only work on evaluated consts.
return None; return None;
@ -902,26 +893,26 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
if let ConstValue::Indirect { alloc_id, offset } = val { if let ConstValue::Indirect { alloc_id, offset } = val {
// Get the length from the slice, using the same formula as // Get the length from the slice, using the same formula as
// [`ConstValue::try_get_slice_bytes_for_diagnostics`]. // [`ConstValue::try_get_slice_bytes_for_diagnostics`].
let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let ptr_size = lcx.tcx.data_layout.pointer_size; let ptr_size = tcx.data_layout.pointer_size;
if a.size() < offset + 2 * ptr_size { if a.size() < offset + 2 * ptr_size {
// (partially) dangling reference // (partially) dangling reference
return None; return None;
} }
let len = a let len = a
.read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false)
.ok()? .ok()?
.to_target_usize(&lcx.tcx) .to_target_usize(&tcx)
.ok()?; .ok()?;
Some(len == 0) Some(len == 0)
} else { } else {
None None
} }
}, },
ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0),
_ => None, _ => None,
}, },
(ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0),
(ConstValue::ZeroSized, _) => Some(true), (ConstValue::ZeroSized, _) => Some(true),
_ => None, _ => None,
} }
@ -929,12 +920,12 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
fn field_of_struct<'tcx>( fn field_of_struct<'tcx>(
adt_def: ty::AdtDef<'tcx>, adt_def: ty::AdtDef<'tcx>,
lcx: &LateContext<'tcx>, tcx: TyCtxt<'tcx>,
result: mir::Const<'tcx>, result: mir::Const<'tcx>,
field: &Ident, field: &Ident,
) -> Option<mir::Const<'tcx>> { ) -> Option<mir::Const<'tcx>> {
if let mir::Const::Val(result, ty) = result if let mir::Const::Val(result, ty) = result
&& let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty) && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty)
&& let Some(dc_variant) = dc.variant && let Some(dc_variant) = dc.variant
&& let Some(variant) = adt_def.variants().get(dc_variant) && let Some(variant) = adt_def.variants().get(dc_variant)
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name)

View File

@ -9,7 +9,7 @@
//! - or-fun-call //! - or-fun-call
//! - option-if-let-else //! - option-if-let-else
use crate::consts::{constant, FullInt}; use crate::consts::{ConstEvalCtxt, FullInt};
use crate::ty::{all_predicates_of, is_copy}; use crate::ty::{all_predicates_of, is_copy};
use crate::visitors::is_const_evaluatable; use crate::visitors::is_const_evaluatable;
use rustc_hir::def::{DefKind, Res}; 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 // `-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; 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. // Thus, we would realistically only delay the lint.
ExprKind::Binary(op, _, right) ExprKind::Binary(op, _, right)
if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) 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; self.eagerness |= NoChange;
}, },
@ -240,9 +240,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
ExprKind::Binary(op, left, right) ExprKind::Binary(op, left, right)
if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem)
&& let right_ty = self.cx.typeck_results().expr_ty(right) && let right_ty = self.cx.typeck_results().expr_ty(right)
&& let left = constant(self.cx, self.cx.typeck_results(), left) && let ecx = ConstEvalCtxt::new(self.cx)
&& let right = constant(self.cx, self.cx.typeck_results(), right) && let left = ecx.eval(left)
.and_then(|c| c.int_value(self.cx, right_ty)) && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty))
&& matches!( && matches!(
(left, right), (left, right),
// `1 / x`: x might be zero // `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) ExprKind::Binary(op, left, right)
if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul)
&& !self.cx.typeck_results().expr_ty(e).is_floating_point() && !self.cx.typeck_results().expr_ty(e).is_floating_point()
&& (constant(self.cx, self.cx.typeck_results(), left).is_none() && let ecx = ConstEvalCtxt::new(self.cx)
|| constant(self.cx, self.cx.typeck_results(), right).is_none()) => && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) =>
{ {
self.eagerness |= NoChange; self.eagerness |= NoChange;
}, },

View File

@ -2,7 +2,7 @@
#![deny(clippy::missing_docs_in_private_items)] #![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::ty::is_type_diagnostic_item;
use crate::{is_expn_of, match_def_path, paths}; 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); return Some(VecInitKind::Default);
} else if name.ident.name.as_str() == "with_capacity" { } else if name.ident.name.as_str() == "with_capacity" {
let arg = args.first()?; 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(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
_ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
}; };

View File

@ -1,4 +1,4 @@
use crate::consts::constant_simple; use crate::consts::ConstEvalCtxt;
use crate::macros::macro_backtrace; use crate::macros::macro_backtrace;
use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt}; use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt};
use crate::tokenize_with_text; use crate::tokenize_with_text;
@ -255,8 +255,8 @@ impl HirEqInterExpr<'_, '_, '_> {
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
&& typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
&& let (Some(l), Some(r)) = ( && let (Some(l), Some(r)) = (
constant_simple(self.inner.cx, typeck_lhs, left), ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left),
constant_simple(self.inner.cx, typeck_rhs, right), ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right),
) )
&& l == r && l == r
{ {
@ -714,9 +714,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
pub fn hash_expr(&mut self, e: &Expr<'_>) { pub fn hash_expr(&mut self, e: &Expr<'_>) {
let simple_const = self let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
.maybe_typeck_results ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e)
.and_then(|typeck_results| constant_simple(self.cx, typeck_results, e)); });
// const hashing may result in the same hash as some unrelated node, so add a sort of // 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 // discriminant depending on which path we're choosing next

View File

@ -125,7 +125,7 @@ use rustc_span::{sym, Span};
use rustc_target::abi::Integer; use rustc_target::abi::Integer;
use visitors::Visitable; 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::higher::Range;
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; 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; use crate::visitors::for_each_expr_without_closures;
@ -1581,8 +1581,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind() if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0) && let bnd_ty = subst.type_at(0)
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
&& let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, 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 start_const == min_const
} else { } else {
@ -1594,8 +1594,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind() if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0) && let bnd_ty = subst.type_at(0)
&& let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
&& let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, 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 end_const == max_const
} else { } else {
@ -1626,7 +1626,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
return true; return true;
} }
let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); 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; return value == v;
} }
false false

View File

@ -6,7 +6,8 @@ use rustc_ast::{LitKind, StrStyle};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{EarlyContext, LateContext};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::source_map::{original_sp, SourceMap};
use rustc_span::{ use rustc_span::{
@ -17,6 +18,30 @@ use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::ops::Range; use std::ops::Range;
pub trait HasSession {
fn sess(&self) -> &Session;
}
impl HasSession for Session {
fn sess(&self) -> &Session {
self
}
}
impl HasSession for TyCtxt<'_> {
fn sess(&self) -> &Session {
self.sess
}
}
impl HasSession for EarlyContext<'_> {
fn sess(&self) -> &Session {
::rustc_lint::LintContext::sess(self)
}
}
impl HasSession for LateContext<'_> {
fn sess(&self) -> &Session {
self.tcx.sess()
}
}
/// Conversion of a value into the range portion of a `Span`. /// Conversion of a value into the range portion of a `Span`.
pub trait SpanRange: Sized { pub trait SpanRange: Sized {
fn into_range(self) -> Range<BytePos>; fn into_range(self) -> Range<BytePos>;
@ -71,19 +96,19 @@ impl IntoSpan for Range<BytePos> {
pub trait SpanRangeExt: SpanRange { pub trait SpanRangeExt: SpanRange {
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// Gets the source file, and range in the file, of the given span. Returns `None` if the span
/// extends through multiple files, or is malformed. /// extends through multiple files, or is malformed.
fn get_source_text(self, cx: &impl LintContext) -> Option<SourceFileRange> { fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> {
get_source_text(cx.sess().source_map(), self.into_range()) get_source_text(cx.sess().source_map(), self.into_range())
} }
/// Calls the given function with the source text referenced and returns the value. Returns /// Calls the given function with the source text referenced and returns the value. Returns
/// `None` if the source text cannot be retrieved. /// `None` if the source text cannot be retrieved.
fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { fn with_source_text<T>(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
with_source_text(cx.sess().source_map(), self.into_range(), f) with_source_text(cx.sess().source_map(), self.into_range(), f)
} }
/// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the
/// source text cannot be retrieved. /// source text cannot be retrieved.
fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
self.with_source_text(cx, pred).unwrap_or(false) self.with_source_text(cx, pred).unwrap_or(false)
} }
@ -91,7 +116,7 @@ pub trait SpanRangeExt: SpanRange {
/// and returns the value. Returns `None` if the source text cannot be retrieved. /// and returns the value. Returns `None` if the source text cannot be retrieved.
fn with_source_text_and_range<T>( fn with_source_text_and_range<T>(
self, self,
cx: &impl LintContext, cx: &impl HasSession,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
) -> Option<T> { ) -> Option<T> {
with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) with_source_text_and_range(cx.sess().source_map(), self.into_range(), f)
@ -104,30 +129,30 @@ pub trait SpanRangeExt: SpanRange {
/// The new range must reside within the same source file. /// The new range must reside within the same source file.
fn map_range( fn map_range(
self, self,
cx: &impl LintContext, cx: &impl HasSession,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
) -> Option<Range<BytePos>> { ) -> Option<Range<BytePos>> {
map_range(cx.sess().source_map(), self.into_range(), f) map_range(cx.sess().source_map(), self.into_range(), f)
} }
/// Extends the range to include all preceding whitespace characters. /// Extends the range to include all preceding whitespace characters.
fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> { fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
with_leading_whitespace(cx.sess().source_map(), self.into_range()) with_leading_whitespace(cx.sess().source_map(), self.into_range())
} }
/// Trims the leading whitespace from the range. /// Trims the leading whitespace from the range.
fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> { fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
trim_start(cx.sess().source_map(), self.into_range()) trim_start(cx.sess().source_map(), self.into_range())
} }
/// Writes the referenced source text to the given writer. Will return `Err` if the source text /// Writes the referenced source text to the given writer. Will return `Err` if the source text
/// could not be retrieved. /// could not be retrieved.
fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result { fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
write_source_text_to(cx.sess().source_map(), self.into_range(), dst) write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
} }
/// Extracts the referenced source text as an owned string. /// Extracts the referenced source text as an owned string.
fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> { fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
self.with_source_text(cx, ToOwned::to_owned) self.with_source_text(cx, ToOwned::to_owned)
} }
} }
@ -227,15 +252,15 @@ impl SourceFileRange {
} }
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
pub fn expr_block<T: LintContext>( pub fn expr_block(
cx: &T, sess: &impl HasSession,
expr: &Expr<'_>, expr: &Expr<'_>,
outer: SyntaxContext, outer: SyntaxContext,
default: &str, default: &str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
app: &mut Applicability, app: &mut Applicability,
) -> String { ) -> String {
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app);
if !from_macro if !from_macro
&& let ExprKind::Block(block, _) = expr.kind && let ExprKind::Block(block, _) = expr.kind
&& block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
@ -260,13 +285,13 @@ pub fn expr_block<T: LintContext>(
/// let x = (); /// let x = ();
/// // ^^^^^^^^^^ /// // ^^^^^^^^^^
/// ``` /// ```
pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span {
first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
} }
fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option<BytePos> {
let line_span = line_span(cx, span); let line_span = line_span(sess, span);
snippet_opt(cx, line_span).and_then(|snip| { snippet_opt(sess, line_span).and_then(|snip| {
snip.find(|c: char| !c.is_whitespace()) snip.find(|c: char| !c.is_whitespace())
.map(|pos| line_span.lo() + BytePos::from_usize(pos)) .map(|pos| line_span.lo() + BytePos::from_usize(pos))
}) })
@ -281,9 +306,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
/// let x = (); /// let x = ();
/// // ^^^^^^^^^^^^^^ /// // ^^^^^^^^^^^^^^
/// ``` /// ```
fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { fn line_span(sess: &impl HasSession, span: Span) -> Span {
let span = original_sp(span, DUMMY_SP); let span = original_sp(span, DUMMY_SP);
let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap();
let line_start = sf.lines()[line]; let line_start = sf.lines()[line];
let line_start = sf.absolute_position(line_start); let line_start = sf.absolute_position(line_start);
span.with_lo(line_start) span.with_lo(line_start)
@ -297,13 +322,13 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
/// let x = (); /// let x = ();
/// // ^^ -- will return 4 /// // ^^ -- will return 4
/// ``` /// ```
pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { pub fn indent_of(sess: &impl HasSession, span: Span) -> Option<usize> {
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
} }
/// Gets a snippet of the indentation of the line of a span /// Gets a snippet of the indentation of the line of a span
pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> { pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> {
snippet_opt(cx, line_span(cx, span)).map(|mut s| { snippet_opt(sess, line_span(sess, span)).map(|mut s| {
let len = s.len() - s.trim_start().len(); let len = s.len() - s.trim_start().len();
s.truncate(len); s.truncate(len);
s s
@ -315,8 +340,8 @@ pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
// sources that the user has no control over. // sources that the user has no control over.
// For some reason these attributes don't have any expansion info on them, so // For some reason these attributes don't have any expansion info on them, so
// we have to check it this way until there is a better way. // we have to check it this way until there is a better way.
pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
if let Some(snippet) = snippet_opt(cx, span) { if let Some(snippet) = snippet_opt(sess, span) {
if snippet.is_empty() { if snippet.is_empty() {
return false; return false;
} }
@ -407,8 +432,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
/// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span1, "..") // -> "value"
/// snippet(cx, span2, "..") // -> "Vec::new()" /// snippet(cx, span2, "..") // -> "Vec::new()"
/// ``` /// ```
pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from)
} }
/// Same as [`snippet`], but it adapts the applicability level by following rules: /// Same as [`snippet`], but it adapts the applicability level by following rules:
@ -417,13 +442,13 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
/// - If the default value is used and the applicability level is `MachineApplicable`, change it to /// - If the default value is used and the applicability level is `MachineApplicable`, change it to
/// `HasPlaceholders` /// `HasPlaceholders`
pub fn snippet_with_applicability<'a, T: LintContext>( pub fn snippet_with_applicability<'a>(
cx: &T, sess: &impl HasSession,
span: Span, span: Span,
default: &'a str, default: &'a str,
applicability: &mut Applicability, applicability: &mut Applicability,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
snippet_with_applicability_sess(cx.sess(), span, default, applicability) snippet_with_applicability_sess(sess.sess(), span, default, applicability)
} }
fn snippet_with_applicability_sess<'a>( fn snippet_with_applicability_sess<'a>(
@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>(
if *applicability != Applicability::Unspecified && span.from_expansion() { if *applicability != Applicability::Unspecified && span.from_expansion() {
*applicability = Applicability::MaybeIncorrect; *applicability = Applicability::MaybeIncorrect;
} }
snippet_opt_sess(sess, span).map_or_else( snippet_opt(sess, span).map_or_else(
|| { || {
if *applicability == Applicability::MachineApplicable { if *applicability == Applicability::MachineApplicable {
*applicability = Applicability::HasPlaceholders; *applicability = Applicability::HasPlaceholders;
@ -447,12 +472,8 @@ fn snippet_with_applicability_sess<'a>(
} }
/// Converts a span to a code snippet. Returns `None` if not available. /// Converts a span to a code snippet. Returns `None` if not available.
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> { pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> {
snippet_opt_sess(cx.sess(), span) sess.sess().source_map().span_to_snippet(span).ok()
}
fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
sess.source_map().span_to_snippet(span).ok()
} }
/// Converts a span (from a block) to a code snippet if available, otherwise use default. /// Converts a span (from a block) to a code snippet if available, otherwise use default.
@ -489,41 +510,41 @@ fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
/// } // aligned with `if` /// } // aligned with `if`
/// ``` /// ```
/// Note that the first line of the snippet always has 0 indentation. /// Note that the first line of the snippet always has 0 indentation.
pub fn snippet_block<'a, T: LintContext>( pub fn snippet_block<'a>(
cx: &T, sess: &impl HasSession,
span: Span, span: Span,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
let snip = snippet(cx, span, default); let snip = snippet(sess, span, default);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
reindent_multiline(snip, true, indent) reindent_multiline(snip, true, indent)
} }
/// Same as `snippet_block`, but adapts the applicability level by the rules of /// Same as `snippet_block`, but adapts the applicability level by the rules of
/// `snippet_with_applicability`. /// `snippet_with_applicability`.
pub fn snippet_block_with_applicability<'a>( pub fn snippet_block_with_applicability<'a>(
cx: &impl LintContext, sess: &impl HasSession,
span: Span, span: Span,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
applicability: &mut Applicability, applicability: &mut Applicability,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
let snip = snippet_with_applicability(cx, span, default, applicability); let snip = snippet_with_applicability(sess, span, default, applicability);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
reindent_multiline(snip, true, indent) reindent_multiline(snip, true, indent)
} }
pub fn snippet_block_with_context<'a>( pub fn snippet_block_with_context<'a>(
cx: &impl LintContext, sess: &impl HasSession,
span: Span, span: Span,
outer: SyntaxContext, outer: SyntaxContext,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
app: &mut Applicability, app: &mut Applicability,
) -> (Cow<'a, str>, bool) { ) -> (Cow<'a, str>, bool) {
let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app); let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
(reindent_multiline(snip, true, indent), from_macro) (reindent_multiline(snip, true, indent), from_macro)
} }
@ -537,13 +558,13 @@ pub fn snippet_block_with_context<'a>(
/// ///
/// This will also return whether or not the snippet is a macro call. /// This will also return whether or not the snippet is a macro call.
pub fn snippet_with_context<'a>( pub fn snippet_with_context<'a>(
cx: &impl LintContext, sess: &impl HasSession,
span: Span, span: Span,
outer: SyntaxContext, outer: SyntaxContext,
default: &'a str, default: &'a str,
applicability: &mut Applicability, applicability: &mut Applicability,
) -> (Cow<'a, str>, bool) { ) -> (Cow<'a, str>, bool) {
snippet_with_context_sess(cx.sess(), span, outer, default, applicability) snippet_with_context_sess(sess.sess(), span, outer, default, applicability)
} }
fn snippet_with_context_sess<'a>( fn snippet_with_context_sess<'a>(
@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
/// writeln!(o, "") -> writeln!(o, "") /// writeln!(o, "") -> writeln!(o, "")
/// ^^ ^^^^ /// ^^ ^^^^
/// ``` /// ```
pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span {
let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true);
extended.with_lo(extended.lo() - BytePos(1)) extended.with_lo(extended.lo() - BytePos(1))
} }
/// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// Converts `expr` to a `char` literal if it's a `str` literal containing a single
/// character (or a single byte with `ascii_only`) /// character (or a single byte with `ascii_only`)
pub fn str_literal_to_char_literal( pub fn str_literal_to_char_literal(
cx: &LateContext<'_>, sess: &impl HasSession,
expr: &Expr<'_>, expr: &Expr<'_>,
applicability: &mut Applicability, applicability: &mut Applicability,
ascii_only: bool, ascii_only: bool,
@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal(
} }
&& len == 1 && len == 1
{ {
let snip = snippet_with_applicability(cx, expr.span, string, applicability); let snip = snippet_with_applicability(sess, expr.span, string, applicability);
let ch = if let StrStyle::Raw(nhash) = style { let ch = if let StrStyle::Raw(nhash) = style {
let nhash = nhash as usize; let nhash = nhash as usize;
// for raw string: r##"a"## // for raw string: r##"a"##