Slightly refactor constant evaluation and add detection for empty macro expansion and cfg
ed statements.
This commit is contained in:
parent
0b3c2ed811
commit
5351170744
@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
|
||||
_ => return,
|
||||
};
|
||||
let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return };
|
||||
let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return };
|
||||
let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { return };
|
||||
if val {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
@ -21,8 +21,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
||||
|
||||
fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
match constant(cx, cx.typeck_results(), e) {
|
||||
Some((Constant::F64(n), _)) => n.is_nan(),
|
||||
Some((Constant::F32(n), _)) => n.is_nan(),
|
||||
Some(Constant::F64(n)) => n.is_nan(),
|
||||
Some(Constant::F32(n)) => n.is_nan(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType;
|
||||
use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
|
||||
|
||||
fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
|
||||
if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
|
||||
if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) {
|
||||
Some(c)
|
||||
} else {
|
||||
None
|
||||
|
@ -29,7 +29,7 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
|
||||
// Don't lint for positive constants.
|
||||
let const_val = constant(cx, cx.typeck_results(), cast_op);
|
||||
if_chain! {
|
||||
if let Some((Constant::Int(n), _)) = const_val;
|
||||
if let Some(Constant::Int(n)) = const_val;
|
||||
if let ty::Int(ity) = *cast_from.kind();
|
||||
if sext(cx.tcx, n, ity) >= 0;
|
||||
then {
|
||||
|
@ -114,7 +114,7 @@ declare_lint_pass!(FloatingPointArithmetic => [
|
||||
// Returns the specialized log method for a given base if base is constant
|
||||
// and is one of 2, 10 and e
|
||||
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), base) {
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), base) {
|
||||
if F32(2.0) == value || F64(2.0) == value {
|
||||
return Some("log2");
|
||||
} else if F32(10.0) == value || F64(10.0) == value {
|
||||
@ -193,8 +193,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
||||
constant(cx, cx.typeck_results(), lhs),
|
||||
constant(cx, cx.typeck_results(), rhs),
|
||||
) {
|
||||
(Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
|
||||
(_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
|
||||
(Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs,
|
||||
(_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
@ -237,7 +237,7 @@ fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
|
||||
|
||||
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
// Check receiver
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) {
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), receiver) {
|
||||
if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
Some("exp")
|
||||
} else if F32(2.0) == value || F64(2.0) == value {
|
||||
@ -258,7 +258,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
||||
}
|
||||
|
||||
// Check argument
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) {
|
||||
let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
|
||||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
@ -298,7 +298,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
||||
}
|
||||
|
||||
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) {
|
||||
if value == Int(2) {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent) {
|
||||
@ -384,8 +384,8 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
|
||||
_
|
||||
) = &add_rhs.kind;
|
||||
if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
|
||||
if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1);
|
||||
if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
|
||||
if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1);
|
||||
if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1);
|
||||
if Int(2) == lvalue && Int(2) == rvalue;
|
||||
then {
|
||||
return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, "..")));
|
||||
@ -416,7 +416,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
|
||||
if cx.typeck_results().expr_ty(lhs).is_floating_point();
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), rhs);
|
||||
if F32(1.0) == value || F64(1.0) == value;
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind;
|
||||
if cx.typeck_results().expr_ty(self_arg).is_floating_point();
|
||||
@ -669,8 +669,8 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
mul_lhs,
|
||||
mul_rhs,
|
||||
) = &div_lhs.kind;
|
||||
if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
|
||||
if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
|
||||
if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs);
|
||||
if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs);
|
||||
then {
|
||||
// TODO: also check for constant values near PI/180 or 180/PI
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
|
||||
|
@ -89,11 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for FnNullCheck {
|
||||
|
||||
// Catching:
|
||||
// (fn_ptr as *<const/mut> <ty>) == <const that evaluates to null_ptr>
|
||||
_ if matches!(
|
||||
constant(cx, cx.typeck_results(), to_check),
|
||||
Some((Constant::RawPtr(0), _))
|
||||
) =>
|
||||
{
|
||||
_ if matches!(constant(cx, cx.typeck_results(), to_check), Some(Constant::RawPtr(0))) => {
|
||||
lint_expr(cx, expr);
|
||||
},
|
||||
|
||||
|
@ -101,10 +101,10 @@ 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 tr = cx.typeck_results();
|
||||
if let Some((Constant::Int(c), _)) = constant(cx, tr, r) {
|
||||
if let Some(Constant::Int(c)) = constant(cx, tr, r) {
|
||||
return Some((c, op.node, l));
|
||||
};
|
||||
if let Some((Constant::Int(c), _)) = constant(cx, tr, l) {
|
||||
if let Some(Constant::Int(c)) = constant(cx, tr, l) {
|
||||
return Some((c, invert_op(op.node)?, r));
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
|
||||
let parent_id = map.parent_id(expr.hir_id);
|
||||
if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
|
||||
if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
|
||||
if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr);
|
||||
if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr);
|
||||
if let Ok(index_value) = index_value.try_into();
|
||||
if index_value < max_suggested_slice;
|
||||
|
||||
|
@ -191,18 +191,14 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
||||
/// Returns a tuple of options with the start and end (exclusive) values of
|
||||
/// the range. If the start or end is not constant, None is returned.
|
||||
fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) {
|
||||
let s = range
|
||||
.start
|
||||
.map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));
|
||||
let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr));
|
||||
let start = match s {
|
||||
Some(Some(Constant::Int(x))) => Some(x),
|
||||
Some(_) => None,
|
||||
None => Some(0),
|
||||
};
|
||||
|
||||
let e = range
|
||||
.end
|
||||
.map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));
|
||||
let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr));
|
||||
let end = match e {
|
||||
Some(Some(Constant::Int(x))) => {
|
||||
if range.limits == RangeLimits::Closed {
|
||||
|
@ -144,7 +144,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
|
||||
|
||||
// Returns the length of the `expr` if it's a constant string or char.
|
||||
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
|
||||
let (value, _) = constant(cx, cx.typeck_results(), expr)?;
|
||||
let value = constant(cx, cx.typeck_results(), expr)?;
|
||||
match value {
|
||||
Constant::Str(value) => Some(value.len() as u128),
|
||||
Constant::Char(value) => Some(value.len_utf8() as u128),
|
||||
|
@ -34,7 +34,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
|
||||
if let Arm { pat, guard: None, .. } = *arm {
|
||||
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
|
||||
let lhs_const = match lhs {
|
||||
Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
|
||||
Some(lhs) => constant(cx, cx.typeck_results(), lhs)?,
|
||||
None => {
|
||||
let min_val_const = ty.numeric_min_val(cx.tcx)?;
|
||||
let min_constant = mir::ConstantKind::from_value(
|
||||
@ -45,7 +45,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
|
||||
},
|
||||
};
|
||||
let rhs_const = match rhs {
|
||||
Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
|
||||
Some(rhs) => constant(cx, cx.typeck_results(), rhs)?,
|
||||
None => {
|
||||
let max_val_const = ty.numeric_max_val(cx.tcx)?;
|
||||
let max_constant = mir::ConstantKind::from_value(
|
||||
|
@ -13,7 +13,7 @@ use super::ITER_NTH_ZERO;
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, expr, sym::Iterator);
|
||||
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg);
|
||||
if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
|
@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg) {
|
||||
if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) {
|
||||
span_lint(
|
||||
cx,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
|
@ -3770,13 +3770,13 @@ impl Methods {
|
||||
unnecessary_sort_by::check(cx, expr, recv, arg, true);
|
||||
},
|
||||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
|
||||
}
|
||||
},
|
||||
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
}
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
|
||||
recv: &'tcx Expr<'_>,
|
||||
repeat_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
|
||||
if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
|
@ -316,7 +316,7 @@ fn parse_iter_usage<'tcx>(
|
||||
};
|
||||
},
|
||||
("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
|
||||
if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
|
||||
if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) {
|
||||
let span = if name.ident.as_str() == "nth" {
|
||||
e.span
|
||||
} else {
|
||||
|
@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>(
|
||||
fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
let cv = constant(cx, cx.typeck_results(), expr)?.0;
|
||||
let cv = constant(cx, cx.typeck_results(), expr)?;
|
||||
|
||||
let which = match (ty.kind(), cv) {
|
||||
(&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
|
||||
|
@ -113,7 +113,7 @@ impl ArithmeticSideEffects {
|
||||
if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
|
||||
return Some(n)
|
||||
}
|
||||
if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) {
|
||||
if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) {
|
||||
return Some(n);
|
||||
}
|
||||
None
|
||||
|
@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op:
|
||||
}
|
||||
|
||||
fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> {
|
||||
match constant(cx, cx.typeck_results(), lit)?.0 {
|
||||
match constant(cx, cx.typeck_results(), lit)? {
|
||||
Constant::Int(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp
|
||||
}
|
||||
|
||||
fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), e) {
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), e) {
|
||||
match value {
|
||||
Constant::F32(num) => num.is_nan(),
|
||||
Constant::F64(num) => num.is_nan(),
|
||||
|
@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(
|
||||
if op == BinOpKind::Div
|
||||
&& let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
|
||||
&& let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right)
|
||||
&& let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right)
|
||||
{
|
||||
let suggested_fn = match (method_path.ident.as_str(), divisor) {
|
||||
("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::consts::{constant_with_source, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::get_item_name;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
@ -18,9 +18,16 @@ pub(crate) fn check<'tcx>(
|
||||
right: &'tcx Expr<'_>,
|
||||
) {
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) {
|
||||
Some((c, s)) if !is_allowed(&c) => s.is_local(),
|
||||
Some(_) => return,
|
||||
None => true,
|
||||
};
|
||||
let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) {
|
||||
Some((c, s)) if !is_allowed(&c) => s.is_local(),
|
||||
Some(_) => return,
|
||||
None => true,
|
||||
};
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
@ -34,10 +41,7 @@ pub(crate) fn check<'tcx>(
|
||||
}
|
||||
}
|
||||
let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
|
||||
let (lint, msg) = get_lint_and_message(
|
||||
is_named_constant(cx, left) || is_named_constant(cx, right),
|
||||
is_comparing_arrays,
|
||||
);
|
||||
let (lint, msg) = get_lint_and_message(left_is_local && right_is_local, is_comparing_arrays);
|
||||
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
|
||||
let lhs = Sugg::hir(cx, left, "..");
|
||||
let rhs = Sugg::hir(cx, right, "..");
|
||||
@ -59,20 +63,8 @@ pub(crate) fn check<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
fn get_lint_and_message(
|
||||
is_comparing_constants: bool,
|
||||
is_comparing_arrays: bool,
|
||||
) -> (&'static rustc_lint::Lint, &'static str) {
|
||||
if is_comparing_constants {
|
||||
(
|
||||
FLOAT_CMP_CONST,
|
||||
if is_comparing_arrays {
|
||||
"strict comparison of `f32` or `f64` constant arrays"
|
||||
} else {
|
||||
"strict comparison of `f32` or `f64` constant"
|
||||
},
|
||||
)
|
||||
} else {
|
||||
fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static rustc_lint::Lint, &'static str) {
|
||||
if is_local {
|
||||
(
|
||||
FLOAT_CMP,
|
||||
if is_comparing_arrays {
|
||||
@ -81,22 +73,23 @@ fn get_lint_and_message(
|
||||
"strict comparison of `f32` or `f64`"
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
|
||||
res
|
||||
} else {
|
||||
false
|
||||
(
|
||||
FLOAT_CMP_CONST,
|
||||
if is_comparing_arrays {
|
||||
"strict comparison of `f32` or `f64` constant arrays"
|
||||
} else {
|
||||
"strict comparison of `f32` or `f64` constant"
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
match constant(cx, cx.typeck_results(), expr) {
|
||||
Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
|
||||
Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
|
||||
Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
|
||||
fn is_allowed(val: &Constant) -> bool {
|
||||
match val {
|
||||
&Constant::F32(f) => f == 0.0 || f.is_infinite(),
|
||||
&Constant::F64(f) => f == 0.0 || f.is_infinite(),
|
||||
Constant::Vec(vec) => vec.iter().all(|f| match f {
|
||||
Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
|
||||
Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
|
||||
_ => false,
|
||||
|
@ -40,7 +40,7 @@ struct OperandInfo {
|
||||
|
||||
fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
|
||||
match constant(cx, cx.typeck_results(), operand) {
|
||||
Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() {
|
||||
Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() {
|
||||
ty::Int(ity) => {
|
||||
let value = sext(cx.tcx, v, ity);
|
||||
return Some(OperandInfo {
|
||||
@ -58,10 +58,10 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) ->
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
Some((Constant::F32(f), _)) => {
|
||||
Some(Constant::F32(f)) => {
|
||||
return Some(floating_point_operand_info(&f));
|
||||
},
|
||||
Some((Constant::F64(f), _)) => {
|
||||
Some(Constant::F64(f)) => {
|
||||
return Some(floating_point_operand_info(&f));
|
||||
},
|
||||
_ => {},
|
||||
|
@ -319,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
|
||||
_ => return None,
|
||||
};
|
||||
if let Some(id) = path_to_local(l) {
|
||||
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
|
||||
if let Some(c) = constant(cx, cx.typeck_results(), r) {
|
||||
return Some(RangeBounds {
|
||||
val: c,
|
||||
expr: r,
|
||||
@ -331,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
|
||||
});
|
||||
}
|
||||
} else if let Some(id) = path_to_local(r) {
|
||||
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
|
||||
if let Some(c) = constant(cx, cx.typeck_results(), l) {
|
||||
return Some(RangeBounds {
|
||||
val: c,
|
||||
expr: l,
|
||||
@ -451,8 +451,8 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
|
||||
let ty = cx.typeck_results().expr_ty(start);
|
||||
if let ty::Int(_) | ty::Uint(_) = ty.kind();
|
||||
if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
|
||||
if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
|
||||
if let Some(start_idx) = constant(cx, cx.typeck_results(), start);
|
||||
if let Some(end_idx) = constant(cx, cx.typeck_results(), end);
|
||||
if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
|
||||
if is_empty_range(limits, ordering);
|
||||
then {
|
||||
|
@ -122,7 +122,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape
|
||||
}
|
||||
|
||||
fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
|
||||
constant(cx, cx.typeck_results(), e).and_then(|(c, _)| match c {
|
||||
constant(cx, cx.typeck_results(), e).and_then(|c| match c {
|
||||
Constant::Str(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
|
@ -31,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
||||
match arg.kind {
|
||||
// Catching:
|
||||
// transmute over constants that resolve to `null`.
|
||||
ExprKind::Path(ref _qpath)
|
||||
if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) =>
|
||||
{
|
||||
ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => {
|
||||
lint_expr(cx, expr);
|
||||
true
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
@ -16,9 +16,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
||||
}
|
||||
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind &&
|
||||
let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg)
|
||||
let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg)
|
||||
{
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
|
@ -84,7 +84,7 @@ impl UselessVec {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = match *vec_args {
|
||||
higher::VecArgs::Repeat(elem, len) => {
|
||||
if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
|
||||
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
|
||||
return;
|
||||
|
@ -1,18 +1,21 @@
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
||||
use crate::source::{span_source_range, walk_span_to_context};
|
||||
use crate::{clip, is_direct_expn_of, sext, unsext};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
|
||||
use rustc_lexer::tokenize;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::ty::SubstsRef;
|
||||
use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{List, SubstsRef};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::SyntaxContext;
|
||||
use std::cmp::Ordering::{self, Equal};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter;
|
||||
@ -227,27 +230,46 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
|
||||
}
|
||||
}
|
||||
|
||||
/// The source of a constant value.
|
||||
pub enum ConstantSource {
|
||||
/// The value is determined solely from the expression.
|
||||
Local,
|
||||
/// The value is dependent on a defined constant.
|
||||
Constant,
|
||||
}
|
||||
impl ConstantSource {
|
||||
pub fn is_local(&self) -> bool {
|
||||
matches!(self, Self::Local)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression as a constant.
|
||||
pub fn constant<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<(Constant, bool)> {
|
||||
let mut cx = ConstEvalLateContext {
|
||||
lcx,
|
||||
typeck_results,
|
||||
param_env: lcx.param_env,
|
||||
needed_resolution: false,
|
||||
substs: ty::List::empty(),
|
||||
};
|
||||
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
|
||||
) -> Option<Constant> {
|
||||
ConstEvalLateContext::new(lcx, typeck_results).expr(e)
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression as a constant.
|
||||
pub fn constant_with_source<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<(Constant, ConstantSource)> {
|
||||
let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results);
|
||||
let res = ctxt.expr(e);
|
||||
res.map(|x| (x, ctxt.source))
|
||||
}
|
||||
|
||||
/// Attempts to evaluate an expression only if it's value is not dependent on other items.
|
||||
pub fn constant_simple<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<Constant> {
|
||||
constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
|
||||
constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c))
|
||||
}
|
||||
|
||||
pub fn constant_full_int<'tcx>(
|
||||
@ -296,29 +318,25 @@ impl Ord for FullInt {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
|
||||
pub fn constant_context<'a, 'tcx>(
|
||||
lcx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
) -> ConstEvalLateContext<'a, 'tcx> {
|
||||
ConstEvalLateContext {
|
||||
lcx,
|
||||
typeck_results,
|
||||
param_env: lcx.param_env,
|
||||
needed_resolution: false,
|
||||
substs: ty::List::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConstEvalLateContext<'a, 'tcx> {
|
||||
lcx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
needed_resolution: bool,
|
||||
source: ConstantSource,
|
||||
substs: SubstsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self {
|
||||
Self {
|
||||
lcx,
|
||||
typeck_results,
|
||||
param_env: lcx.param_env,
|
||||
source: ConstantSource::Local,
|
||||
substs: List::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple constant folding: Insert an expression, get a constant or none.
|
||||
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
|
||||
match e.kind {
|
||||
@ -453,11 +471,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None)
|
||||
.ok()
|
||||
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
|
||||
let result = miri_to_const(self.lcx.tcx, result);
|
||||
if result.is_some() {
|
||||
self.needed_resolution = true;
|
||||
}
|
||||
result
|
||||
let result = miri_to_const(self.lcx.tcx, result)?;
|
||||
self.source = ConstantSource::Constant;
|
||||
Some(result)
|
||||
},
|
||||
// FIXME: cover all usable cases.
|
||||
_ => None,
|
||||
@ -491,8 +507,33 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
|
||||
/// A block can only yield a constant if it only has one constant expression.
|
||||
fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
|
||||
if block.stmts.is_empty() {
|
||||
block.expr.as_ref().and_then(|b| self.expr(b))
|
||||
if block.stmts.is_empty()
|
||||
&& let Some(expr) = block.expr
|
||||
{
|
||||
// Try to detect any `cfg`ed statements or empty macro expansions.
|
||||
let span = block.span.data();
|
||||
if span.ctxt == SyntaxContext::root() {
|
||||
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
|
||||
&& let expr_lo = expr_span.lo()
|
||||
&& expr_lo >= span.lo
|
||||
&& let Some(src) = span_source_range(self.lcx, span.lo..expr_lo)
|
||||
&& let Some(src) = src.as_str()
|
||||
{
|
||||
use rustc_lexer::TokenKind::{Whitespace, LineComment, BlockComment, Semi, OpenBrace};
|
||||
if !tokenize(src)
|
||||
.map(|t| t.kind)
|
||||
.filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
|
||||
.eq([OpenBrace])
|
||||
{
|
||||
self.source = ConstantSource::Constant;
|
||||
}
|
||||
} else {
|
||||
// Unable to access the source. Assume a non-local dependency.
|
||||
self.source = ConstantSource::Constant;
|
||||
}
|
||||
}
|
||||
|
||||
self.expr(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -151,6 +151,8 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
if !self.eq_stmt(left, right) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to detect any `cfg`ed statements or empty macro expansions.
|
||||
let Some(lstmt_span) = walk_span_to_context(left.span, lspan.ctxt) else {
|
||||
return false;
|
||||
};
|
||||
|
@ -1547,7 +1547,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
|
||||
return true;
|
||||
}
|
||||
let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
|
||||
if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
|
||||
if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
|
||||
return value == v;
|
||||
}
|
||||
false
|
||||
|
@ -72,6 +72,18 @@ fn main() {
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => {
|
||||
m!(foo);
|
||||
0
|
||||
},
|
||||
1 => {
|
||||
m!(bar);
|
||||
0
|
||||
},
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => {
|
||||
let mut x = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user