Giter Club home page Giter Club logo

asyncfixer's People

Contributors

marcelolynch avatar semihokur avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asyncfixer's Issues

No violation for async void in Action

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.

warning if dispose object after async

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

Feature request: Prefer "await using" over "using" inside async methods

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?

Is AsyncFixer01 really an anti-pattern?

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 ๐Ÿ™‚

provide as dotnet tool

would be useful to provide this as dotnet tool just to run in CI/CD pipelines, instead of having a nugget dependency...

UnnecessaryAsync warning for a method involving using expression statements

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.

Warning inside query syntax

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.

AsyncFixer01 does not take await foreach into account

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.

`delegate Task FooAsync()` is passed a `Func<Task<T>>` hiding a bug

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>'?
        }

CancellationTokenSource in using block inconsistently triggers AsyncFixer04

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?

What's the preferred way to fix AsyncFixer02 warning when accessing task.Result after Task.WhenAll(tasks)?

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.

AsyncFixer01: false positive with ValueTask awaited in method returning Task

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:

  1. CS0029 Cannot implicitly convert type 'System.Threading.Tasks.ValueTask' to 'System.Threading.Tasks.Task'`
  2. CA2012 ValueTask instances returned from method calls should be directly awaited, returned, or passed as an argument to another method call. Other usage, such as storing an instance into a local or a field, is likely an indication of a bug, as ValueTask instances must only ever be consumed once.

Incorrect AsyncFixer01 violation for Task<T> if T is covariant

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.

Conflicting AsyncFixer01 on use of FluentAssertions ThrowAsync

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

AsyncFixer04 should also include return of Task

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);
}

Incorrect AsyncFixer01 violation when await is needed for inner method call parameter

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:
image

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>)

AsyncFixer05 doesn't cover `return Task.Factory.StartNew(async () =>...);`

        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);
            });
        }

VS2022 Support

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?

Analyzer links should be directed to the docs

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
image

issue with latest version

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'

error AD0001: Analyzer 'AsyncFixer.AsyncCallInsideUsingBlock.AsyncCallInsideUsingBlockAnalyzer' threw an exception of type 'System.NullReferenceException' with message 'Object reference not set to an instance of an object.'

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?

Question: How to fix this AsyncFixer03 issue?

I have the following logic in my Blazor app. How could I resolve this AsyncFixer03 issue?

image

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;

Using declaration treated differently to using blocks

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.