//@run-rustfix

#![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
)]

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>) {}

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

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

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

    f_str(&s);
    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.

    let b = Box::new(Box::new(Box::new(5)));
    let _: &Box<i32> = &b;
    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);

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

    fn _f(x: &String) -> &str {
        x
    }

    fn _f1(x: &String) -> &str {
        { x }
    }

    fn _f2(x: &String) -> &str {
        { x }
    }

    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
        x
    }

    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);
        f2(&x);
        f3(&x);
        f4.callable_str()(&x);
        f5(&x);
        f6(&x);
        f7.callable_str()(&x);
        f8.callable_t()(&x);
        f9(&x);
        f10(&x);
        f11.callable_t()(&x);
    }

    struct S1<'a>(&'a str);
    let _ = S1(&s);

    struct S2<'a> {
        s: &'a str,
    }
    let _ = S2 { s: &s };

    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);
            let _ = Self::S2 { s: s };
        }
    }
    let _ = E1::S1(&s);
    let _ = E1::S2 { s: &s };

    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;
    let _ = b.foo;

    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);
    let ref_ref_str = &ref_str;
    let _ = f_str(ref_ref_str);

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

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

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

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

    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
    }

    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);
    let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> {
        if b {
            return x;
        }
        x
    };

    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
    }
}