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

`len_without_is_empty` triggers when both `len` and `is_empty` have non-standard signatures

Open lopopolo opened this issue 1 year ago • 1 comments

Summary

I have a proxy type (a symbol from a string interner) which has len and is_empty method which:

  • are fallible
  • take the interner as a method parameter

len_without_is_empty triggers with these methods, which is unexpected since neither method matches the method signature the lint looks for.

Lint Name

len_without_is_empty

Reproducer

I tried this code:

#[warn(clippy::len_without_is_empty)]

pub trait Intern {
    type Symbol: Copy;
    
    fn lookup_symbol(&self, sym: Self::Symbol) -> Result<Option<&[u8]>, String>;
}


#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Symbol(u32);

impl Symbol {
    #[inline]
    #[must_use]
    pub fn is_empty<T, U>(self, interner: &T) -> bool
    where
        T: Intern<Symbol = U>,
        U: Copy + From<Symbol>,
    {
        if let Ok(Some(bytes)) = interner.lookup_symbol(self.into()) {
            bytes.is_empty()
        } else {
            true
        }
    }

    #[inline]
    #[must_use]
    // #[allow(clippy::len_without_is_empty)]
    pub fn len<T, U>(self, interner: &T) -> usize
    where
        T: Intern<Symbol = U>,
        U: Copy + From<Symbol>,
    {
        if let Ok(Some(bytes)) = interner.lookup_symbol(self.into()) {
            bytes.len()
        } else {
            0_usize
        }
    }
}

I saw this happen:

    Checking playground v0.0.1 (/playground)
warning: struct `Symbol` has a public `len` method, but the `is_empty` method has an unexpected signature
  --> src/lib.rs:31:5
   |
31 | /     pub fn len<T, U>(self, interner: &T) -> usize
32 | |     where
33 | |         T: Intern<Symbol = U>,
34 | |         U: Copy + From<Symbol>,
   | |_______________________________^
   |
   = note: `#[warn(clippy::len_without_is_empty)]` on by default
note: `is_empty` defined here
  --> src/lib.rs:16:5
   |
16 | /     pub fn is_empty<T, U>(self, interner: &T) -> bool
17 | |     where
18 | |         T: Intern<Symbol = U>,
19 | |         U: Copy + From<Symbol>,
   | |_______________________________^
   = note: expected signature: `(self) -> bool`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty

warning: `playground` (lib) generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s

I expected to see this happen: No error because neither len nor is_empty has the expected signature.

Version

rustc 1.64.0 (a55dd71d5 2022-09-19)
binary: rustc
commit-hash: a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52
commit-date: 2022-09-19
host: x86_64-apple-darwin
release: 1.64.0
LLVM version: 14.0.6

Additional Labels

No response

lopopolo avatar Sep 23 '22 01:09 lopopolo

@rustbot label +good-first-issue

xFrednet avatar Sep 23 '22 08:09 xFrednet

I want to take this up as my first issue. Can I?

akshay1992kalbhor avatar Sep 29 '22 09:09 akshay1992kalbhor

@rustbot claim

akshay1992kalbhor avatar Sep 29 '22 09:09 akshay1992kalbhor

@lopopolo @xFrednet So something interesting is happening here. While checking the len method clippy only looks at the return value type so if its an signed or unsigned int it triggers the len_without_is_empty lint. It does not realise that the method has an extra argument of a different type. But then if you implement the is_empty method as you did above it checks the complete method signature and realises that it is incorrect and should be (self) -> bool. So I think clippy is not checking the complete method signature for len somehow. This is my initial observation. Will update after some more investigation

akshay1992kalbhor avatar Oct 06 '22 15:10 akshay1992kalbhor

Awesome thanks for digging into this @akshay1992kalbhor!

lopopolo avatar Oct 06 '22 16:10 lopopolo

So I think my above hunch was right. We don't check the len function inputs. My solution is to add a check where we see if the len function has any other arguments besides the self. If it does we don't trigger the lint. This is off the top of my head. Please suggest alternatives or improvements.

akshay1992kalbhor avatar Oct 16 '22 11:10 akshay1992kalbhor

Hey @akshay1992kalbhor, thank you for investigating this issue further. Your suggestion to fix the bug sounds like the correct one, so go for it! :upside_down_face:

xFrednet avatar Oct 16 '22 21:10 xFrednet

The same PR should also update the tests for this lint right?

akshay1992kalbhor avatar Oct 17 '22 13:10 akshay1992kalbhor

Yes, one PR should cover all related changes. In clippy this can often look like a lot of changes due to the test files, but that's totally fine :).

xFrednet avatar Oct 18 '22 11:10 xFrednet