Skip to content

Add new redundant_async_block lint #10448

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

Merged
merged 1 commit into from
Mar 8, 2023
Merged

Conversation

samueltardieu
Copy link
Contributor

Fixes #10444

changelog: [redundant_async_block]: new lint to detect async { future.await }

@rustbot
Copy link
Collaborator

rustbot commented Mar 4, 2023

r? @llogiq

(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 Mar 4, 2023
@samueltardieu samueltardieu force-pushed the issue-10444 branch 2 times, most recently from 984422a to 1bd895a Compare March 4, 2023 23:13
Copy link
Contributor

@llogiq llogiq left a comment

Choose a reason for hiding this comment

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

I like this lint, but I think we can make the message shorter. Also I'd want to see some more tests. Finally, before we introduce a warn-by-default lint, we should at least run a lintcheck to get a handle on churn.

/// ### Example
/// ```rust
/// await {
/// function_returning_a_future().await
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be good if we had an async fn function_returning_a_future() {} so the example actually compiles. That applies to both code snippets.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

/// Checks for `async` block that only returns `await` on a future.
///
/// ### Why is this bad?
/// It is shorter to directly use the future.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm curious: Does the async await really get reduced to a single call, or even inlined in release mode? If not, there would even be a perf angle.

Copy link
Contributor Author

@samueltardieu samueltardieu Mar 5, 2023

Choose a reason for hiding this comment

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

I can still see the await process in the MIR expansion in release mode, so I don't think this is optimized away.

/// ```
#[clippy::version = "1.69.0"]
pub REDUNDANT_ASYNC_BLOCK,
complexity,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have a lintcheck run to ensure we don't get too much churn when introducing this lint?

Nowadays we are quite cautious when adding warn-by-default lints.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you are talking about a cargo lintcheck, yes, I ran one (maybe I should mention it in the description). Is there a way to do something similar to a crater run for lint checking?

cx,
REDUNDANT_ASYNC_BLOCK,
expr.span,
"this async-await expression is equivalent to the awaited future itself",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"this async-await expression is equivalent to the awaited future itself",
"this async expression only awaits a single future",

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

let fut1 = async { 42 };
let fut2 = async move { fut1.await };

let fut = async { async { 42 }.await };
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 a test where the future is macro-generated, a test where the whole block outside the future / with the future is macro-generated by an crate-internal/external macro each.

The internal ones may lint, but with the correct snippet; the external ones I think should not lint (because we don't expect people to be able to change code external to their crate). I want to be sure the span.from_expansion() check is sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Checking inside a macro needs a bit more work, because the awaiten future must not come from a macro parameter, as it might contain .await itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right now, it doesn't look into macros at all. I've added a MISSED OPPORTUNITY marker, but I'm not sure how to check inside the macro that no parameter (or another macro) is used as the .await receiver.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's all right. 👍

}
if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 &&
let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
let ExprKind::Await(future) = &last.kind
Copy link
Member

Choose a reason for hiding this comment

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

A check for future.span.from_expansion() would be good, and an accompanying test for something like

macro_rules! await_in_macro {
    ($e:expr) => { std::convert::identity($e).await };
} 

async { await_in_macro!(foo()) }

This could come up with futures::poll like here but having our own macro definition means we don't have to rely on future's implementation staying the same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, without the test on the future itself the proposed solution involved copying code coming from the macro. Done.

@samueltardieu
Copy link
Contributor Author

samueltardieu commented Mar 5, 2023

In addition to all those comments, there is one check missing: there must be no other .await in the expression, as-in

async { func1(func2().await)).await }

I'll work on that, probably in the next few days.

Edit: done, through a visitor

@samueltardieu samueltardieu force-pushed the issue-10444 branch 8 times, most recently from 09cc034 to cd8f6db Compare March 5, 2023 11:38
@llogiq
Copy link
Contributor

llogiq commented Mar 5, 2023

What did your lintcheck run come up with?

I might do a run to reproduce and see what it turns up.

@samueltardieu
Copy link
Contributor Author

samueltardieu commented Mar 5, 2023

What did your lintcheck run come up with?

Nothing, as expected.

$ cargo lintcheck -j 0 --filter redundant_async_block
[…]
$ cat lintcheck-logs/lintcheck_crates_logs.txt
clippy 0.1.69

### Reports



### Stats:

| lint                                               | count |
| -------------------------------------------------- | ----- |


### ICEs:

I might do a run to reproduce and see what it turns up.

You need to rebase this branch on master to use -j 0, tell me if you want me to push my updated branch.

@bors
Copy link
Contributor

bors commented Mar 7, 2023

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

@llogiq
Copy link
Contributor

llogiq commented Mar 7, 2023

r=me when rebased.

@samueltardieu
Copy link
Contributor Author

Checked with the 500 most recently downloaded crates of crates.io: this lint was not triggered (the complete 470klines report is available at https://rfc1149.net/tmp/top500_logs.txt.gz).

@llogiq
Copy link
Contributor

llogiq commented Mar 8, 2023

Yeah, I also ran lintcheck. Thanks everyone!

@bors r+

@bors
Copy link
Contributor

bors commented Mar 8, 2023

📌 Commit 3872d17 has been approved by llogiq

It is now in the queue for this repository.

@bors
Copy link
Contributor

bors commented Mar 8, 2023

🔒 Merge conflict

This pull request and the master branch diverged in a way that cannot be automatically merged. Please rebase on top of the latest master branch, and let the reviewer approve again.

How do I rebase?

Assuming self is your fork and upstream is this repository, you can resolve the conflict following these steps:

  1. git checkout issue-10444 (switch to your branch)
  2. git fetch upstream master (retrieve the latest master)
  3. git rebase upstream/master -p (rebase on top of it)
  4. Follow the on-screen instruction to resolve conflicts (check git status if you got lost).
  5. git push self issue-10444 --force-with-lease (update this PR)

You may also read Git Rebasing to Resolve Conflicts by Drew Blessing for a short tutorial.

Please avoid the "Resolve conflicts" button on GitHub. It uses git merge instead of git rebase which makes the PR commit history more difficult to read.

Sometimes step 4 will complete without asking for resolution. This is usually due to difference between how Cargo.lock conflict is handled during merge and rebase. This is normal, and you should still perform step 5 to update this PR.

Error message
Auto-merging clippy_lints/src/lib.rs
CONFLICT (content): Merge conflict in clippy_lints/src/lib.rs
Auto-merging clippy_lints/src/declared_lints.rs
Auto-merging CHANGELOG.md
Automatic merge failed; fix conflicts and then commit the result.

@samueltardieu
Copy link
Contributor Author

@llogiq: rebased

@llogiq
Copy link
Contributor

llogiq commented Mar 8, 2023

Thanks again!

@bors r+

@bors
Copy link
Contributor

bors commented Mar 8, 2023

📌 Commit d5429ea has been approved by llogiq

It is now in the queue for this repository.

@bors
Copy link
Contributor

bors commented Mar 8, 2023

⌛ Testing commit d5429ea with merge 56fbfe5...

@bors
Copy link
Contributor

bors commented Mar 8, 2023

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

@bors bors merged commit 56fbfe5 into rust-lang:master Mar 8, 2023
@ComputerDruid
Copy link

#10509 notes that this lint triggers even in cases where the suggestion is a behavior change.

@samueltardieu samueltardieu deleted the issue-10444 branch March 24, 2023 23:47
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.

async { <FUTURE_EXPR>.await } can be reduced to <FUTURE_EXPR>
6 participants