-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add a check for some followed by filter #15745
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
base: master
Are you sure you want to change the base?
Conversation
No changes for 6f173cd |
4746c00
to
d34f4ed
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.
Thanks for your contribution. However, the lint cannot be considered for merging as-is:
- It should include autofixes suggestions when possible.
- It should cover more complex cases.
For example, with the following code
fn with_side_effect(x: u32) -> u32 {
println!("In `with_side_effect()`");
x * 10 + 30
}
fn f(x: u32) -> Option<u32> {
Some(with_side_effect(x)).filter(|&x| x > 40)
}
the lint suggests using then_some()
inside f()
. However, I don't see what would be the replacement code here.
You should include tests for the most complicated situations you can think of, as the lint should not fail, don't just cover the simplest case where it is obvious it will work. For example, what is part of the expression comes from a macro? Also, bool::then_some()
has been introduced in Rust 1.62.0, so the MSRV must be checked as well.
Hope this review will help you go forward.
In the example with the 2 functions, what should the linter do? |
Probably nothing. |
It has been about 2 weeks. Can someone take a look? |
@samueltardieu Now there are some less trivial tests involving macros. Please take another look. |
This comment has been minimized.
This comment has been minimized.
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
This comment has been minimized.
This comment has been minimized.
The program will suggest using the `then_some` method. changelog: [`filter_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_some
a74059f
to
6f173cd
Compare
let (condition_text, condition_is_macro) = | ||
snippet_with_context(cx, condition.span, arg.span.ctxt(), "..", &mut applicability); | ||
let parentheses = !condition_is_macro && cx.precedence(condition) < ExprPrecedence::Unambiguous; | ||
let value_text = snippet_with_applicability(cx, value.span, "..", &mut applicability); | ||
let sugg = format!( | ||
"{}{condition_text}{}.then_some({value_text})", | ||
if parentheses { "(" } else { "" }, | ||
if parentheses { ")" } else { "" }, | ||
); |
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.
Sugg::maybe_paren
has some logic to add parentheses, but only when needed -- I think that should be enough to deal with this case.
let (condition_text, condition_is_macro) = | |
snippet_with_context(cx, condition.span, arg.span.ctxt(), "..", &mut applicability); | |
let parentheses = !condition_is_macro && cx.precedence(condition) < ExprPrecedence::Unambiguous; | |
let value_text = snippet_with_applicability(cx, value.span, "..", &mut applicability); | |
let sugg = format!( | |
"{}{condition_text}{}.then_some({value_text})", | |
if parentheses { "(" } else { "" }, | |
if parentheses { ")" } else { "" }, | |
); | |
let condition_text = | |
Sugg::hir_with_context(cx, condition.span, arg.span.ctxt(), "..", &mut applicability).maybe_paren(); | |
let value_text = snippet_with_applicability(cx, value.span, "..", &mut applicability); | |
let sugg = format!("{condition_text}.then_some({value_text})"); |
(Sugg::hir_with_context
is pretty much the same as snippet_with_context
)
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.
With this change applied, the program adds parentheses here. Is it intended?
diff --git a/tests/ui/filter_some.fixed b/tests/ui/filter_some.fixed
index fbfdfd177..386a0264c 100644
--- a/tests/ui/filter_some.fixed
+++ b/tests/ui/filter_some.fixed
@@ -18,12 +18,12 @@ fn main() {
// The condition contains an operator. The program should add parentheses.
let _ = (1 == 0).then_some(0);
//~^ filter_some
- let _ = match 0 {
+ let _ = (match 0 {
//~^ filter_some
0 => false,
1 => true,
_ => true,
- }.then_some(0);
+ }).then_some(0);
// The argument to filter requires the value in the option. The program
// can't figure out how to change it. It should leave it alone for now.
let _ = Some(0).filter(|x| *x == 0);
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 assume that the previous version compiled just fine, and so the parentheses are indeed unnecessary. It could be that there are expressions for which your approach and Sugg
would yield different results, and I must admit I don't which of those will actually have more correct ones, so I guess let's go with yours
Add a check for some followed by filter.
The program will suggest using the
then_some
method.changelog: [
filter_some
]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_some