Skip to content
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

Suggest for loop instead of while-let when looping over iterators #396

Merged
merged 6 commits into from
Oct 27, 2015

Conversation

fhartwig
Copy link
Contributor

Fixes #387
I think this is ready for review. I even remembered to update the readme for once 😄

@fhartwig
Copy link
Contributor Author

Argh. I just realised that I misread rust-lang/rust#8372 . This issue means that there are cases where while let is actually what you want (I thought that for had been fixed). I guess I will have to also check that the iterator isn't used in the loop body to avoid false positives.

@@ -53,6 +53,23 @@ fn main() {
while let Some(x) = y { // no error, obviously
println!("{}", x);
}


while let Option::Some(x) = (1..20).next() { //~ERROR this loop could be written as a `for` loop
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these examples depend on a temporary, they are all actually infinite loops 😄

I'd prefer if we used vector iteration here or stored the ranges in an external mutable variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what I was thinking there 😆 Thanks, I'll fix that.

@fhartwig
Copy link
Contributor Author

I've made fixes for your comments, fixed the false positives where the iterator is used inside the loop body, and rebased the branch onto the current master. Ready for more feedback 😄

@@ -6,7 +6,8 @@ A collection of lints to catch common mistakes and improve your Rust code.
[Jump to usage instructions](#usage)

##Lints
There are 68 lints included in this crate:
There are 69 lints included in this crate:
There are 65 lints included in this crate:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I messed up when resolving a conflict for the rebase. Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happens to the best of us :-)

@fhartwig
Copy link
Contributor Author

Regarding the discussion about iterators being used after the while-let loop:

On the other hand, it shouldn't be very hard to fix.

Ok, I have to eat my words here, I'm a bit stumped. I want to have a function that checks whether the an iterator variable is used again after being used in a while-let loop. I have written a visitor that, given a block, the iterator variable and the id of the while-let loop, checks whether the iterator variable appears after the beginning of the while let loop (i.e. in the loop body or after the loop). But now I need a block to run it on. I thought I could just use get_parent_node with the node id of the variable declaration to get the relevant block, but the parent node isn't necessarily a NodeBlock, it might be a NodeItem or a NodeImpltem or whatever Node variant can contain a Block. I'm guessing there must be some straightforward way to get the block (or a list of statements or something along those lines) that a given binding is in scope, but I can't seem to find it.

@fhartwig
Copy link
Contributor Author

Ok, I just found get_enclosing_scope in rustc, I guess that's something that could be useful.

@Manishearth
Copy link
Member

Note: for tracking uses, try ExprUseVisitor

* remove weird infinite loops from compile-tests
* remove call to Option::unwrap
* in the lint message, show while-let loop rewritten as for loop
@fhartwig
Copy link
Contributor Author

I've fixed the false positives when an iterator is used after the loop. I've not used ExprUseVisitor since that ended up being more complex than what I do now. On the other hand, ExprUseVisitor would catch certain false negatives, e.g. the following:

let mut iter = 0u32..20;
while let Some(x) = iter().next {}
iter = 1..5;
println!("iter: {:?}" iter);

This should strictly speaking warn since it can be written as a for loop, but it currently doesn't since iter is used after the loop. I think this is too much of an edge case to make the code more complicated for it, but I'm not sure.

Regarding the dogfood.sh failure: I think this is a false positive in the needless_lifetime lint, because when I remove the lifetime, rustc complains about missing lifetimes. But it's late now and I'll look into it tomorrow.

@Manishearth
Copy link
Member

 pub fn get_enclosing_block(cx: &LateContext, node: NodeId) -> Option<&Block> {

should work, really.

@fhartwig
Copy link
Contributor Author

@Manishearth That's what I thought, but it doesn't (Probably because of the lifetimes on LateContext? I don't really know the elision rules off the top of my head). So as far as I can tell, this is a false positive. I've just filed #417

@Manishearth
Copy link
Member

Oh, LateContext has a tcx lifetime. Right.

@Manishearth
Copy link
Member

Since you had done most of the review, r? @llogiq

(If you're busy let me know and I'll do it tomorrow)

@llogiq
Copy link
Contributor

llogiq commented Oct 27, 2015

Looks good to me; just slap an #[allow(needless_lifetimes)] on the offending function until we've fixed #417, and I'll be happy to merge.

@fhartwig
Copy link
Contributor Author

Done 😄

llogiq added a commit that referenced this pull request Oct 27, 2015
Suggest for loop instead of while-let when looping over iterators
@llogiq llogiq merged commit aee42f7 into rust-lang:master Oct 27, 2015
@llogiq
Copy link
Contributor

llogiq commented Oct 27, 2015

Thanks a bunch! 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants