//@aux-build:proc_macros.rs
#![allow(unused, clippy::no_effect, clippy::needless_pass_by_ref_mut)]
#![warn(clippy::redundant_locals)]
#![feature(async_closure, coroutines, stmt_expr_attributes)]

extern crate proc_macros;
use proc_macros::{external, with_span};

fn main() {}

fn immutable() {
    let x = 1;
    let x = x;
}

fn mutable() {
    let mut x = 1;
    let mut x = x;
}

fn upgraded_mutability() {
    let x = 1;
    let mut x = x;
}

fn downgraded_mutability() {
    let mut x = 1;
    let x = x;
}

// see #11290
fn shadow_mutation() {
    let mut x = 1;
    {
        let mut x = x;
        x = 2;
    }
}

fn coercion(par: &mut i32) {
    let par: &i32 = par;

    let x: &mut i32 = &mut 1;
    let x: &i32 = x;
}

fn parameter(x: i32) {
    let x = x;
}

fn many() {
    let x = 1;
    let x = x;
    let x = x;
    let x = x;
    let x = x;
}

fn interleaved() {
    let a = 1;
    let b = 2;
    let a = a;
    let b = b;
}

fn block() {
    {
        let x = 1;
        let x = x;
    }
}

fn closure() {
    || {
        let x = 1;
        let x = x;
    };
    |x: i32| {
        let x = x;
    };
}

fn consequential_drop_order() {
    use std::sync::Mutex;

    let mutex = Mutex::new(1);
    let guard = mutex.lock().unwrap();

    {
        let guard = guard;
    }
}

fn inconsequential_drop_order() {
    let x = 1;

    {
        let x = x;
    }
}

fn macros() {
    macro_rules! rebind {
        ($x:ident) => {
            let $x = 1;
            let $x = $x;
        };
    }

    rebind!(x);

    external! {
        let x = 1;
        let x = x;
    }
    with_span! {
        span
        let x = 1;
        let x = x;
    }

    let x = 10;
    macro_rules! rebind_outer_macro {
        ($x:ident) => {
            let x = x;
        };
    }
    rebind_outer_macro!(y);
}

struct WithDrop(usize);
impl Drop for WithDrop {
    fn drop(&mut self) {}
}

struct InnerDrop(WithDrop);

struct ComposeDrop {
    d: WithDrop,
}

struct WithoutDrop(usize);

fn drop_trait() {
    let a = WithDrop(1);
    let b = WithDrop(2);
    let a = a;
}

fn without_drop() {
    let a = WithoutDrop(1);
    let b = WithoutDrop(2);
    let a = a;
}

fn drop_inner() {
    let a = InnerDrop(WithDrop(1));
    let b = InnerDrop(WithDrop(2));
    let a = a;
}

fn drop_compose() {
    let a = ComposeDrop { d: WithDrop(1) };
    let b = ComposeDrop { d: WithDrop(1) };
    let a = a;
}

fn issue12225() {
    fn assert_static<T: 'static>(_: T) {}

    let v1 = String::new();
    let v2 = String::new();
    let v3 = String::new();
    let v4 = String::new();
    let v5 = String::new();
    let v6 = String::new();

    assert_static(|| {
        let v1 = v1;
        dbg!(&v1);
    });
    assert_static(async {
        let v2 = v2;
        dbg!(&v2);
    });
    assert_static(|| async {
        let v3 = v3;
        dbg!(&v3);
    });
    assert_static(async || {
        let v4 = v4;
        dbg!(&v4);
    });
    assert_static(
        #[coroutine]
        static || {
            let v5 = v5;
            yield;
        },
    );
    assert_static(
        #[coroutine]
        || {
            let v6 = v6;
            yield;
        },
    );

    fn foo(a: &str, b: &str) {}

    let do_not_move = String::new();
    let things_to_move = vec!["a".to_string(), "b".to_string()];
    let futures = things_to_move.into_iter().map(|move_me| async {
        let move_me = move_me;
        foo(&do_not_move, &move_me)
    });
}
