#![warn(clippy::len_without_is_empty)]
#![allow(dead_code, unused)]

pub struct PubOne;

impl PubOne {
    pub fn len(&self) -> isize {
        //~^ ERROR: struct `PubOne` has a public `len` method, but no `is_empty` method
        //~| NOTE: `-D clippy::len-without-is-empty` implied by `-D warnings`
        1
    }
}

impl PubOne {
    // A second impl for this struct -- the error span shouldn't mention this.
    pub fn irrelevant(&self) -> bool {
        false
    }
}

// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`.
pub struct PubAllowed;

#[allow(clippy::len_without_is_empty)]
impl PubAllowed {
    pub fn len(&self) -> isize {
        1
    }
}

// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
// impl containing `len`.
impl PubAllowed {
    pub fn irrelevant(&self) -> bool {
        false
    }
}

pub struct PubAllowedFn;

impl PubAllowedFn {
    #[allow(clippy::len_without_is_empty)]
    pub fn len(&self) -> isize {
        1
    }
}

#[allow(clippy::len_without_is_empty)]
pub struct PubAllowedStruct;

impl PubAllowedStruct {
    pub fn len(&self) -> isize {
        1
    }
}

pub trait PubTraitsToo {
    //~^ ERROR: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty`
    fn len(&self) -> isize;
}

impl PubTraitsToo for One {
    fn len(&self) -> isize {
        0
    }
}

pub struct HasIsEmpty;

impl HasIsEmpty {
    pub fn len(&self) -> isize {
        //~^ ERROR: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` me
        1
    }

    fn is_empty(&self) -> bool {
        false
    }
}

pub struct HasWrongIsEmpty;

impl HasWrongIsEmpty {
    pub fn len(&self) -> isize {
        //~^ ERROR: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` met
        1
    }

    pub fn is_empty(&self, x: u32) -> bool {
        false
    }
}

pub struct MismatchedSelf;

impl MismatchedSelf {
    pub fn len(self) -> isize {
        //~^ ERROR: struct `MismatchedSelf` has a public `len` method, but the `is_empty` meth
        1
    }

    pub fn is_empty(&self) -> bool {
        false
    }
}

struct NotPubOne;

impl NotPubOne {
    pub fn len(&self) -> isize {
        // No error; `len` is pub but `NotPubOne` is not exported anyway.
        1
    }
}

struct One;

impl One {
    fn len(&self) -> isize {
        // No error; `len` is private; see issue #1085.
        1
    }
}

trait TraitsToo {
    fn len(&self) -> isize;
    // No error; `len` is private; see issue #1085.
}

impl TraitsToo for One {
    fn len(&self) -> isize {
        0
    }
}

struct HasPrivateIsEmpty;

impl HasPrivateIsEmpty {
    pub fn len(&self) -> isize {
        1
    }

    fn is_empty(&self) -> bool {
        false
    }
}

struct Wither;

pub trait WithIsEmpty {
    fn len(&self) -> isize;
    fn is_empty(&self) -> bool;
}

impl WithIsEmpty for Wither {
    fn len(&self) -> isize {
        1
    }

    fn is_empty(&self) -> bool {
        false
    }
}

pub trait Empty {
    fn is_empty(&self) -> bool;
}

pub trait InheritingEmpty: Empty {
    // Must not trigger `LEN_WITHOUT_IS_EMPTY`.
    fn len(&self) -> isize;
}

// This used to ICE.
pub trait Foo: Sized {}

pub trait DependsOnFoo: Foo {
    //~^ ERROR: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty`
    fn len(&mut self) -> usize;
}

// issue #1562
pub struct MultipleImpls;

impl MultipleImpls {
    pub fn len(&self) -> usize {
        1
    }
}

impl MultipleImpls {
    pub fn is_empty(&self) -> bool {
        false
    }
}

// issue #6958
pub struct OptionalLen;

impl OptionalLen {
    pub fn len(&self) -> Option<usize> {
        Some(0)
    }

    pub fn is_empty(&self) -> Option<bool> {
        Some(true)
    }
}

pub struct OptionalLen2;
impl OptionalLen2 {
    pub fn len(&self) -> Option<usize> {
        Some(0)
    }

    pub fn is_empty(&self) -> bool {
        true
    }
}

pub struct OptionalLen3;
impl OptionalLen3 {
    pub fn len(&self) -> usize {
        //~^ ERROR: struct `OptionalLen3` has a public `len` method, but the `is_empty` method
        0
    }

    // should lint, len is not an option
    pub fn is_empty(&self) -> Option<bool> {
        None
    }
}

pub struct ResultLen;
impl ResultLen {
    pub fn len(&self) -> Result<usize, ()> {
        //~^ ERROR: struct `ResultLen` has a public `len` method, but the `is_empty` method ha
        //~| ERROR: this returns a `Result<_, ()>`
        Ok(0)
    }

    // Differing result types
    pub fn is_empty(&self) -> Option<bool> {
        Some(true)
    }
}

