Skip to content

warning on missing await in non-async function. #58512

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
bsutton opened this issue Sep 15, 2021 · 8 comments
Closed

warning on missing await in non-async function. #58512

bsutton opened this issue Sep 15, 2021 · 8 comments
Labels
devexp-linter Issues with the analyzer's support for the linter package legacy-area-analyzer Use area-devexp instead. linter-lint-request type-enhancement A request for a change that isn't a bug

Comments

@bsutton
Copy link

bsutton commented Sep 15, 2021

I have a problem with the following code:

void recreateDir(String path)  {
  deleteDir(path);
  createDir(path);
}

Future<void> deleteDir(String path) async {};

Future<void> createDir(String path) async {};

Dart will happily let me call async functions from a non-async function without awaiting them.

In the above example it is problematic to call createDir before deleteDir which may occur if they are unawaited.

If the method createDir is async and I use the lint 'unawaited_futures' I get an appropriate warning.
However if I forget to add the async keyword to the createDir signature then I have a serious problem and no warning.

We need a lint to warn of the above issue.
The above example is a mistake that I make regularly in my own code and its rather hard to debug.

@bsutton bsutton added type-enhancement A request for a change that isn't a bug linter-lint-request labels Sep 15, 2021
@eernstg
Copy link
Member

eernstg commented Sep 15, 2021

unawaited_futures targets expressions in async function bodies only, and it would probably be inconveniently aggressive to make it target other function bodies.

However, the actual problem of not awaiting certain futures might very well arise in a non-async function body because of a modification of some other part of a software system: Something changes from non-async to async, say, a top-level function void foo() ... changes to Future<void> foo() ..., and now we're suddenly discarding a future at the statement foo(); in a function bar, even though nothing changed in the library that declares bar. In that situation, the right fix could be to make bar's body async as well, and do await foo();.

So if we don't want to make unawaited_futures target non-async bodies as well, we might need a lint that flags situations where a future is discarded in a non-async body?

@bsutton
Copy link
Author

bsutton commented Sep 15, 2021 via email

@eernstg
Copy link
Member

eernstg commented Sep 15, 2021

I agree that, in my example, the invocation of foo(); as a statement could very will be written when the return type of foo is already Future<void>. In other words, we care about the situation, not the fact that it could also arise because of a change in some other library (but the latter makes software maintenance particularly difficult).

There's nothing wrong per se with normal non-async code that calls a function that returns a future: That future could be stored (say, in a set), and some other piece of code could retrieve it and await it later. That's the reason why I'm focusing on non-async code that silently discards a future.

@bsutton
Copy link
Author

bsutton commented Sep 15, 2021 via email

@eernstg
Copy link
Member

eernstg commented Sep 16, 2021

The testAsync() method generates a warning on the call to _callasync1
whilst the testSync method does't generate a warning.

No, unawaited_futures would lint testAsync() because the invocation _callasync1() does not await the returned future, and the new lint I suggested we'd need (call it discarded_futures) would lint testSync() because the expression statement _callasync1(); discards the returned future.

None of the invocations of _alist.add get a lint, because none of them imply that the future returned from callasync... will be ignored. As I mentioned:

There's nothing wrong per se with normal non-async code that
calls a function that returns a future: That future could be stored ...

and that could of course also happen in an async method.

@bsutton wrote:

what is the design purpose behind allowing a sync method to call async methods

A sync method can call an async method, obtain one or more futures, and handle each of them just like any other object that needs further processing. In particular, the sync method doesn't have to perform any await actions itself, because it could be part of the application logic that the futures are stored somehow, and some async method will discover this and await them.

That's the reason why I'm focusing on non-async code that silently
discards a future.

Is that not the core issue I've raised?

You've raised the issue that it is a problem that unawaited_futures skips over testSync entirely, and I agree that this is a problem. That's the reason why I'm suggesting a new lint, discarded_futures, to flag it as problematic.

With that new lint, you could enable both, and you would then get the diagnostic messages that you want. All is good.

At the same time, everybody else would get to control the situation when they decide whether or not to enable discarded_futures: If they enable it, and it turns out to flag thousands of locations where they do discard a future in sync code, and they do know what they are doing, then they can just ignore the new lint. More likely, they actually do catch some bugs if they enable the new lint, and they might then do it just a few libraries at a time, such that they can fix it at their own pace.

But if we just abruptly change unawaited_futures such that it flags testSync, then (1) the developer can't actually do await inside testSync, and (2) the problem is not that there is a missing await, the problem is that we might never be able to perform the await if we discard the future. It's still possible that the developers will need to change testSync to be an async method (and return a future itself), but that may be a much, much bigger effort, because it could propagate to any number of other declarations.

@bsutton
Copy link
Author

bsutton commented Sep 16, 2021 via email

@srawlins
Copy link
Member

srawlins commented Oct 3, 2022

I believe this has been implemented as discarded_futures.

@bsutton
Copy link
Author

bsutton commented Apr 28, 2023

closing as discared_futures resolves my problem.

Thanks.

@bsutton bsutton closed this as completed Apr 28, 2023
@devoncarew devoncarew added devexp-linter Issues with the analyzer's support for the linter package legacy-area-analyzer Use area-devexp instead. labels Nov 19, 2024
@devoncarew devoncarew transferred this issue from dart-archive/linter Nov 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
devexp-linter Issues with the analyzer's support for the linter package legacy-area-analyzer Use area-devexp instead. linter-lint-request type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

4 participants