Const eval changes (#15773)

First commit treats all constants containing a macro call as non-local
and renames `eval_simple` to be a little clearer.

Second commit removes `CoreConstant` and treats most of them as though
they were local. A couple of the constants like `usize::MAX` are treated
as non-local as different targets may have different values.
`const_is_empty` will now ignore non-local constants since there's no
guarantee that they are the same across all targets/features/versions.

The third commit just changes some `eval` calls to `eval_local`. Most of
these were style lints which shouldn't be assuming the value of a
constant won't ever change.

changelog: none
This commit is contained in:
Samuel Tardieu 2025-10-04 18:16:35 +00:00 committed by GitHub
commit d289009eea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 646 additions and 800 deletions

View file

@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
_ => return,
}
&& let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn)
&& let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition)
&& let Some((Constant::Bool(assert_val), const_src)) =
ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt())
&& let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id)
&& (const_src.is_local() || !in_const_context)
&& !(is_debug && as_bool_lit(condition) == Some(false))

View file

@ -43,12 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
if let Some(anon_const) = &var.disr_expr {
let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body);
let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity();
let constant = cx
.tcx
.const_eval_poly(def_id.to_def_id())
.ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
let constant = cx.tcx.const_eval_poly(def_id.to_def_id()).ok();
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c, ty)) {
if let ty::Adt(adt, _) = ty.kind()
&& adt.is_enum()
{

View file

@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
use rustc_span::SyntaxContext;
use rustc_span::source_map::Spanned;
use std::f32::consts as f32_consts;
use std::f64::consts as f64_consts;
@ -110,8 +111,8 @@ declare_lint_pass!(FloatingPointArithmetic => [
// Returns the specialized log method for a given base if base is constant
// and is one of 2, 10 and e
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
if let Some(value) = ConstEvalCtxt::new(cx).eval(base) {
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> {
if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) {
if F32(2.0) == value || F64(2.0) == value {
return Some("log2");
} else if F32(10.0) == value || F64(10.0) == value {
@ -157,7 +158,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
}
fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
if let Some(method) = get_specialized_log_method(cx, &args[0]) {
if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) {
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
@ -205,7 +206,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
// ranges [-16777215, 16777216) for type f32 as whole number floats outside
// this range are lossy and ambiguous.
#[expect(clippy::cast_possible_truncation)]
fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
match value {
F32(num) if num.fract() == 0.0 => {
if (-16_777_215.0..16_777_216.0).contains(num) {
@ -517,8 +518,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
match op {
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
_ => false,
}
} else {
@ -530,8 +531,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
match op {
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
_ => false,
}
} else {
@ -540,8 +541,8 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
}
/// Returns true iff expr is some zero literal
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match ConstEvalCtxt::new(cx).eval_simple(expr) {
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) {
Some(Int(i)) => i == 0,
Some(F32(f)) => f == 0.0,
Some(F64(f)) => f == 0.0,

View file

@ -60,10 +60,14 @@ impl LateLintPass<'_> for IfNotElse {
),
// Don't lint on `… != 0`, as these are likely to be bit tests.
// For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order.
ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => (
"unnecessary `!=` operation",
"change to `==` and swap the blocks of the `if`/`else`",
),
ExprKind::Binary(op, _, rhs)
if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs, e.span.ctxt()) =>
{
(
"unnecessary `!=` operation",
"change to `==` and swap the blocks of the `if`/`else`",
)
},
_ => return,
};

View file

@ -117,10 +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>)> {
if let ExprKind::Binary(op, l, r) = expr.kind {
let ecx = ConstEvalCtxt::new(cx);
if let Some(Constant::Int(c)) = ecx.eval(r) {
let ctxt = expr.span.ctxt();
if let Some(Constant::Int(c)) = ecx.eval_local(r, ctxt) {
return Some((c, op.node, l));
}
if let Some(Constant::Int(c)) = ecx.eval(l) {
if let Some(Constant::Int(c)) = ecx.eval_local(l, ctxt) {
return Some((c, invert_op(op.node)?, r));
}
}

View file

@ -101,7 +101,7 @@ fn upcast_comparison_bounds_err<'tcx>(
invert: bool,
) {
if let Some((lb, ub)) = lhs_bounds
&& let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs)
&& let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt())
{
if rel == Rel::Eq || rel == Rel::Ne {
if norm_rhs_val < lb || norm_rhs_val > ub {

View file

@ -1,5 +1,5 @@
use clippy_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::SpanRangeExt;
@ -146,13 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
)
&& let [first, second, const_1, const_2] = exprs
&& let ecx = ConstEvalCtxt::new(cx)
&& let Some(const_1) = ecx.eval(const_1)
&& let Some(const_2) = ecx.eval(const_2)
&& let ctxt = expr.span.ctxt()
&& let Some(const_1) = ecx.eval_local(const_1, ctxt)
&& let Some(const_2) = ecx.eval_local(const_2, ctxt)
&& path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
// The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
// case somebody does that for some reason
&& (is_infinity(&const_1) && is_neg_infinity(&const_2)
|| is_neg_infinity(&const_1) && is_infinity(&const_2))
&& (const_1.is_pos_infinity() && const_2.is_neg_infinity()
|| const_1.is_neg_infinity() && const_2.is_pos_infinity())
&& let Some(local_snippet) = first.span.get_source_text(cx)
{
let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
@ -201,21 +202,3 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
}
}
}
fn is_infinity(constant: &Constant<'_>) -> bool {
match constant {
// FIXME(f16_f128): add f16 and f128 when constants are available
Constant::F32(float) => *float == f32::INFINITY,
Constant::F64(float) => *float == f64::INFINITY,
_ => false,
}
}
fn is_neg_infinity(constant: &Constant<'_>) -> bool {
match constant {
// FIXME(f16_f128): add f16 and f128 when constants are available
Constant::F32(float) => *float == f32::NEG_INFINITY,
Constant::F64(float) => *float == f64::NEG_INFINITY,
_ => false,
}
}

View file

@ -8,6 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::SyntaxContext;
declare_clippy_lint! {
/// ### What it does
@ -58,13 +59,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
&& add_lhs.span.ctxt() == ctxt
&& add_rhs.span.ctxt() == ctxt
&& !expr.span.in_external_macro(cx.sess().source_map())
&& let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
&& let Some(const1) = check_for_unsigned_int_constant(cx, ctxt, rem_rhs)
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, ctxt, add_lhs, add_rhs)
&& let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
&& rem2_op.node == BinOpKind::Rem
&& const1 == const2
&& let Some(hir_id) = path_to_local(rem2_lhs)
&& let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs)
&& let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs)
// Also ensures the const is nonzero since zero can't be a divisor
&& const2 == const3
&& rem2_lhs.span.ctxt() == ctxt
@ -103,16 +104,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
// constant along with the other expression unchanged if so
fn check_for_either_unsigned_int_constant<'a>(
cx: &'a LateContext<'_>,
ctxt: SyntaxContext,
left: &'a Expr<'_>,
right: &'a Expr<'_>,
) -> Option<(u128, &'a Expr<'a>)> {
check_for_unsigned_int_constant(cx, left)
check_for_unsigned_int_constant(cx, ctxt, left)
.map(|int_const| (int_const, right))
.or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left)))
.or_else(|| check_for_unsigned_int_constant(cx, ctxt, right).map(|int_const| (int_const, left)))
}
fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?;
fn check_for_unsigned_int_constant<'a>(
cx: &'a LateContext<'_>,
ctxt: SyntaxContext,
expr: &'a Expr<'_>,
) -> Option<u128> {
let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr, ctxt)?;
match int_const {
FullInt::S(s) => s.try_into().ok(),
FullInt::U(u) => Some(u),

View file

@ -66,7 +66,7 @@ fn parse_shift<'tcx>(
BinOpKind::Shr => ShiftDirection::Right,
_ => return None,
};
let const_expr = ConstEvalCtxt::new(cx).eval(r)?;
let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?;
if let Constant::Int(shift) = const_expr {
return Some((dir, shift, l));
}

View file

