-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Add lint checks for unused loop labels #50763
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @estebank (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
src/librustc_lint/lib.rs
Outdated
@@ -176,7 +177,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { | |||
UNUSED_DOC_COMMENT, | |||
UNUSED_EXTERN_CRATES, | |||
UNUSED_FEATURES, | |||
UNUSED_PARENS); | |||
UNUSED_PARENS, | |||
UNUSED_LOOP_LABEL); |
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 one line up so it's in alphabetical order.
src/librustc_lint/unused.rs
Outdated
ast::ExprKind::While(_, _, Some(ref label)) | ||
| ast::ExprKind::WhileLet(_, _, _, Some(ref label)) | ||
| ast::ExprKind::ForLoop(_, _, _, Some(ref label)) | ||
| ast::ExprKind::Loop(_, Some(ref label)) => { |
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.
ref
shouldn't be necessary on these lines since ast::Label
is copyable.
src/librustc_lint/unused.rs
Outdated
} | ||
ast::ExprKind::Break(Some(ref label), _) | ast::ExprKind::Continue(Some(ref label)) => { | ||
'remove_used_label: for i in (0..self.0.len()).rev() { | ||
if self.0.get(i).unwrap().ident.name == label.ident.name { |
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 doable with self.0.iter().enumerate().rev().find()
but there's not much difference in clarity except most people prefer to see iterator chains.
src/librustc_lint/unused.rs
Outdated
| ast::ExprKind::ForLoop(_, _, _, Some(ref label)) | ||
| ast::ExprKind::Loop(_, Some(ref label)) => if !self.0.is_empty() { | ||
{ | ||
let unused_label = self.0.last().unwrap(); |
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'd rather combine this and the !is_empty()
check into if let Some(unused_label) = self.0.last()
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.
In fact this should probably be self.0.pop()
instead and push the label back if the equality check falls though.
Hmm I wonder about which more general name to choose as soon blocks can bear labels too: #50045 It'd be cool to have a more general name ( |
src/librustc_lint/unused.rs
Outdated
} | ||
|
||
#[derive(Clone)] | ||
pub struct UnusedLoopLabel(pub vec::Vec<ast::Label>); |
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.
Vec is part of the prelude so you don't need vec::Vec
here, Vec
is enough. use std::vec;
isn't needed either.
@est31 I considered including block labels in my mentoring instructions but they seem like a much more niche feature. I don't think I've seen them in the wild for quite a long time, what are they even useful for again? Can you use them to actually name a lifetime? |
@abonander there is only a merged RFC for labeled blocks, but no implementation in the compiler. The PR to add compiler support is linked above. The point I made above was to forestall renames of the lint in the future if that PR merges. So it is entirely fine if this PR still keeps the loop in its name, I'll just rename it then... as long as no stable release shipped with the lint that should have no consequences. |
src/librustc_lint/unused.rs
Outdated
} | ||
ast::ExprKind::Break(Some(ref label), _) | ast::ExprKind::Continue(Some(ref label)) => { | ||
'remove_used_label: for i in (0..self.0.len()).rev() { | ||
if self.0.get(i).unwrap().ident.name == label.ident.name { |
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.
Comparing name
s is not correct in general because macro hygiene applies to labels as well.
This lint should not attempt to repeat the name resolution pass (librustc_resolve/lib.rs
), it should become a part of it - each label definition (fn with_resolved_label
) should add that label's id
into "unused" set and then each label use (fn search_label
) should remove the definition it resolves to from the set.
I agree that |
I remember us being wary of adding new lints to the compiler because they are a breaking change. What makes this case different? |
Please remove the file |
@nagisa https://github.com/arcnmx/rust-rfcs/blob/master/text/1105-api-evolution.md#lints Since lint |
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 looks good to me, one nitpick inline.
src/test/ui/lint/unused_label.rs
Outdated
// unreachable_code errors everywhere else | ||
'unused_loop_label: loop { | ||
//~^ WARN unused label | ||
} |
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 add a case for something along the lines of
fn main() {
'used_many: loop {
if true {
break 'used_many;
} else {
break 'used_many;
}
}
}
The code as it is seems to be doing the right thing for this, but want to make sure we don't have a regression in the future.
src/librustc_lint/unused.rs
Outdated
| ast::ExprKind::WhileLet(_, _, _, Some(label)) | ||
| ast::ExprKind::ForLoop(_, _, _, Some(label)) | ||
| ast::ExprKind::Loop(_, Some(label)) => { | ||
self.0.push(label); |
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 add a comment along the lines of "adding all labels as we encounter their definitions"
src/librustc_lint/unused.rs
Outdated
} | ||
ast::ExprKind::Break(Some(label), _) | ast::ExprKind::Continue(Some(label)) => { | ||
if let Some(index) = self.0.iter().rposition(|&l| l.ident == label.ident) { | ||
self.0.remove(index); |
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.
Add another comment along the lines of "Remove labels as they're used. If they're used more than once they will already have been removed, as expected".
src/librustc_lint/unused.rs
Outdated
ctxt.span_lint(UNUSED_LABEL, label.ident.span, "unused label"); | ||
} else { | ||
self.0.push(unused_label); | ||
} |
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'm not sure this is correct. If you have multiple nested scopes with labels, check_expr_post
is going to start going down, right? If this is the case, then the lint will only warn on the bottom-most label. I'm not entirely sure. That being said, I feel this would be easier to follow as a check_crate_post
and merely iterate through &self.0
idents and emit the lint.
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.
Yeah, this approach definitely has an issue with what you mentioned earlier:
fn main() {
'used_many_shadowed: loop {
'used_many_shadowed: loop {
if true {
break 'used_many_shadowed;
} else {
break 'used_many_shadowed;
}
}
}
}
Should warn on the first 'used_many_shadowed
label, but won't. I've implemented a fix using a Vec<(Label, bool)>
to keep track of when each Label
is used instead of just removing it, but honestly it's looking like @petrochenkov 's suggestion to run this check within librustc_resolve
is probably the way to go.
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 add a test case for the following, which should be allowed:
fn main() {
'used_many: loop {
if true {
break 'used_many;
} else {
break 'used_many;
}
}
}
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
@KyleStach1678 |
@petrochenkov Can you go into more detail about your alternative solution? I don't understand resolve as well as I do the syntax/lint part of the frontend so I won't be much help here. |
It only applies to cargo (or build systems that do not neglect to use
--cap-lint) and it does not work that way for end users who do cargo
install.
…On Wed, May 16, 2018, 01:33 Austin Bonander ***@***.***> wrote:
@nagisa <https://github.com/nagisa>
https://github.com/arcnmx/rust-rfcs/blob/master/text/1105-api-evolution.md#lints
Since lint #[deny] errors don't stop compilation of dependencies the only
build breakage would occur for the local crate where the author can
immediately address it.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#50763 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AApc0n0M07YpDJ0EbSnZ5HoAb0wcsTvyks5ty1fBgaJpZM4T--FL>
.
|
Now that #50045 merged, you can implement this lint for labeled blocks as well. All you need to do is to rebase the PR onto current master and add a |
|
@petrochenkov as far as I can tell |
@KyleStach1678 |
This functionality is being reimplemented in the resolver phase This reverts commit 503a69e844970476b27bf1ac7be951bb22194f50.
This reverts commit b9257e2ca161b1bf5aae9d6b667f4d0c6b8d7be6.
This reverts commit b2a2450cf2925498e1c3bdd781aa1978f9eb00e5.
b9257e2
to
6da64a7
Compare
@bors r+ |
📌 Commit 6da64a7 has been approved by |
Add lint checks for unused loop labels Previously no warning was generated when a loop label was written out but never used (i.e. in a `break` or `continue` statement): ```rust fn main() { 'unused_label: loop {} } ``` would compile without complaint. This fix introduces `-W unused_loop_label`, which generates the following warning message for the above snippet: ``` warning: unused loop label --> main.rs:2:5 | 2 | 'unused_label: loop {} | ^^^^^^^^^^^^^ | = note: #[warn(unused_loop_label)] on by default ``` Fixes: #50751.
☀️ Test successful - status-appveyor, status-travis |
Previously no warning was generated when a loop label was written out but never used (i.e. in a
break
orcontinue
statement):would compile without complaint.
This fix introduces
-W unused_loop_label
, which generates the following warning message for the above snippet:Fixes: #50751.