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

Incorrect suggestion in "unnecessary-sort-by" #6001

Closed
RalfJung opened this issue Sep 2, 2020 · 5 comments · Fixed by #6006 or #6078
Closed

Incorrect suggestion in "unnecessary-sort-by" #6001

RalfJung opened this issue Sep 2, 2020 · 5 comments · Fixed by #6006 or #6078

Comments

@RalfJung
Copy link
Member

RalfJung commented Sep 2, 2020

This code triggers the following suggestion:

error: use Vec::sort_by_key here instead
   --> src/report/mod.rs:223:5
    |
223 |     crates.sort_unstable_by(|a, b| a.id().cmp(&b.id()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crates.sort_unstable_by_key(|&a| a.id())`
    |
    = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by

However, the suggested code does not work: &a is rejected as a pattern, it says I "cannot move out of a shared reference". using just a for the closure argument works.

@ebroto
Copy link
Member

ebroto commented Sep 3, 2020

I will work on this one

bors added a commit that referenced this issue Sep 6, 2020
Restrict unnecessary_sort_by to non-reference, Copy types

`Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K`. The lint's suggestion destructures the `T` parameter; this was probably done to avoid different unnamed lifetimes when `K = Reverse<&T>`.

This change fixes two issues:
* Destructuring T when T is non-reference requires the type to be Copy, otherwise we would try to move from a shared reference. We make sure `T: Copy` holds.
* Make sure `T` is actually non-reference. I didn't go for destructuring multiple levels of references, as we would have to compensate in the closure body by removing derefs and maybe adding parens, which would add more complexity.

changelog: Restrict [`unnecessary_sort_by`] to non-reference, Copy types

Fixes #6001
@bors bors closed this as completed in daad592 Sep 6, 2020
@RalfJung
Copy link
Member Author

RalfJung commented Sep 6, 2020

Restrict unnecessary_sort_by to non-reference, Copy types

Hm, that's not really a fix, is it? The lint was very useful here and helped improve the code. Just the auto-suggestions was wrong.

@ebroto
Copy link
Member

ebroto commented Sep 6, 2020

The problem is that the lint suggests using Reverse where applicable

pub struct Reverse<T>(pub T);

For example in this case:

    // Reverse examples
    vec.sort_by(|a, b| b.cmp(a));

The suggestion ends up being:

    vec.sort_by_key(|b| Reverse(b));

If we don't destructure the referenced type, we hit rust-lang/rust#34162

error: lifetime may not live long enough
  --> /home/ebroto/src/ebroto-clippy/tests/ui/unnecessary_sort_by.fixed:19:25
   |
19 |     vec.sort_by_key(|b| Reverse(b));
   |                      -- ^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                      ||
   |                      |return type of closure is Reverse<&'2 isize>
   |                      has type `&'1 isize`

error: aborting due to previous error

But after giving this a bit more thought I agree and this is too restrictive, we should be able to identify when the argument to Reverse is a reference.

I will give this another shot.

@ebroto ebroto reopened this Sep 6, 2020
@RalfJung
Copy link
Member Author

RalfJung commented Sep 7, 2020

Or maybe if the situation is too complicated (references / non-Copy types) it could not to the Reverse thing, but still detect cmp like before to suggest the by_key? That seems like it is unrelated in principle, though I know little about how clippy detects these things.

@wpbrown
Copy link

wpbrown commented Sep 12, 2020

I ran in to failure with the suggestion applied to snapshots.sort_unstable_by(|a, b| b.datetime().cmp(&a.datetime())) where a and b are borrowed. I didn't know about Reverse :)

If we don't destructure the referenced type, we hit rust-lang/rust#34162

In my case I'm just taking the reference and giving Reverse an owned Copy type... DateTime, so it works fine snapshots.sort_unstable_by_key(|b| Reverse(b.datetime())).

Thank you for working on this. This is a great clippy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants