rust-clippy icon indicating copy to clipboard operation
rust-clippy copied to clipboard

Detect redundant always successful `assert_eq!`

Open repi opened this issue 3 years ago • 7 comments

What it does

Detect if assert_eq! always returns succeeds because the same expression is on the left and right side, such as with assert_eq!(a,a).

These type of cases can happen when refactoring and search'n'replacing code, for example we've recently saw a case of this recently:

   assert_eq!(mem::size_of::<usize>(), mem::size_of::<usize>());

It would however be important that it only lints on the expressions being exactly identical, so if using a type alias in the above example that should not trigger this lint, example:

   type MyType = usize;
   assert_eq!(mem::size_of::<usize>(), mem::size_of::<MyType>());

Lint Name

redundant_assert

Category

pedantic

Advantage

Keeping an assert that has no effect is a code smell and negative value as can leave a false sense of safety in tests / coverage and is additional cognitive overhead without any positive effects.

Drawbacks

Likely none.

Probably not that common occurrence, but does happen.

Example

assert_eq!(mem::size_of::<usize>(), mem::size_of::<usize>());

Assert should simply be removed as it has no effect.

repi avatar Mar 18 '22 22:03 repi

Mh, sounds kinda hard, because when there is any kind of mutability or randomness involved, we can still end up with identical expressions that evaluate to completely different things :/

    assert_eq!(
        rand::thread_rng().gen::<u8>(),
        rand::thread_rng().gen::<u8>()
    )

matthiaskrgr avatar Mar 19 '22 08:03 matthiaskrgr

For sure if the expression use global mutable state it doesn't work, I was thinking of it primarily for const functions (though I should have added that in the lint description).

std::mem::size_of for example is a const fn. And that could be easier to detect and cover?

repi avatar Mar 19 '22 11:03 repi

Mh, sounds kinda hard, because when there is any kind of mutability or randomness involved, we can still end up with identical expressions that evaluate to completely different things :/

    assert_eq!(
        rand::thread_rng().gen::<u8>(),
        rand::thread_rng().gen::<u8>()
    )

when assert_eq between random elements wouldn't that be wrong regardless? the lint should warn about identical literal in assert_eq! regardless of randomness? In case of const or randomness for opposite reasons still wouldn't make sense, although the lint would work only for assert_eq! and not for assert_ne!

lucarlig avatar Jan 15 '24 09:01 lucarlig

@lucarlig In more complex cases of non-deterministic functions it might be useful to run the same function twice and assert that the result is the same. For example, testing that a function that internally memoizes some intermediate result gives the same output before and after memoization has kicked in.

jplatte avatar Jan 20 '24 20:01 jplatte

I think this could be a useful lint cause Rust allows shadowing variables, and it's too easy to forget the right name (especially after refactoring). This is a bug I found in tests that led me to this issue:

            let non_optional = NonOptional { vec: vec![1, 2, 3] };
            let non_optional: NonOptional =
                serde_json::from_str(&serde_json::to_string(&non_optional).unwrap()).unwrap();
            assert_eq!(non_optional, non_optional);

Not sure why not make this lint work simply for all cases, when the left and right sides are the same, and leave other corner cases to be enabled/disabled as usual per line/file.

mhnap avatar Jan 24 '24 14:01 mhnap

Makes sense @rustbot claim

lucarlig avatar Feb 24 '24 11:02 lucarlig

i think some parts of this lints overlap with eq_op lint, does it make sense to implement it as separate lint or expand that?

lucarlig avatar Mar 11 '24 08:03 lucarlig