-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support user format-like macros #9948
base: master
Are you sure you want to change the base?
Conversation
r? @Alexendoo (rust-highfive has picked a reviewer for you, use r? to override) |
d9c9b8b
to
362a2c9
Compare
There are some reasonable situations where inline args can't be used, such as macro_rules! error {
($fmt:literal $(, $e:expr)* $(,)?) => { println!(concat!("ERROR: ", $fmt), $($e,)*) }
}
fn main() {
let x = 1;
error!("{}", x);
} Another thing is that it could break macro invocations themselves, initially I wrote that as |
362a2c9
to
725ca3f
Compare
@Alexendoo I just added your example to my unit tests - it passes just fine. Do you have a test that fails? |
6b83237
to
0bdf235
Compare
macro_rules! error {
($fmt:literal, $($e:expr),*) => { println!($fmt, $($e,)*) }
}
fn main() {
let x = 1;
error!("{}", x);
} |
0bdf235
to
60f8dcc
Compare
@Alexendoo that's an excellent catch, thank you! The issue here is with the bug in macro definition (granted that macros are a special beast and a "bug" could be someone's feature). TBH, I am surprised there is no lint that tries to catch this mistake. The bigger question though is if this is something we should or should not process? I.e. something that suggests a cleaner repeated macro arg definition. I would even make this as a That said, on one hand we have badly created user macros that probably should be fixed. On the other, there is a very common pattern with all sorts of log and other macros that use format strings and could really use this lint. Which of these takes priority? |
@Alexendoo I created a lint suggestion #9959 that would solve this types of issues - having both lints on at the same time would fix the issue i think |
It would be neat to be able to lint macro definitions, however as they are almost entirely represented as a big Another one to bring up is that a common way of defining things like this is two arms, so that lint wouldn't be generally applicable ($fmt:literal) => ...
($fmt:literal, $($e:expr),*) => ... e.g. the That would make the suggestion change the behaviour of the code, silently even if the printed variable is otherwise used My biggest concern is probably all the proc macros out there, the current implementation is not super robust against them. If a proc macro did an internal format!() but happened to use certain input spans we could be matching up a completely different format string than the one actually being expanded To that end it may be worth keeping an eye on rust-lang/rust#99012 / rust-lang/compiler-team#541 |
Another fun one: macro_rules! used_twice {
(
large = $large:literal,
small = $small:literal,
$val:expr,
) => {{
if $val < 5 {
println!($small, $val);
} else {
println!($large, $val);
}
}}
}
fn main() {
let x = 1;
used_twice! {
large = "large value: {}",
small = "small value: {}",
x,
}
} |
I have a better idea - let's disable Rust macros, as they are clearly a work of Cthulhu, designed to steer us away from the light... as this is clearly a case of "I miss Perl, so I will make things as interesting as possible". I don't think Clippy should care about such cases TBH as they probably represent 1% - when on the other hand we have 99% of the simple |
0b3e8b5
to
7239dd6
Compare
Please keep in mind that we deem false positives a worse outcome than false negatives. I'd be OK with having either a config that makes the lint apply everywhere instead of just format while making the suggestion |
@llogiq I think we should also take into account how often we get false positive vs false negative. I agree that if they are about the same, false positive is worse, but if we get 100x more false negatives, I would prefer get a few more false positives. I like the idea of an extra config param, something like |
I would fully support that argument for a With that said, as long as the user explicitly asks for it, I'm ok with changing this balance. |
☔ The latest upstream changes (presumably #9860) made this pull request unmergeable. Please resolve the merge conflicts. |
7239dd6
to
26b9957
Compare
☔ The latest upstream changes (presumably #9865) made this pull request unmergeable. Please resolve the merge conflicts. |
26b9957
to
d12944f
Compare
6249c11
to
b8ccdfe
Compare
f1ea9ff
to
0edfbef
Compare
0edfbef
to
58b6bd6
Compare
☔ The latest upstream changes (presumably #13440) made this pull request unmergeable. Please resolve the merge conflicts. |
58b6bd6
to
b802d47
Compare
tests/ui/unused_format_specs.rs
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file produces no lints but unfixable implies it ought to
tests/ui/uninlined_format_args.rs
Outdated
#[clippy::format_args] | ||
macro_rules! my_concat { | ||
($fmt:literal $(, $e:expr)*) => { | ||
println!(concat!("ERROR: ", $fmt), $($e,)*) | ||
} | ||
} | ||
|
||
#[clippy::format_args] | ||
macro_rules! my_good_macro { | ||
($fmt:literal $(, $e:expr)* $(,)?) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[clippy::format_args]
is applied to many of these macros but only my_good_macro
is linting, is that intentional?
☔ The latest upstream changes (presumably #13442) made this pull request unmergeable. Please resolve the merge conflicts. |
b11b685
to
dc752cf
Compare
@Alexendoo thx, I think I fixed all the issues with it now, adding a lot of new tests in various relevant lints. |
@xFrednet @Alexendoo @llogiq friendly ping - it seems this 2 years old PR might actually get merged / put to vote, as I don't see any outstanding issues with it? |
https://rust-lang.zulipchat.com/#narrow/channel/257328-clippy/topic/Can.20we.20start.20FCP.20for.20.60.23.5Bclippy.3A.3Aformat_args.5D.60.20in.20user.20code.3F hasn't had any concerns raised so yeah seems fine We should ensure this is documented somewhere though |
@Alexendoo where would be the best place to document that 3rd party crates can now use this new attribute? Documentation should also include recommended way to declare format-using macros (i.e. to avoid trailing comma gotchas, etc). The only concern for 3rd party crates is how will they deal with the older Clippy versions - wouldn't there be some warning if clippy doesn't recognize an attribute? |
Probably a new section in the book for if we add others
If the crate author uses Clippy I believe they would have to be using one that supports the attribute, but consuming the dep shouldn't show a warning if it's older than the attribute. We should add a test for this though |
6d144ac
to
b799d4c
Compare
Thx, rebased and added documentation page to the book. |
P.S. using undefined attribute is an error, not a warning - that's why |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing that still needs to be figured out is the behavior when those macros are used in a dependency. Aka would adding this (without the rust version::attr
) raise the MSRV of a crate?
@@ -36,7 +37,9 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { | |||
if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { | |||
FORMAT_MACRO_DIAG_ITEMS.contains(&name) | |||
} else { | |||
false | |||
// Allow users to tag any macro as being format!-like | |||
// TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a follow up PR to the rust repo, after this PR is merged and synced
8e0ebf1
to
a728e2c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Just 2 minor comments.
9c829be
to
b3ccc3f
Compare
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!`
b3ccc3f
to
500df35
Compare
I think this is good to go now. @Alexendoo any other change requests? If not r=me |
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 likeformat!
,println!
andwrite!
changelog: Enhancement: [
format_in_format_args
], [recursive_format_impl
], [to_string_in_format_args
], [uninlined_format_args
], [unused_format_specs
]: Recognizes#[clippy::format_args]
to support custom 3rs party format macros.