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

Clear with drain #10528

Merged
merged 13 commits into from
Mar 27, 2023
Merged

Clear with drain #10528

merged 13 commits into from
Mar 27, 2023

Conversation

bluthej
Copy link
Contributor

@bluthej bluthej commented Mar 22, 2023

changelog: [clear_with_drain]: Add new lint

Fixes #9339

@rustbot
Copy link
Collaborator

rustbot commented Mar 22, 2023

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @llogiq (or someone else) soon.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 22, 2023
v.drain(1..v.len() - 1); // Yay
}

fn main() {}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to see some test cases where the drain iterator is actually used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would you like me to add some code that uses the iter variables in the existing test cases? Or did you mean that I should write some additional tests, like for instance:

let mut v = vec![1, 2, 3];
for x in v.drain(..) {
    // do something with x
}

Copy link
Contributor

Choose a reason for hiding this comment

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

We should rule out both cases. Even if it's v.drain(..).count(), the lint should not trigger.

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 added some tests where the drain iterator is used, in both the 'should trigger' and 'should not trigger' cases

/// Checks whether the given `Range` is equivalent to a `RangeFull`.
/// Inclusive ranges are not considered because they already constitute a lint.
pub fn is_range_full(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
range.start.map_or(true, |e| is_integer_const(cx, e, 0))
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you made this a clippy_utils function, we should take care to either document any possible shortcomings or rule them out. For example, I think the method will fail on a Range<i8>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thx for pointing that out! It didn't cross my mind...

Unfortunately I don't see a way to make it work for signed integers because right now the test on the lower bound is performed with is_integer_const, which takes a u128 as a value to check for, so I can't check for i8::MIN.

Do you have some way that that could be accomplished in mind? Otherwise, I will document the function more precisely.

I have to admit that the applicability of this function is a bit awkward, for example I don't think it works on ..usize::MAX either. It was made specifically to detect RangeFulls or equivalents used to index Vecs and VecDeques, but the way it's written it also works in a few other cases. Maybe I can tweak the implementation to only check for RangeTo, RangeFrom and Range if the range is the argument of a method call of a Vec or VecDeque, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess the easiest way is to rename to is_usize_range_full and document the restricted applicability of the method. Otherwise you can get the type (via expr_ty), then the generic, then match on that to figure out the bounds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thx for the pointer, I will try to get the generic case working and if I really can't I will fall back to the usize solution with added documentation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool! Feel free to ping us here or on zulip if you need further assistance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It took a fair amount of work but I think I got it! At least it works as expected in my use case, and I added a couple of tests where I use usize::MIN as a lower bound to make sure that works as well (it already worked before with the hard-coded 0 but I thought it would be nice to make sure that it works).

I haven't added tests for the case where signed integers are used because this lint only applies in a method call to drain which only takes usize ranges. Do you need me to add unit tests for the new is_range_full function before accepting this PR? If so, where are such tests placed in the repo?

Note that I had to significantly alter the function signature of is_range_full to make it more generic. For instance, I made the container optional so that this function can be called on any kind of range.

@llogiq
Copy link
Contributor

llogiq commented Mar 22, 2023

I only found a few nits, with the additional tests and the wrong lower bound in the is_range_full function documented or fixed (it only works correct for unsigned types, because for signed types MIN != 0), I'd gladly merge this.

Make this function work with signed integer types by extracting the
underlying type and finding the min and max values.

Change the signature to make it more consistent:
- The range is now given as an `Expr` in order to extract the type
- The container's path is now passed, and only as an `Option` so that
  the function can be called in the general case without a container
let iter = v.drain(0..); // Yay

let mut v = vec![1, 2, 3];
let next = v.drain(0..).next(); // Yay
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we have an example where the iter is only used later?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing!

@llogiq
Copy link
Contributor

llogiq commented Mar 27, 2023

Thank you!

@bors r+

@bors
Copy link
Collaborator

bors commented Mar 27, 2023

📌 Commit df65d21 has been approved by llogiq

It is now in the queue for this repository.

@bors
Copy link
Collaborator

bors commented Mar 27, 2023

⌛ Testing commit df65d21 with merge 70db226...

@bors
Copy link
Collaborator

bors commented Mar 27, 2023

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: llogiq
Pushing 70db226 to master...

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: vec.drain(..) instead of vec.clear()
4 participants