-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
new lint: macro_metavars_in_unsafe
#12107
Conversation
r? @giraffate (rustbot has picked a reviewer for you, use r? to override) |
067e21f
to
490b25a
Compare
I have run your lint against the top 500 crates. Here are the results, I haven't analyzed it, but it might help you check whether it does what you intend it to. Reportstarget/lintcheck/sources/bitvec-1.0.1/src/macros.rs:219:27 clippy::expr_metavars_in_unsafe "expanding an expression metavariable in an unsafe block" Stats:
|
Thanks! My thoughts on the results: poy3The pyo3 case looks like a real case to me. bitvecLooks like a false positive. This is basically: macro_rules! x {
($v:expr) => {
$v; unsafe { $v; };
}
} It's technically expanding static_assertionsThe two static_assertions cases I already saw and mentioned in my description. I suppose those are technically FPs because they're in a closure that is never called, thus never reached, so it doesn't matter what kinds of unsafety is done, but I don't think this is something we can detect. I want to say that if this is an uncommon case, it could be something that can be |
Oh sorry this specifically is not UB (anymore?). pyo3's macro is using But I also completely missed that the macro is annotated with |
Fixed those 2 FPs. The more logic I add to this, though, I feel like this isn't a good idea, since this is effectively implementing an unsafety checker at the parser level... |
651307c
to
7d36300
Compare
Finally got around to rewriting this by having it look at local macro expansions, rather than trying to parse the macro definition tokenstream.
... so for now I put this in the nursery category. I'm not sure which one of these can be worked around or improved. Will think about it a bit more. |
☔ The latest upstream changes (presumably #12160) made this pull request unmergeable. Please resolve the merge conflicts. |
@GuillaumeGomez would you mind taking a look at this PR? It has some comments, which you probably first need to catch up on. r? xFrednet |
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.
Code is good and well documented. Nice work! Looks ready to go for me.
Yeah the PR history is a bit of a mess, it went through a few rewrites :D Though half the comments are no longer really relevant. It started by inspecting/"parsing" the I think the main "blocker" more or less is that this currently does not check that the expanded metavariable is an expression, contrary to the lint name: macro_rules! m {
($v:literal) => { unsafe { $v } }
}
m!(1) This is linted even though But... maybe it's fine and we could just rename the lint to not mention "expr_metavars"? What about something like |
This sounds good to me. Then it'll also catch statements and other tokens :) |
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.
Very nice implementation! The structure and docs make the code very understandable. I have several nit-picky comments, but hopefully nothing that will require a lot of work. :D
if from_expn { | ||
self.expn_depth += 1; | ||
} |
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.
It feels weird that this only checks if the span is from an expansion. If we nested statements from the same macro it would still increase the self.expn_depth
counter. This is not a problem directly, but the name of the field might be misleading. Can you add a test for this?
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.
Can you elaborate why this is weird or what you would rename this field to to make it less misleading? Or what exact test case you'd want?
To clarify, It's okay for this to falsely return true and it doesn't matter for correctness if we increment the counter multiple times for the same macro or for unrelated macros, as long as we arrive back at 0 when we leave all expansion spans in that function and that the counter is >0
if there are any enclosing macro spans.
This check is intentionally kept "cheap" because we're likely going to execute that a lot.
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.
To clarify, It's okay for this to falsely return true and it doesn't matter for correctness if we increment the counter multiple times for the same macro or for unrelated macros, as long as we arrive back at 0 when we leave all expansion spans
That's a good point. I can't think of an example that would violate the implementation. Then let's leave it at this :)
Thanks for reviewing, I'll address the comments eventually, but I will be busy for a couple more weeks, so it might take a while. |
55ffbed
to
ad424da
Compare
/// Lint: MACRO_METAVARS_IN_UNSAFE. | ||
/// | ||
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. | ||
(warn_unsafe_macro_metavars_in_private_macros: bool = false), |
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 is quite lengthy, if someone has a better and more concise name for this, do tell
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.
I like the name, it's lengthy but descriptive 👍
expr_metavars_in_unsafe
macro_metavars_in_unsafe
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.
Tiny error message NIT, but the rest looks good to me.
Edit 2: You can r=me after the changed message, if you take one of my suggestions. :D
/// Lint: MACRO_METAVARS_IN_UNSAFE. | ||
/// | ||
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. | ||
(warn_unsafe_macro_metavars_in_private_macros: bool = false), |
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.
I like the name, it's lengthy but descriptive 👍
ad424da
to
064aa5a
Compare
I created an FCP thread on zulip for this. Will squash when that's complete. |
Since no comments have come up during the FCP it should be safe to merge this now. @y21 can you resolve the last comment regarding the version? Then you can |
☔ The latest upstream changes (presumably #12620) made this pull request unmergeable. Please resolve the merge conflicts. |
064aa5a
to
aa54572
Compare
aa54572
to
9747c80
Compare
Changed the version atribute to 1.80 and rebased to fix conflicts + squashed. (had to force push twice since the first commit still had the old lint name in its commit message which was slightly misleading) @bors r=xFrednet |
new lint: `macro_metavars_in_unsafe` This implements a lint that I've been meaning to write for a while: a macro with an `expr` metavariable that is then expanded in an unsafe context. It's bad because it lets the user write unsafe code without an unsafe block. Note: this has gone through some major rewrites, so any comment before #12107 (comment) is outdated and was based on an older version that has since been completely rewritten. changelog: new lint: [`macro_metavars_in_unsafe`]
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
👀 Test was successful, but fast-forwarding failed: 422 1 review requesting changes and 1 approving review by reviewers with write access. |
The merge conflict had perfect timing xD @bors retry |
new lint: `macro_metavars_in_unsafe` This implements a lint that I've been meaning to write for a while: a macro with an `expr` metavariable that is then expanded in an unsafe context. It's bad because it lets the user write unsafe code without an unsafe block. Note: this has gone through some major rewrites, so any comment before #12107 (comment) is outdated and was based on an older version that has since been completely rewritten. changelog: new lint: [`macro_metavars_in_unsafe`]
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
👀 Test was successful, but fast-forwarding failed: 422 1 review requesting changes and 1 approving review by reviewers with write access. |
@bors r+ |
💡 This pull request was already approved, no need to approve it again.
|
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
This implements a lint that I've been meaning to write for a while: a macro with an
expr
metavariable that is then expanded in an unsafe context. It's bad because it lets the user write unsafe code without an unsafe block.Note: this has gone through some major rewrites, so any comment before #12107 (comment) is outdated and was based on an older version that has since been completely rewritten.
changelog: new lint: [
macro_metavars_in_unsafe
]