//@aux-build:proc_macros.rs
#![warn(clippy::explicit_deref_methods)]
#![allow(unused_variables, unused_must_use)]
#![allow(
    clippy::borrow_deref_ref,
    clippy::deref_addrof,
    clippy::explicit_auto_deref,
    clippy::needless_borrow,
    clippy::no_effect,
    clippy::useless_borrows_in_formatting,
    clippy::uninlined_format_args,
    clippy::unnecessary_literal_unwrap,
    noop_method_call,
    suspicious_double_ref_op
)]

use std::ops::{Deref, DerefMut};

extern crate proc_macros;

fn concat(deref_str: &str) -> String {
    format!("{}bar", deref_str)
}

fn just_return(deref_str: &str) -> &str {
    deref_str
}

struct CustomVec(Vec<u8>);
impl Deref for CustomVec {
    type Target = Vec<u8>;

    fn deref(&self) -> &Vec<u8> {
        &self.0
    }
}

struct Aaa;

impl Deref for Aaa {
    type Target = ();

    fn deref(&self) -> &Self::Target {
        todo!();
    }
}

impl DerefMut for Aaa {
    fn deref_mut(&mut self) -> &mut Self::Target {
        todo!();
    }
}

fn main() {
    let a: &mut String = &mut String::from("foo");

    // these should require linting

    let b: &str = &*a;
    //~^ explicit_deref_methods

    let b: &mut str = &mut **a;
    //~^ explicit_deref_methods

    // both derefs should get linted here
    let b: String = format!("{}, {}", &*a, &*a);
    //~^ explicit_deref_methods
    //~| explicit_deref_methods

    println!("{}", &*a);
    //~^ explicit_deref_methods

    #[allow(clippy::match_single_binding)]
    match &*a {
        //~^ explicit_deref_methods
        _ => (),
    }

    let b: String = concat(&*a);
    //~^ explicit_deref_methods

    let b = just_return(a);
    //~^ explicit_deref_methods

    let b: String = concat(just_return(a));
    //~^ explicit_deref_methods

    let b: &str = a.deref().deref();

    let opt_a = Some(a.clone());
    let b = opt_a.unwrap().deref();

    Aaa::deref(&Aaa);
    Aaa::deref_mut(&mut Aaa);
    <Aaa as Deref>::deref(&Aaa);
    <Aaa as DerefMut>::deref_mut(&mut Aaa);
    let mut aaa = Aaa;
    Aaa::deref(&aaa);
    Aaa::deref_mut(&mut aaa);

    // following should not require linting

    let cv = CustomVec(vec![0, 42]);
    let c = cv.deref()[0];

    let b: &str = &*a.deref();

    let b: String = a.deref().clone();

    let b: usize = a.deref_mut().len();

    let b: &usize = &a.deref().len();

    let b: &str = &*a;

    let b: &mut str = &mut *a;

    macro_rules! expr_deref {
        ($body:expr) => {
            $body.deref()
        };
    }
    let b: &str = expr_deref!(a);

    let b: &str = expr_deref!(&*a);
    //~^ explicit_deref_methods

    proc_macros::external! {
        let a: &mut String = &mut String::from("foo");
        let b: &str = a.deref();
    }

    // Issue #15168
    proc_macros::with_span! {
        span
        let a: &mut String = &mut String::from("foo");
        let b: &str = a.deref();
    }

    // The struct does not implement Deref trait
    #[derive(Copy, Clone)]
    struct NoLint(u32);
    impl NoLint {
        pub fn deref(self) -> u32 {
            self.0
        }
        pub fn deref_mut(self) -> u32 {
            self.0
        }
    }
    let no_lint = NoLint(42);
    let b = no_lint.deref();
    let b = no_lint.deref_mut();

    let _ = &*&"foo"; //~ explicit_deref_methods
    let mut x = String::new();
    let _ = &&mut **&mut x; //~ explicit_deref_methods
    let _ = &&mut ***(&mut &mut x); //~ explicit_deref_methods
}

mod issue_15392 {
    use std::ops::{Deref, DerefMut};

    struct Wrapper(String);

    impl Deref for Wrapper {
        type Target = str;
        fn deref(&self) -> &Self::Target {
            // forwarding is ok
            let res = Deref::deref(&self.0);
            // we let `deref_mut` pass as well
            let _ = DerefMut::deref_mut(&mut String::new());
            res
        }
    }

    impl DerefMut for Wrapper {
        fn deref_mut(&mut self) -> &mut Self::Target {
            // forwarding is ok
            let res = DerefMut::deref_mut(&mut self.0);
            // we let `deref` pass as well
            let _ = Deref::deref(&String::new());
            res
        }
    }

    struct A(String);
    struct AA(String);
    struct AB(String);

    impl Deref for A {
        type Target = str;

        fn deref(&self) -> &Self::Target {
            // in a top-level `Deref` impl, ok
            let _ = self.0.deref();
            // in a top-level `Deref` impl, acceptable
            let _ = String::new().deref_mut();

            #[allow(non_local_definitions)]
            impl Deref for AA {
                type Target = str;
                fn deref(&self) -> &Self::Target {
                    // in a nested `Deref` impl, acceptable
                    let _ = String::new().deref_mut();
                    // in a nested `Deref` impl, ok
                    self.0.deref()
                }
            }

            // still in a top-level `Deref` impl, ok
            let _ = self.0.deref();
            // still in a top-level `Deref` impl, acceptable
            let _ = String::new().deref_mut();

            #[allow(non_local_definitions)]
            impl DerefMut for AA {
                fn deref_mut(&mut self) -> &mut Self::Target {
                    // in a top-level `DerefMut` impl, acceptable
                    let _ = self.0.deref();
                    // in a top-level `DerefMut` impl, ok
                    self.0.deref_mut()
                }
            }

            // still in a top-level `Deref` impl, acceptable
            let _ = String::new().deref_mut();
            // still in a top-level `Deref` impl, ok
            self.0.deref()
        }
    }

    impl DerefMut for A {
        fn deref_mut(&mut self) -> &mut Self::Target {
            // in a top-level `DerefMut` impl, acceptable
            let _ = self.0.deref();
            // in a top-level `DerefMut` impl, ok
            let _ = self.0.deref_mut();

            #[allow(non_local_definitions)]
            impl Deref for AB {
                type Target = str;
                fn deref(&self) -> &Self::Target {
                    // in a nested `Deref` impl, acceptable
                    let _ = String::new().deref_mut();
                    // in a nested `Deref` impl, ok
                    Deref::deref(&self.0)
                }
            }

            // still in a top-level `DerefMut` impl, acceptable
            let _ = self.0.deref();
            // still in a top-level `DerefMut` impl, ok
            let _ = self.0.deref_mut();

            #[allow(non_local_definitions)]
            impl DerefMut for AB {
                fn deref_mut(&mut self) -> &mut Self::Target {
                    // in a nested `DerefMut` impl, acceptable
                    self.0.deref();
                    // in a nested `DerefMut` impl, ok
                    self.0.deref_mut()
                }
            }

            // still in a top-level `DerefMut` impl, acceptable
            let _ = self.0.deref();
            // still in a top-level `DerefMut` impl, ok
            self.0.deref_mut()
        }
    }
}
