#![feature(closure_lifetime_binder)]
#![warn(clippy::explicit_auto_deref)]
#![allow(
    dead_code,
    unused_braces,
    clippy::borrowed_box,
    clippy::needless_borrow,
    clippy::needless_return,
    clippy::ptr_arg,
    clippy::redundant_field_names,
    clippy::too_many_arguments,
    clippy::borrow_deref_ref,
    clippy::let_unit_value,
    clippy::needless_lifetimes
)]

trait CallableStr {
    type T: Fn(&str);
    fn callable_str(&self) -> Self::T;
}
impl CallableStr for () {
    type T = fn(&str);
    fn callable_str(&self) -> Self::T {
        fn f(_: &str) {}
        f
    }
}
impl CallableStr for i32 {
    type T = <() as CallableStr>::T;
    fn callable_str(&self) -> Self::T {
        ().callable_str()
    }
}

trait CallableT<U: ?Sized> {
    type T: Fn(&U);
    fn callable_t(&self) -> Self::T;
}
impl<U: ?Sized> CallableT<U> for () {
    type T = fn(&U);
    fn callable_t(&self) -> Self::T {
        fn f<U: ?Sized>(_: &U) {}
        f::<U>
    }
}
impl<U: ?Sized> CallableT<U> for i32 {
    type T = <() as CallableT<U>>::T;
    fn callable_t(&self) -> Self::T {
        ().callable_t()
    }
}

fn f_str(_: &str) {}
fn f_string(_: &String) {}
fn f_t<T>(_: T) {}
fn f_ref_t<T: ?Sized>(_: &T) {}

fn f_str_t<T>(_: &str, _: T) {}

fn f_box_t<T>(_: &Box<T>) {}

unsafe extern "C" {
    fn var(_: u32, ...);
}