@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _};
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
use rustc_span::source_map::Spanned;
use rustc_span::{Symbol, sym};
use rustc_span::{Symbol, SyntaxContext, sym};
use std::iter;
declare_clippy_lint! {
@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
return;
}
let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then);
let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then, expr.span.ctxt());
if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) {
let kind_word = match strip_kind {
StripKind::Prefix => "prefix",
@ -166,8 +166,8 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
}
// Returns the length of the `expr` if it's a constant string or char.
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
let value = ConstEvalCtxt::new(cx).eval(expr)?;
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option<u128> {
let value = ConstEvalCtxt::new(cx).eval_local(expr, ctxt)?;
match value {
Constant::Str(value) => Some(value.len() as u128),
Constant::Char(value) => Some(value.len_utf8() as u128),
@ -176,13 +176,18 @@ fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
}
// Tests if `expr` equals the length of the pattern.
fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
fn eq_pattern_length<'tcx>(
cx: &LateContext<'tcx>,
pattern: &Expr<'_>,
expr: &'tcx Expr<'_>,
ctxt: SyntaxContext,
) -> bool {
if let ExprKind::Lit(Spanned {
node: LitKind::Int(n, _),
..
}) = expr.kind
{
constant_length(cx, pattern).is_some_and(|length| n == length)
constant_length(cx, pattern, ctxt).is_some_and(|length| n == length)
} else {
len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg))
}
@ -215,6 +220,7 @@ fn find_stripping<'tcx>(
target: Res,
pattern: &'tcx Expr<'_>,
expr: &'tcx Expr<'tcx>,
ctxt: SyntaxContext,
) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap<Symbol, usize>) {
struct StrippingFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
@ -223,6 +229,7 @@ fn find_stripping<'tcx>(
pattern: &'tcx Expr<'tcx>,
results: Vec<&'tcx Expr<'tcx>>,
bindings: FxHashMap<Symbol, usize>,
ctxt: SyntaxContext,
}
impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> {
@ -236,7 +243,7 @@ fn find_stripping<'tcx>(
{
match (self.strip_kind, start, end) {
(StripKind::Prefix, Some(start), None) => {
if eq_pattern_length(self.cx, self.pattern, start) {
if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) {
self.results.push(ex);
return;
}
@ -252,7 +259,7 @@ fn find_stripping<'tcx>(
&& let Some(left_arg) = len_arg(self.cx, left)
&& let ExprKind::Path(left_path) = &left_arg.kind
&& self.cx.qpath_res(left_path, left_arg.hir_id) == self.target
&& eq_pattern_length(self.cx, self.pattern, right)
&& eq_pattern_length(self.cx, self.pattern, right, self.ctxt)
{
self.results.push(ex);
return;
@ -280,6 +287,7 @@ fn find_stripping<'tcx>(
pattern,
results: vec![],
bindings: FxHashMap::default(),
ctxt,
};
walk_expr(&mut finder, expr);
(finder.results, finder.bindings)

View file

@ -155,7 +155,7 @@ fn handle(
&& cx.typeck_results().expr_adjustments(body_some).is_empty()
&& let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx)
&& let Some(indent) = indent_of(cx, expr.span)
&& ConstEvalCtxt::new(cx).eval_simple(body_none).is_some()
&& ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some()
{
let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent));
let mut app = Applicability::MachineApplicable;

View file

@ -1,4 +1,4 @@
use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const};
use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt};
use clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@ -35,12 +35,12 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
let lhs_const = if let Some(lhs) = lhs {
ConstEvalCtxt::new(cx).eval_pat_expr(lhs)?
} else {
mir_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)?
Constant::new_numeric_min(cx.tcx, ty)?
};
let rhs_const = if let Some(rhs) = rhs {
ConstEvalCtxt::new(cx).eval_pat_expr(rhs)?
} else {
mir_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)?
Constant::new_numeric_max(cx.tcx, ty)?
};
let lhs_val = lhs_const.int_value(cx.tcx, ty)?;
let rhs_val = rhs_const.int_value(cx.tcx, ty)?;

View file

