Support user format-like macros

Add support for `#[clippy::format_args]` attribute that can be attached to any macro to indicate that it functions the same as the built-in format macros like `format!`, `println!` and `write!`
This commit is contained in:
Yuri Astrakhan 2023-03-28 17:04:30 -04:00
parent 627363e811
commit 81dceed8ba
13 changed files with 406 additions and 9 deletions

View file

@ -119,3 +119,32 @@ fn test2() {
format!("something failed at {}", Location::caller())
);
}
#[clippy::format_args]
macro_rules! usr_println {
($target:expr, $($args:tt)*) => {{
if $target {
println!($($args)*)
}
}};
}
fn user_format() {
let error = Error::new(ErrorKind::Other, "bad thing");
let x = 'x';
usr_println!(true, "error: {}", format!("boom at {}", Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
usr_println!(true, "{}: {}", error, format!("boom at {}", Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
usr_println!(true, "{:?}: {}", error, format!("boom at {}", Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
usr_println!(true, "{{}}: {}", format!("boom at {}", Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
usr_println!(true, r#"error: "{}""#, format!("boom at {}", Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
usr_println!(true, "error: {}", format!(r#"boom at "{}""#, Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
usr_println!(true, "error: {}", format!("boom at {} {0}", Location::caller()));
//~^ ERROR: `format!` in `usr_println!` args
}

View file

@ -174,5 +174,68 @@ LL | panic!("error: {}", format!("something failed at {}", Location::caller(
= help: combine the `format!(..)` arguments with the outer `panic!(..)` call
= help: or consider changing `format!` to `format_args!`
error: aborting due to 18 previous errors
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:136:5
|
LL | usr_println!(true, "error: {}", format!("boom at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:138:5
|
LL | usr_println!(true, "{}: {}", error, format!("boom at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:140:5
|
LL | usr_println!(true, "{:?}: {}", error, format!("boom at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:142:5
|
LL | usr_println!(true, "{{}}: {}", format!("boom at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:144:5
|
LL | usr_println!(true, r#"error: "{}""#, format!("boom at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:146:5
|
LL | usr_println!(true, "error: {}", format!(r#"boom at "{}""#, Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `usr_println!` args
--> tests/ui/format_args_unfixable.rs:148:5
|
LL | usr_println!(true, "error: {}", format!("boom at {} {0}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
= help: or consider changing `format!` to `format_args!`
error: aborting due to 25 previous errors

View file

@ -257,8 +257,6 @@ fn tester2() {
my_concat!("{}", local_i32);
my_good_macro!("{}", local_i32);
my_good_macro!("{}", local_i32,);
// FIXME: Broken false positives, currently unhandled
my_bad_macro!("{}", local_i32);
my_bad_macro2!("{}", local_i32);
used_twice! {
@ -267,3 +265,22 @@ fn tester2() {
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}'");
usr_println!(true, "{local_i32}");
usr_println!(true, "{local_i32:#010x}");
usr_println!(true, "{local_f64:.1}");
}

View file

@ -262,8 +262,6 @@ fn tester2() {
my_concat!("{}", local_i32);
my_good_macro!("{}", local_i32);
my_good_macro!("{}", local_i32,);
// FIXME: Broken false positives, currently unhandled
my_bad_macro!("{}", local_i32);
my_bad_macro2!("{}", local_i32);
used_twice! {
@ -272,3 +270,22 @@ fn tester2() {
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);
usr_println!(true, "{}", local_i32);
usr_println!(true, "{:#010x}", local_i32);
usr_println!(true, "{:.1}", local_f64);
}

View file

@ -845,5 +845,53 @@ LL - println!("expand='{}'", local_i32);
LL + println!("expand='{local_i32}'");
|
error: aborting due to 71 previous errors
error: variables can be used directly in the `format!` string
--> tests/ui/uninlined_format_args.rs:287:5
|
LL | usr_println!(true, "val='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: change this to
|
LL - usr_println!(true, "val='{}'", local_i32);
LL + usr_println!(true, "val='{local_i32}'");
|
error: variables can be used directly in the `format!` string
--> tests/ui/uninlined_format_args.rs:288:5
|
LL | usr_println!(true, "{}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: change this to
|
LL - usr_println!(true, "{}", local_i32);
LL + usr_println!(true, "{local_i32}");
|
error: variables can be used directly in the `format!` string
--> tests/ui/uninlined_format_args.rs:289:5
|
LL | usr_println!(true, "{:#010x}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: change this to
|
LL - usr_println!(true, "{:#010x}", local_i32);
LL + usr_println!(true, "{local_i32:#010x}");
|
error: variables can be used directly in the `format!` string
--> tests/ui/uninlined_format_args.rs:290:5
|
LL | usr_println!(true, "{:.1}", local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: change this to
|
LL - usr_println!(true, "{:.1}", local_f64);
LL + usr_println!(true, "{local_f64:.1}");
|
error: aborting due to 75 previous errors

View file

@ -33,3 +33,38 @@ fn should_not_lint() {
let args = format_args!("");
println!("{args}");
}
#[clippy::format_args]
macro_rules! usr_println {
($target:expr, $($args:tt)*) => {{
if $target {
println!($($args)*)
}
}};
}
fn should_lint_user() {
// prints `.`, not ` .`
usr_println!(true, "{:5}.", format!(""));
//~^ ERROR: format specifiers have no effect on `format_args!()`
//prints `abcde`, not `abc`
usr_println!(true, "{:.3}", format!("abcde"));
//~^ ERROR: format specifiers have no effect on `format_args!()`
usr_println!(true, "{}.", format_args_from_macro!());
//~^ ERROR: format specifiers have no effect on `format_args!()`
let args = format_args!("");
usr_println!(true, "{args}");
//~^ ERROR: format specifiers have no effect on `format_args!()`
}
fn should_not_lint_user() {
usr_println!(true, "{}", format_args!(""));
// Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
// debug formatting so allow it
usr_println!(true, "{:?}", format_args!(""));
let args = format_args!("");
usr_println!(true, "{args}");
}

View file

@ -33,3 +33,38 @@ fn should_not_lint() {
let args = format_args!("");
println!("{args}");
}
#[clippy::format_args]
macro_rules! usr_println {
($target:expr, $($args:tt)*) => {{
if $target {
println!($($args)*)
}
}};
}
fn should_lint_user() {
// prints `.`, not ` .`
usr_println!(true, "{}.", format_args!(""));
//~^ ERROR: format specifiers have no effect on `format_args!()`
//prints `abcde`, not `abc`
usr_println!(true, "{}", format_args!("abcde"));
//~^ ERROR: format specifiers have no effect on `format_args!()`
usr_println!(true, "{}.", format_args_from_macro!());
//~^ ERROR: format specifiers have no effect on `format_args!()`
let args = format_args!("");
usr_println!(true, "{args}");
//~^ ERROR: format specifiers have no effect on `format_args!()`
}
fn should_not_lint_user() {
usr_println!(true, "{}", format_args!(""));
// Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
// debug formatting so allow it
usr_println!(true, "{:?}", format_args!(""));
let args = format_args!("");
usr_println!(true, "{args}");
}

View file

@ -33,3 +33,38 @@ fn should_not_lint() {
let args = format_args!("");
println!("{args}");
}
#[clippy::format_args]
macro_rules! usr_println {
($target:expr, $($args:tt)*) => {{
if $target {
println!($($args)*)
}
}};
}
fn should_lint_user() {
// prints `.`, not ` .`
usr_println!(true, "{:5}.", format_args!(""));
//~^ ERROR: format specifiers have no effect on `format_args!()`
//prints `abcde`, not `abc`
usr_println!(true, "{:.3}", format_args!("abcde"));
//~^ ERROR: format specifiers have no effect on `format_args!()`
usr_println!(true, "{:5}.", format_args_from_macro!());
//~^ ERROR: format specifiers have no effect on `format_args!()`
let args = format_args!("");
usr_println!(true, "{args:5}");
//~^ ERROR: format specifiers have no effect on `format_args!()`
}
fn should_not_lint_user() {
usr_println!(true, "{}", format_args!(""));
// Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
// debug formatting so allow it
usr_println!(true, "{:?}", format_args!(""));
let args = format_args!("");
usr_println!(true, "{args}");
}

View file

@ -58,5 +58,63 @@ LL - println!("{args:5}");
LL + println!("{args}");
|
error: aborting due to 4 previous errors
error: format specifiers have no effect on `format_args!()`
--> tests/ui/unused_format_specs.rs:48:25
|
LL | usr_println!(true, "{:5}.", format_args!(""));
| ^^^^
|
help: for the width to apply consider using `format!()`
|
LL | usr_println!(true, "{:5}.", format!(""));
| ~~~~~~
help: if the current behavior is intentional, remove the format specifiers
|
LL - usr_println!(true, "{:5}.", format_args!(""));
LL + usr_println!(true, "{}.", format_args!(""));
|
error: format specifiers have no effect on `format_args!()`
--> tests/ui/unused_format_specs.rs:51:25
|
LL | usr_println!(true, "{:.3}", format_args!("abcde"));
| ^^^^^
|
help: for the precision to apply consider using `format!()`
|
LL | usr_println!(true, "{:.3}", format!("abcde"));
| ~~~~~~
help: if the current behavior is intentional, remove the format specifiers
|
LL - usr_println!(true, "{:.3}", format_args!("abcde"));
LL + usr_println!(true, "{}", format_args!("abcde"));
|
error: format specifiers have no effect on `format_args!()`
--> tests/ui/unused_format_specs.rs:54:25
|
LL | usr_println!(true, "{:5}.", format_args_from_macro!());
| ^^^^
|
= help: for the width to apply consider using `format!()`
help: if the current behavior is intentional, remove the format specifiers
|
LL - usr_println!(true, "{:5}.", format_args_from_macro!());
LL + usr_println!(true, "{}.", format_args_from_macro!());
|
error: format specifiers have no effect on `format_args!()`
--> tests/ui/unused_format_specs.rs:58:25
|
LL | usr_println!(true, "{args:5}");
| ^^^^^^^^
|
= help: for the width to apply consider using `format!()`
help: if the current behavior is intentional, remove the format specifiers
|
LL - usr_println!(true, "{args:5}");
LL + usr_println!(true, "{args}");
|
error: aborting due to 8 previous errors