#![warn(clippy::manual_pop_if)]
#![allow(clippy::collapsible_if, clippy::redundant_closure)]
#![feature(binary_heap_pop_if)]

use std::collections::{BinaryHeap, VecDeque};
use std::marker::PhantomData;

fn main() {}

// FakeVec has the same methods as Vec but isn't actually a Vec
struct FakeVec<T>(PhantomData<T>);

impl<T> FakeVec<T> {
    fn last(&self) -> Option<&T> {
        None
    }

    fn pop(&mut self) -> Option<T> {
        None
    }
}

fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_back_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_front_if(|x| *x > 2);

    //~v manual_pop_if
    heap.pop_if(|x| *x > 2);
}

fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
    // Do not lint, different vectors
    let mut vec2 = vec![0];
    if vec.last().is_some_and(|x| *x > 2) {
        vec2.pop().unwrap();
    }

    // Do not lint, non-Vec type
    let mut fake_vec: FakeVec<i32> = FakeVec(PhantomData);
    if fake_vec.last().is_some_and(|x| *x > 2) {
        fake_vec.pop().unwrap();
    }

    // Do not lint, else block
    let _result = if vec.last().is_some_and(|x| *x > 2) {
        vec.pop().unwrap()
    } else {
        0
    };
}

fn if_let_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_back_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_front_if(|x| *x > 2);

    //~v manual_pop_if
    heap.pop_if(|x| *x > 2);
}

fn if_let_pattern_negative(mut vec: Vec<i32>) {
    // Do not lint, different vectors
    let mut vec2 = vec![0];
    if let Some(x) = vec.last() {
        if *x > 2 {
            vec2.pop().unwrap();
        }
    }

    // Do not lint, intervening statements
    if let Some(x) = vec.last() {
        println!("Checking {}", x);
        if *x > 2 {
            vec.pop().unwrap();
        }
    }

    // Do not lint, bound variable not used in condition
    if let Some(_x) = vec.last() {
        if vec.len() > 2 {
            vec.pop().unwrap();
        }
    }

    // Do not lint, else block
    let _result = if let Some(x) = vec.last() {
        if *x > 2 { vec.pop().unwrap() } else { 0 }
    } else {
        0
    };
}

fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
    vec.pop_if(|x| *x > 2);

    vec.pop_if(|x| *x > 2);

    vec.pop_if(|x| *x > 2);

    deque.pop_back_if(|x| *x > 2);

    deque.pop_front_if(|x| *x > 2);

    heap.pop_if(|x| *x > 2);
}

fn let_chain_pattern_negative(mut vec: Vec<i32>) {
    // Do not lint, different vectors
    let mut vec2 = vec![0];
    if let Some(x) = vec.last()
        && *x > 2
    {
        vec2.pop().unwrap();
    }

    // Do not lint, bound variable not used in condition
    if let Some(_x) = vec.last()
        && vec.len() > 2
    {
        vec.pop().unwrap();
    }

    // Do not lint, else block
    let _result = if let Some(x) = vec.last()
        && *x > 2
    {
        vec.pop().unwrap()
    } else {
        0
    };
}

fn map_unwrap_or_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_back_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_front_if(|x| *x > 2);

    //~v manual_pop_if
    heap.pop_if(|x| *x > 2);
}

fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
    // Do not lint, unwrap_or(true) instead of false
    if vec.last().map(|x| *x > 2).unwrap_or(true) {
        vec.pop().unwrap();
    }

    // Do not lint, different vectors
    let mut vec2 = vec![0];
    if vec.last().map(|x| *x > 2).unwrap_or(false) {
        vec2.pop().unwrap();
    }

    // Do not lint, non-Vec type
    let mut fake_vec: FakeVec<i32> = FakeVec(PhantomData);
    if fake_vec.last().map(|x| *x > 2).unwrap_or(false) {
        fake_vec.pop().unwrap();
    }

    // Do not lint, map returns non-boolean
    if vec.last().map(|x| x + 1).unwrap_or(0) > 2 {
        vec.pop().unwrap();
    }

    // Do not lint, else block
    let _result = if vec.last().map(|x| *x > 2).unwrap_or(false) {
        vec.pop().unwrap()
    } else {
        0
    };
}

// this makes sure we do not expand vec![] in the suggestion
fn handle_macro_in_closure(mut vec: Vec<Vec<i32>>) {
    //~v manual_pop_if
    vec.pop_if(|e| *e == vec![1]);
}

#[clippy::msrv = "1.85.0"]
fn msrv_too_low_vec(mut vec: Vec<i32>) {
    if vec.last().is_some_and(|x| *x > 2) {
        vec.pop().unwrap();
    }
}

#[clippy::msrv = "1.92.0"]
fn msrv_too_low_vecdeque(mut deque: VecDeque<i32>) {
    if deque.back().is_some_and(|x| *x > 2) {
        deque.pop_back().unwrap();
    }

    if deque.front().is_some_and(|x| *x > 2) {
        deque.pop_front().unwrap();
    }
}

#[clippy::msrv = "1.86.0"]
fn msrv_high_enough_vec(mut vec: Vec<i32>) {
    //~v manual_pop_if
    vec.pop_if(|x| *x > 2);
}

#[clippy::msrv = "1.93.0"]
fn msrv_high_enough_vecdeque(mut deque: VecDeque<i32>) {
    //~v manual_pop_if
    deque.pop_back_if(|x| *x > 2);

    //~v manual_pop_if
    deque.pop_front_if(|x| *x > 2);
}
