Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find non-awaited async method calls

Tags:

c#

async-await

I've just stumbled across a rather dangerous scenario while migrating an ASP.NET application to the async/await model.

The situation is that I made a method async: async Task DoWhateverAsync(), changed the declaration in the interface to Task DoWhateverAsync() and hoped that the compiler would tell me where the code now is wrong, via that Warning. Well, tough luck. Wherever that object gets injected via the interface, the warning doesn't happen. :-(

This is dangerous. Is there any way to check automatically for non-awaited methods that return tasks? I don't mind a few warnings too many, but I wouldn't want to miss one.

Here's an example:

using System.Threading.Tasks; namespace AsyncAwaitGames {     // In my real case, that method just returns Task.     public interface ICallee { Task<int> DoSomethingAsync(); }      public class Callee: ICallee     {         public async Task<int> DoSomethingAsync() => await Task.FromResult(0);     }     public class Caller     {         public void DoCall()         {             ICallee xxx = new Callee();              // In my real case, the method just returns Task,             // so there is no type mismatch when assigning a result              // either.             xxx.DoSomethingAsync(); // This is where I had hoped for a warning.         }     } } 
like image 261
Volker Avatar asked Jan 18 '17 13:01

Volker


People also ask

What happens if an async method is not awaited?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

Can you call an async method without await?

You can call this method with or without the await keyword. The syntax with the await keyword looks like this: Customer cust = await GetCustomerById("A123");

What happens when you call async method without await C#?

However, just to address "Call an async method in C# without await", you can execute the async method inside a Task. Run . This approach will wait until MyAsyncMethod finish. await asynchronously unwraps the Result of your task, whereas just using Result would block until the task had completed.

When an asynchronous method is executed the code runs but nothing happens?

When a asynchronous method is executed, the code runs but nothing happens other than a compiler warning.


1 Answers

After quite some difficulties with this problem I decided to create an Analyzer with code fix to solve it.

The code is available here: https://github.com/ykoksen/unused-task-warning

It is also as a NuGet package that can be used as an analyzer for a project (when it is build): https://www.nuget.org/packages/Lindhart.Analyser.MissingAwaitWarning/#

Furthermore it is also available as a Visual Studio Extension (for 2017). However this only analyses currently open files, so I'd recommend using the NuGet package. The extension is available here (or search for it in Visual Studio): https://marketplace.visualstudio.com/items?itemName=Lindhart.missingAwaitWarning#overview

The code for the analyzer:

    public override void Initialize(AnalysisContext context)     {         context.RegisterSyntaxNodeAction(AnalyseSymbolNode, SyntaxKind.InvocationExpression);     }      private void AnalyseSymbolNode(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext)     {         if (syntaxNodeAnalysisContext.Node is InvocationExpressionSyntax node)         {             if (syntaxNodeAnalysisContext                     .SemanticModel                     .GetSymbolInfo(node.Expression, syntaxNodeAnalysisContext.CancellationToken)                     .Symbol is IMethodSymbol methodSymbol)             {                 if (node.Parent is ExpressionStatementSyntax)                 {                     // Only checks for the two most common awaitable types. In principle this should instead check all types that are awaitable                     if (EqualsType(methodSymbol.ReturnType, typeof(Task), typeof(ConfiguredTaskAwaitable)))                     {                         var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());                          syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);                     }                 }             }         }     }      /// <summary>     /// Checks if the <paramref name="typeSymbol"/> is one of the types specified     /// </summary>     /// <param name="typeSymbol"></param>     /// <param name="type"></param>     /// <returns></returns>     /// <remarks>This method should probably be rewritten so it doesn't merely compare the names, but instead the actual type.</remarks>     private static bool EqualsType(ITypeSymbol typeSymbol, params Type[] type)     {         var fullSymbolNameWithoutGeneric = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";         return type.Any(x => fullSymbolNameWithoutGeneric.Equals(x.FullName));     } 
like image 77
Ykok Avatar answered Sep 26 '22 14:09

Ykok