MustAwaitAnalyzer 1.0.0

dotnet add package MustAwaitAnalyzer --version 1.0.0
                    
NuGet\Install-Package MustAwaitAnalyzer -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MustAwaitAnalyzer" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MustAwaitAnalyzer" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="MustAwaitAnalyzer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MustAwaitAnalyzer --version 1.0.0
                    
#r "nuget: MustAwaitAnalyzer, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MustAwaitAnalyzer@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MustAwaitAnalyzer&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=MustAwaitAnalyzer&version=1.0.0
                    
Install as a Cake Tool

MustAwaitAnalyzer

CI

Overview

MustAwaitAnalyzer is a roslyn analyzer that enforces the use of await when calling methods that return:

Install into Unity Project

Requires Unity 2021.1.2f1 or later. You can add https://github.com/DeNA/MustAwaitAnalyzer.git?path=com.dena.must-await-analyzer to Package Manager.

DENA008: Must use await

Item Value
Category DenaUnityAnalyzers
Enabled True
Severity Error
CodeFix False

Analysis Targets

Examples are provided below:

// bad
namespace BadCase
{
    public class Task1
    {
        Task Task_NoAsync() => Task.Run(() => {}); // DENA008, "Must use await" is reported

        public Task UseNoAsync()
        {
            Task_NoAsync(); // DENA008, "Must use await" is reported
        }
        
        Action<object> action = (object obj) => {};
    
        public async Task Hoge()
        {
            new Task(action, "a"); // DENA008, "Must use await" is reported
        }
    }
}

// good
namespace GoodCase
{
    public class Task1
    {
        async Task Task_NoAsync() => await Task.Run(() => {}); // DENA008, "Must use await" is not reported

        public async Task UseNoAsync()
        {
            await Task_NoAsync(); // DENA008, "Must use await" is not reported
        }
        
        Action<object> action = (object obj) => {};
    
        public async Task Hoge()
        {
            await new Task(action, "a"); // DENA008, "Must use await" is not reported
        }
    }
}

NOTE1: The following types are not analyzed:

NOTE2: If the last method in a method chain is named Terminate or Forget, the analysis does not take place.

static async UniTask HogeAsync()
{
    await Task.Run(() => {});
}

static void TerminateCase()
{
    HogeAsync(); // DENA008 is reported due to missing await
    HogeAsync().GetAwaiter(); // DENA008 is reported due to missing await
    HogeAsync().GetAwaiter().Terminate(); // Nothing is reported
    HogeAsync().GetAwaiter().Forget(); // Nothing is reported
}

NOTE3: If a method call directly references a property that is an analysis target, the property reference is analyzed instead of the method call.

// bad
public async static Task<Task> BadPropertyCase()
{
    var p = new Program();
    p.Example(); // DENA008 is reported
    return p.Example().Result; // Result returns Task, hence DENA008 is reported
}

public async Task<Task> Example()
{
    return await new Task<Task>(null);
}

// good
public async static Task<int> GoodPropertyCase()
{
    var p = new Program();
    p.Example(); // DENA008 is reported
    return p.Example().Result; // Result returns int, hence DENA008 is not reported
}

public async Task<int> Example()
{
    return await new Task<int>(null);
}

NOTE4: The following method calls, property references, and field references are excluded from analysis:

  • System.Threading.Tasks.Task.CompletedTask
  • System.Threading.Tasks.Task.FromResult
  • Cysharp.Threading.Tasks.UniTask.CompletedTask
  • Cysharp.Threading.Tasks.UniTask.FromResult

NOTE5: If Task or UniTask objects are assigned to a variable and then awaited, no warning is issued. Additionally, if Task or UniTask objects are stored in a list (a class that implements System.Collections.Generic.IList) and awaited collectively, no warning is issued either.

// good
public async Task M1()
{
    var task = Task.Run(() => {}); // DENA008 is not reported
    await task;
}
// good
public async Task M1()
{
    var list = new List<Task>();
    list.Add(Task.Run(() => {})); // DENA008 is not reported
    list.Add(Task.Run(() => {})); // DENA008 is not reported
    await Task.WhenAll(list);
}

Analysis Locations

If the target of analysis exists within the following syntax and lacks an await, DENA008 will be reported.

Within a Block
// bad
namespace BadCase
{
    public class Task1
    {
        Task Task_NoAsync() => Task.Run(() => {}); // DENA008, "Must use await" is reported

        public Task UseNoAsync()
        {
            Task_NoAsync(); // DENA008, "Must use await" is reported
        }
    }
}

