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

feat: new lint for and_then when returning Option or Result #14051

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

aaron-ang
Copy link
Contributor

close #6436

changelog: add new return_and_then lint

Additional src files changed to reflect the lint.

@rustbot
Copy link
Collaborator

rustbot commented Jan 21, 2025

r? @Manishearth

rustbot has assigned @Manishearth.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jan 21, 2025
clippy_lints/src/methods/mod.rs Show resolved Hide resolved
clippy_lints/src/methods/mod.rs Show resolved Hide resolved
clippy_lints/src/methods/return_and_then.rs Outdated Show resolved Hide resolved
clippy_lints/src/methods/return_and_then.rs Outdated Show resolved Hide resolved
clippy_lints/src/methods/return_and_then.rs Outdated Show resolved Hide resolved
@Manishearth
Copy link
Member

r? @samueltardieu

bit swamped rn

@rustbot
Copy link
Collaborator

rustbot commented Jan 22, 2025

Could not assign reviewer from: samueltardieu.
User(s) samueltardieu are either the PR author, already assigned, or on vacation. Please use r? to specify someone else to assign.

@Manishearth
Copy link
Member

r? clippy

@rustbot rustbot assigned Centri3 and unassigned Manishearth Jan 22, 2025
Copy link
Contributor

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

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

You need to check that the expression you split in two parts doesn't use references to temporaries between those parts.

For example, the following code will fail

        String::from("<BOOM>")
            .strip_prefix("<")
            .and_then(|s| s.strip_suffix(">").map(String::from))

as the suggestion to use

        let s = String::from("<BOOM>")
            .strip_prefix("<")?;
        s.strip_suffix(">").map(String::from)

will not pass the borrow checker.

Maybe you could, after you check that the receiver is Option or Result, use something like:

    if let ty::Adt(_, args) = recv_type.kind()
        && args.iter().next().and_then(GenericArg::as_type).is_some_and(Ty::is_ref)
        && for_each_unconsumed_temporary(cx, recv, |_| ControlFlow::Break(())).is_break()
    {
        return;
    }

I've not checked if it would cover all the cases, as the lambda might not be called on non droppable types according to the comment on for_each_unconsumed_temporary(), but in practice it seems to work well.

clippy_lints/src/methods/return_and_then.rs Outdated Show resolved Hide resolved
clippy_lints/src/methods/return_and_then.rs Outdated Show resolved Hide resolved
clippy_lints/src/methods/return_and_then.rs Outdated Show resolved Hide resolved
@Centri3
Copy link
Member

Centri3 commented Jan 22, 2025

Happy to merge when Samuel approves 👍

@aaron-ang
Copy link
Contributor Author

You need to check that the expression you split in two parts doesn't use references to temporaries between those parts.

For example, the following code will fail

        String::from("<BOOM>")
            .strip_prefix("<")
            .and_then(|s| s.strip_suffix(">").map(String::from))

as the suggestion to use

        let s = String::from("<BOOM>")
            .strip_prefix("<")?;
        s.strip_suffix(">").map(String::from)

will not pass the borrow checker.

Maybe you could, after you check that the receiver is Option or Result, use something like:

    if let ty::Adt(_, args) = recv_type.kind()
        && args.iter().next().and_then(GenericArg::as_type).is_some_and(Ty::is_ref)
        && for_each_unconsumed_temporary(cx, recv, |_| ControlFlow::Break(())).is_break()
    {
        return;
    }

I've not checked if it would cover all the cases, as the lambda might not be called on non droppable types according to the comment on for_each_unconsumed_temporary(), but in practice it seems to work well.

thanks for the comments! I've incorporated the suggestions :)

@samueltardieu
Copy link
Contributor

samueltardieu commented Jan 23, 2025

This is starting to look very good. Could you please squash this? The ideal would be two commits (so that other people can easily review the lint code and its effects separately):

  • one commit which implements the lint.
  • one commit which applies it to Clippy sources.

I've started a FCP thread on Zulip.

@samueltardieu
Copy link
Contributor

Also, this work could probably extend to such expressions placed after an explicit return as well, but this can be done in another PR.

@aaron-ang
Copy link
Contributor Author

Also, this work could probably extend to such expressions placed after an explicit return as well, but this can be done in another PR.

wouldn't https://rust-lang.github.io/rust-clippy/master/index.html#needless_return catch that?

@samueltardieu
Copy link
Contributor

samueltardieu commented Jan 23, 2025

wouldn't https://rust-lang.github.io/rust-clippy/master/index.html#needless_return catch that?

I'm specifically talking about a non-needless (early) return, i.e. a return which is not the last expression of a function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New Lint: Use question mark instead of Option::and_then
6 participants