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:
commit
d289009eea
58 changed files with 646 additions and 800 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 don’t 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 don’t 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ generate! {
|
|||
collapsible_if,
|
||||
collect,
|
||||
const_ptr,
|
||||
consts,
|
||||
contains,
|
||||
copied,
|
||||
copy_from,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>);
|
||||
|
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue