Skip to content

New lint hashset_insert_after_contains #11296

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

Closed
wants to merge 3 commits into from

Conversation

lochetti
Copy link
Contributor

@lochetti lochetti commented Aug 5, 2023

This PR closes 11103.

This is my first PR creating a new lint, so I am opening as a draft and thanks for the patience :)

I also have used the simpler span_lint() when emitting the lint. If the reviewers think that it is not enough, I can try to evolve the code the use the others span_lint functions.

The idea of the lint is to find insert in hashmanps inside if staments that are checking if the hashmap contains the same value that is being inserted. This is not necessary since you could simply call the insert and check for the bool returned if you still need the if statement.

changelog: new lint: [hashset_insert_after_contains]

@rustbot
Copy link
Collaborator

rustbot commented Aug 5, 2023

r? @Manishearth

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Aug 5, 2023
Copy link
Member

@Centri3 Centri3 left a comment

Choose a reason for hiding this comment

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

Pretty good for a first lint, thanks!

@lochetti
Copy link
Contributor Author

lochetti commented Aug 7, 2023

Pretty good for a first lint, thanks!

Thank you for the review, @Centri3 ! The code is looking way simpler with your suggestions :)

Could you please take a look again and let me know what you think? The changes are in the commit

@Manishearth
Copy link
Member

r? @Centri3 you've already reviewed most of this 😄

@rustbot rustbot assigned Centri3 and unassigned Manishearth Aug 7, 2023
@lochetti lochetti marked this pull request as ready for review August 8, 2023 07:27
kind: ExprKind::AddrOf(_, _, value),
span: value_span,
..
},
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't account for if it's not a reference. Of course, contains takes a reference but it's free to be x or something if x is &Q. I'd say just call peel_borrows here and also do so for insert_expr.value.

) = expr.kind
{
let receiver_ty = cx.typeck_results().expr_ty(receiver);
if value_span.ctxt() == expr.span.ctxt()
Copy link
Member

Choose a reason for hiding this comment

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

If the first argument and the call itself are both from the same expansion, this won't catch it. I'd just recommend calling from_expansion on value_span since that'll also check expr.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's see if I understood the changes that I need to apply here:

  1. Remove the if value_span.ctxt() == expr.span.ctxt() comparison.
  2. Check if value_span is not from an expansion. If it is from an expasion, we will not lint
  3. Check if value_span is nto from external_macro. If it is from an external macro, we will not lint.

Is it correct?

@lochetti
Copy link
Contributor Author

Hey @Centri3 !

I think I have addressed almost everything from you comments.
I have made the lint work for three more cases that were not being linted before:

if (&set).contains(&value) {
    set.insert(value);
}
let borrow_value = &6;
if !set.contains(borrow_value) {
    set.insert(*borrow_value);
}
let borrow_set = &mut set;
if !borrow_set.contains(&value) {
    borrow_set.insert(value);
}

But I could not understand your comment about from_expansion, I am sorry.
Do you mind going in more details, please? Probably I am missing some knowledge here. If you could also come up with a specific test case -if it is not hard, of course- that is not being addressed, it would make it easier for me to understand :)

Thanks! :)

@Centri3
Copy link
Member

Centri3 commented Aug 14, 2023

The issue with ctxt here is that if they both come from the same context, then I think this will wrongly trigger. This is fine for root (no expansions), this is what you want to check for after all, but if they both come from the same expansion it will wrongly trigger.

However, I think ignoring all expansions isn't the right way to go about this; if it happens to expand to code that references a local, and also inserts into it, then it was likely intentionally done (if the local isn't also from an expansion, of course). So if it's not external, it should still lint.


impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !expr.span.from_expansion()
Copy link
Member

Choose a reason for hiding this comment

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

This probably also needs an is_from_proc_macro check right before emitting

span: Span,
}
fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<ContainsExpr<'tcx>> {
let expr = peel_hir_expr_while(expr, |e| match e.kind {
Copy link
Member

Choose a reason for hiding this comment

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

This could be an if let.

Comment on lines +55 to +56
&& find_insert_calls(cx, &contains_expr, then_expr) {
span_lint_and_then(
Copy link
Member

Choose a reason for hiding this comment

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

Very nitpicky, but:

Suggested change
&& find_insert_calls(cx, &contains_expr, then_expr) {
span_lint_and_then(
&& find_insert_calls(cx, &contains_expr, then_expr)
{
span_lint_and_then(

fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
if let ExprKind::MethodCall(path, receiver, [value], _) = expr.kind {
let value = value.peel_borrows();
let value = peel_hir_expr_while(value, |e| match e.kind {
Copy link
Member

Choose a reason for hiding this comment

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

Same here

@bors
Copy link
Contributor

bors commented Aug 23, 2023

☔ The latest upstream changes (presumably #11373) made this pull request unmergeable. Please resolve the merge conflicts.

@Centri3
Copy link
Member

Centri3 commented Feb 6, 2024

Hey @lochetti, any progress on this? This doesn't need too many more changes. Do you need help on anything?

@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 Feb 6, 2024
@lochetti
Copy link
Contributor Author

lochetti commented Feb 7, 2024

Hey @Centri3 hello! Sorry for that long delay, but I think I will not be able to finish the PR 😬
I am sorry.

@Centri3
Copy link
Member

Centri3 commented Feb 7, 2024

No worries about it! I'll close it then and others can finish it if they wish. Feel free to create a new PR if you wish :3

@Centri3 Centri3 closed this Feb 7, 2024
@Centri3 Centri3 added the S-inactive-closed Status: Closed due to inactivity label Feb 7, 2024
@lochetti
Copy link
Contributor Author

I have opened a new PR to try to continue the work :)
It is the #12873

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-inactive-closed Status: Closed due to inactivity 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.

Optimize HashSet contains+insert usage
5 participants