Is there a nice way to work around clippy warning in "generic" code on macros? (by generic here, I mean generic through cfg not through generic types)
#[cfg(feature = "multi-foo")]
type Foo = i32;
#[cfg(feature = "single-foo")]
type Foo = ();
struct Bar {
/// Makes sure we only operate on "compatible" Bars, identified by this Foo.
foo: Foo,
...
}
fn bar(x: Bar, y: Bar) {
assert_eq!(x.foo, y.foo); // triggers clippy::unit_cmp with --features=single-foo
...
}
We cannot simply prefix assert_eq with #[allow(clippy::unit_cmp)] because the attribute applies to the macro invocation in the case, while we want it to apply on the expansion.
Here are the current workarounds I know:
// the most obvious workaround, but it's very verbose
fn bar_1(x: Bar, y: Bar) {
#[allow(clippy::unit_cmp)]
{
assert_eq!(x.foo, y.foo);
}
}
// same idea but shorter although uglier
fn bar_2(x: Bar, y: Bar) {
#[allow(clippy::unit_cmp)]
(assert_eq!(x.foo, y.foo));
}
// same idea, but requires hiding another clippy warning
fn bar_3(x: Bar, y: Bar) {
#[allow(clippy::unit_cmp, clippy::let_unit_value)]
let () = assert_eq!(x.foo, y.foo);
}
// same idea, but requires hiding another clippy warning
fn bar_4(x: Bar, y: Bar) {
#[allow(clippy::unit_cmp, clippy::drop_copy)]
drop(assert_eq!(x.foo, y.foo));
}
The best option seems to be bar_2, but I'm not satisfied with it. Is there any other alternative? What is the most common workaround?
If you need to disable a warning in a specific scope, then use a block (solution #1). Solution #2 apparently works but it's weird and definitely non-idiomatic.
That's only one workaround to my problem but not my root problem. My root problem is applying an attribute (in particular an allow(clippy::)) on the expansion of a macro (in particular assert_eq). However, if you're implicitly telling me that there's no way to talk about this scope (the expansion of a macro) than by creating a new one around it, then this means the workarounds I suggest above are probably complete (i.e. variations around hiding an expression under a bigger expression/statement which is not a macro invocation). I agree bar_2 is not great but bar_1 is definitely too verbose. I ended up going with bar_3 for now (because it's a common way to document unit return types) but will still monitor this thread in case there are new one-line alternatives.
If the clippy::unit_cmp lint is doing more harm than good in your crate, then just add #![allow(clippy::unit_cmp)] to the top of your lib.rs or maybe just the offending module.
The circumstances the lint is checking for are pretty obscure, so I doubt it has actually flagged much in your codebase anyway.
Alternatively, you could define Foo as a struct that may or may not have an i32 field.
I think the lint is useful for non-"generic" code or "generic"-code where all "variants" are units. That's why I'd like to keep the allow-scope as small as possible.
Oh wow that's actually a pretty good idea. I ended up doing a variation of it though:
#[derive(Debug, Copy, Clone, PartialEq, Eq)] // new
struct Unit; // new
#[cfg(feature = "multi-foo")]
type Foo = i32;
#[cfg(feature = "single-foo")]
type Foo = Unit; // updated from ()
The reason I use this variation is because the definition of Foo is actually something like <Type as Trait>::T so I changed the definition of T for the implementation of the trait where it was unit.