-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Adding the expect
attribute (RFC 2383)
#86024
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Hey @nikomatsakis, have you been able to have a look at this implementation draft yet? 🙃 |
This comment has been minimized.
This comment has been minimized.
ecd043e
to
31ec6f7
Compare
expect
attribute (RFC 2383)expect
attribute (RFC 2383)
This comment has been minimized.
This comment has been minimized.
31ec6f7
to
1cbef78
Compare
This comment has been minimized.
This comment has been minimized.
1cbef78
to
65bec5b
Compare
This comment has been minimized.
This comment has been minimized.
65bec5b
to
de5f21d
Compare
Hey @nikomatsakis, sorry to ping you again. This PR has been lying around for a while, which is understandable due to the size and that you're probably also quite busy with the 2021 edition. If you're too busy please say so, I can also try to find another reviewer. This PR could probably be split up into smaller packages of ~300 lines, with the disadvantage that the implementation could only be tested with the last package. The changes could be split up into:
Would that help with the review? |
de5f21d
to
bbe13f3
Compare
I've now restructured the changes into 5 commits, each of them is focussing on a certain aspect of this implementation and should hopefully aid with the review. The previous development commits can still be found under |
This comment has been minimized.
This comment has been minimized.
bbe13f3
to
5e176b0
Compare
@flip1995 might be a good reviewer - I know they're on T-clippy, but niko is pretty busy unfortunately :/ I think most people in T-compiler would be a good final approver, I'll assign r? @wesleywiser but feel free to reassign :) |
It might be easier to test rustdoc instead, so you don't have to mess with testing clippy from rust-lang/rust (it's quite difficult currently: #76495) |
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 don't really know this code but here are a few things that I noticed. My main concerns are that it might not behave correctly in more complex scenarios (with macros and such), and that it might not be efficient enough for the compiler's standards. I hope this review helps.
let krate = tcx.hir().krate(); | ||
|
||
let mut checker = LintExpectationChecker::new(tcx, tcx.sess, store); | ||
checker.check_crate(krate); |
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.
Visiting the whole crate to check that expect
s are fulfilled seems very inefficient to me. We already collected all the lint level attributes in lint_levels
, can't we use that information instead?
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 also have this concern, but at the time of development I couldn't think of a nicer solution. It's relatively simple to also collect all expectations in the lint_levels
query, but then I was lost what to do with that data. However, your suggestion here to do the expectation checking could solve this and enable the collection in the lint_level
query.
} | ||
|
||
impl CheckScope { | ||
fn includes_span(&self, emission: &MultiSpan) -> bool { |
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.
Using spans is pretty hacky, and I suspect it might break because of things like macro expansion and stuff.
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 was one of the main problems with this task in general. Clippy and I suspect rustc as well emits almost all of its diagnostics via struct_span_lint
which only provides us with very limited information. I asked on Zulip and there seems to be no real way to map from a Span
to something like the HirId
. This method could most likely support macros etc. by going through the span expansion data, but that seems performance intensive as it would also need to be done during check_expect_query
.
Any other way of matching the emission span to the expectation would probably require a change to the way lints are emitted. Any suggestions are very welcome!
I like the suggestion you have here about tracking the expectations in the diagnostic emission. I'll first investigate possible solutions over there before going further into this. 🙃
@@ -332,6 +337,10 @@ struct HandlerInner { | |||
/// twice. | |||
emitted_diagnostics: FxHashSet<u128>, | |||
|
|||
/// This contains all lint emission information from this compiling session | |||
/// with the emission level `Expect`. | |||
expected_lint_emissions: Vec<LintEmission>, |
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.
Suggestion: what if we turn this the other way? Say we have a FxHashMap
(which is a hash map with a faster hashing algorithm that we use throughout the compiler) that represents all the #[expect]
attributes that need to be fulfilled. It takes a (LintStackIndex, LintId)
pair as its key and everything that we need to emit the lint (the #[expect]
attribute's span, the reason, etc) as its value. Every time we emit an expect
ed lint, we remove that lint's entry from the map, and at the end of compilation, we emit an unfulfilled_lint_expectation
lint for every remaining entry in the map.
This would require a few changes to how lints are emitted so I don't feel confident to say "it's the way to go", maybe you'll want to wait on someone more qualified to weigh in.
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 a very interesting idea that I haven't thought about! I actually think that it won't require any major change to the lint emission. The logic already retrieves the lint level for a specific lint. The implementation would only find away to store which expectation set the level to expect in this concrete stance and mark that one as fulfilled.
I'll try to do some prototyping for this suggestion, as I believe it would be the nicer solution. As log as we can find a way to match the diagnostic to the specific expectation.
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.
Thank you very much @LeSeulArtichaut, this was the kind feedback I was hoping for! You gave me some new ideas and suggested some awesome improvements. I'll update the small NITs which only require small renames and refactorings in a new commit.
The prototyping for your bigger suggestions will be done in a different branch. Can I ping you again, when I've made some progress on that? 🙃
let krate = tcx.hir().krate(); | ||
|
||
let mut checker = LintExpectationChecker::new(tcx, tcx.sess, store); | ||
checker.check_crate(krate); |
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 also have this concern, but at the time of development I couldn't think of a nicer solution. It's relatively simple to also collect all expectations in the lint_levels
query, but then I was lost what to do with that data. However, your suggestion here to do the expectation checking could solve this and enable the collection in the lint_level
query.
} | ||
|
||
impl CheckScope { | ||
fn includes_span(&self, emission: &MultiSpan) -> bool { |
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 was one of the main problems with this task in general. Clippy and I suspect rustc as well emits almost all of its diagnostics via struct_span_lint
which only provides us with very limited information. I asked on Zulip and there seems to be no real way to map from a Span
to something like the HirId
. This method could most likely support macros etc. by going through the span expansion data, but that seems performance intensive as it would also need to be done during check_expect_query
.
Any other way of matching the emission span to the expectation would probably require a change to the way lints are emitted. Any suggestions are very welcome!
I like the suggestion you have here about tracking the expectations in the diagnostic emission. I'll first investigate possible solutions over there before going further into this. 🙃
@@ -332,6 +337,10 @@ struct HandlerInner { | |||
/// twice. | |||
emitted_diagnostics: FxHashSet<u128>, | |||
|
|||
/// This contains all lint emission information from this compiling session | |||
/// with the emission level `Expect`. | |||
expected_lint_emissions: Vec<LintEmission>, |
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 a very interesting idea that I haven't thought about! I actually think that it won't require any major change to the lint emission. The logic already retrieves the lint level for a specific lint. The implementation would only find away to store which expectation set the level to expect in this concrete stance and mark that one as fulfilled.
I'll try to do some prototyping for this suggestion, as I believe it would be the nicer solution. As log as we can find a way to match the diagnostic to the specific expectation.
Sure! Feel free to ping me on Zulip as well if you have any questions that I can try to answer or forward to the knowledgeable people |
Thanks for the ping! Yeah, I can help reviewing this. But since @LeSeulArtichaut already started with the review, I first want to ask if you want to continue or pass this on? |
@LeSeulArtichaut Suggested moving the logic into a different part of the compiler. I really like this suggestion and want to try it at least. I would therefore suggest waiting on further reviews until I've investigated the new lead. 🙃 |
I've created a new implementation in #87835 based on the feedback in this PR. The new implementation is in my opinion better in every regard. I'll therefore close this PR and focus on the new implementation. Thank you to everyone that has given me feedback in this thread! 🙃 |
This is a draft implementation of the
expect
attribute as defined in RFC 2383. It basically allows to allow a lint but cause a lint message itself if no lint has been triggered in the defined scope.Example:
Output:
Implementation
Rust now has a new lint level called
Expect
. This indicates that a diagnostic should be emitted to the context, but it will not be passed to the user. It is instead saved insiderustc_error::HandlerInner
duringrustc_error::HandlerInner::emit_diagnostic(diag)
. This suppresses all lint message with the level expect. (Some lints in rustc and Clippy have a specific check to see if they are allowed in the current scope. Adding a new level allows the addition of the feature without changing such implementations)Lint expectations have to be checked after all lint passes have been completed. This is done in a new
check_expect_lint
query. It works similar to theLintLevelMapBuilder
like a stack machine. Here is an example:TODOs I need help with
#[expect(clippy::nice)]
issues a warning, when Clippy runs)Open questions
expect
attribute to something else. (Initiated by@lcrec
here, suggestion from@scottmcm
to use#[should_lint(...)]
here). No real conclusion was drawn on that point from my understanding. Is this still open for discussion, or was this discarded with the merge of the RFC?expectation_missing
tounfulfilled_lint_expectation
. The name was marked as "name open to change" Is this okay with everyone, or should the name be changed back?force-warn
lint level?I know that this is quite a chunk of code. I sadly don't see a simple way of splitting it up into smaller batches without having partial implementations with loose ends in the compiler. The changes are now split up into 5 commits, with each of them focussing on one area of this implementation.
r? @nikomatsakis as you mentored me in the issue :)
Mentoring/Implementation issue: #85549
RFC tracking issue: #54503