//@aux-build:proc_macros.rs

#![warn(clippy::uninlined_format_args)]
#![allow(named_arguments_used_positionally, unused)]
#![allow(
    clippy::eq_op,
    clippy::format_in_format_args,
    clippy::print_literal,
    clippy::unnecessary_literal_unwrap
)]

extern crate proc_macros;
use proc_macros::with_span;

macro_rules! no_param_str {
    () => {
        "{}"
    };
}

macro_rules! my_println {
   ($($args:tt),*) => {{
        println!($($args),*)
    }};
}

macro_rules! my_println_args {
    ($($args:tt),*) => {{
        println!("foo: {}", format_args!($($args),*))
    }};
}

fn tester(fn_arg: i32) {
    let local_i32 = 1;
    let local_f64 = 2.0;
    let local_opt: Option<i32> = Some(3);
    let width = 4;
    let prec = 5;
    let val = 6;

    // make sure this file hasn't been corrupted with tabs converted to spaces
    // let _ = '	';  // <- this is a single tab character
    let _: &[u8; 3] = b"	 	"; // <- <tab><space><tab>

    println!("val='{local_i32}'");
    //~^ uninlined_format_args
    println!("val='{local_i32}'"); // 3 spaces
    //
    //~^^ uninlined_format_args
    println!("val='{local_i32}'"); // tab
    //
    //~^^ uninlined_format_args
    println!("val='{local_i32}'"); // space+tab
    //
    //~^^ uninlined_format_args
    println!("val='{local_i32}'"); // tab+space
    //
    //~^^ uninlined_format_args
    println!(
        //~^ uninlined_format_args
        "val='{local_i32}'"
    );
    println!("{local_i32}");
    //~^ uninlined_format_args
    println!("{fn_arg}");
    //~^ uninlined_format_args
    println!("{local_i32:?}");
    //~^ uninlined_format_args
    println!("{local_i32:#?}");
    //~^ uninlined_format_args
    println!("{local_i32:4}");
    //~^ uninlined_format_args
    println!("{local_i32:04}");
    //~^ uninlined_format_args
    println!("{local_i32:<3}");
    //~^ uninlined_format_args
    println!("{local_i32:#010x}");
    //~^ uninlined_format_args
    println!("{local_f64:.1}");
    //~^ uninlined_format_args
    println!("Hello {} is {:.*}", "x", local_i32, local_f64);
    println!("Hello {} is {:.*}", local_i32, 5, local_f64);
    println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
    println!("{local_i32} {local_f64}");
    //~^ uninlined_format_args
    println!("{}, {}", local_i32, local_opt.unwrap());
    println!("{val}");
    //~^ uninlined_format_args
    println!("{val}");
    //~^ uninlined_format_args
    println!("{} {1}", local_i32, 42);
    println!("val='{local_i32}'");
    //~^ uninlined_format_args
    println!("val='{local_i32}'");
    //~^ uninlined_format_args
    println!("val='{local_i32}'");
    //~^ uninlined_format_args
    println!("val='{fn_arg}'");
    //~^ uninlined_format_args
    println!("{local_i32}");
    //~^ uninlined_format_args
    println!("{local_i32:?}");
    //~^ uninlined_format_args
    println!("{local_i32:#?}");
    //~^ uninlined_format_args
    println!("{local_i32:04}");
    //~^ uninlined_format_args
    println!("{local_i32:<3}");
    //~^ uninlined_format_args
    println!("{local_i32:#010x}");
    //~^ uninlined_format_args
    println!("{local_f64:.1}");
    //~^ uninlined_format_args
    println!("{local_i32} {local_i32}");
    //~^ uninlined_format_args
    println!("{local_f64} {local_i32} {local_i32} {local_f64}");
    //~^ uninlined_format_args
    println!("{local_i32} {local_f64}");
    //~^ uninlined_format_args
    println!("{local_f64} {local_i32}");
    //~^ uninlined_format_args
    println!("{local_f64} {local_i32} {local_f64} {local_i32}");
    //~^ uninlined_format_args
    println!("{1} {0}", "str", local_i32);
    println!("{local_i32}");
    //~^ uninlined_format_args
    println!("{local_i32:width$}");
    //~^ uninlined_format_args
    println!("{local_i32:width$}");
    //~^ uninlined_format_args
    println!("{local_i32:.prec$}");
    //~^ uninlined_format_args
    println!("{local_i32:.prec$}");
    //~^ uninlined_format_args
    println!("{val:val$}");
    //~^ uninlined_format_args
    println!("{val:val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{val:val$.val$}");
    //~^ uninlined_format_args
    println!("{width:width$}");
    //~^ uninlined_format_args
    println!("{local_i32:width$}");
    //~^ uninlined_format_args
    println!("{width:width$}");
    //~^ uninlined_format_args
    println!("{local_i32:width$}");
    //~^ uninlined_format_args
    println!("{prec:.prec$}");
    //~^ uninlined_format_args
    println!("{local_i32:.prec$}");
    //~^ uninlined_format_args
    println!("{prec:.prec$}");
    //~^ uninlined_format_args
    println!("{local_i32:.prec$}");
    //~^ uninlined_format_args
    println!("{width:width$.prec$}");
    //~^ uninlined_format_args
    println!("{width:width$.prec$}");
    //~^ uninlined_format_args
    println!("{local_f64:width$.prec$}");
    //~^ uninlined_format_args
    println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
    //~^ uninlined_format_args
    println!(
        //~^ uninlined_format_args
        "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}",
    );
    println!(
        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
        local_i32,
        width,
        prec,
        1 + 2
    );
    println!("Width = {local_i32}, value with width = {local_f64:local_i32$}");
    //~^ uninlined_format_args
    println!("{local_i32:width$.prec$}");
    //~^ uninlined_format_args
    println!("{width:width$.prec$}");
    //~^ uninlined_format_args
    println!("{}", format!("{}", local_i32));
    my_println!("{}", local_i32);
    my_println_args!("{}", local_i32);

    // these should NOT be modified by the lint
    println!(concat!("nope ", "{}"), local_i32);
    println!("val='{local_i32}'");
    println!("val='{local_i32 }'");
    println!("val='{local_i32	}'"); // with tab
    println!("val='{local_i32\n}'");
    println!("{}", usize::MAX);
    println!("{}", local_opt.unwrap());
    println!(
        "val='{local_i32
    }'"
    );
    println!(no_param_str!(), local_i32);

    println!(
        //~^ uninlined_format_args
        "{val}",
    );
    println!("{val}");
    //~^ uninlined_format_args

    println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64);
    println!("{}", with_span!(span val));

    if local_i32 > 0 {
        panic!("p1 {local_i32}");
        //~^ uninlined_format_args
    }
    if local_i32 > 0 {
        panic!("p2 {local_i32}");
        //~^ uninlined_format_args
    }
    if local_i32 > 0 {
        panic!("p3 {local_i32}");
        //~^ uninlined_format_args
    }
    if local_i32 > 0 {
        panic!("p4 {local_i32}");
    }
}

