#![allow(unused, clippy::manual_async_fn)]
#![warn(clippy::redundant_async_block)]

use std::future::{Future, IntoFuture};

async fn func1(n: usize) -> usize {
    n + 1
}

async fn func2() -> String {
    let s = String::from("some string");
    let f = async { (*s).to_owned() };
    let x = f;
    x.await
}

fn main() {
    let fut1 = async { 17 };
    // Lint
    let fut2 = fut1;

    let fut1 = async { 25 };
    // Lint
    let fut2 = fut1;

    // Lint
    let fut = async { 42 };

    // Do not lint: not a single expression
    let fut = async {
        func1(10).await;
        func2().await
    };

    // Do not lint: expression contains `.await`
    let fut = async { func1(func2().await.len()).await };
}

#[allow(clippy::let_and_return)]
fn capture_local() -> impl Future<Output = i32> {
    let fut = async { 17 };
    // Lint
    fut
}

fn capture_local_closure(s: &str) -> impl Future<Output = &str> {
    let f = move || std::future::ready(s);
    // Do not lint: `f` would not live long enough
    async move { f().await }
}

#[allow(clippy::let_and_return)]
fn capture_arg(s: &str) -> impl Future<Output = &str> {
    let fut = async move { s };
    // Lint
    fut
}

fn capture_future_arg<T>(f: impl Future<Output = T>) -> impl Future<Output = T> {
    // Lint
    f
}

fn capture_func_result<FN, F, T>(f: FN) -> impl Future<Output = T>
where
    F: Future<Output = T>,
    FN: FnOnce() -> F,
{
    // Do not lint, as f() would be evaluated prematurely
    async { f().await }
}

fn double_future(f: impl Future<Output = impl Future<Output = u32>>) -> impl Future<Output = u32> {
    // Do not lint, we will get a `.await` outside a `.async`
    async { f.await.await }
}

fn await_in_async<F, R>(f: F) -> impl Future<Output = u32>
where
    F: FnOnce() -> R,
    R: Future<Output = u32>,
{
    // Lint
    async { f().await + 1 }
}

#[derive(Debug, Clone)]
struct F {}

impl F {
    async fn run(&self) {}
}

pub async fn run() {
    let f = F {};
    let c = f.clone();
    // Do not lint: `c` would not live long enough
    spawn(async move { c.run().await });
    let _f = f;
}

fn spawn<F: Future + 'static>(_: F) {}

async fn work(_: &str) {}

fn capture() {
    let val = "Hello World".to_owned();
    // Do not lint: `val` would not live long enough
    spawn(async { work(&{ val }).await });
}

fn await_from_macro() -> impl Future<Output = u32> {
    macro_rules! mac {
        ($e:expr) => {
            $e.await
        };
    }
    // Do not lint: the macro may change in the future
    // or return different things depending on its argument
    async { mac!(async { 42 }) }
}

fn async_expr_from_macro() -> impl Future<Output = u32> {
    macro_rules! mac {
        () => {
            async { 42 }
        };
    }
    // Do not lint: the macro may change in the future
    async { mac!().await }
}

fn async_expr_from_macro_deep() -> impl Future<Output = u32> {
    macro_rules! mac {
        () => {
            async { 42 }
        };
    }
    // Do not lint: the macro may change in the future
    async { ({ mac!() }).await }
}

fn all_from_macro() -> impl Future<Output = u32> {
    macro_rules! mac {
        () => {
            // Lint
            async { 42 }
        };
    }
    mac!()
}

fn parts_from_macro() -> impl Future<Output = u32> {
    macro_rules! mac {
        ($e: expr) => {
            // Do not lint: `$e` might not always be side-effect free
            async { $e.await }
        };
    }
    mac!(async { 42 })
}

fn safe_parts_from_macro() -> impl Future<Output = u32> {
    macro_rules! mac {
        ($e: expr) => {
            // Lint
            async { $e }
        };
    }
    mac!(42)
}

fn parts_from_macro_deep() -> impl Future<Output = u32> {
    macro_rules! mac {
        ($e: expr) => {
            // Do not lint: `$e` might not always be side-effect free
            async { ($e,).0.await }
        };
    }
    let f = std::future::ready(42);
    mac!(f)
}

fn await_from_macro_deep() -> impl Future<Output = u32> {
    macro_rules! mac {
        ($e:expr) => {{ $e }.await};
    }
    // Do not lint: the macro may change in the future
    // or return different things depending on its argument
    async { mac!(async { 42 }) }
}

// Issue 11959
fn from_into_future(a: impl IntoFuture<Output = u32>) -> impl Future<Output = u32> {
    // Do not lint: `a` is not equivalent to this expression
    async { a.await }
}