@ -17,10 +17,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
cx.tcx.get_diagnostic_name(func_def_id),
Some(sym::Ipv4Addr | sym::Ipv6Addr)
)
&& let ecx = ConstEvalCtxt::new(cx)
&& let ctxt = expr.span.ctxt()
&& let Some(args) = args
.iter()
.map(|arg| {
if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) {
if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ecx.eval_local(arg, ctxt) {
u8::try_from(constant).ok()
} else {
None

View file

@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(
return;
}
if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) {
if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix, expr.span.ctxt()) {
let (num, replacement) = match radix_val {
FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),

View file

@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
&& let def_id = item.owner_id.to_def_id()
&& is_trait_method(cx, expr, sym::Iterator)
&& let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg)
&& let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt())
&& !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext)
{
let mut app = Applicability::MachineApplicable;

View file

@ -11,13 +11,15 @@ use super::ITER_SKIP_ZERO;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) {
if !expr.span.from_expansion()
&& is_trait_method(cx, expr, sym::Iterator)
&& let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| {
if let Constant::Int(arg) = constant {
Some(arg)
} else {
None
}
})
&& let Some(arg) = ConstEvalCtxt::new(cx)
.eval_local(arg_expr, expr.span.ctxt())
.and_then(|constant| {
if let Constant::Int(arg) = constant {
Some(arg)
} else {
None
}
})
&& arg == 0
&& !is_from_proc_macro(cx, expr)
{

View file

@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
recv: &'tcx Expr<'_>,
repeat_arg: &'tcx Expr<'_>,
) {
if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) {
if ConstEvalCtxt::new(cx).eval_local(repeat_arg, expr.span.ctxt()) == Some(Constant::Int(1)) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if ty.is_str() {
span_lint_and_sugg(

View file

@ -304,7 +304,7 @@ fn parse_iter_usage<'tcx>(
};
},
(sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => {
if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) {
if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval_local(idx_expr, ctxt) {
let span = if name.ident.as_str() == "nth" {
e.span
} else if let Some((_, Node::Expr(next_expr))) = iter.next()

View file

@ -1,7 +1,7 @@
use std::cmp::Ordering;
use super::UNNECESSARY_MIN_OR_MAX;
use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt};
use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
@ -25,8 +25,9 @@ pub(super) fn check<'tcx>(
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(id)
&& matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max)
{
if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
&& let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
let ctxt = expr.span.ctxt();
if let Some(left) = ecx.eval_local(recv, ctxt)
&& let Some(right) = ecx.eval_local(arg, ctxt)
{
let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
return;

View file

@ -4,6 +4,7 @@ use clippy_utils::{is_trait_method, sym};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::SyntaxContext;
use std::cmp::Ordering::{Equal, Greater, Less};
declare_clippy_lint! {
@ -60,7 +61,7 @@ enum MinMax {
Max,
}
fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> {
fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
match expr.kind {
ExprKind::Call(path, args) => {
if let ExprKind::Path(ref qpath) = path.kind {
@ -68,8 +69,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM
.qpath_res(qpath, path.hir_id)
.opt_def_id()
.and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min),
Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max),
Some(sym::cmp_min) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Min),
Some(sym::cmp_max) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Max),
_ => None,
})
} else {
@ -79,8 +80,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM
ExprKind::MethodCall(path, receiver, args @ [_], _) => {
if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) {
match path.ident.name {
sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max),
sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min),
sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max),
sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min),
_ => None,
}
} else {
@ -91,12 +92,13 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM
}
}
fn fetch_const<'a, 'tcx>(
cx: &LateContext<'tcx>,
fn fetch_const<'a>(
cx: &LateContext<'_>,
ctxt: SyntaxContext,
receiver: Option<&'a Expr<'a>>,
args: &'a [Expr<'a>],
m: MinMax,
) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> {
) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
let mut args = receiver.into_iter().chain(args);
let first_arg = args.next()?;
let second_arg = args.next()?;
@ -104,7 +106,7 @@ fn fetch_const<'a, 'tcx>(
return None;
}
let ecx = ConstEvalCtxt::new(cx);
match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) {
match (ecx.eval_local(first_arg, ctxt), ecx.eval_local(second_arg, ctxt)) {
(Some(c), None) => Some((m, c, second_arg)),
(None, Some(c)) => Some((m, c, first_arg)),
// otherwise ignore

View file

@ -190,7 +190,7 @@ impl ArithmeticSideEffects {
lhs: &'tcx hir::Expr<'_>,
rhs: &'tcx hir::Expr<'_>,
) {
if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() {
if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_some() {
return;
}
if !matches!(
@ -283,7 +283,7 @@ impl ArithmeticSideEffects {
let Some(arg) = args.first() else {
return;
};
if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() {
if ConstEvalCtxt::new(cx).eval_local(receiver, expr.span.ctxt()).is_some() {
return;
}
let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver);

View file

@ -22,7 +22,7 @@ fn comparison_to_const<'tcx>(
cx: &LateContext<'tcx>,
typeck: &'tcx TypeckResults<'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, Ty<'tcx>)> {
if let ExprKind::Binary(operator, left, right) = expr.kind
&& let Ok(cmp_op) = CmpOp::try_from(operator.node)
{

View file

@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(
if op == BinOpKind::Div
&& let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
&& let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right)
&& let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt())
{
let suggested_fn = match (method_path.ident.name, divisor) {
(sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis",

View file

@ -39,7 +39,9 @@ fn check_op<'tcx>(
other: &Expr<'tcx>,
parent: &Expr<'tcx>,
) {
if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) {
if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_local(op, parent.span.ctxt())
== Some(Constant::Int(0))
{
if different_types(tck, other, parent) {
return;
}

View file

@ -18,12 +18,13 @@ pub(crate) fn check<'tcx>(
) {
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) {
let ecx = ConstEvalCtxt::new(cx);
let left_is_local = match ecx.eval_with_source(left) {
let ctxt = expr.span.ctxt();
let left_is_local = match ecx.eval_with_source(left, ctxt) {
Some((c, s)) if !is_allowed(&c) => s.is_local(),
Some(_) => return,
None => true,
};
let right_is_local = match ecx.eval_with_source(right) {
let right_is_local = match ecx.eval_with_source(right, ctxt) {
Some((c, s)) if !is_allowed(&c) => s.is_local(),
Some(_) => return,
None => true,
@ -84,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static
}
}
fn is_allowed(val: &Constant<'_>) -> bool {
fn is_allowed(val: &Constant) -> bool {
match val {
// FIXME(f16_f128): add when equality check is available on all platforms
&Constant::F32(f) => f == 0.0 || f.is_infinite(),

View file

@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::{Span, kw};
use rustc_span::{Span, SyntaxContext, kw};
use super::IDENTITY_OP;
@ -41,42 +41,43 @@ pub(crate) fn check<'tcx>(
(span, is_coerced)
};
let ctxt = expr.span.ctxt();
match op {
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
if is_redundant_op(cx, left, 0) {
if is_redundant_op(cx, left, 0, ctxt) {
let paren = needs_parenthesis(cx, expr, right);
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
} else if is_redundant_op(cx, right, 0) {
} else if is_redundant_op(cx, right, 0, ctxt) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
if is_redundant_op(cx, right, 0) {
if is_redundant_op(cx, right, 0, ctxt) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Mul => {
if is_redundant_op(cx, left, 1) {
if is_redundant_op(cx, left, 1, ctxt) {
let paren = needs_parenthesis(cx, expr, right);
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
} else if is_redundant_op(cx, right, 1) {
} else if is_redundant_op(cx, right, 1, ctxt) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Div => {
if is_redundant_op(cx, right, 1) {
if is_redundant_op(cx, right, 1, ctxt) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::BitAnd => {
if is_redundant_op(cx, left, -1) {
if is_redundant_op(cx, left, -1, ctxt) {
let paren = needs_parenthesis(cx, expr, right);
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
} else if is_redundant_op(cx, right, -1) {
} else if is_redundant_op(cx, right, -1, ctxt) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
@ -184,14 +185,17 @@ fn is_allowed<'tcx>(
// This lint applies to integers and their references
cx.typeck_results().expr_ty(left).peel_refs().is_integral()
&& 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
&& !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1))
&& !(cmp == BinOpKind::Shl
&& is_zero_integer_const(cx, right, expr.span.ctxt())
&& integer_const(cx, left, expr.span.ctxt()) == Some(1))
}
fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
let ecx = ConstEvalCtxt::new(cx);
if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) {
let ctxt = span.ctxt();
if match (ecx.eval_full_int(left, ctxt), ecx.eval_full_int(right, ctxt)) {
(Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
_ => return,
@ -200,8 +204,8 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
}
}
fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool {
if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) {
fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, ctxt: SyntaxContext) -> bool {
if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_local(e, ctxt).map(Constant::peel_refs) {
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
ty::Uint(uty) => clip(cx.tcx, !0, uty),

View file

@ -13,14 +13,14 @@ use super::MANUAL_IS_MULTIPLE_OF;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &Expr<'_>,
expr: &'tcx Expr<'tcx>,
op: BinOpKind,
lhs: &'tcx Expr<'tcx>,
rhs: &'tcx Expr<'tcx>,
msrv: Msrv,
) {
if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF)
&& let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs)
&& let Some(operand) = uint_compare_to_zero(cx, expr, op, lhs, rhs)
&& let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind
&& operand_op.node == BinOpKind::Rem
&& matches!(
@ -57,18 +57,19 @@ pub(super) fn check<'tcx>(
// If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand
fn uint_compare_to_zero<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
op: BinOpKind,
lhs: &'tcx Expr<'tcx>,
rhs: &'tcx Expr<'tcx>,
) -> Option<&'tcx Expr<'tcx>> {
let operand = if matches!(lhs.kind, ExprKind::Binary(..))
&& matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt)
&& is_zero_integer_const(cx, rhs)
&& is_zero_integer_const(cx, rhs, e.span.ctxt())
{
lhs
} else if matches!(rhs.kind, ExprKind::Binary(..))
&& matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt)
&& is_zero_integer_const(cx, lhs)
&& is_zero_integer_const(cx, lhs, e.span.ctxt())
{
rhs
} else {

View file

@ -39,7 +39,9 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
&& let BinOpKind::Eq | BinOpKind::Ne = op.node
{
let ecx = ConstEvalCtxt::new(cx);
matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0)))
let ctxt = expr.span.ctxt();
matches!(ecx.eval_local(lhs, ctxt), Some(Constant::Int(0)))
|| matches!(ecx.eval_local(rhs, ctxt), Some(Constant::Int(0)))
} else {
false
}
@ -55,7 +57,7 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) ->
match ConstEvalCtxt::new(cx).eval(operand)? {
Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() {
ty::Int(ity) => {
let value = sext(cx.tcx, v, ity);
let value: i128 = sext(cx.tcx, v, ity);
Some(OperandInfo {
string_representation: Some(value.to_string()),
is_negative: value < 0,

View file

@ -55,7 +55,7 @@ impl Context {
return;
}
let ty = cx.typeck_results().expr_ty(arg);
if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() {
if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_none() && ty.is_floating_point() {
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.expr_id = Some(expr.hir_id);
}

View file

@ -299,8 +299,8 @@ fn check_possible_range_contains(
}
}
struct RangeBounds<'a, 'tcx> {
val: Constant<'tcx>,
struct RangeBounds<'a> {
val: Constant,
expr: &'a Expr<'a>,
id: HirId,
name_span: Span,
@ -312,7 +312,7 @@ struct RangeBounds<'a, 'tcx> {
// Takes a binary expression such as x <= 2 as input
// Breaks apart into various pieces, such as the value of the number,
// hir id of the variable, and direction/inclusiveness of the operator
fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a, 'tcx>> {
fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
if let ExprKind::Binary(ref op, l, r) = ex.kind {
let (inclusive, ordering) = match op.node {
BinOpKind::Gt => (false, Ordering::Greater),

View file

@ -37,8 +37,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv {
// That's probably fine for this lint - it's pretty unlikely that someone would
// do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
&& let ecx = ConstEvalCtxt::new(cx)
&& let Some(lhs_value) = ecx.eval_simple(left)
&& let Some(rhs_value) = ecx.eval_simple(right)
&& let ctxt = expr.span.ctxt()
&& let Some(lhs_value) = ecx.eval_local(left, ctxt)
&& let Some(rhs_value) = ecx.eval_local(right, ctxt)
// FIXME(f16_f128): add these types when eq is available on all platforms
&& (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value)
&& (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value)

View file

@ -5,25 +5,21 @@
#![allow(clippy::float_cmp)]
use crate::source::{SpanRangeExt, walk_span_to_context};
use crate::{clip, is_direct_expn_of, sext, unsext};
use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext};
use rustc_abi::Size;
use rustc_apfloat::Float;
use rustc_apfloat::ieee::{Half, Quad};
use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp,
};
use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp};
use rustc_lexer::{FrontmatterAllowed, tokenize};
use rustc_lint::LateContext;
use rustc_middle::mir::ConstValue;
use rustc_middle::mir::interpret::{Scalar, alloc_range};
use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::def_id::DefId;
use rustc_span::symbol::Ident;
use rustc_span::{SyntaxContext, sym};
use rustc_span::{Symbol, SyntaxContext};
use std::cell::Cell;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
@ -31,8 +27,8 @@ use std::iter;
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
pub enum Constant<'tcx> {
Adt(mir::Const<'tcx>),
pub enum Constant {
Adt(ConstValue),
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
@ -54,15 +50,15 @@ pub enum Constant<'tcx> {
/// `true` or `false`.
Bool(bool),
/// An array of constants.
Vec(Vec<Constant<'tcx>>),
Vec(Vec<Constant>),
/// Also an array, but with only one constant, repeated N times.
Repeat(Box<Constant<'tcx>>, u64),
Repeat(Box<Constant>, u64),
/// A tuple of constants.
Tuple(Vec<Constant<'tcx>>),
Tuple(Vec<Constant>),
/// A raw pointer.
RawPtr(u128),
/// A reference
Ref(Box<Constant<'tcx>>),
Ref(Box<Constant>),
/// A literal with syntax error.
Err,
}
@ -124,7 +120,7 @@ impl IntTypeBounds for IntTy {
}
}
impl PartialEq for Constant<'_> {
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Str(ls), Self::Str(rs)) => ls == rs,
@ -132,16 +128,12 @@ impl PartialEq for Constant<'_> {
(&Self::Char(l), &Self::Char(r)) => l == r,
(&Self::Int(l), &Self::Int(r)) => l == r,
(&Self::F64(l), &Self::F64(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
l.to_bits() == r.to_bits()
// `to_bits` is required to catch non-matching `0.0` and `-0.0`.
l.to_bits() == r.to_bits() && !l.is_nan()
},
(&Self::F32(l), &Self::F32(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
f64::from(l).to_bits() == f64::from(r).to_bits()
// `to_bits` is required to catch non-matching `0.0` and `-0.0`.
l.to_bits() == r.to_bits() && !l.is_nan()
},
(&Self::Bool(l), &Self::Bool(r)) => l == r,
(&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
@ -153,7 +145,7 @@ impl PartialEq for Constant<'_> {
}
}
impl Hash for Constant<'_> {
impl Hash for Constant {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
@ -209,7 +201,7 @@ impl Hash for Constant<'_> {
}
}
impl Constant<'_> {
impl Constant {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)),
@ -297,10 +289,129 @@ impl Constant<'_> {
let f: Quad = s.parse().unwrap();
Self::F128(f.to_bits())
}
pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Self> {
match *ty.kind() {
ty::Uint(_) => Some(Self::Int(0)),
ty::Int(ty) => {
let val = match ty.normalize(tcx.sess.target.pointer_width) {
IntTy::I8 => i128::from(i8::MIN),
IntTy::I16 => i128::from(i16::MIN),
IntTy::I32 => i128::from(i32::MIN),
IntTy::I64 => i128::from(i64::MIN),
IntTy::I128 => i128::MIN,
IntTy::Isize => return None,
};
Some(Self::Int(val.cast_unsigned()))
},
ty::Char => Some(Self::Char(char::MIN)),
ty::Float(FloatTy::F32) => Some(Self::F32(f32::NEG_INFINITY)),
ty::Float(FloatTy::F64) => Some(Self::F64(f64::NEG_INFINITY)),
_ => None,
}
}
pub fn new_numeric_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Self> {
match *ty.kind() {
ty::Uint(ty) => Some(Self::Int(match ty.normalize(tcx.sess.target.pointer_width) {
UintTy::U8 => u128::from(u8::MAX),
UintTy::U16 => u128::from(u16::MAX),
UintTy::U32 => u128::from(u32::MAX),
UintTy::U64 => u128::from(u64::MAX),
UintTy::U128 => u128::MAX,
UintTy::Usize => return None,
})),
ty::Int(ty) => {
let val = match ty.normalize(tcx.sess.target.pointer_width) {
IntTy::I8 => i128::from(i8::MAX),
IntTy::I16 => i128::from(i16::MAX),
IntTy::I32 => i128::from(i32::MAX),
IntTy::I64 => i128::from(i64::MAX),
IntTy::I128 => i128::MAX,
IntTy::Isize => return None,
};
Some(Self::Int(val.cast_unsigned()))
},
ty::Char => Some(Self::Char(char::MAX)),
ty::Float(FloatTy::F32) => Some(Self::F32(f32::INFINITY)),
ty::Float(FloatTy::F64) => Some(Self::F64(f64::INFINITY)),
_ => None,
}
}
pub fn is_numeric_min<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match (self, ty.kind()) {
(&Self::Int(x), &ty::Uint(_)) => x == 0,
(&Self::Int(x), &ty::Int(ty)) => {
let limit = match ty.normalize(tcx.sess.target.pointer_width) {
IntTy::I8 => i128::from(i8::MIN),
IntTy::I16 => i128::from(i16::MIN),
IntTy::I32 => i128::from(i32::MIN),
IntTy::I64 => i128::from(i64::MIN),
IntTy::I128 => i128::MIN,
IntTy::Isize => return false,
};
x.cast_signed() == limit
},
(&Self::Char(x), &ty::Char) => x == char::MIN,
(&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::NEG_INFINITY,
(&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::NEG_INFINITY,
_ => false,
}
}
pub fn is_numeric_max<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match (self, ty.kind()) {
(&Self::Int(x), &ty::Uint(ty)) => {
let limit = match ty.normalize(tcx.sess.target.pointer_width) {
UintTy::U8 => u128::from(u8::MAX),
UintTy::U16 => u128::from(u16::MAX),
UintTy::U32 => u128::from(u32::MAX),
UintTy::U64 => u128::from(u64::MAX),
UintTy::U128 => u128::MAX,
UintTy::Usize => return false,
};
x == limit
},
(&Self::Int(x), &ty::Int(ty)) => {
let limit = match ty.normalize(tcx.sess.target.pointer_width) {
IntTy::I8 => i128::from(i8::MAX),
IntTy::I16 => i128::from(i16::MAX),
IntTy::I32 => i128::from(i32::MAX),
IntTy::I64 => i128::from(i64::MAX),
IntTy::I128 => i128::MAX,
IntTy::Isize => return false,
};
x.cast_signed() == limit
},
(&Self::Char(x), &ty::Char) => x == char::MAX,
(&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::INFINITY,
(&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::INFINITY,
_ => false,
}
}
pub fn is_pos_infinity(&self) -> bool {
match *self {
// FIXME(f16_f128): add f16 and f128 when constants are available
Constant::F32(x) => x == f32::INFINITY,
Constant::F64(x) => x == f64::INFINITY,
_ => false,
}
}
pub fn is_neg_infinity(&self) -> bool {
match *self {
// FIXME(f16_f128): add f16 and f128 when constants are available
Constant::F32(x) => x == f32::NEG_INFINITY,
Constant::F64(x) => x == f64::NEG_INFINITY,
_ => false,
}
}
}
/// Parses a `LitKind` to a `Constant`.
pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constant<'tcx> {
pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
@ -331,10 +442,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan
pub enum ConstantSource {
/// The value is determined solely from the expression.
Local,
/// The value is dependent on a defined constant.
Constant,
/// The value is dependent on a constant defined in `core` crate.
CoreConstant,
/// The value is dependent on another definition that may change independently from the local
/// expression.
NonLocal,
}
impl ConstantSource {
pub fn is_local(self) -> bool {
@ -387,6 +497,7 @@ pub struct ConstEvalCtxt<'tcx> {
typing_env: ty::TypingEnv<'tcx>,
typeck: &'tcx TypeckResults<'tcx>,
source: Cell<ConstantSource>,
ctxt: Cell<SyntaxContext>,
}
impl<'tcx> ConstEvalCtxt<'tcx> {
@ -398,6 +509,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
typing_env: cx.typing_env(),
typeck: cx.typeck_results(),
source: Cell::new(ConstantSource::Local),
ctxt: Cell::new(SyntaxContext::root()),
}
}
@ -408,38 +520,50 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
typing_env,
typeck,
source: Cell::new(ConstantSource::Local),
ctxt: Cell::new(SyntaxContext::root()),
}
}
/// 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)> {
pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant, ConstantSource)> {
self.source.set(ConstantSource::Local);
self.ctxt.set(ctxt);
self.expr(e).map(|c| (c, self.source.get()))
}
/// Attempts to evaluate the expression.
pub fn eval(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
pub fn eval(&self, e: &Expr<'_>) -> Option<Constant> {
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) {
///
/// The context argument is the context used to view the evaluated expression. e.g. when
/// evaluating the argument in `f(m!(1))` the context of the call expression should be used.
/// This is need so the const evaluator can see the `m` macro and marke the evaluation as
/// non-local independant of what the macro expands to.
pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<Constant> {
match self.eval_with_source(e, ctxt) {
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) {
///
/// The context argument is the context used to view the evaluated expression. e.g. when
/// evaluating the argument in `f(m!(1))` the context of the call expression should be used.
/// This is need so the const evaluator can see the `m` macro and marke the evaluation as
/// non-local independant of what the macro expands to.
pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<FullInt> {
match self.eval_with_source(e, ctxt) {
Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)),
_ => None,
}
}
pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant<'tcx>> {
pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant> {
match &pat_expr.kind {
PatExprKind::Lit { lit, negated } => {
let ty = self.typeck.node_type_opt(pat_expr.hir_id);
@ -455,39 +579,31 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option<Constant<'tcx>> {
let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() {
self.tcx.crate_name(def_id.krate) == sym::core
} else {
false
};
self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| {
let result = mir_to_const(self_.tcx, result)?;
// If source is already Constant we wouldn't want to override it with CoreConstant
self_.source.set(
if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
ConstantSource::CoreConstant
} else {
ConstantSource::Constant
},
);
Some(result)
})
fn check_ctxt(&self, ctxt: SyntaxContext) {
if self.ctxt.get() != ctxt {
self.source.set(ConstantSource::NonLocal);
}
}
fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option<Constant> {
self.fetch_path(qpath, hir_id)
.and_then(|c| mir_to_const(self.tcx, c, self.typeck.node_type(hir_id)))
}
/// Simple constant folding: Insert an expression, get a constant or none.
fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
fn expr(&self, e: &Expr<'_>) -> Option<Constant> {
self.check_ctxt(e.span.ctxt());
match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value),
ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Block(block, _) => {
self.check_ctxt(block.span.ctxt());
self.block(block)
},
ExprKind::Lit(lit) => {
if is_direct_expn_of(e.span, sym::cfg).is_some() {
None
} else {
Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e)))
}
self.check_ctxt(lit.span.ctxt());
Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e)))
},
ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
@ -504,7 +620,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
}),
ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
ExprKind::Binary(op, left, right) => self.binop(op.node, left, right),
ExprKind::Binary(op, left, right) => {
self.check_ctxt(e.span.ctxt());
self.binop(op.node, left, right)
},
ExprKind::Call(callee, []) => {
// We only handle a few const functions for now.
if let ExprKind::Path(qpath) = &callee.kind
@ -524,17 +643,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
},
ExprKind::Index(arr, index, _) => self.index(arr, index),
ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
ExprKind::Field(local_expr, ref field) => {
let result = self.expr(local_expr);
if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
&& let ty::Adt(adt_def, _) = constant.ty().kind()
ExprKind::Field(base, ref field)
if let base_ty = self.typeck.expr_ty(base)
&& match self.typeck.expr_adjustments(base) {
[] => true,
[.., a] => a.target == base_ty,
}
&& let Some(Constant::Adt(constant)) = self.expr(base)
&& let ty::Adt(adt_def, _) = *base_ty.kind()
&& adt_def.is_struct()
&& let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field)
{
mir_to_const(self.tcx, desired_field)
} else {
result
}
&& let Some((desired_field, ty)) =
field_of_struct(adt_def, self.tcx, constant, base_ty, field.name) =>
{
self.check_ctxt(field.span.ctxt());
mir_to_const(self.tcx, desired_field, ty)
},
_ => None,
}
@ -547,19 +669,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir_body(body).value),
ExprKind::DropTemps(e) => self.eval_is_empty(e),
ExprKind::Path(ref qpath) => {
if !self
.typeck
.qpath_res(qpath, e.hir_id)
.opt_def_id()
.is_some_and(DefId::is_local)
{
return None;
}
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
mir_is_empty(self_.tcx, result)
})
},
ExprKind::Lit(lit) => {
if is_direct_expn_of(e.span, sym::cfg).is_some() {
None
@ -584,7 +693,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
#[expect(clippy::cast_possible_wrap)]
fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
use self::Constant::{Bool, Int};
match *o {
Bool(b) => Some(Bool(!b)),
@ -600,7 +709,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
use self::Constant::{F32, F64, Int};
match *o {
Int(value) => {
@ -626,48 +735,128 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
/// Create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part.
fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
}
/// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
fn fetch_path_and_apply<T, F>(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
where
F: FnOnce(&Self, mir::Const<'tcx>) -> Option<T>,
{
let res = self.typeck.qpath_res(qpath, id);
match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
// Check if this constant is based on `cfg!(..)`,
// which is NOT constant for our purposes.
if let Some(node) = self.tcx.hir_get_if_local(def_id)
&& let Node::Item(Item {
kind: ItemKind::Const(.., body_id),
..
}) = node
&& let Node::Expr(Expr {
kind: ExprKind::Lit(_),
span,
..
}) = self.tcx.hir_node(body_id.hir_id)
&& is_direct_expn_of(*span, sym::cfg).is_some()
{
return None;
}
let args = self.typeck.node_args(id);
let result = self
.tcx
.const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
.ok()
.map(|val| mir::Const::from_value(val, ty))?;
f(self, result)
#[expect(clippy::too_many_lines)]
fn fetch_path(&self, qpath: &QPath<'_>, id: HirId) -> Option<ConstValue> {
// Resolve the path to a constant and check if that constant is known to
// not change based on the target.
//
// This should be replaced with an attribute at some point.
let did = match *qpath {
QPath::Resolved(None, path)
if path.span.ctxt() == self.ctxt.get()
&& path.segments.iter().all(|s| self.ctxt.get() == s.ident.span.ctxt())
&& let Res::Def(DefKind::Const, did) = path.res
&& (matches!(
self.tcx.get_diagnostic_name(did),
Some(
sym::f32_legacy_const_digits
| sym::f32_legacy_const_epsilon
| sym::f32_legacy_const_infinity
| sym::f32_legacy_const_mantissa_dig
| sym::f32_legacy_const_max
| sym::f32_legacy_const_max_10_exp
| sym::f32_legacy_const_max_exp
| sym::f32_legacy_const_min
| sym::f32_legacy_const_min_10_exp
| sym::f32_legacy_const_min_exp
| sym::f32_legacy_const_min_positive
| sym::f32_legacy_const_nan
| sym::f32_legacy_const_neg_infinity
| sym::f32_legacy_const_radix
| sym::f64_legacy_const_digits
| sym::f64_legacy_const_epsilon
| sym::f64_legacy_const_infinity
| sym::f64_legacy_const_mantissa_dig
| sym::f64_legacy_const_max
| sym::f64_legacy_const_max_10_exp
| sym::f64_legacy_const_max_exp
| sym::f64_legacy_const_min
| sym::f64_legacy_const_min_10_exp
| sym::f64_legacy_const_min_exp
| sym::f64_legacy_const_min_positive
| sym::f64_legacy_const_nan
| sym::f64_legacy_const_neg_infinity
| sym::f64_legacy_const_radix
| sym::u8_legacy_const_min
| sym::u16_legacy_const_min
| sym::u32_legacy_const_min
| sym::u64_legacy_const_min
| sym::u128_legacy_const_min
| sym::usize_legacy_const_min
| sym::u8_legacy_const_max
| sym::u16_legacy_const_max
| sym::u32_legacy_const_max
| sym::u64_legacy_const_max
| sym::u128_legacy_const_max
| sym::i8_legacy_const_min
| sym::i16_legacy_const_min
| sym::i32_legacy_const_min
| sym::i64_legacy_const_min
| sym::i128_legacy_const_min
| sym::i8_legacy_const_max
| sym::i16_legacy_const_max
| sym::i32_legacy_const_max
| sym::i64_legacy_const_max
| sym::i128_legacy_const_max
)
) || self.tcx.opt_parent(did).is_some_and(|parent| {
paths::F16_CONSTS.matches(&self.tcx, parent)
|| paths::F32_CONSTS.matches(&self.tcx, parent)
|| paths::F64_CONSTS.matches(&self.tcx, parent)
|| paths::F128_CONSTS.matches(&self.tcx, parent)
})) =>
{
did
},
_ => None,
}
QPath::TypeRelative(ty, const_name)
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind
&& let [.., ty_name] = ty_path.segments
&& (matches!(
ty_name.ident.name,
sym::i8
| sym::i16
| sym::i32
| sym::i64
| sym::i128
| sym::u8
| sym::u16
| sym::u32
| sym::u64
| sym::u128
| sym::f32
| sym::f64
| sym::char
) || (ty_name.ident.name == sym::usize && const_name.ident.name == sym::MIN))
&& const_name.ident.span.ctxt() == self.ctxt.get()
&& ty.span.ctxt() == self.ctxt.get()
&& ty_name.ident.span.ctxt() == self.ctxt.get()
&& matches!(ty_path.res, Res::PrimTy(_))
&& let Some((DefKind::AssocConst, did)) = self.typeck.type_dependent_def(id) =>
{
did
},
_ if let Res::Def(DefKind::Const | DefKind::AssocConst, did) = self.typeck.qpath_res(qpath, id) => {
self.source.set(ConstantSource::NonLocal);
did
},
_ => return None,
};
self.tcx
.const_eval_resolve(
self.typing_env,
mir::UnevaluatedConst::new(did, self.typeck.node_args(id)),
qpath.span(),
)
.ok()
}
fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
let lhs = self.expr(lhs);
let index = self.expr(index);
@ -697,7 +886,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
/// A block can only yield a constant if it has exactly one constant expression.
fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> {
fn block(&self, block: &Block<'_>) -> Option<Constant> {
if block.stmts.is_empty()
&& let Some(expr) = block.expr
{
@ -716,11 +905,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
.filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
.eq([OpenBrace])
{
self.source.set(ConstantSource::Constant);
self.source.set(ConstantSource::NonLocal);
}
} else {
// Unable to access the source. Assume a non-local dependency.
self.source.set(ConstantSource::Constant);
self.source.set(ConstantSource::NonLocal);
}
}
@ -730,7 +919,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
if let Some(Constant::Bool(b)) = self.expr(cond) {
if b {
self.expr(then)
@ -742,7 +931,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
@ -778,6 +967,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
BinOpKind::BitXor => Some(zext(l ^ r)),
BinOpKind::BitOr => Some(zext(l | r)),
BinOpKind::BitAnd => Some(zext(l & r)),
// FIXME: f32/f64 currently consider `0.0` and `-0.0` as different.
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
@ -856,14 +1046,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts.
return None;
};
match (val, result.ty().kind()) {
(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> Option<Constant> {
match (val, ty.kind()) {
(_, &ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(val)),
(ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() {
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))),
ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())),
@ -877,7 +1063,6 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option
let data = val.try_get_slice_bytes_for_diagnostics(tcx)?;
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
},
(_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
(ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let len = len.try_to_target_usize(tcx)?;
@ -902,64 +1087,32 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option
}
}
fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts.
return None;
};
match (val, result.ty().kind()) {
(_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() {
ty::Str | ty::Slice(_) => {
if let ConstValue::Indirect { alloc_id, offset } = val {
// Get the length from the slice, using the same formula as
// [`ConstValue::try_get_slice_bytes_for_diagnostics`].
let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let ptr_size = tcx.data_layout.pointer_size();
if a.size() < offset + 2 * ptr_size {
// (partially) dangling reference
return None;
}
let len = a
.read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false)
.ok()?
.to_target_usize(&tcx)
.discard_err()?;
Some(len == 0)
} else {
None
}
},
ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0),
_ => None,
},
(ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0),
(ConstValue::ZeroSized, _) => Some(true),
_ => None,
}
}
fn field_of_struct<'tcx>(
adt_def: ty::AdtDef<'tcx>,
tcx: TyCtxt<'tcx>,
result: mir::Const<'tcx>,
field: &Ident,
) -> Option<mir::Const<'tcx>> {
if let mir::Const::Val(result, ty) = result
&& let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty)
value: ConstValue,
ty: Ty<'tcx>,
field: Symbol,
) -> Option<(ConstValue, Ty<'tcx>)> {
if let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(value, ty)
&& let Some(dc_variant) = 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(&(val, ty)) = dc.fields.get(field_idx)
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == field)
{
Some(mir::Const::Val(val, ty))
dc.fields.get(field_idx).copied()
} else {
None
}
}
/// If `expr` evaluates to an integer constant, return its value.
pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) {
///
/// The context argument is the context used to view the evaluated expression. e.g. when evaluating
/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so
/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of
/// what the macro expands to.
pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option<u128> {
if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) {
Some(value)
} else {
None
@ -967,7 +1120,12 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
}
/// Check if `expr` evaluates to an integer constant of 0.
///
/// The context argument is the context used to view the evaluated expression. e.g. when evaluating
/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so
/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of
/// what the macro expands to.
#[inline]
pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
integer_const(cx, expr) == Some(0)
pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
integer_const(cx, expr, ctxt) == Some(0)
}

View file

@ -454,7 +454,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
return Some(VecInitKind::Default);
} else if name.ident.name == sym::with_capacity {
let arg = args.first()?;
return match ConstEvalCtxt::new(cx).eval_simple(arg) {
return match ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) {
Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
_ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
};

View file

@ -290,8 +290,10 @@ impl HirEqInterExpr<'_, '_, '_> {
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
&& typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
&& let (Some(l), Some(r)) = (
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left),
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right),
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs)
.eval_local(left, self.left_ctxt),
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs)
.eval_local(right, self.right_ctxt),
)
&& l == r
{
@ -842,7 +844,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
#[expect(clippy::too_many_lines)]
pub fn hash_expr(&mut self, e: &Expr<'_>) {
let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e)
ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_local(e, e.span.ctxt())
});
// const hashing may result in the same hash as some unrelated node, so add a sort of

View file

@ -126,7 +126,7 @@ use source::{SpanRangeExt, walk_span_to_context};
use visitors::{Visitable, for_each_unconsumed_temporary};
use crate::ast_utils::unordered_over;
use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
use crate::consts::{ConstEvalCtxt, Constant};
use crate::higher::Range;
use crate::msrvs::Msrv;
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
@ -1423,11 +1423,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
let start_is_none_or_min = start.is_none_or(|start| {
if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0)
&& let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
&& let Some(min_const) = mir_to_const(cx.tcx, min_const)
&& let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
{
start_const == min_const
start_const.is_numeric_min(cx.tcx, bnd_ty)
} else {
false
}
@ -1436,11 +1434,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
RangeLimits::Closed => {
if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0)
&& let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
&& let Some(max_const) = mir_to_const(cx.tcx, max_const)
&& let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
{
end_const == max_const
end_const.is_numeric_max(cx.tcx, bnd_ty)
} else {
false
}

View file

@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{ItemKind, Node, UseKind};
use rustc_lint::LateContext;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy};
use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol};
use std::sync::OnceLock;
@ -74,8 +75,8 @@ impl PathLookup {
}
/// Returns the list of [`DefId`]s that the path resolves to
pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] {
self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path))
pub fn get<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>) -> &[DefId] {
self.once.get_or_init(|| lookup_path(tcx.tcx(), self.ns, self.path))
}
/// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into
@ -90,8 +91,8 @@ impl PathLookup {
}
/// Checks if the path resolves to the given `def_id`
pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool {
self.get(cx).contains(&def_id)
pub fn matches<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, def_id: DefId) -> bool {
self.get(&tcx.tcx()).contains(&def_id)
}
/// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it
@ -100,8 +101,8 @@ impl PathLookup {
}
/// Checks if the path resolves to `ty`'s definition, must be an `Adt`
pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did()))
pub fn matches_ty<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, ty: Ty<'_>) -> bool {
ty.ty_adt_def().is_some_and(|adt| self.matches(&tcx.tcx(), adt.did()))
}
}
@ -126,6 +127,11 @@ path_macros! {
macro_path: PathNS::Macro,
}
pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts);
pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts);
pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts);
pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts);
// Paths in external crates
pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);

