Rename eval_simple to eval_local and take the SyntaxContext as an argument.
This commit is contained in:
parent
f08486481f
commit
3e24d50407
21 changed files with 134 additions and 82 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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv, ctxt)
|
||||
&& let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(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! {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -93,6 +94,7 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM
|
|||
|
||||
fn fetch_const<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
receiver: Option<&'a Expr<'a>>,
|
||||
args: &'a [Expr<'a>],
|
||||
m: MinMax,
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -387,6 +387,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 +399,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,13 +410,15 @@ 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<'tcx>, ConstantSource)> {
|
||||
self.source.set(ConstantSource::Local);
|
||||
self.ctxt.set(ctxt);
|
||||
self.expr(e).map(|c| (c, self.source.get()))
|
||||
}
|
||||
|
||||
|
|
@ -424,16 +428,16 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
|
|||
}
|
||||
|
||||
/// 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) {
|
||||
pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<Constant<'tcx>> {
|
||||
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) {
|
||||
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,
|
||||
}
|
||||
|
|
@ -455,6 +459,14 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ctxt(&self, ctxt: SyntaxContext) {
|
||||
self.source.set(if self.ctxt.get() != ctxt {
|
||||
ConstantSource::Constant
|
||||
} else {
|
||||
self.source.get()
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -477,17 +489,18 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
|
|||
|
||||
/// Simple constant folding: Insert an expression, get a constant or none.
|
||||
fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
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 +517,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
|
||||
|
|
@ -525,6 +541,7 @@ 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) => {
|
||||
self.check_ctxt(field.span.ctxt());
|
||||
let result = self.expr(local_expr);
|
||||
if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
|
||||
&& let ty::Adt(adt_def, _) = constant.ty().kind()
|
||||
|
|
@ -958,8 +975,8 @@ fn field_of_struct<'tcx>(
|
|||
}
|
||||
|
||||
/// 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) {
|
||||
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
|
||||
|
|
@ -968,6 +985,6 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
|
|||
|
||||
/// Check if `expr` evaluates to an integer constant of 0.
|
||||
#[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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue