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

Add option_and_then_some lint #4386

Merged
merged 3 commits into from
Aug 19, 2019
Merged

Conversation

tesuji
Copy link
Contributor

@tesuji tesuji commented Aug 15, 2019

changelog: Add complexity lint to warn about option.and_then(|o| Some(x)) and suggest replacing with option.map(|o| x).

Closes #4299

@flip1995
Copy link
Member

It seems that this lint would fit in the methods module.

@flip1995 flip1995 added the S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) label Aug 15, 2019
@tesuji
Copy link
Contributor Author

tesuji commented Aug 15, 2019

Looks like this lint conflicts with tests in

// Check `OPTION_MAP_OR_NONE`.
// Single line case.
let _ = opt.map_or(None, |x| Some(x + 1));
// Multi-line case.
#[rustfmt::skip]
let _ = opt.map_or(None, |x| {
Some(x + 1)
});

option_map_or_none lint suggest to use x.and_then(|v| Some(v.len())), but then this lint suggest
to use x.map(|v| v.len()).

Should I fix those test?

@flip1995
Copy link
Member

2 step linting is totally fine. So I think adding an allow(option_and_then_some) to the test file is the best solution here.

@tesuji
Copy link
Contributor Author

tesuji commented Aug 15, 2019

What about linting against .map_or(None, |x| y) and suggesting to use map(|x| y)
instead?

@flip1995
Copy link
Member

That would also be possible. I don't really know how this lint is implemented, so this is a shot in the dark: My concern with this is, that the lint of map_or(None, expr) (porbably) just copies the expr of the closure and builds a suggestion from there. Directly suggesting map(expr') would require to change every return of expr of type Option<T> to a return of type T. Which obviously is way harder to do.

What could be done is, to add a special case to option_map_or_none that suggests map directly in the easy cases (similar to your implementation of this lint)

@bors
Copy link
Collaborator

bors commented Aug 15, 2019

☔ The latest upstream changes (presumably #4348) made this pull request unmergeable. Please resolve the merge conflicts.

@tesuji tesuji force-pushed the lint-option-and_then-some branch 2 times, most recently from 110dd53 to e21a363 Compare August 16, 2019 02:42
@tesuji
Copy link
Contributor Author

tesuji commented Aug 16, 2019

Blocked on #4395

@tesuji tesuji force-pushed the lint-option-and_then-some branch 2 times, most recently from 9317ed9 to af15539 Compare August 16, 2019 19:45
@tesuji
Copy link
Contributor Author

tesuji commented Aug 16, 2019

Ready for another round of review! 🎉

@tesuji tesuji changed the title [WIP] Add option_and_then_some lint Add option_and_then_some lint Aug 16, 2019
clippy_lints/src/methods/mod.rs Outdated Show resolved Hide resolved

// Different type
let x: Result<u32, &str> = Ok(1);
let _ = x.and_then(Ok);
Copy link
Member

Choose a reason for hiding this comment

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

Couldn't be the lint be applied to Result as well?

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 think we should open a different issue and PR.

@tesuji
Copy link
Contributor Author

tesuji commented Aug 18, 2019

So I have a counter example which we do not want to lint:

fn foo() -> Option<String> {
    let x = Some(String::from("hello"));
    Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
}

How do I check if expression in Some have try block?

@flip1995
Copy link
Member

You can write a visitor, that walks the expression inside the Some(_) and searches for return expressions.

You can search the Clippy repo on how to implement the Iterator or have a look at the documentation: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/hir/intravisit/trait.Visitor.html

@tesuji
Copy link
Contributor Author

tesuji commented Aug 19, 2019

Tests passed locally.

@ghost
Copy link

ghost commented Aug 19, 2019

I made a Visitor like that in #3427. I prefer this one but you should probably check for return expressions as well. Does the lint handle the case below correctly?

pub fn example2(x: bool) -> Option<&'static str> {
    Some("a").and_then(|s| Some(if x { "b" } else { return None } ))
}

fn main() {
    println!("{:?}", example2(true));
    println!("{:?}", example2(false));
}

We should probably make a common function for this in util but maybe not in this PR.

@flip1995
Copy link
Member

Thanks!

@bors r+

@bors
Copy link
Collaborator

bors commented Aug 19, 2019

📌 Commit 41eba2f has been approved by flip1995

@bors
Copy link
Collaborator

bors commented Aug 19, 2019

⌛ Testing commit 41eba2f with merge d1f1844...

bors added a commit that referenced this pull request Aug 19, 2019
Add option_and_then_some lint

changelog: Add complexity lint to warn about `option.and_then(|o| Some(x))` and suggest replacing with `option.map(|o| x)`.

Closes #4299
@bors
Copy link
Collaborator

bors commented Aug 19, 2019

☀️ Test successful - checks-travis, status-appveyor
Approved by: flip1995
Pushing d1f1844 to master...

@bors bors merged commit 41eba2f into rust-lang:master Aug 19, 2019
@tesuji tesuji deleted the lint-option-and_then-some branch August 19, 2019 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New lint: and_then_some
3 participants