fn main() {}

#[clippy::msrv = "1.57"]
fn _under_msrv() {
    let local_i32 = 1;
    println!("don't expand='{}'", local_i32);
}

#[clippy::msrv = "1.58"]
fn _meets_msrv() {
    let local_i32 = 1;
    println!("expand='{local_i32}'");
    //~^ uninlined_format_args
}

fn _do_not_fire() {
    println!("{:?}", None::<()>);
}

macro_rules! _internal {
    ($($args:tt)*) => {
        println!("{}", format_args!($($args)*))
    };
}

macro_rules! my_println2 {
   ($target:expr, $($args:tt)+) => {{
       if $target {
           _internal!($($args)+)
       }
    }};
}

macro_rules! my_println2_args {
    ($target:expr, $($args:tt)+) => {{
       if $target {
           _internal!("foo: {}", format_args!($($args)+))
       }
    }};
}

macro_rules! my_concat {
    ($fmt:literal $(, $e:expr)*) => {
        println!(concat!("ERROR: ", $fmt), $($e,)*)
    }
}

macro_rules! my_good_macro {
    ($fmt:literal $(, $e:expr)* $(,)?) => {
        println!($fmt $(, $e)*)
    }
}

macro_rules! my_bad_macro {
    ($fmt:literal, $($e:expr),*) => {
        println!($fmt, $($e,)*)
    }
}

macro_rules! my_bad_macro2 {
    ($fmt:literal) => {
        let s = $fmt.clone();
        println!("{}", s);
    };
    ($fmt:literal, $($e:expr)+) => {
        println!($fmt, $($e,)*)
    };
}

// This abomination was suggested by @Alexendoo, may the Rust gods have mercy on their soul...
// https://github.com/rust-lang/rust-clippy/pull/9948#issuecomment-1327965962
macro_rules! used_twice {
    (
        large = $large:literal,
        small = $small:literal,
        $val:expr,
    ) => {
        if $val < 5 {
            println!($small, $val);
        } else {
            println!($large, $val);
        }
    };
}

fn tester2() {
    let local_i32 = 1;
    my_println2_args!(true, "{}", local_i32);
    my_println2!(true, "{}", local_i32);
    my_concat!("{}", local_i32);
    my_good_macro!("{}", local_i32);
    my_good_macro!("{}", local_i32,);
    my_bad_macro!("{}", local_i32);
    my_bad_macro2!("{}", local_i32);
    used_twice! {
        large = "large value: {}",
        small = "small value: {}",
        local_i32,
    };
}

#[clippy::format_args]
macro_rules! usr_println {
    ($target:expr, $($args:tt)*) => {{
        if $target {
            println!($($args)*)
        }
    }};
}

fn user_format() {
    let local_i32 = 1;
    let local_f64 = 2.0;

    usr_println!(true, "val='{local_i32}'");
    //~^ uninlined_format_args
    usr_println!(true, "{local_i32}");
    //~^ uninlined_format_args
    usr_println!(true, "{local_i32:#010x}");
    //~^ uninlined_format_args
    usr_println!(true, "{local_f64:.1}");
    //~^ uninlined_format_args
}

// issue #16411: false negative when #[clippy::format_args] macro uses nested format_args
// without binding the inner args first
#[clippy::format_args]
macro_rules! nested_format_args {
    ($($arg:tt)*) => {{
        // Wraps the user args in another format_args call
        ::core::format_args!("{}\n", ::core::format_args!($($arg)*))
    }};
}

fn nested_format_args_user() {
    let local_i32 = 1;
    let local_f64 = 2.0;

    // false negative: should warn but currently doesn't because the inner format_args
    // is not processed when it's used as an argument to another format_args
    nested_format_args!("{}", local_i32);
    nested_format_args!("val='{}'", local_i32);
    nested_format_args!("{:.1}", local_f64);
}
