Skip to content
This repository was archived by the owner on Nov 8, 2018. It is now read-only.

Commit e66e1d6

Browse files
committed
Add 'AvoidAsyncVoid' analyzer
Fixes #5
1 parent a84b82d commit e66e1d6

File tree

6 files changed

+201
-0
lines changed

6 files changed

+201
-0
lines changed

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/AsyncUsageAnalyzers.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
<ItemGroup>
125125
<Compile Include="Naming\AvoidAsyncSuffixUnitTests.cs" />
126126
<Compile Include="Naming\UseAsyncSuffixUnitTests.cs" />
127+
<Compile Include="Reliability\AvoidAsyncVoidUnitTests.cs" />
127128
<Compile Include="Verifiers\CodeFixVerifier.cs" />
128129
<Compile Include="Verifiers\DiagnosticVerifier.cs" />
129130
<Compile Include="Helpers\CodeFixVerifier.Helper.cs" />
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
namespace AsyncUsageAnalyzers.Test.Reliability
2+
{
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using AsyncUsageAnalyzers.Reliability;
6+
using Microsoft.CodeAnalysis.Diagnostics;
7+
using TestHelper;
8+
using Xunit;
9+
10+
public class AvoidAsyncVoidUnitTests : DiagnosticVerifier
11+
{
12+
[Fact]
13+
public async Task TestAsyncReturnVoidAsync()
14+
{
15+
string testCode = @"
16+
class ClassName
17+
{
18+
async void MethodNameAsync() { }
19+
}
20+
";
21+
22+
DiagnosticResult expected = CSharpDiagnostic().WithArguments("MethodNameAsync").WithLocation(4, 16);
23+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
24+
}
25+
26+
[Fact]
27+
public async Task TestAsyncEventHandlerReturnVoidAsync()
28+
{
29+
string testCode = @"
30+
using System;
31+
class ClassName
32+
{
33+
ClassName()
34+
{
35+
AppDomain.CurrentDomain.DomainUnload += MethodNameAsync;
36+
}
37+
38+
async void MethodNameAsync(object sender, EventArgs e) { }
39+
}
40+
";
41+
42+
// This analyzer does not currently handle this case differently from any other method
43+
DiagnosticResult expected = CSharpDiagnostic().WithArguments("MethodNameAsync").WithLocation(10, 16);
44+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
45+
}
46+
47+
[Fact]
48+
public async Task TestAsyncReturnTaskAsync()
49+
{
50+
string testCode = @"
51+
using System.Threading.Tasks;
52+
class ClassName
53+
{
54+
async Task MethodNameAsync() { }
55+
}
56+
";
57+
58+
await VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
59+
}
60+
61+
[Fact]
62+
public async Task TestAsyncReturnGenericTaskAsync()
63+
{
64+
string testCode = @"
65+
using System.Threading.Tasks;
66+
class ClassName
67+
{
68+
async Task<int> MethodNameAsync() { return 3; }
69+
}
70+
";
71+
72+
await VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
73+
}
74+
75+
[Fact]
76+
public async Task TestReturnTaskAsync()
77+
{
78+
string testCode = @"
79+
using System.Threading.Tasks;
80+
class ClassName
81+
{
82+
Task MethodNameAsync() { return Task.FromResult(3); }
83+
}
84+
";
85+
86+
await VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
87+
}
88+
89+
[Fact]
90+
public async Task TestReturnGenericTaskAsync()
91+
{
92+
string testCode = @"
93+
using System.Threading.Tasks;
94+
class ClassName
95+
{
96+
Task<int> MethodNameAsync() { return Task.FromResult(3); }
97+
}
98+
";
99+
100+
await VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
101+
}
102+
103+
protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
104+
{
105+
return new AvoidAsyncVoidAnalyzer();
106+
}
107+
}
108+
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers/AsyncUsageAnalyzers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<Compile Include="Naming\UseAsyncSuffixCodeFixProvider.cs" />
5858
<Compile Include="NoCodeFixAttribute.cs" />
5959
<Compile Include="Properties\AssemblyInfo.cs" />
60+
<Compile Include="Reliability\AvoidAsyncVoidAnalyzer.cs" />
6061
<Compile Include="Reliability\ReliabilityResources.Designer.cs">
6162
<AutoGen>True</AutoGen>
6263
<DesignTime>True</DesignTime>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
namespace AsyncUsageAnalyzers.Reliability
2+
{
3+
using System.Collections.Immutable;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
7+
/// <summary>
8+
/// This analyzer identifies code using <c>void</c>-returning <see langword="async"/> methods, and reports a
9+
/// warning.
10+
/// </summary>
11+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
12+
[NoCodeFix("No clear transformation to correct violations of this rule.")]
13+
public class AvoidAsyncVoidAnalyzer : DiagnosticAnalyzer
14+
{
15+
/// <summary>
16+
/// The ID for diagnostics produced by the <see cref="AvoidAsyncVoidAnalyzer"/> analyzer.
17+
/// </summary>
18+
public const string DiagnosticId = "AvoidAsyncVoid";
19+
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(ReliabilityResources.AvoidAsyncVoidTitle), ReliabilityResources.ResourceManager, typeof(ReliabilityResources));
20+
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(ReliabilityResources.AvoidAsyncVoidMessageFormat), ReliabilityResources.ResourceManager, typeof(ReliabilityResources));
21+
private static readonly string Category = "AsyncUsage.CSharp.Reliability";
22+
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(ReliabilityResources.AvoidAsyncVoidDescription), ReliabilityResources.ResourceManager, typeof(ReliabilityResources));
23+
private static readonly string HelpLink = null;
24+
25+
private static readonly DiagnosticDescriptor Descriptor =
26+
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, true, Description, HelpLink);
27+
28+
private static readonly ImmutableArray<DiagnosticDescriptor> SupportedDiagnosticsValue =
29+
ImmutableArray.Create(Descriptor);
30+
31+
/// <inheritdoc/>
32+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
33+
{
34+
get
35+
{
36+
return SupportedDiagnosticsValue;
37+
}
38+
}
39+
40+
/// <inheritdoc/>
41+
public override void Initialize(AnalysisContext context)
42+
{
43+
context.RegisterSymbolAction(HandleMethodDeclaration, SymbolKind.Method);
44+
}
45+
46+
private void HandleMethodDeclaration(SymbolAnalysisContext context)
47+
{
48+
IMethodSymbol symbol = (IMethodSymbol)context.Symbol;
49+
if (!symbol.IsAsync || !symbol.ReturnsVoid)
50+
return;
51+
52+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, symbol.Locations[0], symbol.Name));
53+
}
54+
}
55+
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers/Reliability/ReliabilityResources.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

AsyncUsageAnalyzers/AsyncUsageAnalyzers/Reliability/ReliabilityResources.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,13 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120+
<data name="AvoidAsyncVoidDescription" xml:space="preserve">
121+
<value>Asynchronous methods should return a Task instead of void</value>
122+
</data>
123+
<data name="AvoidAsyncVoidMessageFormat" xml:space="preserve">
124+
<value>Asynchronous method '{0}' should not return void</value>
125+
</data>
126+
<data name="AvoidAsyncVoidTitle" xml:space="preserve">
127+
<value>Avoid async void</value>
128+
</data>
120129
</root>

0 commit comments

Comments
 (0)