View file

@ -115,6 +115,7 @@ generate! {
collapsible_if,
collect,
const_ptr,
consts,
contains,
copied,
copy_from,

View file

@ -43,6 +43,7 @@ fn main() {
assert_const!(-1);
assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
//~^ assertions_on_constants
let flag: bool = cfg!(not(feature = "asdf"));
assert!(flag);

View file

@ -72,8 +72,16 @@ LL | debug_assert!(true);
|
= help: remove the assertion
error: this assertion has a constant value
--> tests/ui/assertions_on_constants.rs:45:5
|
LL | assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider moving this into a const block: `const { assert!(..) }`
error: this assertion is always `true`
--> tests/ui/assertions_on_constants.rs:53:19
--> tests/ui/assertions_on_constants.rs:54:19
|
LL | const _: () = assert!(true);
| ^^^^^^^^^^^^^
@ -81,7 +89,7 @@ LL | const _: () = assert!(true);
= help: remove the assertion
error: this assertion is always `true`
--> tests/ui/assertions_on_constants.rs:56:5
--> tests/ui/assertions_on_constants.rs:57:5
|
LL | assert!(8 == (7 + 1));
| ^^^^^^^^^^^^^^^^^^^^^
@ -89,7 +97,7 @@ LL | assert!(8 == (7 + 1));
= help: remove the assertion
error: this assertion is always `true`
--> tests/ui/assertions_on_constants.rs:67:5
--> tests/ui/assertions_on_constants.rs:68:5
|
LL | assert!(true);
| ^^^^^^^^^^^^^
@ -97,7 +105,7 @@ LL | assert!(true);
= help: remove the assertion
error: this assertion is always `true`
--> tests/ui/assertions_on_constants.rs:70:5
--> tests/ui/assertions_on_constants.rs:71:5
|
LL | assert!(8 == (7 + 1));
| ^^^^^^^^^^^^^^^^^^^^^
@ -105,7 +113,7 @@ LL | assert!(8 == (7 + 1));
= help: remove the assertion
error: this assertion has a constant value
--> tests/ui/assertions_on_constants.rs:78:5
--> tests/ui/assertions_on_constants.rs:79:5
|
LL | assert!(C);
| ^^^^^^^^^^
@ -113,7 +121,7 @@ LL | assert!(C);
= help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }`
error: this assertion has a constant value
--> tests/ui/assertions_on_constants.rs:89:5
--> tests/ui/assertions_on_constants.rs:90:5
|
LL | assert!(C);
| ^^^^^^^^^^
@ -121,12 +129,12 @@ LL | assert!(C);
= help: consider moving this into a const block: `const { assert!(..) }`
error: this assertion has a constant value
--> tests/ui/assertions_on_constants.rs:95:5
--> tests/ui/assertions_on_constants.rs:96:5
|
LL | assert!(C);
| ^^^^^^^^^^
|
= help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }`
error: aborting due to 16 previous errors
error: aborting due to 17 previous errors

View file

@ -1,9 +1,11 @@
#![allow(unused)]
#![warn(clippy::impossible_comparisons)]
#![warn(clippy::redundant_comparisons)]
#![allow(clippy::no_effect)]
#![allow(clippy::short_circuit_statement)]
#![allow(clippy::manual_range_contains)]
#![allow(
unused,
clippy::identity_op,
clippy::manual_range_contains,
clippy::no_effect,
clippy::short_circuit_statement
)]
#![warn(clippy::impossible_comparisons, clippy::redundant_comparisons)]
const STATUS_BAD_REQUEST: u16 = 400;
const STATUS_SERVER_ERROR: u16 = 500;

