rust-clippy icon indicating copy to clipboard operation
rust-clippy copied to clipboard

Further enhance `needless_borrow`, mildly refactor `redundant_clone`

Open smoelius opened this issue 3 years ago • 20 comments

This PR does the following:

  • Moves some code from redundant_clone into a new clippy_utils module called mir, and wraps that code in a function called dropped_without_further_use.
  • Relaxes the "is copyable" condition condition from #9136 by also suggesting to remove borrows from values dropped without further use. The changes involve the just mentioned function.
  • Separates redundant_clone into modules.

Strictly speaking, the last bullet is independent of the others. redundant_clone is somewhat hairy, IMO. Separating it into modules makes it slightly less so, by helping to delineate what depends upon what.

I've tried to break everything up into digestible commits.

r? @Jarcho

(@Jarcho I hope you don't mind.)

changelog: continuation of #9136

smoelius avatar Aug 27 '22 11:08 smoelius

Just did a quick check. Does the extension to needless_borrow handle closures? I think those have a separate MIR body from the containing function. I think what you have works fine, but it's better to check.

Jarcho avatar Aug 27 '22 13:08 Jarcho

I just add a closure-related test. Please let me know if it doesn't address your specific concern.

smoelius avatar Aug 27 '22 15:08 smoelius

The use of env in the test could have the borrow removed (not trivially, you would have to prove the closure is only called once). It would be better to have some use of it after the closure is created.

As this suggestion changes the drop order it would be better to limit this to types without a significant drop function. You can use has_significant_drop.

Jarcho avatar Aug 27 '22 20:08 Jarcho

It would be better to have some use of it after the closure is created.

This has revealed a bug. Please give me a day or so to look into it.

smoelius avatar Aug 27 '22 22:08 smoelius

Any idea what I might have done to cause this? https://github.com/rust-lang/rust-clippy/runs/8089040744?check_suite_focus=true#step:7:1473

smoelius avatar Aug 30 '22 10:08 smoelius

You can try rebasing and see if that helps. Is it working locally?

Jarcho avatar Aug 30 '22 15:08 Jarcho

I think I figured it out. Sorry for the noise.

Today's changes move the possible_borrower code under clippy_utils::mir so that it can be used in dereference.rs.

The bug I mentioned is resolved, and the PR should be ready for review (again).

smoelius avatar Aug 30 '22 18:08 smoelius

:umbrella: The latest upstream changes (presumably #9400) made this pull request unmergeable. Please resolve the merge conflicts.

bors avatar Sep 03 '22 07:09 bors

Okay to rebase?

smoelius avatar Sep 03 '22 09:09 smoelius

A couple of things here:

  • This can calculate possible borrowers multiple times per function. It also fully recalculates the liveness bitset each time as well. What's the perf impact from this?
  • Some suggestions from this are arguably downgrades (see later comment for example).

Given the second point it might be better to only lint on temporaries.

This strategy feels a little extreme to me. For example, it would seem to eliminate a large number of valid suggestions from https://github.com/rust-lang/rust-clippy/pull/9386/commits/29a705761a03028e61eeabd47057e823c67ef7c3.

To address the first bullet, we could compute the possible borrowers once (lazily) in a check_body implementation, and make the current check_expr a method of a visitor called from check_body (e.g., make the Option<PossibleBorrowMap<...>> a field of the visitor).

To address the second bullet, may I suggest the following alternative? For each body, build a map from def ids to substitutions used in calls to that def id. (This could also be done lazily.) Then, suggest to remove a & from an argument in a call to def_id only if it would not increase the total number of substitutions used in calls to def_id within the body.

Whichever way we go, can we make the current, unhindered strategy something that could be optionally enabled?

smoelius avatar Sep 04 '22 01:09 smoelius

Most of the changes are on temporaries (52/74) by a quick count. Could limit locals to those which are used only once, which would lint almost all cases. The main point is to avoid changing something like:

let x = ...;
use_x(&x);
...
use_x_again(&x);

To address the first bullet, we could compute the possible borrowers once (lazily) in a check_body implementation, and make the current check_expr a method of a visitor called from check_body (e.g., make the Option<PossibleBorrowMap<...>> a field of the visitor).

That would unconditionally add another traversal over every function. which would probably be worse on average. Ideally we could store this in the lint pass, but that's not possible.

Jarcho avatar Sep 04 '22 02:09 Jarcho

That would unconditionally add another traversal over every function.

I thought what I was proposing would conditionally add another traversal (i.e., if all of the conditions were met so that the possible borrowers were needed to proceed).

Why would it be unconditional?

Could limit locals to those which are used only once,

That sounds reasonable to me. Okay to turn this off with an option, though?

smoelius avatar Sep 04 '22 10:09 smoelius

You suggested to switching the lint to use check_body with the current check_expr moved into a visitor, correct? That leaves the visitor with all the lints, plus the new visitor from check_body running over the code.

Jarcho avatar Sep 04 '22 19:09 Jarcho

You suggested to switching the lint to use check_body with the current check_expr moved into a visitor, correct? That leaves the visitor with all the lints, plus the new visitor from check_body running over the code.

OK. I see what you are saying.

Then what if we essentially queued the arguments to needless_borrow_impl_arg_position inside Position::ImplArg, and actually processed those arguments/performed those calls in an implementation of check_body_post?

Admittedly, one thing I am not clear on is whether returning an Position::ImplArg as a possibility rather than a certainty would break the surrounding code. For example, would this code break if no warning were ultimately produced for the ImplArg? Maybe the answer is obvious to you. https://github.com/rust-lang/rust-clippy/blob/5860abff70714c27daaf90b8f783812ee4da222b/clippy_lints/src/dereference.rs#L364-L365

smoelius avatar Sep 04 '22 20:09 smoelius

I think you can make that work by storing the expression's HirId the target function's DefId and argument position and then running over all those in check_body_post. This would be slightly complicated by nested functions.

I'll check to see if we can change rustc to allow storing TyCtxt bounded items in lint passes as that would allow caching the MIR result directly.

Posted on zulip: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Allowing.20non.20.60'static.60.20lint.20passes/near/297154930

Jarcho avatar Sep 05 '22 01:09 Jarcho

I'll check to see if we can change rustc to allow storing TyCtxt bounded items in lint passes as that would allow caching the MIR result directly.

Thanks!

Did you already start those changes? If not, I could make them. Please just let me know.

smoelius avatar Sep 05 '22 08:09 smoelius

Implemented in rust-lang/rust#101501. Hopefully that will be synced to clippy this week.

Jarcho avatar Sep 06 '22 19:09 Jarcho

Thanks, @Jarcho!

smoelius avatar Sep 06 '22 22:09 smoelius

I saw you PR was merged. But it looks like it didn't make it in time for the most recent rustup?

If I'm reading everything right, the current toolchain (nightly-2022-09-08) is based on commit c2804e6, which is older than 8778809 (the one which includes your PR).

smoelius avatar Sep 09 '22 12:09 smoelius

That is correct. Two weeks till the next sync.

Jarcho avatar Sep 11 '22 01:09 Jarcho

I think this is ready for review again, @Jarcho.

(Rebasing was a bit of bear, so hopefully I did not screw it up.)

smoelius avatar Sep 30 '22 02:09 smoelius

:umbrella: The latest upstream changes (presumably #9510) made this pull request unmergeable. Please resolve the merge conflicts.

bors avatar Sep 30 '22 22:09 bors

I think the three most recent comments have been addressed.

smoelius avatar Oct 03 '22 21:10 smoelius

:umbrella: The latest upstream changes (presumably #9547) made this pull request unmergeable. Please resolve the merge conflicts.

bors avatar Oct 04 '22 07:10 bors

I think the only thing remaining is the previous point about changing f1(&x); f2(&x); to f1(&x); f2(x);

Jarcho avatar Oct 05 '22 14:10 Jarcho

I think the only thing remaining is the previous point about changing f1(&x); f2(&x); to f1(&x); f2(x);

Meaning don't suggest that, right?

It's possible there's a problem, but I think I addressed that, actually, e.g.:

  • https://github.com/rust-lang/rust-clippy/blob/252f598f5a850bde9026cf3582a54e1c2423a230/clippy_lints/src/dereference.rs#L1126
  • https://github.com/rust-lang/rust-clippy/blob/252f598f5a850bde9026cf3582a54e1c2423a230/tests/ui/needless_borrow.rs#L367-L383
  • https://github.com/rust-lang/rust-clippy/blob/252f598f5a850bde9026cf3582a54e1c2423a230/tests/ui/needless_borrow.stderr#L207-L211

smoelius avatar Oct 05 '22 14:10 smoelius

So it is. I was going by the change in clippy_dev/src/new_lint.rs still being there. (there's no need to change this).

You can rebase and squash.

Jarcho avatar Oct 05 '22 15:10 Jarcho

So it is. I was going by the change in clippy_dev/src/new_lint.rs still being there. (there's no need to change this).

It should have occurred to me to fix that.

I will fix that and any other similar changes before rebasing.

smoelius avatar Oct 05 '22 15:10 smoelius

The most recent push squashes all of the commits except for the "Fix adjacent code" ones.

The current "Fix adjacent code" commit is the result of rerunning the lint.

Sadly, the introduction of possible_borrowers eliminated more suggestions than I realized.

I think there may be ways possible_borrowers could be improved, but that could be the subject of a separate PR.

smoelius avatar Oct 07 '22 11:10 smoelius

Everything looks good. If you can catch more single use cases that would be great.

Thank you. @bors r+

Jarcho avatar Oct 08 '22 21:10 Jarcho