semihokur / asyncfixer Goto Github PK
View Code? Open in Web Editor NEWAdvanced async/await Diagnostics and CodeFixes for C#
License: Apache License 2.0
Advanced async/await Diagnostics and CodeFixes for C#
License: Apache License 2.0
Consider the following code:
public static class Class1
{
public static void Do()
{
Process(async input => await ProcessAsync());
}
private static void Process(Action<int> action)
{
action(12);
}
private static Task ProcessAsync()
{
return Task.CompletedTask;
}
}
This doesn't report the async void delegate in Do() though it violates AsyncFixer03. In e-mail Semih confirmed this being a bug indeed.
Task foo()
{
using var destination = new MemoryStream();
using FileStream source = File.Open("data", FileMode.Open);
return source.CopyToAsync(destination);
}
should warn that streams could be closed before task executed
Thank you for this great extension!
I have realized that I often miss the presence of IAsyncDisposable interfaces and there is no warning that indicates this. Example:
static async Task Main(string[] args)
{
using SqlConnection connection = new SqlConnection();
// Do something with connection
// ...
}
This could (should?) be converted to:
static async Task Main(string[] args)
{
await using SqlConnection connection = new SqlConnection();
// Do something with connection
// ...
}
Would it be a good idea to add a warning or an information, that await using
could be used instead of using
?
Thanks for the analyzer! ๐
We've been using the analyzer in several of our projects for a while now. We always disable the AsyncFixer01: "Unnecessary async/await usage" warning, because the fix elides methods from the stack trace when an exception is thrown.
For example:
async Task SaveItemAsync() {
throw new Exception();
}
async Task UpdateAndSaveAsync() {
//...
await SaveItemAsync();
}
Main() {
await UpdateAndSaveAsync();
}
In this case the stack track will show like this:
Main()
UpdateAndSaveAsync()
SaveItemAsync()
If we take the AsyncFixer01 suggestion and change the code to this:
async Task SaveItemAsync() {
throw new Exception();
}
Task UpdateAndSaveAsync() {
//...
return SaveItemAsync();
}
Main() {
await UpdateAndSaveAsync();
}
Then the stack track will not contain PrintAsync()
:
Main()
UpdateAndSaveAsync()
To me, this suggests that "unnecessarily" using await
isn't an really an antipattern.
Would like to hear your thoughts. Thanks again for the analyzer ๐
would be useful to provide this as dotnet tool just to run in CI/CD pipelines, instead of having a nugget dependency...
async Task foo()
{
MemoryStream destination = new MemoryStream();
using FileStream source = File.Open("data", FileMode.Open);
await source.CopyToAsync(destination);
}
AsyncFixer currently gives a warning to remove async/await keywords in the method below. It suggests replacing the last statement with:
return source.CopyToAsync(destination);
However, this can cause an exception if CopyToAsync
takes slightly longer because the operation will continue with the disposed source
object.
Is it possible to disable warnings inside query syntax?
For example:
var query = from person in db.Persons
select new
{
person.Id,
roles = db.Roles.Where(r => r.PersonId == person.Id).ToList()
};
var personsWithRoles = await query.ToListAsync();
It complains about db.Roles.Where(r => r.PersonId == person.Id).ToList()
.
AsyncFixer02 ToListAsync should be used instead of ...ToList
It is practically undoable to make the inner delegate expression async and as far as I know, ToListAsync cannot be translated to an sql statement.
The AsyncFixer01 makes suggestions without taking into account the possible await foreach
in the method.
For example, this method is flagged by the AsyncFixer01 warning:
public async Task MyFunction()
{
await foreach(var i in GetAsync(query).ConfigureAwait(false))
{
...
}
await DoSomethingAsync().ConfigureAwait(false);
}
If we apply the automatic suggestion, we get this:
public Task MyFunction()
{
await foreach(var i in GetAsync(query).ConfigureAwait(false))
{
...
}
return DoSomethingAsync().ConfigureAwait(false);
}
As you can see, the async
keyword is removed although we still have the await
keyword before the foreach
which causes a compilation error.
https://github.com/buvinghausen/TaskTupleAwaiter
var (a, b) = await (task1, task2); // AsyncFixer05
This will not compile:
delegate void Foo();
void Call(Foo f) { f(); }
void TestCall()
{
Call(() => { return true; } ); // CS8030 Anonymous function converted to a void returning delegate cannot return a value
}
but this will:
delegate Task FooAsync();
Task CallAsync(FooAsync f) { return f(); }
void TestCallAsync()
{
CallAsync(() => { return Task.FromResult(true); });
}
FWIW this does throw an error:
delegate Task FooAsync();
Task CallAsync(FooAsync f) { return f(); }
void TestCallAsync()
{
CallAsync(async () => { await Task.Yield(); return true; }); //CS8031 Async lambda expression converted to a 'Task' returning delegate cannot return a value. Did you intend to return 'Task<T>'?
}
Nuget points to asyncfix.com as the project site but it doesn't work.
I'm using a CancellationTokenSource for two asynchronous actions, then awaiting them using Task.WhenAny
But this is triggering AsyncFixer (Code AsyncFixer04) in a way that I don't understand.
If I set the scope of the CancellationTokenSource with a using statement in brackets, like so
using System.Threading;
using System.Threading.Tasks;
namespace AsyncFixerIssue
{
internal class Program
{
static Task Main(string[] args)
{
using (var cts = new CancellationTokenSource())
{
var a = Task.Delay(1, cts.Token);
var b = Task.Delay(1, cts.Token);
return Task.WhenAny(a, b);
}
}
}
}
it triggers a warning from AsyncFixer04.
But if I format it like this, there's no problem
static Task Main(string[] args)
{
using var cts = new CancellationTokenSource();
var a = Task.Delay(1, cts.Token);
var b = Task.Delay(1, cts.Token);
return Task.WhenAny(a, b);
}
Also, I can just set cts.Token
to a variable and make the analyzer happy
static Task Main(string[] args)
{
using (var cts = new CancellationTokenSource())
{
var cancellationToken = cts.Token;
var a = Task.Delay(1, cancellationToken);
var b = Task.Delay(1, cancellationToken);
return Task.WhenAny(a, b);
}
}
It seems like either all of these should trigger the analyzer, or none of them should.
Is this a bug?
If not, what's your recommended fix for passing along a CancellationTokenSource?
Take a look at a SO question I have asked:
https://stackoverflow.com/questions/65891357/is-there-a-way-to-warn-about-an-unawaited-tasks
Someone suggested this package, however it does not cover such case.
Is this something that can potentially be added to AsyncFixer?
We have several occurrences of the following pattern:
Task[] tasks = CreateTasks();
await Task.WhenAll(tasks);
foreach(var task in tasks)
Console.WriteLine(task.Result);
The above code will generate an AsyncFixer02 warning for task.Result
. This seems to be not necessary, because the task
will already be completed at this point.
What is the expected fix to this? I'm tired of suppressing the warning for every such place in our code.
This simple example from a Blazor (.NET 6) triggers the AsyncFixer01
analyzer on the method OnAfterRenderAsync
:
protected override async Task OnAfterRenderAsync(bool firstRender)
=> await DoStuff();
private async ValueTask DoStuff() { ... }
Changing OnAfterRenderAsync
as suggested causes a compiler error.
protected override Task OnAfterRenderAsync(bool firstRender)
=> DoStuff();
The two errors are:
Consider this:
Assert.AreEqual(1, dbContext.Person.Count());
AF raises 02 for this and suggests CountAsync(), but asking VS to make the change results in:
await Assert.AreEqualAsync(1, dbContext.Person.Count());
rather than
Assert.AreEqual(1, await dbContext.Person.CountAsync());
Was wondering if there are plans to update this extension for VS 2022? Migration guide can be found here: https://docs.microsoft.com/en-us/visualstudio/extensibility/migration/update-visual-studio-extension
Consider the following code:
public class Test
{
public async Task<IEnumerable<string>> CreateEnumerableAsync() =>
await CreateListAsync();
public Task<IList<string>> CreateListAsync() => Task.FromResult(new List<string>() as IList<string>);
}
CreateEnumerableAsync()
violates AsyncFixer01 with "The method 'CreateEnumerableAsync' does not need to use async/await.". While at a first glance this seems correct as after await CreateListAsync()
there's nothing else done, and IList<string>
can be cast to IEnumerable<string>
, however, since Task<IList<string>>
can NOT be cast to Task<IEnumerable<string>>
the code without asnyc
and await
causes a compiler error:
public Task<IEnumerable<string>> CreateEnumerableAsync() =>
CreateListAsync();
This is because Task<T>
, on the contrary to the T
in question above, is not covariant.
I get a conflicting report that await is not necessary on the last line of this code snippet, but then the compiler complains if I remote the await.
#pragma warning disable AsyncFixer01
[Fact]
public async Task CreateUserProfileAsync_WhenNotInBlockListAndActorIdNotAssigned_ThrowsError()
{
// Arrange
CreateUserProfileModel createUserProfileModel = CreateUserProfileModel();
CreateUserRequestDto createUserRequestDto = new();
User user = CreateUser();
_userClient.Setup(s => s.CreateUserAsync(createUserRequestDto)).ReturnsAsync(user);
// Act
Func<Task> func = ()=> _service.CreateUserProfileAsync(createUserProfileModel);
// Assert
// Issue is with the following line.
await func.Should().ThrowExactlyAsync<InvalidOperationException>().WithMessage("The actorId property was not stored on the user profile for user with login [email protected] ");
}
#pragma warning restore AsyncFixer01
This situation might fail as well as the fire and forget version - even if the task is awaited higher in the call stack. So we should also recommend awaiting here.
Task MyMethod()
{
using(var a = new A());
return _someObject.SomeMethod(a);
}
Consider the following code:
public class Test
{
public async Task<bool> OuterAsync() => await InnerAsync(await Task.FromResult(true));
public Task<bool> InnerAsync(bool parameter) => Task.FromResult(parameter);
}
This causes an AsyncFixer01 violation for OuterAsync()
though due to parameter
an await
and thus async
on the method are needed.
Furthermore, if I hit Ctrl+. on OuterAsync()
then I get this:
Stack trace:
System.InvalidCastException : Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.ReturnStatementSyntax' to type 'Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax'.
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitListElement[TNode](TNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitList[TNode](SeparatedSyntaxList`1 list)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitArgumentList(ArgumentListSyntax node)
at Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.Replacer`1.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitInvocationExpression(InvocationExpressionSyntax node)
at Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.Replacer`1.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitAwaitExpression(AwaitExpressionSyntax node)
at Microsoft.CodeAnalysis.CSharp.Syntax.AwaitExpressionSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.Replacer`1.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitArrowExpressionClause(ArrowExpressionClauseSyntax node)
at Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.Replacer`1.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitMethodDeclaration(MethodDeclarationSyntax node)
at Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.Replacer`1.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.Syntax.SyntaxReplacer.Replace[TNode](SyntaxNode root,IEnumerable`1 nodes,Func`3 computeReplacementNode,IEnumerable`1 tokens,Func`3 computeReplacementToken,IEnumerable`1 trivia,Func`3 computeReplacementTrivia)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode.ReplaceCore[TNode](IEnumerable`1 nodes,Func`3 computeReplacementNode,IEnumerable`1 tokens,Func`3 computeReplacementToken,IEnumerable`1 trivia,Func`3 computeReplacementTrivia)
at Microsoft.CodeAnalysis.SyntaxNodeExtensions.ReplaceNodes[TRoot,TNode](TRoot root,IEnumerable`1 nodes,Func`3 computeReplacementNode)
at AsyncFixer.Helpers.ReplaceAll[T](T node,IEnumerable`1 replacementPairs)
at async AsyncFixer.UnnecessaryAsync.UnnecessaryAsyncFixer.RemoveAsyncAwait(<Unknown Parameters>)
at async Microsoft.CodeAnalysis.CodeActions.CodeAction.GetChangedSolutionAsync(<Unknown Parameters>)
at async Microsoft.CodeAnalysis.CodeActions.CodeAction.ComputeOperationsAsync(<Unknown Parameters>)
at async Microsoft.CodeAnalysis.CodeActions.CodeAction.GetPreviewOperationsAsync(<Unknown Parameters>)
at async Microsoft.CodeAnalysis.Editor.Implementation.Suggestions.SuggestedAction.GetPreviewResultAsync(<Unknown Parameters>)
at async Microsoft.CodeAnalysis.Editor.Implementation.Suggestions.SuggestedActionWithNestedFlavors.<>c__DisplayClass11_0.<GetPreviewAsync>b__0(<Unknown Parameters>)
at async Microsoft.CodeAnalysis.Extensions.IExtensionManagerExtensions.PerformFunctionAsync[T](<Unknown Parameters>)
Consider:
var x = myEfDbContext.MyTable.First().Id
Fixing the 02 on First() generates:
var x = await myEfDbContext.MyTable.FirstAsync().Id
When it would ideally be:
var x = (await myEfDbContext.MyTable.FirstAsync()).Id
public async Task Run()
{
Console.WriteLine(DateTime.Now);
await SomeDelay();
Console.WriteLine(DateTime.Now);
}
// Warns as expected
private async Task SomeDelay()
{
await Task.Factory.StartNew(async () =>
{
await Task.Delay(10000);
});
}
// Doesn't warn
private Task SomeDelay()
{
return Task.Factory.StartNew(async () =>
{
await Task.Delay(10000);
});
}
I know you did a preview, but the current download only supports 2019.
@semihokur you had talked about a 1.6 with more improvements back in July - is there a possibility of an update for 2022 anytime soon?
@SijyKijy are you just using your repo - building locally and installing, because I did not see a publish. If no plans I may just do that?
When using the nuget package and there are issue if you want to take a look at the issue in more depth you can click the link on the anaylzer code. These codes current direct to a bing.dev url but should really link to the docs/the repo.
Heres an example of the link currently seen in visual studio 2022
If you are using both Code Analyzer packages you will want to disable one or the other of these couple of duplicates.
AsyncFixer | VSThreading |
---|---|
AsyncFixer03 | VSTHRD100 |
AsyncFixer02 | VSTHRD103 |
Closing immediately after opening this issue as I just don't know where else to document this.
seeing these errors with the latest version of AsynFixer 1.5.1 and NetAnalyzers 5.0.3
Error AD0001: Analyzer 'AsyncFixer.UnnecessaryAsync.UnnecessaryAsyncAnalyzer' threw an exception of type 'System.InvalidCastException' with message 'Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEAssemblySymbol' to type 'Microsoft.CodeAnalysis.CSharp.Symbols.SourceAssemblySymbol'
Haven't tracked this down to a specific method yet - this just started happening when updating from 1.3.0 to 1.4.0.
It doesn't happen on all projects using it just some and those ones are the larger projects where there are thousands of calls.
is there anything I can do to get more detail to help track this down?
I have the following logic in my Blazor app. How could I resolve this AsyncFixer03 issue?
protected override void OnInitialized()
{
_appState.StateChanged += async (Source, Property) => await AppState_StateChanged(Source, Property);
}
// StateChanged is an event handler that takes a component and a string to denote which property changed
public event Action<ComponentBase, string> StateChanged;
A using block containing an await is correctly identified as needing async-await to be present.
public async Task DoSomething1()
{
using (var disposable = new Disposable())
{
await Task.Delay(1000);
}
}
A semantically identical using declaration, however, is not - AsyncFixer suggests the removal of async-await from this:
public async Task DoSomething2()
{
using var disposable = new Disposable();
await Task.Delay(1000);
}
This is an incorrect suggestion because disposable
will be disposed at the end of DoSomething2
and must therefore wait for Task.Delay
to finish, as is correctly identified in DoSomething1
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.