View file

@ -1,5 +1,5 @@
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:45:5
--> tests/ui/const_comparisons.rs:47:5
|
LL | status_code <= 400 && status_code > 500;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | status_code <= 400 && status_code > 500;
= help: to override `-D warnings` add `#[allow(clippy::impossible_comparisons)]`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:48:5
--> tests/ui/const_comparisons.rs:50:5
|
LL | status_code > 500 && status_code < 400;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,7 +17,7 @@ LL | status_code > 500 && status_code < 400;
= note: since `500` > `400`, the expression evaluates to false for any value of `status_code`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:51:5
--> tests/ui/const_comparisons.rs:53:5
|
LL | status_code < 500 && status_code > 500;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL | status_code < 500 && status_code > 500;
= note: `status_code` cannot simultaneously be greater than and less than `500`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:55:5
--> tests/ui/const_comparisons.rs:57:5
|
LL | status_code < { 400 } && status_code > { 500 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -33,7 +33,7 @@ LL | status_code < { 400 } && status_code > { 500 };
= note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:58:5
--> tests/ui/const_comparisons.rs:60:5
|
LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -41,7 +41,7 @@ LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR;
= note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:61:5
--> tests/ui/const_comparisons.rs:63:5
|
LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -49,7 +49,7 @@ LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR;
= note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:64:5
--> tests/ui/const_comparisons.rs:66:5
|
LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -57,7 +57,7 @@ LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR;
= note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:68:5
--> tests/ui/const_comparisons.rs:70:5
|
LL | status < { 400 } && status > { 500 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -65,7 +65,7 @@ LL | status < { 400 } && status > { 500 };
= note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:71:5
--> tests/ui/const_comparisons.rs:73:5
|
LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -73,7 +73,7 @@ LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR;
= note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:74:5
--> tests/ui/const_comparisons.rs:76:5
|
LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -81,7 +81,7 @@ LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR;
= note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:77:5
--> tests/ui/const_comparisons.rs:79:5
|
LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -89,7 +89,7 @@ LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR;
= note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:86:5
--> tests/ui/const_comparisons.rs:88:5
|
LL | 500 >= status_code && 600 < status_code;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -97,7 +97,7 @@ LL | 500 >= status_code && 600 < status_code;
= note: since `500` < `600`, the expression evaluates to false for any value of `status_code`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:90:5
--> tests/ui/const_comparisons.rs:92:5
|
LL | 500 >= status_code && status_code > 600;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -105,7 +105,7 @@ LL | 500 >= status_code && status_code > 600;
= note: since `500` < `600`, the expression evaluates to false for any value of `status_code`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:99:5
--> tests/ui/const_comparisons.rs:101:5
|
LL | 500 >= status && 600 < status;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -113,7 +113,7 @@ LL | 500 >= status && 600 < status;
= note: since `500` < `600`, the expression evaluates to false for any value of `status`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:103:5
--> tests/ui/const_comparisons.rs:105:5
|
LL | 500 >= status && status > 600;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -121,13 +121,13 @@ LL | 500 >= status && status > 600;
= note: since `500` < `600`, the expression evaluates to false for any value of `status`
error: right-hand side of `&&` operator has no effect
--> tests/ui/const_comparisons.rs:107:5
--> tests/ui/const_comparisons.rs:109:5
|
LL | status_code < 200 && status_code <= 299;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well
--> tests/ui/const_comparisons.rs:107:23
--> tests/ui/const_comparisons.rs:109:23
|
LL | status_code < 200 && status_code <= 299;
| ^^^^^^^^^^^^^^^^^^^^^
@ -135,67 +135,67 @@ LL | status_code < 200 && status_code <= 299;
= help: to override `-D warnings` add `#[allow(clippy::redundant_comparisons)]`
error: left-hand side of `&&` operator has no effect
--> tests/ui/const_comparisons.rs:110:5
--> tests/ui/const_comparisons.rs:112:5
|
LL | status_code > 200 && status_code >= 299;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well
--> tests/ui/const_comparisons.rs:110:5
--> tests/ui/const_comparisons.rs:112:5
|
LL | status_code > 200 && status_code >= 299;
| ^^^^^^^^^^^^^^^^^^^^^
error: left-hand side of `&&` operator has no effect
--> tests/ui/const_comparisons.rs:114:5
--> tests/ui/const_comparisons.rs:116:5
|
LL | status_code >= 500 && status_code > 500;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well
--> tests/ui/const_comparisons.rs:114:5
--> tests/ui/const_comparisons.rs:116:5
|
LL | status_code >= 500 && status_code > 500;
| ^^^^^^^^^^^^^^^^^^^^^^
error: right-hand side of `&&` operator has no effect
--> tests/ui/const_comparisons.rs:118:5
--> tests/ui/const_comparisons.rs:120:5
|
LL | status_code > 500 && status_code >= 500;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well
--> tests/ui/const_comparisons.rs:118:23
--> tests/ui/const_comparisons.rs:120:23
|
LL | status_code > 500 && status_code >= 500;
| ^^^^^^^^^^^^^^^^^^^^^
error: left-hand side of `&&` operator has no effect
--> tests/ui/const_comparisons.rs:122:5
--> tests/ui/const_comparisons.rs:124:5
|
LL | status_code <= 500 && status_code < 500;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well
--> tests/ui/const_comparisons.rs:122:5
--> tests/ui/const_comparisons.rs:124:5
|
LL | status_code <= 500 && status_code < 500;
| ^^^^^^^^^^^^^^^^^^^^^^
error: right-hand side of `&&` operator has no effect
--> tests/ui/const_comparisons.rs:126:5
--> tests/ui/const_comparisons.rs:128:5
|
LL | status_code < 500 && status_code <= 500;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well
--> tests/ui/const_comparisons.rs:126:23
--> tests/ui/const_comparisons.rs:128:23
|
LL | status_code < 500 && status_code <= 500;
| ^^^^^^^^^^^^^^^^^^^^^
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:131:5
--> tests/ui/const_comparisons.rs:133:5
|
LL | name < "Jennifer" && name > "Shannon";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -203,7 +203,7 @@ LL | name < "Jennifer" && name > "Shannon";
= note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:135:5
--> tests/ui/const_comparisons.rs:137:5
|
LL | numbers < [3, 4] && numbers > [5, 6];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -211,7 +211,7 @@ LL | numbers < [3, 4] && numbers > [5, 6];
= note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:139:5
--> tests/ui/const_comparisons.rs:141:5
|
LL | letter < 'b' && letter > 'c';
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -219,7 +219,7 @@ LL | letter < 'b' && letter > 'c';
= note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter`
error: boolean expression will never evaluate to 'true'
--> tests/ui/const_comparisons.rs:143:5
--> tests/ui/const_comparisons.rs:145:5
|
LL | area < std::f32::consts::E && area > std::f32::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -55,53 +55,6 @@ const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2];
const EMPTY_REF_ARRAY: &[u32; 0] = &[];
const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3];
fn test_from_const() {
let _ = EMPTY_STR.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_STR.is_empty();
//~^ const_is_empty
let _ = EMPTY_BSTR.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_BSTR.is_empty();
//~^ const_is_empty
let _ = EMPTY_ARRAY.is_empty();
//~^ const_is_empty
let _ = EMPTY_ARRAY_REPEAT.is_empty();
//~^ const_is_empty
let _ = EMPTY_U8_SLICE.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_U8_SLICE.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_ARRAY.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_ARRAY_REPEAT.is_empty();
//~^ const_is_empty
let _ = EMPTY_REF_ARRAY.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_REF_ARRAY.is_empty();
//~^ const_is_empty
let _ = EMPTY_SLICE.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_SLICE.is_empty();
//~^ const_is_empty
let _ = NON_EMPTY_SLICE_REPEAT.is_empty();
//~^ const_is_empty
}
fn main() {
let value = "foobar";
let _ = value.is_empty();
@ -120,7 +73,7 @@ fn main() {
fn str_from_arg(var: &str) {
var.is_empty();
// Do not lint, we know nothiny about var
// Do not lint, we know nothing about var
}
fn update_str() {
@ -200,6 +153,5 @@ fn issue_13106() {
const {
EMPTY_STR.is_empty();
//~^ const_is_empty
}
}

View file

@ -37,131 +37,35 @@ error: this expression always evaluates to false
LL | if non_empty2.is_empty() {
| ^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:59:13
|
LL | let _ = EMPTY_STR.is_empty();
| ^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:62:13
|
LL | let _ = NON_EMPTY_STR.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:65:13
|
LL | let _ = EMPTY_BSTR.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:68:13
|
LL | let _ = NON_EMPTY_BSTR.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:71:13
|
LL | let _ = EMPTY_ARRAY.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:74:13
|
LL | let _ = EMPTY_ARRAY_REPEAT.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:77:13
|
LL | let _ = EMPTY_U8_SLICE.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:80:13
|
LL | let _ = NON_EMPTY_U8_SLICE.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:83:13
|
LL | let _ = NON_EMPTY_ARRAY.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:86:13
|
LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:89:13
|
LL | let _ = EMPTY_REF_ARRAY.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:92:13
|
LL | let _ = NON_EMPTY_REF_ARRAY.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:95:13
|
LL | let _ = EMPTY_SLICE.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:98:13
|
LL | let _ = NON_EMPTY_SLICE.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:101:13
|
LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:107:13
--> tests/ui/const_is_empty.rs:60:13
|
LL | let _ = value.is_empty();
| ^^^^^^^^^^^^^^^^
error: this expression always evaluates to false
--> tests/ui/const_is_empty.rs:111:13
--> tests/ui/const_is_empty.rs:64:13
|
LL | let _ = x.is_empty();
| ^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:114:13
--> tests/ui/const_is_empty.rs:67:13
|
LL | let _ = "".is_empty();
| ^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:117:13
--> tests/ui/const_is_empty.rs:70:13
|
LL | let _ = b"".is_empty();
| ^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:171:13
--> tests/ui/const_is_empty.rs:124:13
|
LL | let _ = val.is_empty();
| ^^^^^^^^^^^^^^
error: this expression always evaluates to true
--> tests/ui/const_is_empty.rs:202:9
|
LL | EMPTY_STR.is_empty();
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 27 previous errors
error: aborting due to 11 previous errors

View file

@ -25,8 +25,7 @@ fn main() {
// Handle constants
const NANOS_IN_MICRO: u32 = 1_000;
let _ = dur.subsec_micros();
//~^ duration_subsec
let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
// Other literals aren't linted
let _ = dur.subsec_nanos() / 699;

View file

@ -26,7 +26,6 @@ fn main() {
// Handle constants
const NANOS_IN_MICRO: u32 = 1_000;
let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
//~^ duration_subsec
// Other literals aren't linted
let _ = dur.subsec_nanos() / 699;

View file

@ -25,11 +25,5 @@ error: calling `subsec_micros()` is more concise than this calculation
LL | let _ = (&dur).subsec_nanos() / 1_000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
error: calling `subsec_micros()` is more concise than this calculation
--> tests/ui/duration_subsec.rs:28:13
|
LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
error: aborting due to 5 previous errors
error: aborting due to 4 previous errors

View file

@ -14,10 +14,8 @@ fn check_log_base() {
//~^ suboptimal_flops
let _ = x.ln();
//~^ suboptimal_flops
let _ = x.log2();
//~^ suboptimal_flops
let _ = x.ln();
//~^ suboptimal_flops
let _ = x.log(TWO);
let _ = x.log(E);
let _ = (x as f32).log2();
//~^ suboptimal_flops

View file

@ -15,9 +15,7 @@ fn check_log_base() {
let _ = x.log(std::f32::consts::E);
//~^ suboptimal_flops
let _ = x.log(TWO);
//~^ suboptimal_flops
let _ = x.log(E);
//~^ suboptimal_flops
let _ = (x as f32).log(2f32);
//~^ suboptimal_flops

View file

@ -19,44 +19,32 @@ error: logarithm for bases 2, 10 and e can be computed more accurately
LL | let _ = x.log(std::f32::consts::E);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
error: logarithm for bases 2, 10 and e can be computed more accurately
--> tests/ui/floating_point_log.rs:17:13
|
LL | let _ = x.log(TWO);
| ^^^^^^^^^^ help: consider using: `x.log2()`
error: logarithm for bases 2, 10 and e can be computed more accurately
--> tests/ui/floating_point_log.rs:19:13
|
LL | let _ = x.log(E);
| ^^^^^^^^ help: consider using: `x.ln()`
error: logarithm for bases 2, 10 and e can be computed more accurately
--> tests/ui/floating_point_log.rs:21:13
|
LL | let _ = (x as f32).log(2f32);
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()`
error: logarithm for bases 2, 10 and e can be computed more accurately
--> tests/ui/floating_point_log.rs:25:13
--> tests/ui/floating_point_log.rs:23:13
|
LL | let _ = x.log(2f64);
| ^^^^^^^^^^^ help: consider using: `x.log2()`
error: logarithm for bases 2, 10 and e can be computed more accurately
--> tests/ui/floating_point_log.rs:27:13
--> tests/ui/floating_point_log.rs:25:13
|
LL | let _ = x.log(10f64);
| ^^^^^^^^^^^^ help: consider using: `x.log10()`
error: logarithm for bases 2, 10 and e can be computed more accurately
--> tests/ui/floating_point_log.rs:29:13
--> tests/ui/floating_point_log.rs:27:13
|
LL | let _ = x.log(std::f64::consts::E);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:35:13
--> tests/ui/floating_point_log.rs:33:13
|
LL | let _ = (1f32 + 2.).ln();
| ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
@ -65,118 +53,118 @@ LL | let _ = (1f32 + 2.).ln();
= help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:37:13
--> tests/ui/floating_point_log.rs:35:13
|
LL | let _ = (1f32 + 2.0).ln();
| ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:39:13
--> tests/ui/floating_point_log.rs:37:13
|
LL | let _ = (1.0 + x).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:41:13
--> tests/ui/floating_point_log.rs:39:13
|
LL | let _ = (1.0 + x / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:43:13
--> tests/ui/floating_point_log.rs:41:13
|
LL | let _ = (1.0 + x.powi(3)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:45:13
--> tests/ui/floating_point_log.rs:43:13
|
LL | let _ = (1.0 + x.powi(3) / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:47:13
--> tests/ui/floating_point_log.rs:45:13
|
LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:49:13
--> tests/ui/floating_point_log.rs:47:13
|
LL | let _ = (x + 1.0).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:51:13
--> tests/ui/floating_point_log.rs:49:13
|
LL | let _ = (x.powi(3) + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:53:13
--> tests/ui/floating_point_log.rs:51:13
|
LL | let _ = (x + 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:55:13
--> tests/ui/floating_point_log.rs:53:13
|
LL | let _ = (x / 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:64:13
--> tests/ui/floating_point_log.rs:62:13
|
LL | let _ = (1f64 + 2.).ln();
| ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:66:13
--> tests/ui/floating_point_log.rs:64:13
|
LL | let _ = (1f64 + 2.0).ln();
| ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:68:13
--> tests/ui/floating_point_log.rs:66:13
|
LL | let _ = (1.0 + x).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:70:13
--> tests/ui/floating_point_log.rs:68:13
|
LL | let _ = (1.0 + x / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:72:13
--> tests/ui/floating_point_log.rs:70:13
|
LL | let _ = (1.0 + x.powi(3)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:74:13
--> tests/ui/floating_point_log.rs:72:13
|
LL | let _ = (x + 1.0).ln();
| ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:76:13
--> tests/ui/floating_point_log.rs:74:13
|
LL | let _ = (x.powi(3) + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:78:13
--> tests/ui/floating_point_log.rs:76:13
|
LL | let _ = (x + 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately
--> tests/ui/floating_point_log.rs:80:13
--> tests/ui/floating_point_log.rs:78:13
|
LL | let _ = (x / 2.0 + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
error: aborting due to 29 previous errors
error: aborting due to 27 previous errors

View file

@ -72,33 +72,44 @@ const CONST_U16_1: u16 = 1;
fn const_test1() {
use std::net::Ipv4Addr;
let _ = Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = Ipv4Addr::UNSPECIFIED;
//~^ ip_constant
let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
use std::net::Ipv6Addr;
let _ = Ipv6Addr::LOCALHOST;
let _ = Ipv6Addr::new(
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_1,
);
let _ = Ipv6Addr::UNSPECIFIED;
let _ = Ipv6Addr::new(
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
);
}
fn const_test2() {
use std::net::Ipv4Addr;
let _ = Ipv4Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv4Addr::BROADCAST;
//~^ ip_constant
let _ = Ipv4Addr::UNSPECIFIED;
//~^ ip_constant
let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
use std::net::Ipv6Addr;
let _ = Ipv6Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv6Addr::LOCALHOST;
//~^ ip_constant
let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
}
macro_rules! ipv4_new {

View file

@ -73,15 +73,11 @@ const CONST_U16_1: u16 = 1;
fn const_test1() {
use std::net::Ipv4Addr;
let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
//~^ ip_constant
let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
//~^ ip_constant
let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
//~^ ip_constant
use std::net::Ipv6Addr;
let _ = Ipv6Addr::new(
//~^ ip_constant
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
@ -93,7 +89,6 @@ fn const_test1() {
);
let _ = Ipv6Addr::new(
//~^ ip_constant
CONST_U16_0,
CONST_U16_0,
CONST_U16_0,
@ -110,15 +105,11 @@ fn const_test2() {
let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
//~^ ip_constant
let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
//~^ ip_constant
let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
//~^ ip_constant
use std::net::Ipv6Addr;
let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
//~^ ip_constant
let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
//~^ ip_constant
}
macro_rules! ipv4_new {

View file

@ -241,101 +241,7 @@ LL + let _ = std::net::Ipv6Addr::UNSPECIFIED;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:75:13
|
LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1);
LL + let _ = Ipv4Addr::LOCALHOST;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:77:13
|
LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255);
LL + let _ = Ipv4Addr::BROADCAST;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:79:13
|
LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0);
LL + let _ = Ipv4Addr::UNSPECIFIED;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:83:13
|
LL | let _ = Ipv6Addr::new(
| _____________^
LL | |
LL | | CONST_U16_0,
LL | | CONST_U16_0,
... |
LL | | CONST_U16_1,
LL | | );
| |_____^
|
help: use
|
LL - let _ = Ipv6Addr::new(
LL -
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_1,
LL - );
LL + let _ = Ipv6Addr::LOCALHOST;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:95:13
|
LL | let _ = Ipv6Addr::new(
| _____________^
LL | |
LL | | CONST_U16_0,
LL | | CONST_U16_0,
... |
LL | | CONST_U16_0,
LL | | );
| |_____^
|
help: use
|
LL - let _ = Ipv6Addr::new(
LL -
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - CONST_U16_0,
LL - );
LL + let _ = Ipv6Addr::UNSPECIFIED;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:110:13
--> tests/ui/ip_constant.rs:105:13
|
LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -346,53 +252,5 @@ LL - let _ = Ipv4Addr::new(126 + 1, 0, 0, 1);
LL + let _ = Ipv4Addr::LOCALHOST;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:112:13
|
LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255);
LL + let _ = Ipv4Addr::BROADCAST;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:114:13
|
LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 });
LL + let _ = Ipv4Addr::UNSPECIFIED;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:118:13
|
LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1);
LL + let _ = Ipv6Addr::LOCALHOST;
|
error: hand-coded well-known IP address
--> tests/ui/ip_constant.rs:120:13
|
LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use
|
LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1);
LL + let _ = Ipv6Addr::LOCALHOST;
|
error: aborting due to 30 previous errors
error: aborting due to 21 previous errors

View file

@ -8,9 +8,6 @@
#[macro_use]
extern crate proc_macros;
const INFINITE: f32 = f32::INFINITY;
const NEG_INFINITE: f32 = f32::NEG_INFINITY;
fn fn_test() -> f64 {
f64::NEG_INFINITY
}
@ -25,10 +22,6 @@ fn main() {
//~^ manual_is_infinite
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
//~^ manual_is_finite
if x == INFINITE || x == NEG_INFINITE {}
//~^ manual_is_infinite
if x != INFINITE && x != NEG_INFINITE {}
//~^ manual_is_finite
let x = 1.0f64;
if x == f64::INFINITY || x == f64::NEG_INFINITY {}
//~^ manual_is_infinite
@ -64,4 +57,12 @@ fn main() {
if x == f32::INFINITY || x == f32::NEG_INFINITY {}
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
}
{
let x = 1.0f32;
const X: f32 = f32::INFINITY;
const Y: f32 = f32::NEG_INFINITY;
if x == X || x == Y {}
if x != X && x != Y {}
}
}

View file

@ -1,5 +1,5 @@
error: manually checking if a float is infinite
--> tests/ui/manual_float_methods.rs:24:8
--> tests/ui/manual_float_methods.rs:21:8
|
LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
@ -8,7 +8,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {}
= help: to override `-D warnings` add `#[allow(clippy::manual_is_infinite)]`
error: manually checking if a float is finite
--> tests/ui/manual_float_methods.rs:26:8
--> tests/ui/manual_float_methods.rs:23:8
|
LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -32,41 +32,13 @@ LL + if !x.is_infinite() {}
|
error: manually checking if a float is infinite
--> tests/ui/manual_float_methods.rs:28:8
|
LL | if x == INFINITE || x == NEG_INFINITE {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
error: manually checking if a float is finite
--> tests/ui/manual_float_methods.rs:30:8
|
LL | if x != INFINITE && x != NEG_INFINITE {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use the dedicated method instead
|
LL - if x != INFINITE && x != NEG_INFINITE {}
LL + if x.is_finite() {}
|
help: this will alter how it handles NaN; if that is a problem, use instead
|
LL - if x != INFINITE && x != NEG_INFINITE {}
LL + if x.is_finite() || x.is_nan() {}
|
help: or, for conciseness
|
LL - if x != INFINITE && x != NEG_INFINITE {}
LL + if !x.is_infinite() {}
|
error: manually checking if a float is infinite
--> tests/ui/manual_float_methods.rs:33:8
--> tests/ui/manual_float_methods.rs:26:8
|
LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
error: manually checking if a float is finite
--> tests/ui/manual_float_methods.rs:35:8
--> tests/ui/manual_float_methods.rs:28:8
|
LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -88,10 +60,10 @@ LL + if !x.is_infinite() {}
|
error: manually checking if a float is infinite
--> tests/ui/manual_float_methods.rs:50:12
--> tests/ui/manual_float_methods.rs:43:12
|
LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()`
error: aborting due to 7 previous errors
error: aborting due to 5 previous errors

View file

@ -97,9 +97,6 @@ LL | if s.starts_with(PREFIX) {
help: try using the `strip_prefix` method
|
LL ~ if let Some(<stripped>) = s.strip_prefix(PREFIX) {
LL ~ str::to_string(<stripped>);
LL |
LL |
LL ~ str::to_string(<stripped>);
|

View file

@ -10,8 +10,7 @@ fn main() {
//~^ repeat_once
let b = slice.to_vec();
//~^ repeat_once
let c = "hello".to_string();
//~^ repeat_once
let c = "hello".repeat(N);
let d = "hi".to_string();
//~^ repeat_once
let e = s.to_string();

View file

@ -11,7 +11,6 @@ fn main() {
let b = slice.repeat(1);
//~^ repeat_once
let c = "hello".repeat(N);
//~^ repeat_once
let d = "hi".repeat(1);
//~^ repeat_once
let e = s.repeat(1);

View file

@ -14,28 +14,22 @@ LL | let b = slice.repeat(1);
| ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()`
error: calling `repeat(1)` on str
--> tests/ui/repeat_once.rs:13:13
|
LL | let c = "hello".repeat(N);
| ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()`
error: calling `repeat(1)` on str
--> tests/ui/repeat_once.rs:15:13
--> tests/ui/repeat_once.rs:14:13
|
LL | let d = "hi".repeat(1);
| ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()`
error: calling `repeat(1)` on str
--> tests/ui/repeat_once.rs:17:13
--> tests/ui/repeat_once.rs:16:13
|
LL | let e = s.repeat(1);
| ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()`
error: calling `repeat(1)` on a string literal
--> tests/ui/repeat_once.rs:19:13
--> tests/ui/repeat_once.rs:18:13
|
LL | let f = string.repeat(1);
| ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()`
error: aborting due to 6 previous errors
error: aborting due to 5 previous errors