#![warn(clippy::iter_filter_is_ok)]
#![allow(
    clippy::map_identity,
    clippy::result_filter_map,
    clippy::needless_borrow,
    clippy::redundant_closure
)]

fn main() {
    {
        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok);
        //~^ HELP: consider using `flatten` instead
        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok());
        //~^ HELP: consider using `flatten` instead
        #[rustfmt::skip]
        let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() });
        //~^ HELP: consider using `flatten` instead
    }

    {
        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok());
        //~^ HELP: consider using `flatten` instead

        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok());
        //~^ HELP: consider using `flatten` instead

        #[rustfmt::skip]
        let _ = vec![Ok(1), Err(2)].into_iter().filter(|&o| { o.is_ok() });
        //~^ HELP: consider using `flatten` instead
    }

    {
        let _ = vec![Ok(1), Err(2), Ok(3)]
            .into_iter()
            .filter(std::result::Result::is_ok);
        //~^ HELP: consider using `flatten` instead

        let _ = vec![Ok(1), Err(2), Ok(3)]
            .into_iter()
            .filter(|a| std::result::Result::is_ok(a));
        //~^ HELP: consider using `flatten` instead
        #[rustfmt::skip]
        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| { std::result::Result::is_ok(a) });
        //~^ HELP: consider using `flatten` instead
    }

    {
        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok());
        //~^ HELP: consider using `flatten` instead

        let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok());
        //~^ HELP: consider using `flatten` instead

        #[rustfmt::skip]
        let _ = vec![Ok(1), Err(2)].into_iter().filter(|ref o| { o.is_ok() });
        //~^ HELP: consider using `flatten` instead
    }
}

fn avoid_linting_when_filter_has_side_effects() {
    // Don't lint below
    let mut counter = 0;
    let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
        counter += 1;
        o.is_ok()
    });
}

fn avoid_linting_when_commented() {
    let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
        // Roses are red,
        // Violets are blue,
        // `Err` is not an `Option`,
        // and this doesn't ryme
        o.is_ok()
    });
}

fn ice_12058() {
    // check that checking the parent node doesn't cause an ICE
    // by indexing the parameters of a closure without parameters
    Some(1).or_else(|| {
        vec![Ok(1), Err(())].into_iter().filter(|z| *z != Ok(2));
        None
    });
}

fn avoid_linting_map() {
    // should not lint
    let _ = vec![Ok(1), Err(())]
        .into_iter()
        .filter(|o| o.is_ok())
        .map(|o| o.unwrap());

    // should not lint
    let _ = vec![Ok(1), Err(())].into_iter().filter(|o| o.is_ok()).map(|o| o);
}

fn avoid_false_positive_due_to_is_ok_and_iterator_impl() {
    #[derive(Default, Clone)]
    struct Foo {}

    impl Foo {
        fn is_ok(&self) -> bool {
            true
        }
    }

    impl Iterator for Foo {
        type Item = Foo;
        fn next(&mut self) -> Option<Self::Item> {
            Some(Foo::default())
        }
    }

    let data = vec![Foo::default()];
    // should not lint
    let _ = data.clone().into_iter().filter(Foo::is_ok);
    // should not lint
    let _ = data.clone().into_iter().filter(|f| f.is_ok());
}

fn avoid_false_positive_due_to_is_ok_and_into_iterator_impl() {
    #[derive(Default, Clone)]
    struct Foo {}

    impl Foo {
        fn is_ok(&self) -> bool {
            true
        }
    }

    let data = vec![Foo::default()];
    // should not lint
    let _ = data.clone().into_iter().filter(Foo::is_ok);
    // should not lint
    let _ = data.clone().into_iter().filter(|f| f.is_ok());
}

fn avoid_fp_for_trivial() {
    let _ = vec![Ok(1), Err(()), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| (Err(()) as Result<i32, ()>).is_ok());
}

fn avoid_false_positive_due_to_method_name() {
    fn is_ok(x: &Result<i32, i32>) -> bool {
        x.is_ok()
    }

    vec![Ok(1), Err(2), Ok(3)].into_iter().filter(is_ok);
    // should not lint
}

fn avoid_fp_due_to_trait_type() {
    struct Foo {
        bar: i32,
    }
    impl Foo {
        fn is_ok(obj: &Result<i32, i32>) -> bool {
            obj.is_ok()
        }
    }
    vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Foo::is_ok);
    // should not lint
}

fn avoid_fp_with_call_to_outside_var() {
    let outside: Result<i32, ()> = Ok(1);

    let _ = vec![Ok(1), Err(2), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| outside.is_ok());

    let _ = vec![Ok(1), Err(2), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| Result::is_ok(&outside));

    let _ = vec![Ok(1), Err(2), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| std::result::Result::is_ok(&outside));
}

fn avoid_fp_with_call_to_outside_var_mix_match_types() {
    let outside = Some(1);

    let _ = vec![Ok(1), Err(2), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| outside.is_some());

    let _ = vec![Ok(1), Err(2), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| Option::is_some(&outside));

    let _ = vec![Ok(1), Err(2), Ok(3)]
        .into_iter()
        // should not lint
        .filter(|o| std::option::Option::is_some(&outside));
}