fn main() {
    let s = String::new();

    let _: &str = &*s;
    //~^ explicit_auto_deref
    let _: &str = &*{ String::new() };
    //~^ explicit_auto_deref
    let _: &str = &mut *{ String::new() };
    //~^ explicit_auto_deref
    let _ = &*s; // Don't lint. Inferred type would change.
    let _: &_ = &*s; // Don't lint. Inferred type would change.

    f_str(&*s);
    //~^ explicit_auto_deref
    f_t(&*s); // Don't lint. Inferred type would change.
    f_ref_t(&*s); // Don't lint. Inferred type would change.

    f_str_t(&*s, &*s); // Don't lint second param.
    //
    //~^^ explicit_auto_deref

    let b = Box::new(Box::new(Box::new(5)));
    let _: &Box<i32> = &**b;
    //~^ explicit_auto_deref
    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.

    f_box_t(&**b); // Don't lint. Inferred type would change.

    let c = |_x: &str| ();
    c(&*s);
    //~^ explicit_auto_deref

    let c = |_x| ();
    c(&*s); // Don't lint. Inferred type would change.

    fn _f(x: &String) -> &str {
        &**x
        //~^ explicit_auto_deref
    }

    fn _f1(x: &String) -> &str {
        { &**x }
        //~^ explicit_auto_deref
    }

    fn _f2(x: &String) -> &str {
        &**{ x }
        //~^ explicit_auto_deref
    }

    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
        &***x
        //~^ explicit_auto_deref
    }

    fn _f4(
        x: String,
        f1: impl Fn(&str),
        f2: &dyn Fn(&str),
        f3: fn(&str),
        f4: impl CallableStr,
        f5: <() as CallableStr>::T,
        f6: <i32 as CallableStr>::T,
        f7: &dyn CallableStr<T = fn(&str)>,
        f8: impl CallableT<str>,
        f9: <() as CallableT<str>>::T,
        f10: <i32 as CallableT<str>>::T,
        f11: &dyn CallableT<str, T = fn(&str)>,
    ) {
        f1(&*x);
        //~^ explicit_auto_deref
        f2(&*x);
        //~^ explicit_auto_deref
        f3(&*x);
        //~^ explicit_auto_deref
        f4.callable_str()(&*x);
        //~^ explicit_auto_deref
        f5(&*x);
        //~^ explicit_auto_deref
        f6(&*x);
        //~^ explicit_auto_deref
        f7.callable_str()(&*x);
        //~^ explicit_auto_deref
        f8.callable_t()(&*x);
        //~^ explicit_auto_deref
        f9(&*x);
        //~^ explicit_auto_deref
        f10(&*x);
        //~^ explicit_auto_deref
        f11.callable_t()(&*x);
        //~^ explicit_auto_deref
    }

    struct S1<'a>(&'a str);
    let _ = S1(&*s);
    //~^ explicit_auto_deref

    struct S2<'a> {
        s: &'a str,
    }
    let _ = S2 { s: &*s };
    //~^ explicit_auto_deref

    struct S3<'a, T: ?Sized>(&'a T);
    let _ = S3(&*s); // Don't lint. Inferred type would change.

    struct S4<'a, T: ?Sized> {
        s: &'a T,
    }
    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.

    enum E1<'a> {
        S1(&'a str),
        S2 { s: &'a str },
    }
    impl<'a> E1<'a> {
        fn m1(s: &'a String) {
            let _ = Self::S1(&**s);
            //~^ explicit_auto_deref
            let _ = Self::S2 { s: &**s };
            //~^ explicit_auto_deref
        }
    }
    let _ = E1::S1(&*s);
    //~^ explicit_auto_deref
    let _ = E1::S2 { s: &*s };
    //~^ explicit_auto_deref

    enum E2<'a, T: ?Sized> {
        S1(&'a T),
        S2 { s: &'a T },
    }
    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.

    let ref_s = &s;
    let _: &String = &*ref_s; // Don't lint reborrow.
    f_string(&*ref_s); // Don't lint reborrow.

    struct S5 {
        foo: u32,
    }
    let b = Box::new(Box::new(S5 { foo: 5 }));
    let _ = b.foo;
    let _ = (*b).foo;
    //~^ explicit_auto_deref
    let _ = (**b).foo;
    //~^ explicit_auto_deref

    struct S6 {
        foo: S5,
    }
    impl core::ops::Deref for S6 {
        type Target = S5;
        fn deref(&self) -> &Self::Target {
            &self.foo
        }
    }
    let s6 = S6 { foo: S5 { foo: 5 } };
    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`

    let ref_str = &"foo";
    let _ = f_str(*ref_str);
    //~^ explicit_auto_deref
    let ref_ref_str = &ref_str;
    let _ = f_str(**ref_ref_str);
    //~^ explicit_auto_deref

    fn _f5(x: &u32) -> u32 {
        if true {
            *x
        } else {
            return *x;
        }
    }

    f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
    //
    //~^^ explicit_auto_deref
    f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
    //
    //~^^ explicit_auto_deref

    let x = &&40;
    unsafe {
        var(0, &**x);
    }

    let s = &"str";
    let _ = || return *s;
    let _ = || -> &'static str { return *s };
    //~^ explicit_auto_deref

    struct X;
    struct Y(X);
    impl core::ops::Deref for Y {
        type Target = X;
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    let _: &X = &*{ Y(X) };
    let _: &X = &*match 0 {
        #[rustfmt::skip]
        0 => { Y(X) },
        _ => panic!(),
    };
    let _: &X = &*if true { Y(X) } else { panic!() };

    fn deref_to_u<U, T: core::ops::Deref<Target = U>>(x: &T) -> &U {
        &**x
        //~^ explicit_auto_deref
    }

    let _ = |x: &'static Box<dyn Iterator<Item = u32>>| -> &'static dyn Iterator<Item = u32> { &**x };
    fn ret_any(x: &Box<dyn std::any::Any>) -> &dyn std::any::Any {
        &**x
    }

    let x = String::new();
    let _: *const str = &*x;

    struct S7([u32; 1]);
    impl core::ops::Deref for S7 {
        type Target = [u32; 1];
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    let x = S7([0]);
    let _: &[u32] = &*x;

    let c1 = |_: &Vec<&u32>| {};
    let x = &&vec![&1u32];
    c1(*x);
    //~^ explicit_auto_deref
    let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> {
        if b {
            return *x;
            //~^ explicit_auto_deref
        }
        *x
        //~^ explicit_auto_deref
    };

    trait WithAssoc {
        type Assoc: ?Sized;
        fn to_assoc(&self) -> &Self::Assoc {
            panic!()
        }
    }
    impl WithAssoc for String {
        type Assoc = str;
    }
    fn takes_assoc<T: WithAssoc>(_: &T::Assoc) -> T {
        unimplemented!()
    }
    let _: String = takes_assoc(&*String::new());

    // Issue #9901
    fn takes_ref(_: &i32) {}
    takes_ref(*Box::new(&0i32));

    // Issue #10384
    impl<'a> WithAssoc for &'a u32 {
        type Assoc = dyn core::fmt::Display;
        fn to_assoc(&self) -> &Self::Assoc {
            *self
        }
    }
    fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
        *x
    }

    // Issue #11366
    let _: &mut u32 = match &mut Some(&mut 0u32) {
        Some(x) => &mut *x,
        //~^ explicit_auto_deref
        None => panic!(),
    };

    // Issue #11474
    #[derive(Clone, Copy)]
    struct Wrap<T>(T);
    impl<T> core::ops::Deref for Wrap<T> {
        type Target = T;
        fn deref(&self) -> &T {
            &self.0
        }
    }
    impl<T> core::ops::DerefMut for Wrap<T> {
        fn deref_mut(&mut self) -> &mut T {
            &mut self.0
        }
    }

    union U<T: Copy> {
        u: T,
    }

    #[derive(Clone, Copy)]
    struct S8 {
        x: &'static str,
    }

    unsafe {
        let mut x = U {
            u: core::mem::ManuallyDrop::new(S8 { x: "" }),
        };
        let _ = &mut (*x.u).x;
        let _ = &mut (*{ x.u }).x;
        //~^ explicit_auto_deref
        let _ = &mut ({ *x.u }).x;

        let mut x = U {
            u: Wrap(core::mem::ManuallyDrop::new(S8 { x: "" })),
        };
        let _ = &mut (**x.u).x;
        //~^ explicit_auto_deref
        let _ = &mut (**{ x.u }).x;
        //~^ explicit_auto_deref
        let _ = &mut ({ **x.u }).x;

        let mut x = U { u: Wrap(S8 { x: "" }) };
        let _ = &mut (*x.u).x;
        //~^ explicit_auto_deref
        let _ = &mut (*{ x.u }).x;
        //~^ explicit_auto_deref
        let _ = &mut ({ *x.u }).x;
    }
}

mod issue_12969 {
    use std::ops::Deref;

    struct Wrapper<T>(T);

    impl<T> Deref for Wrapper<T> {
        type Target = T;

        fn deref(&self) -> &T {
            &self.0
        }
    }

    fn foo(_bar: &str) {}

    fn bar() {
        let wrapped_bar = Wrapper("");

        foo(&*wrapped_bar);
        //~^ explicit_auto_deref
    }
}

mod issue_9841 {
    fn takes_array_ref<T, const N: usize>(array: &&[T; N]) {
        takes_slice(*array)
    }

    fn takes_array_ref_ref<T, const N: usize>(array: &&&[T; N]) {
        takes_slice(**array)
    }

    fn takes_slice<T>(slice: &[T]) {
        todo!()
    }
}
