[Analyzer Proposal] Warn when an awaitable result is ignored outside an async method
- Dominant language
- C#
- Stars
- 3.2k
- Forks
- 1.3k
- PR merge metrics
- PR metrics pending
Description
### Is your feature request related to a problem?
Calls that return an awaitable can be silently ignored when the containing method is not marked `async`. CS4014 only reports these calls inside `async` methods, so mistakes such as the following compile without a warning:
```csharp
[TestMethod]
public void AsyncThrow()
{
Assert.ThrowsAsync(async () =>
{
throw new ArgumentException("exception", "value");
});
}
```
The test completes before the assertion is observed and can incorrectly pass.
This is not specific to test frameworks. It also occurs when a synchronous method accidentally calls an async API after a refactoring, or when an async delegate is invoked without observing its returned task.
### Describe the solution you'd like
Add an opt-in `Microsoft.CodeAnalysis.NetAnalyzers` rule that reports an invocation used as an expression statement when its result is awaitable and completely ignored, regardless of whether the containing method is `async`.
The rule should not report when the result is observed in any way, including when it is:
- awaited;
- returned;
- assigned to a variable or member;
- passed to another method; or
- explicitly assigned to a discard, such as `_ = StartAsync();`, to document intentional fire-and-forget behavior.
A disabled-by-default rule would avoid introducing warnings into existing codebases while allowing projects that do not permit implicit fire-and-forget calls to enable it. The explicit-discard escape hatch also distinguishes accidental omission from intentional behavior, matching how developers commonly suppress CS4014.
The analysis should ideally support general awaitable types, not only `Task`, `Task`, `ValueTask`, and `ValueTask`.
### Alternatives you've considered
- Extending CS4014 was previously declined for compatibility reasons in dotnet/roslyn#20782, and the compiler team reiterated that decision in dotnet/roslyn#76721.
- Extending CA1806 unconditionally was proposed in dotnet/roslyn-analyzers#6181 and declined because intentional fire-and-forget patterns would create many false positives. Making the rule opt-in and accepting an explicit discard addresses that concern without changing existing defaults.
- VSTHRD110 from `Microsoft.VisualStudio.Threading.Analyzers` and MA0134 from `Meziantou.Analyzer` already implement similar behavior, but most .NET projects do not receive either analyzer by default.
- A framework-specific analyzer could catch this only in test methods, but the underlying bug pattern is general and would be better implemented once in the first-party .NET analyzers.
### Additional context
The motivating MSTest scenario is tracked in microsoft/testfx#9816. The expected corrected code is:
```csharp
[TestMethod]
public async Task AsyncThrow()
{
await Assert.ThrowsAsync(async () =>
{
throw new ArgumentException("exception", "value");
});
}
```
Existing implementations demonstrating the rule's usefulness:
- VSTHRD110: https://github.com/microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD110.md
- MA0134: https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0134.md
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.