Auto merge of #10478 - Jarcho:block_eq, r=xFrednet
`SpanlessEq` improvements
fixes #9775
Also includes a simplification to `consts::constant`'s interface since I was already touching the code.
At the start of `eq_expr` the check:
```rust
if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
return false;
}
```
was removed. This was added in 49e2501
to handle `cfg` macros. This is better handled by the newly added `check_ctxt`.
changelog: [various lints]: Don't consider different `cfg!` expansions to be the same unless they are for the same config.
changelog: [various lints]: Don't consider the expansion of two different macros to be equal, even when they expand to the same token sequence.
changelog: [various lints]: Don't consider two blocks to be equal if they contain disabled code or empty macro expansions, unless those section contain the exact same token sequence.
This commit is contained in:
commit
1e8d2c59bc
@ -38,7 +38,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
_ => 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 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 @@
|
||||
// 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
||||
// 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 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
/// 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),
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet_opt, walk_span_to_context};
|
||||
use clippy_utils::{higher, in_constant, is_span_match};
|
||||
use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lexer::TokenKind;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
@ -1147,12 +1147,7 @@ fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
|
||||
// Assume true. This would require either an invalid span, or one which crosses file boundaries.
|
||||
return true;
|
||||
};
|
||||
let mut pos = 0usize;
|
||||
let mut iter = tokenize(&snip).map(|t| {
|
||||
let start = pos;
|
||||
pos += t.len as usize;
|
||||
(t.kind, start..pos)
|
||||
});
|
||||
let mut iter = tokenize_with_text(&snip);
|
||||
|
||||
// Search for the token sequence [`#`, `[`, `cfg`]
|
||||
while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
|
||||
@ -1163,7 +1158,7 @@ fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
|
||||
)
|
||||
});
|
||||
if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
|
||||
&& matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg")
|
||||
&& matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -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 @@
|
||||
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 @@
|
||||
|
||||
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 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
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 @@ fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
|
||||
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 is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
|
||||
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 @@ fn check_vec_macro<'tcx>(
|
||||
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::{get_source_text, 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 @@ fn cmp_s_u(s: i128, u: u128) -> Ordering {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 @@ fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<C
|
||||
.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 @@ fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant>
|
||||
|
||||
/// 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) = get_source_text(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
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::consts::constant_simple;
|
||||
use crate::macros::macro_backtrace;
|
||||
use crate::source::snippet_opt;
|
||||
use crate::source::{get_source_text, snippet_opt, walk_span_to_context, SpanRange};
|
||||
use crate::tokenize_with_text;
|
||||
use rustc_ast::ast::InlineAsmTemplatePiece;
|
||||
use rustc_data_structures::fx::FxHasher;
|
||||
use rustc_hir::def::Res;
|
||||
@ -13,8 +14,9 @@
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Range;
|
||||
|
||||
/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
|
||||
/// other conditions would make them equal.
|
||||
@ -65,6 +67,8 @@ pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bo
|
||||
pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
|
||||
HirEqInterExpr {
|
||||
inner: self,
|
||||
left_ctxt: SyntaxContext::root(),
|
||||
right_ctxt: SyntaxContext::root(),
|
||||
locals: HirIdMap::default(),
|
||||
}
|
||||
}
|
||||
@ -92,6 +96,8 @@ pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegmen
|
||||
|
||||
pub struct HirEqInterExpr<'a, 'b, 'tcx> {
|
||||
inner: &'a mut SpanlessEq<'b, 'tcx>,
|
||||
left_ctxt: SyntaxContext,
|
||||
right_ctxt: SyntaxContext,
|
||||
|
||||
// When binding are declared, the binding ID in the left expression is mapped to the one on the
|
||||
// right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
|
||||
@ -126,52 +132,88 @@ pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
}
|
||||
|
||||
/// Checks whether two blocks are the same.
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
match (left.stmts, left.expr, right.stmts, right.expr) {
|
||||
([], None, [], None) => {
|
||||
// For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
|
||||
// expanded to nothing, or the cfg attribute was used.
|
||||
let (Some(left), Some(right)) = (
|
||||
snippet_opt(self.inner.cx, left.span),
|
||||
snippet_opt(self.inner.cx, right.span),
|
||||
) else { return true };
|
||||
let mut left_pos = 0;
|
||||
let left = tokenize(&left)
|
||||
.map(|t| {
|
||||
let end = left_pos + t.len as usize;
|
||||
let s = &left[left_pos..end];
|
||||
left_pos = end;
|
||||
(t, s)
|
||||
})
|
||||
.filter(|(t, _)| {
|
||||
!matches!(
|
||||
t.kind,
|
||||
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
|
||||
)
|
||||
})
|
||||
.map(|(_, s)| s);
|
||||
let mut right_pos = 0;
|
||||
let right = tokenize(&right)
|
||||
.map(|t| {
|
||||
let end = right_pos + t.len as usize;
|
||||
let s = &right[right_pos..end];
|
||||
right_pos = end;
|
||||
(t, s)
|
||||
})
|
||||
.filter(|(t, _)| {
|
||||
!matches!(
|
||||
t.kind,
|
||||
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
|
||||
)
|
||||
})
|
||||
.map(|(_, s)| s);
|
||||
left.eq(right)
|
||||
},
|
||||
_ => {
|
||||
over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
|
||||
&& both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
|
||||
},
|
||||
use TokenKind::{BlockComment, LineComment, Semi, Whitespace};
|
||||
if left.stmts.len() != right.stmts.len() {
|
||||
return false;
|
||||
}
|
||||
let lspan = left.span.data();
|
||||
let rspan = right.span.data();
|
||||
if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() {
|
||||
// Don't try to check in between statements inside macros.
|
||||
return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right))
|
||||
&& both(&left.expr, &right.expr, |left, right| self.eq_expr(left, right));
|
||||
}
|
||||
if lspan.ctxt != rspan.ctxt {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut lstart = lspan.lo;
|
||||
let mut rstart = rspan.lo;
|
||||
|
||||
for (left, right) in left.stmts.iter().zip(right.stmts) {
|
||||
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;
|
||||
};
|
||||
let Some(rstmt_span) = walk_span_to_context(right.span, rspan.ctxt) else {
|
||||
return false;
|
||||
};
|
||||
let lstmt_span = lstmt_span.data();
|
||||
let rstmt_span = rstmt_span.data();
|
||||
|
||||
if lstmt_span.lo < lstart && rstmt_span.lo < rstart {
|
||||
// Can happen when macros expand to multiple statements, or rearrange statements.
|
||||
// Nothing in between the statements to check in this case.
|
||||
continue;
|
||||
}
|
||||
if lstmt_span.lo < lstart || rstmt_span.lo < rstart {
|
||||
// Only one of the blocks had a weird macro.
|
||||
return false;
|
||||
}
|
||||
if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lstart = lstmt_span.hi;
|
||||
rstart = rstmt_span.hi;
|
||||
}
|
||||
|
||||
let (lend, rend) = match (left.expr, right.expr) {
|
||||
(Some(left), Some(right)) => {
|
||||
if !self.eq_expr(left, right) {
|
||||
return false;
|
||||
}
|
||||
let Some(lexpr_span) = walk_span_to_context(left.span, lspan.ctxt) else {
|
||||
return false;
|
||||
};
|
||||
let Some(rexpr_span) = walk_span_to_context(right.span, rspan.ctxt) else {
|
||||
return false;
|
||||
};
|
||||
(lexpr_span.lo(), rexpr_span.lo())
|
||||
},
|
||||
(None, None) => (lspan.hi, rspan.hi),
|
||||
(Some(_), None) | (None, Some(_)) => return false,
|
||||
};
|
||||
|
||||
if lend < lstart && rend < rstart {
|
||||
// Can happen when macros rearrange the input.
|
||||
// Nothing in between the statements to check in this case.
|
||||
return true;
|
||||
} else if lend < lstart || rend < rstart {
|
||||
// Only one of the blocks had a weird macro
|
||||
return false;
|
||||
}
|
||||
eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)
|
||||
})
|
||||
}
|
||||
|
||||
fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
|
||||
@ -207,7 +249,7 @@ pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
|
||||
if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -440,6 +482,45 @@ pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
|
||||
fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
|
||||
left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
|
||||
}
|
||||
|
||||
fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
|
||||
if self.left_ctxt == left && self.right_ctxt == right {
|
||||
return true;
|
||||
} else if self.left_ctxt == left || self.right_ctxt == right {
|
||||
// Only one context has changed. This can only happen if the two nodes are written differently.
|
||||
return false;
|
||||
} else if left != SyntaxContext::root() {
|
||||
let mut left_data = left.outer_expn_data();
|
||||
let mut right_data = right.outer_expn_data();
|
||||
loop {
|
||||
use TokenKind::{BlockComment, LineComment, Whitespace};
|
||||
if left_data.macro_def_id != right_data.macro_def_id
|
||||
|| (matches!(left_data.kind, ExpnKind::Macro(MacroKind::Bang, name) if name == sym::cfg)
|
||||
&& !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. })
|
||||
}))
|
||||
{
|
||||
// Either a different chain of macro calls, or different arguments to the `cfg` macro.
|
||||
return false;
|
||||
}
|
||||
let left_ctxt = left_data.call_site.ctxt();
|
||||
let right_ctxt = right_data.call_site.ctxt();
|
||||
if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() {
|
||||
break;
|
||||
}
|
||||
if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() {
|
||||
// Different lengths for the expansion stack. This can only happen if nodes are written differently,
|
||||
// or shouldn't be compared to start with.
|
||||
return false;
|
||||
}
|
||||
left_data = left_ctxt.outer_expn_data();
|
||||
right_data = right_ctxt.outer_expn_data();
|
||||
}
|
||||
}
|
||||
self.left_ctxt = left;
|
||||
self.right_ctxt = right;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Some simple reductions like `{ return }` => `return`
|
||||
@ -1038,3 +1119,34 @@ pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
|
||||
h.hash_expr(e);
|
||||
h.finish()
|
||||
}
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_span_tokens(
|
||||
cx: &LateContext<'_>,
|
||||
left: impl SpanRange,
|
||||
right: impl SpanRange,
|
||||
pred: impl Fn(TokenKind) -> bool,
|
||||
) -> bool {
|
||||
fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
|
||||
if let Some(lsrc) = get_source_text(cx, left)
|
||||
&& let Some(lsrc) = lsrc.as_str()
|
||||
&& let Some(rsrc) = get_source_text(cx, right)
|
||||
&& let Some(rsrc) = rsrc.as_str()
|
||||
{
|
||||
let pred = |t: &(_, _)| pred(t.0);
|
||||
let map = |(_, x)| x;
|
||||
|
||||
let ltok = tokenize_with_text(lsrc)
|
||||
.filter(pred)
|
||||
.map(map);
|
||||
let rtok = tokenize_with_text(rsrc)
|
||||
.filter(pred)
|
||||
.map(map);
|
||||
ltok.eq(rtok)
|
||||
} else {
|
||||
// Unable to access the source. Conservatively assume the blocks aren't equal.
|
||||
false
|
||||
}
|
||||
}
|
||||
f(cx, left.into_range(), right.into_range(), pred)
|
||||
}
|
||||
|
@ -77,6 +77,7 @@
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::ast::{self, LitKind, RangeLimits};
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -1498,7 +1499,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
|
||||
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
|
||||
&& let min_const_kind = ConstantKind::from_value(const_val, bnd_ty)
|
||||
&& let Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
|
||||
&& let Some((start_const, _)) = constant(cx, cx.typeck_results(), start)
|
||||
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
|
||||
{
|
||||
start_const == min_const
|
||||
} else {
|
||||
@ -1514,7 +1515,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
|
||||
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
|
||||
&& let max_const_kind = ConstantKind::from_value(const_val, bnd_ty)
|
||||
&& let Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
|
||||
&& let Some((end_const, _)) = constant(cx, cx.typeck_results(), end)
|
||||
&& let Some(end_const) = constant(cx, cx.typeck_results(), end)
|
||||
{
|
||||
end_const == max_const
|
||||
} else {
|
||||
@ -1546,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
|
||||
@ -2490,6 +2491,17 @@ pub fn walk_to_expr_usage<'tcx, T>(
|
||||
None
|
||||
}
|
||||
|
||||
/// Tokenizes the input while keeping the text associated with each token.
|
||||
pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str)> {
|
||||
let mut pos = 0;
|
||||
tokenize(s).map(move |t| {
|
||||
let end = pos + t.len;
|
||||
let range = pos as usize..end as usize;
|
||||
pos = end;
|
||||
(t.kind, s.get(range).unwrap_or_default())
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks whether a given span has any comment token
|
||||
/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
|
||||
pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
|
||||
@ -2506,23 +2518,11 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
|
||||
/// Comments are returned wrapped with their relevant delimiters
|
||||
pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
|
||||
let snippet = sm.span_to_snippet(span).unwrap_or_default();
|
||||
let mut comments_buf: Vec<String> = Vec::new();
|
||||
let mut index: usize = 0;
|
||||
|
||||
for token in tokenize(&snippet) {
|
||||
let token_range = index..(index + token.len as usize);
|
||||
index += token.len as usize;
|
||||
match token.kind {
|
||||
TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
|
||||
if let Some(comment) = snippet.get(token_range) {
|
||||
comments_buf.push(comment.to_string());
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
comments_buf.join("\n")
|
||||
let res = tokenize_with_text(&snippet)
|
||||
.filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
|
||||
.map(|(_, s)| s)
|
||||
.join("\n");
|
||||
res
|
||||
}
|
||||
|
||||
pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
|
||||
|
@ -2,14 +2,64 @@
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene;
|
||||
use rustc_span::source_map::{original_sp, SourceMap};
|
||||
use rustc_span::{hygiene, SourceFile};
|
||||
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
|
||||
/// A type which can be converted to the range portion of a `Span`.
|
||||
pub trait SpanRange {
|
||||
fn into_range(self) -> Range<BytePos>;
|
||||
}
|
||||
impl SpanRange for Span {
|
||||
fn into_range(self) -> Range<BytePos> {
|
||||
let data = self.data();
|
||||
data.lo..data.hi
|
||||
}
|
||||
}
|
||||
impl SpanRange for SpanData {
|
||||
fn into_range(self) -> Range<BytePos> {
|
||||
self.lo..self.hi
|
||||
}
|
||||
}
|
||||
impl SpanRange for Range<BytePos> {
|
||||
fn into_range(self) -> Range<BytePos> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SourceFileRange {
|
||||
pub sf: Lrc<SourceFile>,
|
||||
pub range: Range<usize>,
|
||||
}
|
||||
impl SourceFileRange {
|
||||
/// Attempts to get the text from the source file. This can fail if the source text isn't
|
||||
/// loaded.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.sf.src.as_ref().and_then(|x| x.get(self.range.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span
|
||||
/// extends through multiple files, or is malformed.
|
||||
pub fn get_source_text(cx: &impl LintContext, sp: impl SpanRange) -> Option<SourceFileRange> {
|
||||
fn f(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
|
||||
let start = sm.lookup_byte_offset(sp.start);
|
||||
let end = sm.lookup_byte_offset(sp.end);
|
||||
if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
|
||||
return None;
|
||||
}
|
||||
let range = start.pos.to_usize()..end.pos.to_usize();
|
||||
Some(SourceFileRange { sf: start.sf, range })
|
||||
}
|
||||
f(cx.sess().source_map(), sp.into_range())
|
||||
}
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||
pub fn expr_block<T: LintContext>(
|
||||
|
@ -1,5 +1,10 @@
|
||||
//@run-rustfix
|
||||
#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
|
||||
#![allow(
|
||||
clippy::assertions_on_constants,
|
||||
clippy::equatable_if_let,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::eq_op
|
||||
)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[warn(clippy::collapsible_if)]
|
||||
|
@ -1,5 +1,10 @@
|
||||
//@run-rustfix
|
||||
#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
|
||||
#![allow(
|
||||
clippy::assertions_on_constants,
|
||||
clippy::equatable_if_let,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::eq_op
|
||||
)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[warn(clippy::collapsible_if)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:9:5
|
||||
--> $DIR/collapsible_if.rs:14:5
|
||||
|
|
||||
LL | / if x == "hello" {
|
||||
LL | | if y == "world" {
|
||||
@ -17,7 +17,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:15:5
|
||||
--> $DIR/collapsible_if.rs:20:5
|
||||
|
|
||||
LL | / if x == "hello" || x == "world" {
|
||||
LL | | if y == "world" || y == "hello" {
|
||||
@ -34,7 +34,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:21:5
|
||||
--> $DIR/collapsible_if.rs:26:5
|
||||
|
|
||||
LL | / if x == "hello" && x == "world" {
|
||||
LL | | if y == "world" || y == "hello" {
|
||||
@ -51,7 +51,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:27:5
|
||||
--> $DIR/collapsible_if.rs:32:5
|
||||
|
|
||||
LL | / if x == "hello" || x == "world" {
|
||||
LL | | if y == "world" && y == "hello" {
|
||||
@ -68,7 +68,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:33:5
|
||||
--> $DIR/collapsible_if.rs:38:5
|
||||
|
|
||||
LL | / if x == "hello" && x == "world" {
|
||||
LL | | if y == "world" && y == "hello" {
|
||||
@ -85,7 +85,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:39:5
|
||||
--> $DIR/collapsible_if.rs:44:5
|
||||
|
|
||||
LL | / if 42 == 1337 {
|
||||
LL | | if 'a' != 'A' {
|
||||
@ -102,7 +102,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:95:5
|
||||
--> $DIR/collapsible_if.rs:100:5
|
||||
|
|
||||
LL | / if x == "hello" {
|
||||
LL | | if y == "world" { // Collapsible
|
||||
@ -119,7 +119,7 @@ LL + }
|
||||
|
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:154:5
|
||||
--> $DIR/collapsible_if.rs:159:5
|
||||
|
|
||||
LL | / if matches!(true, true) {
|
||||
LL | | if matches!(true, true) {}
|
||||
@ -127,7 +127,7 @@ LL | | }
|
||||
| |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}`
|
||||
|
||||
error: this `if` statement can be collapsed
|
||||
--> $DIR/collapsible_if.rs:159:5
|
||||
--> $DIR/collapsible_if.rs:164:5
|
||||
|
|
||||
LL | / if matches!(true, true) && truth() {
|
||||
LL | | if matches!(true, true) {}
|
||||
|
@ -53,4 +53,84 @@ pub fn name(&self) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
macro_rules! m {
|
||||
(foo) => {};
|
||||
(bar) => {};
|
||||
}
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
1
|
||||
};
|
||||
}
|
||||
macro_rules! bar {
|
||||
() => {
|
||||
1
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 0;
|
||||
let _ = match 0 {
|
||||
0 => {
|
||||
m!(foo);
|
||||
x
|
||||
},
|
||||
1 => {
|
||||
m!(bar);
|
||||
x
|
||||
},
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => {
|
||||
m!(foo);
|
||||
0
|
||||
},
|
||||
1 => {
|
||||
m!(bar);
|
||||
0
|
||||
},
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => {
|
||||
let mut x = 0;
|
||||
#[cfg(not_enabled)]
|
||||
{
|
||||
x = 5;
|
||||
}
|
||||
#[cfg(not(not_enabled))]
|
||||
{
|
||||
x = 6;
|
||||
}
|
||||
x
|
||||
},
|
||||
1 => {
|
||||
let mut x = 0;
|
||||
#[cfg(also_not_enabled)]
|
||||
{
|
||||
x = 5;
|
||||
}
|
||||
#[cfg(not(also_not_enabled))]
|
||||
{
|
||||
x = 6;
|
||||
}
|
||||
x
|
||||
},
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => foo!(),
|
||||
1 => bar!(),
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => cfg!(not_enabled),
|
||||
1 => cfg!(also_not_enabled),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
@ -239,4 +239,10 @@ struct Bar {
|
||||
3 => core::convert::identity::<u32>(todo!()),
|
||||
_ => 5,
|
||||
};
|
||||
|
||||
let _ = match 0 {
|
||||
0 => cfg!(not_enable),
|
||||
1 => cfg!(not_enable),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
@ -192,5 +192,20 @@ note: other arm here
|
||||
LL | Some(Bar { x: 0, y: 5, .. }) => 1,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:245:9
|
||||
|
|
||||
LL | 1 => cfg!(not_enable),
|
||||
| -^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `1 | 0`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:244:9
|
||||
|
|
||||
LL | 0 => cfg!(not_enable),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
//@run-rustfix
|
||||
#![warn(clippy::partialeq_to_none)]
|
||||
#![allow(clippy::eq_op)]
|
||||
|
||||
struct Foobar;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
//@run-rustfix
|
||||
#![warn(clippy::partialeq_to_none)]
|
||||
#![allow(clippy::eq_op)]
|
||||
|
||||
struct Foobar;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:14:8
|
||||
--> $DIR/partialeq_to_none.rs:15:8
|
||||
|
|
||||
LL | if f != None { "yay" } else { "nay" }
|
||||
| ^^^^^^^^^ help: use `Option::is_some()` instead: `f.is_some()`
|
||||
@ -7,55 +7,55 @@ LL | if f != None { "yay" } else { "nay" }
|
||||
= note: `-D clippy::partialeq-to-none` implied by `-D warnings`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:44:13
|
||||
--> $DIR/partialeq_to_none.rs:45:13
|
||||
|
|
||||
LL | let _ = x == None;
|
||||
| ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:45:13
|
||||
--> $DIR/partialeq_to_none.rs:46:13
|
||||
|
|
||||
LL | let _ = x != None;
|
||||
| ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:46:13
|
||||
--> $DIR/partialeq_to_none.rs:47:13
|
||||
|
|
||||
LL | let _ = None == x;
|
||||
| ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:47:13
|
||||
--> $DIR/partialeq_to_none.rs:48:13
|
||||
|
|
||||
LL | let _ = None != x;
|
||||
| ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:49:8
|
||||
--> $DIR/partialeq_to_none.rs:50:8
|
||||
|
|
||||
LL | if foobar() == None {}
|
||||
| ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:51:8
|
||||
--> $DIR/partialeq_to_none.rs:52:8
|
||||
|
|
||||
LL | if bar().ok() != None {}
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:53:13
|
||||
--> $DIR/partialeq_to_none.rs:54:13
|
||||
|
|
||||
LL | let _ = Some(1 + 2) != None;
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:55:13
|
||||
--> $DIR/partialeq_to_none.rs:56:13
|
||||
|
|
||||
LL | let _ = { Some(0) } == None;
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:57:13
|
||||
--> $DIR/partialeq_to_none.rs:58:13
|
||||
|
|
||||
LL | let _ = {
|
||||
| _____________^
|
||||
@ -77,31 +77,31 @@ LL ~ }.is_some();
|
||||
|
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:67:13
|
||||
--> $DIR/partialeq_to_none.rs:68:13
|
||||
|
|
||||
LL | let _ = optref() == &&None;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:68:13
|
||||
--> $DIR/partialeq_to_none.rs:69:13
|
||||
|
|
||||
LL | let _ = &&None != optref();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:69:13
|
||||
--> $DIR/partialeq_to_none.rs:70:13
|
||||
|
|
||||
LL | let _ = **optref() == None;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:70:13
|
||||
--> $DIR/partialeq_to_none.rs:71:13
|
||||
|
|
||||
LL | let _ = &None != *optref();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
|
||||
|
||||
error: binary comparison to literal `Option::None`
|
||||
--> $DIR/partialeq_to_none.rs:73:13
|
||||
--> $DIR/partialeq_to_none.rs:74:13
|
||||
|
|
||||
LL | let _ = None != *x;
|
||||
| ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()`
|
||||
|
Loading…
Reference in New Issue
Block a user