pub struct ResultLen2;
impl ResultLen2 {
    pub fn len(&self) -> Result<usize, ()> {
        //~^ ERROR: this returns a `Result<_, ()>`
        Ok(0)
    }

    pub fn is_empty(&self) -> Result<bool, ()> {
        //~^ ERROR: this returns a `Result<_, ()>`
        Ok(true)
    }
}

pub struct ResultLen3;
impl ResultLen3 {
    pub fn len(&self) -> Result<usize, ()> {
        //~^ ERROR: this returns a `Result<_, ()>`
        Ok(0)
    }

    // Non-fallible result is ok.
    pub fn is_empty(&self) -> bool {
        true
    }
}

pub struct OddLenSig;
impl OddLenSig {
    // don't lint
    pub fn len(&self) -> bool {
        true
    }
}

// issue #6958
pub struct AsyncLen;
impl AsyncLen {
    async fn async_task(&self) -> bool {
        true
    }

    pub async fn len(&self) -> usize {
        usize::from(!self.async_task().await)
    }

    pub async fn is_empty(&self) -> bool {
        self.len().await == 0
    }
}

// issue #7232
pub struct AsyncLenWithoutIsEmpty;
impl AsyncLenWithoutIsEmpty {
    pub async fn async_task(&self) -> bool {
        true
    }

    pub async fn len(&self) -> usize {
        //~^ ERROR: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empt
        usize::from(!self.async_task().await)
    }
}

// issue #7232
pub struct AsyncOptionLenWithoutIsEmpty;
impl AsyncOptionLenWithoutIsEmpty {
    async fn async_task(&self) -> bool {
        true
    }

    pub async fn len(&self) -> Option<usize> {
        //~^ ERROR: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `i
        None
    }
}

// issue #7232
pub struct AsyncOptionLenNonIntegral;
impl AsyncOptionLenNonIntegral {
    // don't lint
    pub async fn len(&self) -> Option<String> {
        None
    }
}

// issue #7232
pub struct AsyncResultLenWithoutIsEmpty;
impl AsyncResultLenWithoutIsEmpty {
    async fn async_task(&self) -> bool {
        true
    }

    pub async fn len(&self) -> Result<usize, ()> {
        //~^ ERROR: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `i
        Err(())
    }
}

// issue #7232
pub struct AsyncOptionLen;
impl AsyncOptionLen {
    async fn async_task(&self) -> bool {
        true
    }

    pub async fn len(&self) -> Result<usize, ()> {
        Err(())
    }

    pub async fn is_empty(&self) -> bool {
        true
    }
}

pub struct AsyncLenSyncIsEmpty;
impl AsyncLenSyncIsEmpty {
    pub async fn len(&self) -> u32 {
        0
    }

    pub fn is_empty(&self) -> bool {
        true
    }
}

// issue #9520
pub struct NonStandardLen;
impl NonStandardLen {
    // don't lint
    pub fn len(&self, something: usize) -> usize {
        something
    }
}

// issue #9520
pub struct NonStandardLenAndIsEmptySignature;
impl NonStandardLenAndIsEmptySignature {
    // don't lint
    pub fn len(&self, something: usize) -> usize {
        something
    }

    pub fn is_empty(&self, something: usize) -> bool {
        something == 0
    }
}

// test case for #9520 with generics in the function signature
pub trait TestResource {
    type NonStandardSignatureWithGenerics: Copy;
    fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result<Option<&[u8]>, String>;
}
pub struct NonStandardSignatureWithGenerics(u32);
impl NonStandardSignatureWithGenerics {
    pub fn is_empty<T, U>(self, resource: &T) -> bool
    where
        T: TestResource<NonStandardSignatureWithGenerics = U>,
        U: Copy + From<NonStandardSignatureWithGenerics>,
    {
        if let Ok(Some(content)) = resource.lookup_content(self.into()) {
            content.is_empty()
        } else {
            true
        }
    }

    // test case for #9520 with generics in the function signature
    pub fn len<T, U>(self, resource: &T) -> usize
    where
        T: TestResource<NonStandardSignatureWithGenerics = U>,
        U: Copy + From<NonStandardSignatureWithGenerics>,
    {
        if let Ok(Some(content)) = resource.lookup_content(self.into()) {
            content.len()
        } else {
            0_usize
        }
    }
}

pub struct DifferingErrors;
impl DifferingErrors {
    pub fn len(&self) -> Result<usize, u8> {
        Ok(0)
    }

    pub fn is_empty(&self) -> Result<bool, u16> {
        Ok(true)
    }
}

// Issue #11165
pub struct Aliased1;
pub type Alias1 = Aliased1;

impl Alias1 {
    pub fn len(&self) -> usize {
        todo!()
    }

    pub fn is_empty(&self) -> bool {
        todo!()
    }
}

pub struct Aliased2;
pub type Alias2 = Aliased2;
impl Alias2 {
    pub fn len(&self) -> usize {
        //~^ ERROR: type `Alias2` has a public `len` method, but no `is_empty` method
        todo!()
    }
}

fn main() {}
