Skip to content

Commit

Permalink
Fix #22: Await ForEach statements
Browse files Browse the repository at this point in the history
  • Loading branch information
semihokur committed May 11, 2022
1 parent 5000c6d commit 8bba5a6
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 7 deletions.
21 changes: 21 additions & 0 deletions AsyncFixer.Samples/UnnecessaryAsync.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

Expand Down Expand Up @@ -110,5 +111,25 @@ public static async Task foo4(int i)
await Task.Delay(2000);
}
}

public async Task MyFunction()
{
await Task.Run(async () =>
{
await foreach (var i in RangeAsync(10, 3))
{
Console.WriteLine(i);
}
});
}

static async IAsyncEnumerable<int> RangeAsync(int start, int count)
{
for (int i = 0; i < count; i++)
{
await Task.Delay(i);
yield return start + i;
}
}
}
}
1 change: 0 additions & 1 deletion AsyncFixer.Test/Helpers/CSharpCodeFixVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticRes
var test = new Test
{
TestCode = source,
LanguageVersion = LanguageVersion.Latest
};

test.ExpectedDiagnostics.AddRange(expected);
Expand Down
113 changes: 111 additions & 2 deletions AsyncFixer.Test/UnnecessaryAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ async Task foo()
}

[Fact]
public async Task OutOfScopeSiblingUsingBlock()
public Task OutOfScopeSiblingUsingBlock()
{
var test = @"
using System;
Expand Down Expand Up @@ -506,7 +506,7 @@ Task foo(bool cond)
}
}";

await new Verify.Test
return new Verify.Test
{
TestState =
{
Expand Down Expand Up @@ -942,6 +942,115 @@ async Task<int> boo(bool c) {
VerifyCSharpDiagnostic(test);
}

[Fact]
public Task NoWarn_AwaitForEach()
{
//No diagnostics expected to show up

var test = @"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
internal class Program
{
public async Task MyFunction()
{
await foreach (var i in RangeAsync(10, 3))
{
Console.WriteLine(i);
}
await Task.Delay(1);
}
static async IAsyncEnumerable<int> RangeAsync(int start, int count)
{
for (int i = 0; i < count; i++)
{
await Task.Delay(i);
yield return start + i;
}
}
}";
return Verify.VerifyAsync(test);
}

[Fact]
public Task AwaitForEachUnderLambda()
{
var test = @"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
internal class Program
{
public async Task MyFunction()
{
await Task.Run(async () =>
{
await foreach (var i in RangeAsync(10, 3))
{
Console.WriteLine(i);
}
});
}
static async IAsyncEnumerable<int> RangeAsync(int start, int count)
{
for (int i = 0; i < count; i++)
{
await Task.Delay(i);
yield return start + i;
}
}
}";

var fixtest = @"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
internal class Program
{
public Task MyFunction()
{
return Task.Run(async () =>
{
await foreach (var i in RangeAsync(10, 3))
{
Console.WriteLine(i);
}
});
}
static async IAsyncEnumerable<int> RangeAsync(int start, int count)
{
for (int i = 0; i < count; i++)
{
await Task.Delay(i);
yield return start + i;
}
}
}";
return new Verify.Test
{
TestState =
{
Sources = { test },
ExpectedDiagnostics =
{
Verify.Diagnostic().WithSpan(8, 5, 17, 6).WithArguments("MyFunction"),
},
},
FixedState =
{
Sources = { fixtest },
},
}.RunAsync();
}

protected override CodeFixProvider GetCSharpCodeFixProvider()
{
return new UnnecessaryAsyncFixer();
Expand Down
16 changes: 12 additions & 4 deletions AsyncFixer/UnnecessaryAsync/UnnecessaryAsyncAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ private void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context)
return;
}

var awaitForEachStatements = node.DescendantNodes().OfType<ForEachStatementSyntax>()
.Where(a => a.AwaitKeyword != null && a.FirstAncestorOrSelfUnderGivenNode<LambdaExpressionSyntax>(node) == null).ToList();

if (awaitForEachStatements.Any())
{
return;
}

// Retrieve all await expressions excluding the ones under lambda functions.
var awaitExpressions = node.DescendantNodes().OfType<AwaitExpressionSyntax>().Where(a => a.FirstAncestorOrSelfUnderGivenNode<LambdaExpressionSyntax>(node) == null).ToList();

Expand Down Expand Up @@ -130,7 +138,7 @@ private void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context)

var controlFlow = context.SemanticModel.AnalyzeControlFlow(node.Body);
var returnStatements = controlFlow?.ReturnStatements ?? ImmutableArray<SyntaxNode>.Empty;
var numAwait = 0;
var numAwaitsToRemove = 0;

if (returnStatements.Any())
{
Expand Down Expand Up @@ -158,7 +166,7 @@ private void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context)
return;
}

numAwait++;
numAwaitsToRemove++;
}
}
else
Expand Down Expand Up @@ -190,10 +198,10 @@ private void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context)
return;
}

numAwait++;
numAwaitsToRemove++;
}

if (numAwait < awaitExpressions.Count())
if (numAwaitsToRemove < awaitExpressions.Count())
{
return;
}
Expand Down

0 comments on commit 8bba5a6

Please sign in to comment.