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

New lint: map to Option followed by flatten #4496

Closed
leudz opened this issue Sep 3, 2019 · 10 comments · Fixed by #5846
Closed

New lint: map to Option followed by flatten #4496

leudz opened this issue Sep 3, 2019 · 10 comments · Fixed by #5846
Labels
A-lint Area: New lints good-first-issue These issues are a good way to get started with Clippy L-complexity Lint: Belongs in the complexity lint group L-suggestion Lint: Improving, adding or fixing lint suggestions

Comments

@leudz
Copy link

leudz commented Sep 3, 2019

Calling map to wrap the value in an Option and flatten right after is equivalent to filter_map.

@flip1995
Copy link
Member

flip1995 commented Sep 4, 2019

Playground

fn main() {
    let v = [10, 20, 30];

    let filtered1 = v
        .iter()
        .map(|x| if *x > 10 { Some(x) } else { None })
        .flatten()
        .collect::<Vec<_>>();

    let filtered2 = v
        .iter()
        .filter_map(|x| if *x > 10 { Some(x) } else { None })
        .collect::<Vec<_>>();

    assert_eq!(filtered1, filtered2);
}

This can be implemented in clippy_lints/src/methods/mod.rs, similar to multiple other lints in this module.

@flip1995 flip1995 added L-complexity Lint: Belongs in the complexity lint group good-first-issue These issues are a good way to get started with Clippy A-lint Area: New lints L-suggestion Lint: Improving, adding or fixing lint suggestions labels Sep 4, 2019
@derivmug
Copy link

derivmug commented Sep 4, 2019

I would like to work on this as a first issue.

@flip1995
Copy link
Member

flip1995 commented Sep 4, 2019

Awesome, don't hesitate to ask questions, if you have any!

@derivmug
Copy link

derivmug commented Sep 4, 2019

I am not sure whether this lint would be EarlyLintPass or LateLintPass. I guess EarlyLintPass as it doesn't need to check any types, is that right?

@flip1995
Copy link
Member

flip1995 commented Sep 5, 2019

You don't really have to care about that. You can just add another case here

match method_names.as_slice() {
["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false),
["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),

and implement the lint from there.

You should create a new file in the methods module and implement it there instead of mod.rs directly. See:

["unwrap_or", arith @ "checked_add"]
| ["unwrap_or", arith @ "checked_sub"]
| ["unwrap_or", arith @ "checked_mul"] => {
manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
},

manual_saturating_arithmetic


But to answer your question: This should be a LateLintPass, since you have to make sure, that the type map is called on is an Iterator

@derivmug
Copy link

derivmug commented Sep 5, 2019

Ok, thank you for the explanation.
I have added the following to the match statement and also created the lint using declare_clippy_lint! and appended it to declare_lint_pass! inside the mod.rs file, however it doesn't seem to match over my test. I have used the example above as the test.

            ["map", "flatten"] => {
                println!("LINT CALLED");
                map_flatten_filtermap::lint(cx, expr, arg_lists[0])
            }

(I am relatively new to Rust and this is the first time I am writing a lint, sorry for asking so much)

@flip1995
Copy link
Member

flip1995 commented Sep 6, 2019

From https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md#lint-declaration:

Next we need to run util/dev update_lints to register the lint in various places, mainly in clippy_lints/src/lib.rs.

Have you done this? If not, run util/dev update_lints and try running your test again. If you have, I don't really know what could cause this from your description, so please open a WIP PR, so I can have a look at your code.

@derivmug
Copy link

derivmug commented Sep 7, 2019

I ran it but still no luck, I have opened a WIP PR here: #4521
Thank you for all your help :)

@ghost
Copy link

ghost commented Sep 9, 2019

Do we need a new lint for this? map_flatten already lints on this code. I think this would have to be modification to that lint. Just check if the map closure produces Options and recommend filter_map instead of flat_map.

@derivmug
Copy link

I have noticed that the map_flatten lint already lints my example. I'll try to modify the other lint instead.

flip1995 added a commit to flip1995/rust-clippy that referenced this issue Aug 4, 2020
…flip1995

Handle mapping to Option in `map_flatten` lint

Fixes rust-lang#4496

The existing [`map_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten) lint suggests changing `expr.map(...).flatten()` to `expr.flat_map(...)` when `expr` is `Iterator`. This PR changes suggestion to `filter_map` instead of `flat_map` when mapping to `Option`, because it is more natural

Also here are some questions:
* If expression has type which implements `Iterator` trait (`match_trait_method(cx, expr, &paths::ITERATOR) == true`), how can I get type of iterator elements? Currently I use return type of closure inside `map`, but probably it is not good way
* I would like to change suggestion range to cover only `.map(...).flatten()`, that is from:
```
    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map
```
to
```
    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
                                             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
```
Is it ok?
* Is `map_flatten` lint intentionally in `pedantic` category, or could it be moved to `complexity`?

changelog: Handle mapping to Option in [`map_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten) lint
@bors bors closed this as completed in 378ba2e Aug 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints good-first-issue These issues are a good way to get started with Clippy L-complexity Lint: Belongs in the complexity lint group L-suggestion Lint: Improving, adding or fixing lint suggestions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants