Skip to content

Analyzer suggestion: Report improvability of the async/await pattern #6021

@CollinAlpert

Description

@CollinAlpert

Describe the problem you are trying to solve

What I see often in code written by developers who are not completely familiar with how async/await works is the following pattern:

public async Task RunAsync() {
  await DoSomethingAsync();
}

or

public async Task<int> RunAsync() {
  if(new Random().Next() % 2 == 0) {
    return await GetValueAsync();
  }
  
  return await GetOtherValueAsync();
}

These methods can be optimized to return the Tasks directly without a need for awaiting them:

public Task RunAsync() {
  return DoSomethingAsync();
}

and

public Task<int> RunAsync() {
  if(new Random().Next() % 2 == 0) {
    return GetValueAsync();
  }
  
  return GetOtherValueAsync();
}

respectively. This prevents the compiler from creating an unnecessary state machine and can delegate awaiting to the caller.

I am proposing an Analyzer with the "Info" severity which analyzes methods, local functions and lambdas and can report when a Task can be returned directly instead of awaiting it.

Describe suggestions on how to achieve the rule

I have a pretty solid Analyzer + CodeFix implemented already. It targets methods, local functions and lambdas, checks if the return type is Task or Task<T> and then acts accordingly.
For a Task-returning method, an await expression must either immediately be followed by a return; or must be the last statement in the body.
For a Task<T>-returning method, an await expression must be immediately returned.

If the body contains one await expression which does not match these rules, the diagnostic is not reported.

Additional context

The Analyzer should not report an await within a try or using block, since Tasks need to be awaited in these scenarios.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions