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 [manual_find] lint for function return case #8649

Merged
merged 1 commit into from
Jun 27, 2022

Conversation

ebobrow
Copy link
Contributor

@ebobrow ebobrow commented Apr 6, 2022

part of the implementation discussed in #7143

changelog: add [manual_find] lint for function return case

@rust-highfive
Copy link

r? @flip1995

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Apr 6, 2022
@xFrednet
Copy link
Member

xFrednet commented Apr 19, 2022

r? @Alexendoo (I'll keep @flip1995 assigned, as either one of us might do the final review 🙃)

@xFrednet xFrednet self-assigned this Apr 19, 2022
Copy link
Member

@Alexendoo Alexendoo left a comment

Choose a reason for hiding this comment

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

Hi, thanks for the PR!

So far I've mostly been looking at the pattern/ident stuff, I think it could do with being more conservative to deal with all the wild patterns out there, say for where PAT binds binding

for PAT in input {
    if COND {
        return Some(binding);
    }
    None
}

default to the assumption we're going to suggest

input.[into_iter/iter/etc]().map(|PAT| binding).find(|&binding| COND)

Then add more focused variants, like if PAT is already a PatKind::Binding omit the .map(), or if it's a single ref to a binding use .copied() instead

clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
@xFrednet
Copy link
Member

xFrednet commented May 5, 2022

Hey @ebobrow, this is a ping from triage, can take a look at the review comments and address them? 🙃

@rustbot author

@rustbot rustbot 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 May 5, 2022
@ebobrow
Copy link
Contributor Author

ebobrow commented May 5, 2022

Hey, sorry about that! I've been really busy lately but I'll see if I can work on it this weekend.

@ebobrow
Copy link
Contributor Author

ebobrow commented May 7, 2022

Thanks for the review @Alexendoo and apologies for the delay! I think this should address your comments.

@ebobrow ebobrow force-pushed the imperative_find branch 2 times, most recently from 2618c73 to fb48453 Compare May 7, 2022 22:58
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
@ebobrow ebobrow force-pushed the imperative_find branch 2 times, most recently from a88210c to c47ebef Compare May 14, 2022 22:25
@ebobrow
Copy link
Contributor Author

ebobrow commented May 14, 2022

Thanks for the review! This should address your comments.

clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
Comment on lines 58 to 73
match cond.kind {
ExprKind::Binary(_, l, r) => {
for side in [l, r] {
if !matches!(
side.kind,
ExprKind::MethodCall(_, [caller, ..], _) if ident_in_expr(cx, ident, caller)
) && ident_in_expr(cx, ident, side)
{
borrows = 1;
}
}
},
ExprKind::Call(..) => borrows = 1,
ExprKind::MethodCall(_, [caller, ..], _) => {
if !ident_in_expr(cx, ident, caller) {
borrows = 1;
}
},
_ => {},
}
Copy link
Member

Choose a reason for hiding this comment

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

I have found quite the spanner to throw in the works:

fn vec_string(strings: Vec<String>) -> Option<String> {
    for s in strings {
        if s == String::new() {
            return Some(s);
        }
    }
    None
}

both |s| and |&s| would result in errors:

error[E0277]: can't compare `&std::string::String` with `std::string::String`
  --> t.rs:25:36
   |
25 |     strings.into_iter().find(|s| s == String::new())
   |                                    ^^ no implementation for `&std::string::String == std::string::String`
   |
   = help: the trait `std::cmp::PartialEq<std::string::String>` is not implemented for `&std::string::String`
error[E0507]: cannot move out of a shared reference
  --> t.rs:25:31
   |
25 |     strings.into_iter().find(|&s| s == String::new())
   |                               ^-
   |                               ||
   |                               |data moved here
   |                               |move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait
   |                               help: consider removing the `&`: `s`

I believe the only way to create a correct suggestion here would be

strings.into_iter().find(|s| *s == String::new())

Copy link
Member

Choose a reason for hiding this comment

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

One thing that you could do is check if inner_ret is Copy, in that case you can add the extra borrow unconditionally and the body of find can be unchanged

For the non Copy case it's more tricky, I think turning every use of ident into (*ident) would be correct, though it would be a fair bit of work creating such a snippet. I'd need to think a bit longer on the cases where you can leave it as ident

Another option would be to have an Applicability::HasPlaceholders suggestion for the non-Copy case, e.g. for the strings case suggesting

strings.into_iter().find(|s| { ... })

Copy link
Contributor Author

@ebobrow ebobrow May 15, 2022

Choose a reason for hiding this comment

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

I went with the Applicability::HasPlaceholders implementation for now, but I can change it to dereference each ident. The only way I can think to do that would be to walk the tree and add partial snippets with asterisks and parentheses in between.

As for the cases that don't have to be dereferenced, all I can think of so far is a method called on ident or maybe if PartialEq is implemented for a struct and a reference to the struct (so we can call &Data(1) == Data(1)). I'm probably missing some.

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.

Thanks for the review @Alexendoo!

I left some more comments. Their mostly about how to improve the suggestion a bit further and making the code more maintainable by adding a few more comments.

clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/manual_find.rs Outdated Show resolved Hide resolved
clippy_lints/src/loops/mod.rs Outdated Show resolved Hide resolved
@ebobrow
Copy link
Contributor Author

ebobrow commented May 17, 2022

Thanks for the review @flip1995! I've applied your suggestions. I didn't end up using strip_return because I already had to do most of the pattern matching there to check if the last statement was a return, but let me know if I'm missing something and should use it.

@xFrednet xFrednet removed their assignment May 17, 2022
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.

Thanks for addressing this so quickly! I have one more question left.

let is_ref_to_binding =
matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..)));
// If `pat` is not a binding or a reference to a binding (`x` or `&x`)
// we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
Copy link
Member

Choose a reason for hiding this comment

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

Why can't you do this in the find(|(x, _)| ...) closure?

Copy link
Contributor Author

@ebobrow ebobrow May 17, 2022

Choose a reason for hiding this comment

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

If we did that, the function would return the tuple instead of just the x. In the imperative version, the code would look something like

for (x, _) in arr {
    if /* ... */ {
        return Some(x);
    }
}

so it only returns the x and ignores the rest of the pattern, but in a find the elements in the iterator remain as the entire tuple.

@ebobrow
Copy link
Contributor Author

ebobrow commented Jun 25, 2022

Hey @flip1995, just checking in! Are you going to review this or have you been waiting for me to do something? (Apologies if so!)

@flip1995
Copy link
Member

I think you answered my question. I'll have another look on the PR soon and will then merge it. I don't think there's anything left for you to do.

Sorry for taking so long. My real life is a bit busy recently, so open source suffers... :/

@ebobrow
Copy link
Contributor Author

ebobrow commented Jun 25, 2022

No worries! I'm in no rush, just wanted to make sure this hadn't been lost.

@flip1995
Copy link
Member

Thanks for the patience again!

@bors r+

@bors
Copy link
Collaborator

bors commented Jun 27, 2022

📌 Commit 430575b has been approved by flip1995

@bors
Copy link
Collaborator

bors commented Jun 27, 2022

⌛ Testing commit 430575b with merge fdd0e72...

@bors
Copy link
Collaborator

bors commented Jun 27, 2022

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

@bors bors merged commit fdd0e72 into rust-lang:master Jun 27, 2022
@ebobrow ebobrow deleted the imperative_find branch June 27, 2022 17:39
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.

None yet

7 participants