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: Manual Flatten #6646

Merged
merged 9 commits into from
Feb 4, 2021

Conversation

nahuakang
Copy link
Contributor

@nahuakang nahuakang commented Jan 27, 2021

This is a draft PR for Issue 6564.

r? @camsteffen

  • Followed [lint naming conventions][lint_naming]
  • Added passing UI tests (including committed .stderr file)
  • cargo test passes locally
  • Executed cargo dev update_lints
  • Added lint documentation
  • Run cargo dev fmt

Please write a short comment explaining your change (or "none" for internal only changes)
changelog: Add new lint [manual_flatten] to check for loops over a single if let expression with Result or Option.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jan 27, 2021
@nahuakang
Copy link
Contributor Author

@camsteffen Worked to this barebone version that checks for for loops and a single if let statement on Option/Result.

I have a few questions I'd like to ask for help on:

  1. Is there a way I can check if the arg is an Iterator or implements IntoIterator so as to adjust the hint to cater to the arg type? Would it make sense to adjust the lint this way?
  2. For using span_lint_and_help, is there a way to highlight the if let statement as well as the arg?

Thanks a lot 🙏

@camsteffen
Copy link
Contributor

Good start!

r? @camsteffen

I'm not sure if I'm qualified as a reviewer around here 😄 but I'm glad to help you with this!

Is there a way I can check if the arg is an Iterator or implements IntoIterator so as to adjust the hint to cater to the arg type? Would it make sense to adjust the lint this way?

You mean for x in if let Some(y) = x? Hmm I don't think it will help to check x in that way. Once we see if let Some, we already know that Option implements IntoIterator. If some unrecognized enum implements IntoIterator, we can't assume it is implemented in a way that flatten() would be equivalent.

Here is a good example for what you are doing.

For using span_lint_and_help, is there a way to highlight the if let statement as well as the arg?

Sure. That method takes two Spans. Just use the Span from the Expr with ExprKind::Match and then the Span from the Expr inside: ExprKind::Match(Expr, ..). I assume the "help" would be "this implements IntoIterator" which is great!

Finally, I'd suggest naming the lint manual_flatten. The lint name typically should describe what is wrong with the code.

@camsteffen
Copy link
Contributor

To clarify about the lint name, a for loop over Option is not inherently "bad". It is only "bad" if Nones are just skipped over.

@flip1995
Copy link
Member

I'm not sure if I'm qualified as a reviewer around here

@camsteffen The question is if you want to get qualified as a reviewer here. :)

I think this is a "good-first-review" PR. If you think this PR is ready, just ping me to take a quick look and then merge it 👍

@camsteffen
Copy link
Contributor

The question is if you want to get qualified as a reviewer here. :)

Sure!

just ping me

Will do.

@nahuakang
Copy link
Contributor Author

@camsteffen Hey Cam :) Thanks for the help. I did not explain myself well (and perhaps don't understand the language well enough either). There are two scenarios from what I can see:

// Scenario 1: probably a rare case
for n in vec![Some(1), Some(2), Some(3)].iter() {
    if let Some(n) = n {
        println!("{}", n);
    }
}

// Scenario 2
let x = vec![Some(1), Some(2), Some(3)];
for n in x {
    if let Some(n) = n {
        println!("{}", n);
    }
}

For the first one, if we recommend flatten, we'd only need to say:

Consider using flatten on the Iterator: vec![Some(1), Some(2), Some(3)].iter().flatten()
For the second one, if we recommend flatten, we'd need to say:
x implements IntoIterator. Consider using flatten on the Iterator: x.iter().flatten().

So my first original question was how we could know if the type for the arg as in for ... in arg is an Iterator or it implements IntoIterator?

@camsteffen
Copy link
Contributor

Ah right, that is a little tricky.

In for x in iter, iter implements IntoIterator as a rule. The question is whether iter is already an Iterator or if there is an implicit call to into_iter(). Implicit adjustments like this are called..."adjustments". You can get the type of an expression pre-adjustment using cx.typeck_results().expr_ty(_) (rather than expr_ty_adjusted). Check whether that type implements Iterator using get_trait_def_id and implements_trait from utils.

@nahuakang nahuakang marked this pull request as ready for review January 30, 2021 20:15
@nahuakang
Copy link
Contributor Author

nahuakang commented Jan 30, 2021

@camsteffen Check out commit 005b0fd. The new lint already brought an internal improvement via dogfood 🐶

Edit: I decided to do the linting as show in the stderr for now. Though it's not ideal, it marks both the for pat in arg as well as the if let statement. Did not make parent_iter work properly 🙈

@nahuakang nahuakang changed the title For loops over options or results New Lint: Manual Flatten Jan 30, 2021
@camsteffen
Copy link
Contributor

Add a test for if let None = .. inside the loop (should not lint). I think you will have to adjust for this. See the link I posted above for an example of detecting a Some constructor.

I found out that you can get the Span for the entire for-loop by using arms[0].span in higher::for_loop. If you feel so inclined, you can adjust that function to additionally return a Span.

@camsteffen
Copy link
Contributor

Oh you need to suggest into_iter() instead of iter().

clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@camsteffen camsteffen left a comment

Choose a reason for hiding this comment

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

A couple minor things and then it looks good! Can you rebase onto master and squash into fewer commits?

clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
@nahuakang nahuakang force-pushed the for_loops_over_options_or_results branch from 448cd8c to b87e189 Compare February 1, 2021 16:01
@camsteffen
Copy link
Contributor

@flip1995 ready for final review

Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

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

@camsteffen great first review! You missed some things, that come up in some Clippy PRs, but those are things you'll pick up with experience 😉

@nahuakang Code LGTM so far. Mostly tests are missing (that may result in some changes that need to be made).

tests/ui/manual_flatten.rs Show resolved Hide resolved
clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
tests/ui/manual_flatten.rs Show resolved Hide resolved
@flip1995 flip1995 added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Feb 2, 2021
@flip1995
Copy link
Member

flip1995 commented Feb 2, 2021

r? @flip1995

@rust-highfive rust-highfive assigned flip1995 and unassigned camsteffen Feb 2, 2021
clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

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

Two small things left.

(Remember to bless the tests again after those changes)

clippy_lints/src/loops.rs Outdated Show resolved Hide resolved
tests/ui/manual_flatten.rs Show resolved Hide resolved
@flip1995
Copy link
Member

flip1995 commented Feb 4, 2021

@bors r+

Thanks!

@bors
Copy link
Collaborator

bors commented Feb 4, 2021

📌 Commit 2f8a8d3 has been approved by flip1995

@bors
Copy link
Collaborator

bors commented Feb 4, 2021

⌛ Testing commit 2f8a8d3 with merge 1551f19...

bors added a commit that referenced this pull request Feb 4, 2021
…flip1995

New Lint: Manual Flatten

This is a draft PR for [Issue 6564](#6564).

r? `@camsteffen`

- \[x] Followed [lint naming conventions][lint_naming]
- \[x] Added passing UI tests (including committed `.stderr` file)
- \[x] `cargo test` passes locally
- \[x] Executed `cargo dev update_lints`
- \[x] Added lint documentation
- \[x] Run `cargo dev fmt`

---

*Please write a short comment explaining your change (or "none" for internal only changes)*
changelog:
Add new lint `manual_flatten` to check for loops over a single `if let` expression with `Result` or `Option`.
@bors
Copy link
Collaborator

bors commented Feb 4, 2021

💔 Test failed - checks-action_test

@flip1995
Copy link
Member

flip1995 commented Feb 4, 2021

@bors retry

@bors
Copy link
Collaborator

bors commented Feb 4, 2021

⌛ Testing commit 2f8a8d3 with merge 357c6a7...

@bors
Copy link
Collaborator

bors commented Feb 4, 2021

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: flip1995
Pushing 357c6a7 to master...

@bors bors merged commit 357c6a7 into rust-lang:master Feb 4, 2021
@nahuakang
Copy link
Contributor Author

@flip1995 @camsteffen Thank you both so much for your patience and help!

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.

5 participants