-
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: iter_overeager_cloned
#8203
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @llogiq (or someone else) soon. Please see the contribution instructions for more information. |
3360d24
to
ec208b4
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.
This looks good to me, modulo running update_lints
and checking in the changes. I have an idea how we could make that lint even better. If you want to do that within this PR, go ahead, otherwise let me know and I'll r+.
expr.span, | ||
msg, | ||
"try this", | ||
format!("{}.last().cloned()", iter_snippet), |
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.
If the .last()
is unwrap()
ed or ?
d, we could even suggest to .clone()
that. Just a suggestion for extending the lint, this won't stop us from merging.
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.
@llogiq That's a great suggestion.
I see: for Option<>
/ Result<>
.
recv.cloned().unwrap()
could be
recv.unwrap().clone
.
I see that cloned
should be moved furthermost the chain as long as possible, not just for last
.
For example here:
vec.iter().cloned().filter(...).skip(5).take(10).last().unwrap()
That could be just written as
let val = vec.cloned().filter(...).skip(5).take(10).last().unwrap().clone()
This may even allow us to drop the clone()
entirely, in some cases, when clone() is not needed.
It may be a good idea to propagate clone from before, to after
last
take
skip
next
.unwrap
filter
// has lambda, so requires some more checksfind
.// has lambda,so requires some more checksmap
-vec.iter().cloned().map(|x|x.len())
<-- this probably doesn't need clone either.foreach
- maybe?vec.iter().cloned().foreach_each(|x|println!{"{}", x))
<-- this doesn't need clone- ... probably some others as well
map/for_each/try_foreach
require detecting whenever lambda can accept reference instead
The question is, should each be a separate lint, or should they be combined together?
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 think your example is a bit off, but otherwise full ack.
I'd be OK with multiple lints if there are useful distinctions to make. E.g. if one part of the check can have false positives, it would be a good idea to put that into a different nursery
lint. Or if one part could reasonably be style
and another perf
. Otherwise, putting all the checks into one lint seems natural. One thing I that comes to mind is that we already have the needless_clone
lint, so we could add cases where the cloned
can be removed outright to that, making the rest early_iter_cloned
or some such.
Again, I'm also OK if you restrict the scope of this PR and do a followup later. Have fun!
9c9f804
to
04619ef
Compare
I'd be OK with merging this with the |
I added implementation for
TODO
Questions:
I think that support for Fixing |
My name suggestion as per comment above is: Add the cases where the |
Oh, and I would like to run some tests before deciding whether to put it in |
clippy_lints/src/methods/mod.rs
Outdated
@@ -107,6 +108,29 @@ declare_clippy_lint! { | |||
"used `cloned` where `copied` could be used instead" | |||
} | |||
|
|||
declare_clippy_lint! { | |||
/// ### What it does | |||
/// Checks for usage of `_.cloned().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.
The documentation should also reflect the complete 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.
@llogiq I updated the documentation.
I agree, as a use of clippy I think like to see it divided maybe into
|
I'm ok with the I would avoid adding I don't think the Note that we'll have to extend the |
8da266c
to
baad6c2
Compare
9f175f5
to
77dd958
Compare
@llogiq I think some more changes are needed. --) Cloning small values, references of references is actually beneficial. Consider following /// cloned() should not be removed
let _ = [0u8, 0u8].iter().cloned()....
/// cloned() should not be removed
let _ = Vec::<&&String>.iter().cloned().... Cloning I think we should apply the same rules as If the type after cloning is |
That should be |
I added a test, and I see that this would be applied automatically:
|
Cool stuff! My remaining nits are with the documentation, otherwise this looks great. |
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.
A few more nits to fix. Do you need help with the size check?
I'm still thinking whenever the check is needed. For example, for
I'm not entirely convinced whenever the check is needed. For example. [0u8, 1u8, 2u8, 3u8].iter().cloned().count() /// cloned() should be removed
[0, 1, 2, 3, 4].last().cloned().last(); /// it's better to not deference values, and move cloned() last
[0, 1, 2, 3, 4].last().cloned().nth(40); /// same here
[0, 1, 2, 3, 4].last().cloned().skip(40); /// same here
[0, 1, 2, 3, 4].last().cloned().next(); // probably it's better not to print - not sure
[0, 1, 2, 3, 4].last().cloned().take(50); // probably it's better not to print - not sure
[0, 1, 2, 3, 4].last().cloned().flatten(); // probably it's better not to print - not sure @llogiq I'm not sure how to get the type from the iterator. I'll have to spend some time and figure out a better way to navigate through code. Type hinting doesn't seem to work correctly with Rust plugin for Intellij, which makes things harder. My understanding is that the receiver is some generic iterator, that implements Anyway, I should probably take a look at |
f1b6ebe
to
ecf660f
Compare
58fdacd
to
cf95b86
Compare
I just had a discussion with someone from libs. There's a Rust PR #90209 that aims to remove more clones through specialization. Apparently the libs team thinks that as there never was any guarantee of current behavior, they're free to change it. Which is fine by me. That somewhat reduces the need for this lint, but doesn't render it moot, because as we all know specialization is a fickle beast and may sometimes fail to apply. So we should still add this lint, but perhaps note the possibility of not seeing a perf win in the docs. |
☔ The latest upstream changes (presumably #8198) made this pull request unmergeable. Please resolve the merge conflicts. |
That PR was closed, so we're left with the clippy lint and some docs on |
Ok, just let me know what you think is best. Whenever we should ships this PR as it is. Or improve it, or close this PR. |
Re-reading this, I think we can look into refining the lint later. For now let's just merge it. @bors r+ |
📌 Commit e209ff8 has been approved by |
🔒 Merge conflict This pull request and the master branch diverged in a way that cannot be automatically merged. Please rebase on top of the latest master branch, and let the reviewer approve again. How do I rebase?Assuming
You may also read Git Rebasing to Resolve Conflicts by Drew Blessing for a short tutorial. Please avoid the "Resolve conflicts" button on GitHub. It uses Sometimes step 4 will complete without asking for resolution. This is usually due to difference between how Error message
|
@bors r- |
I overlooked that you need to rebase the PR on current master. Shouldn't be too hard. |
e209ff8
to
1c9b31d
Compare
I rebased the PR. |
d5075d9
to
36396c6
Compare
@bors r+ |
📌 Commit 36396c6 has been approved by |
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
Closes #8202
changelog: New lint: [
iter_overeager_cloned
]