Skip to content

Commit b30cda3

Browse files
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
2 parents 0a7b328 + c535b2e commit b30cda3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+642
-793
lines changed

clippy_lints/src/assertions_on_constants.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
5050
_ => return,
5151
}
5252
&& let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn)
53-
&& let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition)
53+
&& let Some((Constant::Bool(assert_val), const_src)) =
54+
ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt())
5455
&& let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id)
5556
&& (const_src.is_local() || !in_const_context)
5657
&& !(is_debug && as_bool_lit(condition) == Some(false))

clippy_lints/src/enum_clike.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
4343
if let Some(anon_const) = &var.disr_expr {
4444
let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body);
4545
let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity();
46-
let constant = cx
47-
.tcx
48-
.const_eval_poly(def_id.to_def_id())
49-
.ok()
50-
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
51-
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
46+
let constant = cx.tcx.const_eval_poly(def_id.to_def_id()).ok();
47+
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c, ty)) {
5248
if let ty::Adt(adt, _) = ty.kind()
5349
&& adt.is_enum()
5450
{

clippy_lints/src/floating_point_arithmetic.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
1111
use rustc_lint::{LateContext, LateLintPass};
1212
use rustc_middle::ty;
1313
use rustc_session::declare_lint_pass;
14+
use rustc_span::SyntaxContext;
1415
use rustc_span::source_map::Spanned;
1516
use std::f32::consts as f32_consts;
1617
use std::f64::consts as f64_consts;
@@ -110,8 +111,8 @@ declare_lint_pass!(FloatingPointArithmetic => [
110111

111112
// Returns the specialized log method for a given base if base is constant
112113
// and is one of 2, 10 and e
113-
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
114-
if let Some(value) = ConstEvalCtxt::new(cx).eval(base) {
114+
fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> {
115+
if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) {
115116
if F32(2.0) == value || F64(2.0) == value {
116117
return Some("log2");
117118
} 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
157158
}
158159

159160
fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
160-
if let Some(method) = get_specialized_log_method(cx, &args[0]) {
161+
if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) {
161162
span_lint_and_sugg(
162163
cx,
163164
SUBOPTIMAL_FLOPS,
@@ -205,7 +206,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
205206
// ranges [-16777215, 16777216) for type f32 as whole number floats outside
206207
// this range are lossy and ambiguous.
207208
#[expect(clippy::cast_possible_truncation)]
208-
fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
209+
fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
209210
match value {
210211
F32(num) if num.fract() == 0.0 => {
211212
if (-16_777_215.0..16_777_216.0).contains(num) {
@@ -517,8 +518,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
517518
fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
518519
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
519520
match op {
520-
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
521-
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
521+
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
522+
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
522523
_ => false,
523524
}
524525
} else {
@@ -530,8 +531,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
530531
fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
531532
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
532533
match op {
533-
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
534-
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
534+
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test),
535+
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test),
535536
_ => false,
536537
}
537538
} else {
@@ -540,8 +541,8 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
540541
}
541542

542543
/// Returns true iff expr is some zero literal
543-
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
544-
match ConstEvalCtxt::new(cx).eval_simple(expr) {
544+
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
545+
match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) {
545546
Some(Int(i)) => i == 0,
546547
Some(F32(f)) => f == 0.0,
547548
Some(F64(f)) => f == 0.0,

clippy_lints/src/if_not_else.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,14 @@ impl LateLintPass<'_> for IfNotElse {
6060
),
6161
// Don't lint on `… != 0`, as these are likely to be bit tests.
6262
// For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order.
63-
ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => (
64-
"unnecessary `!=` operation",
65-
"change to `==` and swap the blocks of the `if`/`else`",
66-
),
63+
ExprKind::Binary(op, _, rhs)
64+
if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs, e.span.ctxt()) =>
65+
{
66+
(
67+
"unnecessary `!=` operation",
68+
"change to `==` and swap the blocks of the `if`/`else`",
69+
)
70+
},
6771
_ => return,
6872
};
6973

clippy_lints/src/implicit_saturating_add.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> {
117117
fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> {
118118
if let ExprKind::Binary(op, l, r) = expr.kind {
119119
let ecx = ConstEvalCtxt::new(cx);
120-
if let Some(Constant::Int(c)) = ecx.eval(r) {
120+
let ctxt = expr.span.ctxt();
121+
if let Some(Constant::Int(c)) = ecx.eval_local(r, ctxt) {
121122
return Some((c, op.node, l));
122123
}
123-
if let Some(Constant::Int(c)) = ecx.eval(l) {
124+
if let Some(Constant::Int(c)) = ecx.eval_local(l, ctxt) {
124125
return Some((c, invert_op(op.node)?, r));
125126
}
126127
}

clippy_lints/src/invalid_upcast_comparisons.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn upcast_comparison_bounds_err<'tcx>(
101101
invert: bool,
102102
) {
103103
if let Some((lb, ub)) = lhs_bounds
104-
&& let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs)
104+
&& let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt())
105105
{
106106
if rel == Rel::Eq || rel == Rel::Ne {
107107
if norm_rhs_val < lb || norm_rhs_val > ub {

clippy_lints/src/manual_float_methods.rs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_config::Conf;
2-
use clippy_utils::consts::{ConstEvalCtxt, Constant};
2+
use clippy_utils::consts::ConstEvalCtxt;
33
use clippy_utils::diagnostics::span_lint_and_then;
44
use clippy_utils::msrvs::{self, Msrv};
55
use clippy_utils::source::SpanRangeExt;
@@ -146,13 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
146146
)
147147
&& let [first, second, const_1, const_2] = exprs
148148
&& let ecx = ConstEvalCtxt::new(cx)
149-
&& let Some(const_1) = ecx.eval(const_1)
150-
&& let Some(const_2) = ecx.eval(const_2)
149+
&& let ctxt = expr.span.ctxt()
150+
&& let Some(const_1) = ecx.eval_local(const_1, ctxt)
151+
&& let Some(const_2) = ecx.eval_local(const_2, ctxt)
151152
&& path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
152153
// The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
153154
// case somebody does that for some reason
154-
&& (is_infinity(&const_1) && is_neg_infinity(&const_2)
155-
|| is_neg_infinity(&const_1) && is_infinity(&const_2))
155+
&& (const_1.is_pos_infinity() && const_2.is_neg_infinity()
156+
|| const_1.is_neg_infinity() && const_2.is_pos_infinity())
156157
&& let Some(local_snippet) = first.span.get_source_text(cx)
157158
{
158159
let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
@@ -201,21 +202,3 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
201202
}
202203
}
203204
}
204-
205-
fn is_infinity(constant: &Constant<'_>) -> bool {
206-
match constant {
207-
// FIXME(f16_f128): add f16 and f128 when constants are available
208-
Constant::F32(float) => *float == f32::INFINITY,
209-
Constant::F64(float) => *float == f64::INFINITY,
210-
_ => false,
211-
}
212-
}
213-
214-
fn is_neg_infinity(constant: &Constant<'_>) -> bool {
215-
match constant {
216-
// FIXME(f16_f128): add f16 and f128 when constants are available
217-
Constant::F32(float) => *float == f32::NEG_INFINITY,
218-
Constant::F64(float) => *float == f64::NEG_INFINITY,
219-
_ => false,
220-
}
221-
}

clippy_lints/src/manual_rem_euclid.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_errors::Applicability;
88
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
99
use rustc_lint::{LateContext, LateLintPass, LintContext};
1010
use rustc_session::impl_lint_pass;
11+
use rustc_span::SyntaxContext;
1112

1213
declare_clippy_lint! {
1314
/// ### What it does
@@ -58,13 +59,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
5859
&& add_lhs.span.ctxt() == ctxt
5960
&& add_rhs.span.ctxt() == ctxt
6061
&& !expr.span.in_external_macro(cx.sess().source_map())
61-
&& let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
62-
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
62+
&& let Some(const1) = check_for_unsigned_int_constant(cx, ctxt, rem_rhs)
63+
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, ctxt, add_lhs, add_rhs)
6364
&& let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
6465
&& rem2_op.node == BinOpKind::Rem
6566
&& const1 == const2
6667
&& let Some(hir_id) = path_to_local(rem2_lhs)
67-
&& let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs)
68+
&& let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs)
6869
// Also ensures the const is nonzero since zero can't be a divisor
6970
&& const2 == const3
7071
&& rem2_lhs.span.ctxt() == ctxt
@@ -103,16 +104,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
103104
// constant along with the other expression unchanged if so
104105
fn check_for_either_unsigned_int_constant<'a>(
105106
cx: &'a LateContext<'_>,
107+
ctxt: SyntaxContext,
106108
left: &'a Expr<'_>,
107109
right: &'a Expr<'_>,
108110
) -> Option<(u128, &'a Expr<'a>)> {
109-
check_for_unsigned_int_constant(cx, left)
111+
check_for_unsigned_int_constant(cx, ctxt, left)
110112
.map(|int_const| (int_const, right))
111-
.or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left)))
113+
.or_else(|| check_for_unsigned_int_constant(cx, ctxt, right).map(|int_const| (int_const, left)))
112114
}
113115

114-
fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
115-
let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?;
116+
fn check_for_unsigned_int_constant<'a>(
117+
cx: &'a LateContext<'_>,
118+
ctxt: SyntaxContext,
119+
expr: &'a Expr<'_>,
120+
) -> Option<u128> {
121+
let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr, ctxt)?;
116122
match int_const {
117123
FullInt::S(s) => s.try_into().ok(),
118124
FullInt::U(u) => Some(u),

clippy_lints/src/manual_rotate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fn parse_shift<'tcx>(
6666
BinOpKind::Shr => ShiftDirection::Right,
6767
_ => return None,
6868
};
69-
let const_expr = ConstEvalCtxt::new(cx).eval(r)?;
69+
let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?;
7070
if let Constant::Int(shift) = const_expr {
7171
return Some((dir, shift, l));
7272
}

clippy_lints/src/manual_strip.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _};
1616
use rustc_middle::ty;
1717
use rustc_session::impl_lint_pass;
1818
use rustc_span::source_map::Spanned;
19-
use rustc_span::{Symbol, sym};
19+
use rustc_span::{Symbol, SyntaxContext, sym};
2020
use std::iter;
2121

2222
declare_clippy_lint! {
@@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
9292
return;
9393
}
9494

95-
let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then);
95+
let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then, expr.span.ctxt());
9696
if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) {
9797
let kind_word = match strip_kind {
9898
StripKind::Prefix => "prefix",
@@ -166,8 +166,8 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
166166
}
167167

168168
// Returns the length of the `expr` if it's a constant string or char.
169-
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
170-
let value = ConstEvalCtxt::new(cx).eval(expr)?;
169+
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option<u128> {
170+
let value = ConstEvalCtxt::new(cx).eval_local(expr, ctxt)?;
171171
match value {
172172
Constant::Str(value) => Some(value.len() as u128),
173173
Constant::Char(value) => Some(value.len_utf8() as u128),
@@ -176,13 +176,18 @@ fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
176176
}
177177

178178
// Tests if `expr` equals the length of the pattern.
179-
fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
179+
fn eq_pattern_length<'tcx>(
180+
cx: &LateContext<'tcx>,
181+
pattern: &Expr<'_>,
182+
expr: &'tcx Expr<'_>,
183+
ctxt: SyntaxContext,
184+
) -> bool {
180185
if let ExprKind::Lit(Spanned {
181186
node: LitKind::Int(n, _),
182187
..
183188
}) = expr.kind
184189
{
185-
constant_length(cx, pattern).is_some_and(|length| n == length)
190+
constant_length(cx, pattern, ctxt).is_some_and(|length| n == length)
186191
} else {
187192
len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg))
188193
}
@@ -215,6 +220,7 @@ fn find_stripping<'tcx>(
215220
target: Res,
216221
pattern: &'tcx Expr<'_>,
217222
expr: &'tcx Expr<'tcx>,
223+
ctxt: SyntaxContext,
218224
) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap<Symbol, usize>) {
219225
struct StrippingFinder<'a, 'tcx> {
220226
cx: &'a LateContext<'tcx>,
@@ -223,6 +229,7 @@ fn find_stripping<'tcx>(
223229
pattern: &'tcx Expr<'tcx>,
224230
results: Vec<&'tcx Expr<'tcx>>,
225231
bindings: FxHashMap<Symbol, usize>,
232+
ctxt: SyntaxContext,
226233
}
227234

228235
impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> {
@@ -236,7 +243,7 @@ fn find_stripping<'tcx>(
236243
{
237244
match (self.strip_kind, start, end) {
238245
(StripKind::Prefix, Some(start), None) => {
239-
if eq_pattern_length(self.cx, self.pattern, start) {
246+
if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) {
240247
self.results.push(ex);
241248
return;
242249
}
@@ -252,7 +259,7 @@ fn find_stripping<'tcx>(
252259
&& let Some(left_arg) = len_arg(self.cx, left)
253260
&& let ExprKind::Path(left_path) = &left_arg.kind
254261
&& self.cx.qpath_res(left_path, left_arg.hir_id) == self.target
255-
&& eq_pattern_length(self.cx, self.pattern, right)
262+
&& eq_pattern_length(self.cx, self.pattern, right, self.ctxt)
256263
{
257264
self.results.push(ex);
258265
return;
@@ -280,6 +287,7 @@ fn find_stripping<'tcx>(
280287
pattern,
281288
results: vec![],
282289
bindings: FxHashMap::default(),
290+
ctxt,
283291
};
284292
walk_expr(&mut finder, expr);
285293
(finder.results, finder.bindings)

0 commit comments

Comments
 (0)