//@aux-build:option_helpers.rs
#![warn(clippy::manual_is_variant_and)]

#[macro_use]
extern crate option_helpers;

struct Foo<T>(T);

impl<T> Foo<T> {
    fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> {
        Some(f(self.0))
    }
}

fn foo() -> Option<bool> {
    Some(true)
}

macro_rules! some_true {
    () => {
        Some(true)
    };
}
macro_rules! some_false {
    () => {
        Some(false)
    };
}

macro_rules! mac {
    (some $e:expr) => {
        Some($e)
    };
    (some_map $e:expr) => {
        Some($e).map(|x| x % 2 == 0)
    };
    (map $e:expr) => {
        $e.map(|x| x % 2 == 0)
    };
    (eq $a:expr, $b:expr) => {
        $a == $b
    };
}

#[rustfmt::skip]
fn option_methods() {
    let opt = Some(1);

    // Check for `option.map(_).unwrap_or_default()` use.
    // Single line case.
    let _ = opt.is_some_and(|x| x > 1);
    // Multi-line cases.
    let _ = opt.is_some_and(|x| {
    //~^ manual_is_variant_and
        x > 1
    });
    let _ = opt.is_some_and(|x| x > 1);
    //~^ manual_is_variant_and
    let _ = opt
        .is_some_and(|x| x > 1);

    let _ = Some(2).is_some_and(|x| x % 2 == 0);
    //~^ manual_is_variant_and
    let _ = Some(2).is_none_or(|x| x % 2 != 0);
    //~^ manual_is_variant_and
    let _ = Some(2).is_some_and(|x| x % 2 == 0);
    //~^ manual_is_variant_and
    let _ = Some(2).is_none_or(|x| x % 2 == 0);
    //~^ manual_is_variant_and

    // won't fix because the return type of the closure is not `bool`
    let _ = opt.map(|x| x + 1).unwrap_or_default();

    let opt2 = Some('a');
    let _ = opt2.is_some_and(char::is_alphanumeric); // should lint
    //~^ manual_is_variant_and
    let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint

    // Should not lint.
    let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
    let _ = Some(2).map(|x| x % 2 == 0) != foo();
    let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
    let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
    let _ = mac!(some_map 2) == Some(true);
    let _ = mac!(map Some(2)) == Some(true);
}

#[rustfmt::skip]
fn result_methods() {
    let res: Result<i32, ()> = Ok(1);

    // multi line cases
    let _ = res.is_ok_and(|x| {
    //~^ manual_is_variant_and
        x > 1
    });
    let _ = res.is_ok_and(|x| x > 1);

    let _ = Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
    //~^ manual_is_variant_and
    let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
    //~^ manual_is_variant_and
    let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
    //~^ manual_is_variant_and

    // won't fix because the return type of the closure is not `bool`
    let _ = res.map(|x| x + 1).unwrap_or_default();

    let res2: Result<char, ()> = Ok('a');
    let _ = res2.is_ok_and(char::is_alphanumeric); // should lint
    //~^ manual_is_variant_and
    let _ = opt_map!(res2, |x| x == 'a').unwrap_or_default(); // should not lint
}

fn main() {
    option_methods();
    result_methods();
}

fn issue15202() {
    let xs = [None, Some(b'_'), Some(b'1')];
    for x in xs {
        let a1 = x.is_none_or(|b| !b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = x.is_none_or(|b| !b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    for x in xs {
        let a1 = x.is_none_or(|b| b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = x.is_none_or(|b| b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    for x in xs {
        let a1 = x.is_some_and(|b| b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = x.is_some_and(|b| b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    for x in xs {
        let a1 = x.is_some_and(|b| !b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = x.is_some_and(|b| !b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    let xs = [Err("foo"), Ok(b'_'), Ok(b'1')];
    for x in xs {
        let a1 = !x.is_ok_and(|b| b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = !x.is_ok_and(|b| b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    for x in xs {
        let a1 = !x.is_ok_and(|b| !b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = !x.is_ok_and(|b| !b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    for x in xs {
        let a1 = x.is_ok_and(|b| b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = x.is_ok_and(|b| b.is_ascii_digit());
        assert_eq!(a1, a2);
    }

    for x in xs {
        let a1 = x.is_ok_and(|b| !b.is_ascii_digit());
        //~^ manual_is_variant_and
        let a2 = x.is_ok_and(|b| !b.is_ascii_digit());
        assert_eq!(a1, a2);
    }
}

mod with_func {
    fn iad(b: u8) -> bool {
        b.is_ascii_digit()
    }

    fn check_option(b: Option<u8>) {
        let a1 = b.is_some_and(iad);
        //~^ manual_is_variant_and
        let a2 = b.is_some_and(iad);
        assert_eq!(a1, a2);

        let a1 = b.is_some_and(|x| !iad(x));
        //~^ manual_is_variant_and
        let a2 = b.is_some_and(|x| !iad(x));
        assert_eq!(a1, a2);

        let a1 = b.is_none_or(|x| !iad(x));
        //~^ manual_is_variant_and
        let a2 = b.is_none_or(|x| !iad(x));
        assert_eq!(a1, a2);

        let a1 = b.is_none_or(iad);
        //~^ manual_is_variant_and
        let a2 = b.is_none_or(iad);
        assert_eq!(a1, a2);
    }

    fn check_result(b: Result<u8, ()>) {
        let a1 = b.is_ok_and(iad);
        //~^ manual_is_variant_and
        let a2 = b.is_ok_and(iad);
        assert_eq!(a1, a2);

        let a1 = b.is_ok_and(|x| !iad(x));
        //~^ manual_is_variant_and
        let a2 = b.is_ok_and(|x| !iad(x));
        assert_eq!(a1, a2);

        let a1 = !b.is_ok_and(iad);
        //~^ manual_is_variant_and
        let a2 = !b.is_ok_and(iad);
        assert_eq!(a1, a2);

        let a1 = !b.is_ok_and(|x| !iad(x));
        //~^ manual_is_variant_and
        let a2 = !b.is_ok_and(|x| !iad(x));
        assert_eq!(a1, a2);
    }
}
