-
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
Add heuristic to filter_map and find_map #6453
Conversation
r? @llogiq (rust-highfive has picked a reviewer for you, use r? to override) |
bc1af63
to
ddaebbd
Compare
Thanks for working on this. The (corrected) suggestions to fix this lint from #3188 are:
I really think that these are the only times Why have you gone with the heuristic instead of matching the above? Is it because the heuristic was easier or did you find some other cases that should be let through? |
@mikerite Actually I would be fine with implementing that instead and would even prefer it tbh. I didn't do that because I wasn't sure if there was a consensus - maybe some people would want the lint to be kept a wide net. I think there will be some missed cases if the lint is made that specific. But I think those cases would be relatively rare. As a general rule, I think it's better to not have false positives than to have comprehensiveness. Basically I'm convincing myself that you are right as I'm typing 😆. Here's an example of a case that would be missed: .filter(|x| match x {
Foo(a) => a.is_some(),
Bar(_) => true,
})
.map(|x| match x {
Foo(a) => a.unwrap(),
Bar(b) => b,
}) It looks like a hard problem to generalize that...but it's possible! Maybe that could be a future enhancement. Or maybe I CAN do it!!! I'm really gaining steam here! On a more technical note, in order to implement your suggestion, I believe we need to enhance So I think I'll start to look into this but it would be good to hear more opinions. |
Given recent feedback, I'd say we should start out conservative and experiment with linting more from there. We might put a |
☔ The latest upstream changes (presumably #6316) made this pull request unmergeable. Please resolve the merge conflicts. Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:
|
rustbot modify labels: +S-waiting-on-review -S-waiting-on-author |
@llogiq I'm not sure I understand. The "heuristic" makes the lint more conservative but @mikerite's suggestion makes it even more conservative, to the point where there should be no false positives. So to "start out conservative," do you mean to change the PR and adopt @mikerite's suggestion (which is what I'm leaning towards)? And then we could possibly take my "heuristic" approach and turn that into the |
Exactly. The recent feedback we got was overwhelming annoyance at false positives. So let's be careful to introduce the least of them we can, and work up from there, starting in the nursery for further approaches. |
Rather than a new lint with |
Is there anything needed from our side, @camsteffen? |
I have made good progress and just have some finishing touches to do before updating this PR. I have some thoughts though. Does this change merit a rename like I'm also wondering if we should just discard the "heuristic" thing. I just don't feel like it adds much value. But I'm open to keeping it with the configuration if anyone feels differently. |
I'm against renames just to reset false positives but I think this needs a rename anyway. I like the name Eventually Clippy (or rustc) will get something like #3122 add the problem of fixed false positives will be solved. |
Agreed. I wouldn't suggest a rename without a significant change to what the lint does.
Sounds good to me (but I'll wait for maintainers to chime in).
Actually
|
I'm also good with the rename to |
Well shoot there's another complication
Personally I feel that these lints go against the principle of zero-cost abstractions. That is, iterator adapters are a zero-cost abstraction (at least usually and ideally) and we should not be afraid to use them liberally if doing so makes the code more concise/readable. Perhaps |
I'd be OK with option 1. We can always get a follow-up PR. I'd also be OK with yet another lint. In either case, I think that unless there's a clear perf argument against it, the lints should be in the style group. |
But I am trying to make the case that combining iterator adapters can be bad style. For example, let _ = (0_u32..100)
.filter_map(|n| match n {
0 => None,
1 => Some("one"),
2 => Some("two"),
n => Some("many"),
})
.map(my_function); I suggested a "restriction" lint based on the idea that the programmer may want to combine iterator adapters at the cost of style. |
I opened #6591 to replace this PR. |
`manual_filter_map` and `manual_find_map` changelog: Add `manual_filter_map` and replace `find_map` with `manual_find_map` Replaces #6453 Fixes #3188 Fixes #4193 ~Depends on #6567 (to fix an internal lint false positive)~ This replaces `filter_map` and `find_map` with `manual_filter_map` and `manual_find_map` respectively. However, `filter_map` is left in place since it is used for a variety of other cases. See discussion in #6453.
changelog: Reduce false positives for filter_map and find_map
Fixes #3188
Fixes #4193
Simply check if
filter(..)
contains a closure that usesOption
orResult
anywhere in any way (usingExprUseVisitor
). The assumption is: if thefilter
does not useOption
orResult
at all, it is perfectly fine as it is and turning it intofilter_map
would make the code more complex. And the very same thing applies tofind_map
.The heuristic could probably be improved or made more specific down the road. But my goal here is remove false positives without losing any positive cases. I think this is a big improvement.
To anticipate a potential concern, there is one "category" of cases for these lints that will no longer be detected: where the code in
filter
andmap
are redundant, doing the same transformation on the value (e.g.filter(|n| n + 1 > 5).map(|n| n + 1)
. But I think this is an acceptable loss because 1) there is no logic in these lints that actually recognizes redundant code and 2) this could be a separate lint (e.g.repetitive_iterator
) - such a lint could apply to a wider variety of Iterator methods (e.g.iter.inspect(|n| println!("{}", n+1)).fold(0, |s, n| s += n + 1)
).