Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,9 @@ define_Conf! {
/// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
#[lints(cargo_common_metadata)]
cargo_ignore_publish: bool = false,
/// Check fn calls in eq_op lint
#[lints(expect_used)]
check_fn_call_in_eq_op: bool = true,
/// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
#[lints(incompatible_msrv)]
check_incompatible_msrv_in_tests: bool = false,
Expand Down
18 changes: 1 addition & 17 deletions clippy_lints/src/copies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,23 +614,7 @@ fn method_caller_is_mutable<'tcx>(

/// Implementation of `IFS_SAME_COND`.
fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) {
for group in search_same(
conds,
|e| hash_expr(cx, e),
|lhs, rhs| {
// Ignore eq_expr side effects iff one of the expression kind is a method call
// and the caller is not a mutable, including inner mutable type.
if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
if method_caller_is_mutable(cx, caller, interior_mut) {
false
} else {
SpanlessEq::new(cx).eq_expr(lhs, rhs)
}
} else {
eq_expr_value(cx, lhs, rhs)
}
},
) {
for group in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) {
let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect();
span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition");
}
Expand Down
20 changes: 16 additions & 4 deletions clippy_lints/src/operators/eq_op.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
use clippy_utils::ast_utils::is_useless_with_eq_exprs;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
use clippy_utils::{eq_expr_value, is_in_test_function, sym};
use clippy_utils::{eq_expr_value, eq_expr_value_with_sideffects, is_in_test_function, sym};
use rustc_hir::{BinOpKind, Expr};
use rustc_lint::LateContext;

use super::EQ_OP;

pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, with_sideffects: bool) {
if let Some(macro_call) = first_node_macro_backtrace(cx, e).find(|macro_call| {
matches!(
cx.tcx.get_diagnostic_name(macro_call.def_id),
Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro)
)
}) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
&& eq_expr_value(cx, lhs, rhs)
&& if with_sideffects {
eq_expr_value_with_sideffects(cx, lhs, rhs)
} else {
eq_expr_value(cx, lhs, rhs)
}
&& macro_call.is_local()
&& !is_in_test_function(cx.tcx, e.hir_id)
{
Expand All @@ -36,8 +40,16 @@ pub(crate) fn check<'tcx>(
op: BinOpKind,
left: &'tcx Expr<'_>,
right: &'tcx Expr<'_>,
with_sideffects: bool,
) {
if is_useless_with_eq_exprs(op) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) {
if is_useless_with_eq_exprs(op)
&& if with_sideffects {
eq_expr_value_with_sideffects(cx, left, right)
} else {
eq_expr_value(cx, left, right)
}
&& !is_in_test_function(cx.tcx, e.hir_id)
{
span_lint_and_then(
cx,
EQ_OP,
Expand Down
6 changes: 4 additions & 2 deletions clippy_lints/src/operators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ pub struct Operators {
verbose_bit_mask_threshold: u64,
modulo_arithmetic_allow_comparison_to_zero: bool,
msrv: Msrv,
check_fn_call_in_eq_op: bool,
}
impl Operators {
pub fn new(conf: &'static Conf) -> Self {
Expand All @@ -843,6 +844,7 @@ impl Operators {
verbose_bit_mask_threshold: conf.verbose_bit_mask_threshold,
modulo_arithmetic_allow_comparison_to_zero: conf.allow_comparison_to_zero,
msrv: conf.msrv,
check_fn_call_in_eq_op: conf.check_fn_call_in_eq_op,
}
}
}
Expand Down Expand Up @@ -878,13 +880,13 @@ impl_lint_pass!(Operators => [

impl<'tcx> LateLintPass<'tcx> for Operators {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
eq_op::check_assert(cx, e);
eq_op::check_assert(cx, e, self.check_fn_call_in_eq_op);
match e.kind {
ExprKind::Binary(op, lhs, rhs) => {
if !e.span.from_expansion() {
absurd_extreme_comparisons::check(cx, e, op.node, lhs, rhs);
if !(macro_with_not_op(lhs) || macro_with_not_op(rhs)) {
eq_op::check(cx, e, op.node, lhs, rhs);
eq_op::check(cx, e, op.node, lhs, rhs, self.check_fn_call_in_eq_op);
op_ref::check(cx, e, op.node, lhs, rhs);
}
erasing_op::check(cx, e, op.node, lhs, rhs);
Expand Down
7 changes: 6 additions & 1 deletion clippy_utils/src/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,12 @@ pub fn count_eq<X: Sized>(

/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
eq_expr_value_with_sideffects(cx, left, right)
}

/// Checks if two expressions evaluate to the same value, ignoring side effects.
pub fn eq_expr_value_with_sideffects(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
SpanlessEq::new(cx).eq_expr(left, right)
}

/// Returns the segments of a path that might have generic parameters.
Expand Down
3 changes: 2 additions & 1 deletion clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ pub mod visitors;
pub use self::attrs::*;
pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
pub use self::hir_utils::{
HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, eq_expr_value_with_sideffects, hash_expr,
hash_stmt, is_bool, over,
};

use core::mem;
Expand Down
Loading