// good
namespace GoodCase
{
    public class Task1
    {
        async Task Task_NoAsync() => await Task.Run(() => {});

        public async Task UseNoAsync()
        {
            await Task_NoAsync();
        }
    }
}
Within a Statement

Examples for various statement types are shown below.

  • using Statement
// bad

namespace BadCase
{
    public class Task1
    {
        public async void M1() 
        {
            using (M2()) // DENA008, "Must use await" is reported
            {
            }
        }
        
        public async Task<IDisposable> M2()
        {
            return await (Task<IDisposable>)Task.Run(() => {});
        }
    }
}

// good
namespace GoodCase
{
    public class Task1
    {
        public async void M1() 
        {
            using (await M2()) // DENA008 is not reported
            {
            }
        }
        
        public async Task<IDisposable> M2()
        {
            return await (Task<IDisposable>)Task.Run(() => {});
        }
    }
}
  • do-while Statement
// bad
public class BadCase {
    public async void M() 
    {
        do 
        {
            Task.Run(() => {}); // DENA008, "Must use await" is reported.
        }
        while (true);
        {
        }
    }
}

// good
public class GoodCase {
    public async void M() 
    {
        do 
        {
            await Task.Run(() => {}); // DENA008 is not reported.
        }
        while (true);
        {
        }
    }
}
  • if Statement
// bad case
namespace FailedCase
{
    public class C {
        public async void M() 
        {
            if (M2()) // DENA008 is reported
            {
            }
        }

        public async Task<bool> M2()
        {
            return await (Task<bool>)Task.Run(() => {});
        }
    }
}

// good case
namespace SuccessCase
{
    public class C {
        public async void M() 
        {
            if (await M2())
            {
            }
        }

        public async Task<bool> M2()
        {
            return await (Task<bool>)Task.Run(() => {});
        }
    }
}
  • yield Statement
// bad
namespace BadCase
{
    public class YieldReturn
    {
        public async IAsyncEnumerable<int> M1()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return (Task<int>)Task.Run(() => 0); // DENA008 and CS0029 are reported
            }
        }
    }
}

// good
namespace GoodCase
{
    public class YieldReturn
    {
        public async IAsyncEnumerable<int> M1()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return await (Task<int>)Task.Run(() => 0);
            }
        }
    }
}
Lambda
// bad
namespace FailedCase
{
    public class Task1
    {
        Func<Task<int>> Example() => (() => Task.Run(() => 0)); // DENA008, "Must use await" is reported

        public void UseAsync()
        {
            Example(); // DENA008 is not reported since the return type is Func<Task<int>>, not Task<int>.
        }
    }
}

// good
namespace SuccessCase
{
    public class Task1
    {
        Func<Task<int>> Example() => (async () => await Task.Run(() => 0));

        public void UseAsync()
        {
            Example(); // DENA008 is not reported since the return type is Func<Task<int>>, not Task<int>.
        }
    }
}
Asynchronous Processing within Partial Methods
namespace BadCase
{
    interface IStudent
    {
        Task GetName();
    }

    partial class PartialMethod : IStudent
    {
        public virtual partial Task GetName();
    }

    public partial class PartialMethod
    {
        public virtual partial async Task GetName()
        {
            Task.Run(() => {}); // DENA008 is reported.
        }
    }
}

namespace GoodCase
{
    interface IStudent
    {
        Task GetName();
    }

    partial class PartialMethod : IStudent
    {
        public virtual partial Task GetName();
    }

    public partial class PartialMethod
    {
        public virtual partial async Task GetName()
        {
            await Task.Run(() => {}); // DENA008, "Must use await" is not reported
        }
    }
}
Target-typed Inference
// badcase
namespace BadCase
{
    public class TargetTypedNewExpression
    {
        Func<object, int> func = null;

        public async Task<int> Hoge()
        {
            return await new Task<int>(func, "a");
        }

        public async void M()
        {
            Dictionary<string, int> field = new()
            {
                { "item1", Hoge() } // DENA008 is reported
            };
        }
    }
}

// GoodCase
namespace GoodCase
{
    public class TargetTypedNewExpression
    {
        Func<object, int> func = null;

        public async Task<int> Hoge()
        {
            return await new Task<int>(func, "a");
        }

        public async void M()
        {
            Dictionary<string, int> field = new()
            {
                { "item1", await Hoge() } // DENA008, "Must use await" is not reported
            };
        }
    }
}
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 148 10/31/2024