-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Async methods should not have a void return type
- Loading branch information
Showing
9 changed files
with
428 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
262 changes: 262 additions & 0 deletions
262
...ostics/VSDiagnostics/VSDiagnostics.Test/Tests/Async/AsyncMethodWithVoidReturnTypeTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using RoslynTester.Helpers.CSharp; | ||
using VSDiagnostics.Diagnostics.Async.AsyncMethodWithVoidReturnType; | ||
|
||
namespace VSDiagnostics.Test.Tests.Async | ||
{ | ||
[TestClass] | ||
public class AsyncMethodWithVoidReturnTypeTests : CSharpCodeFixVerifier | ||
{ | ||
protected override DiagnosticAnalyzer DiagnosticAnalyzer => new AsyncMethodWithVoidReturnTypeAnalyzer(); | ||
protected override CodeFixProvider CodeFixProvider => new AsyncMethodWithVoidReturnTypeCodeFix(); | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncAndTask() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async Task MyMethod() | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_NoAsync() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
void MyMethod() | ||
{ | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncAndTaskGeneric() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async Task<int> MyMethod() | ||
{ | ||
return 32; | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncAndEventHandlerArguments() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async void MyHandler(object o, EventArgs e) | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncAndEventHandlerSubClassArguments() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async void MyHandler(object o, MyEventArgs e) | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
class MyEventArgs : EventArgs | ||
{ | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncDelegate() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
public void MyMethod() | ||
{ | ||
TestMethod(async () => await Task.Run(() => {})); | ||
} | ||
public void TestMethod(Action callback) | ||
{ | ||
callback(); | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncVoidAndArbitraryArguments() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async void MyHandler(object o, int e) | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
var result = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async Task MyHandler(object o, int e) | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original, "Method MyHandler is marked as async but has a void return type"); | ||
VerifyFix(original, result); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithAsyncAndVoid() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async void MyMethod() | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
var result = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
class MyClass | ||
{ | ||
async Task MyMethod() | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original, "Method MyMethod is marked as async but has a void return type"); | ||
VerifyFix(original, result); | ||
} | ||
|
||
[TestMethod] | ||
public void AsyncMethodWithVoidReturnType_WithPartialMethod() | ||
{ | ||
var original = @" | ||
using System; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
namespace ConsoleApplication1 | ||
{ | ||
partial class A | ||
{ | ||
partial void OnSomethingHappened(); | ||
} | ||
partial class A | ||
{ | ||
async partial void OnSomethingHappened() | ||
{ | ||
await Task.Run(() => { }); | ||
} | ||
} | ||
}"; | ||
|
||
VerifyDiagnostic(original); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
.../Diagnostics/Async/AsyncMethodWithVoidReturnType/AsyncMethodWithVoidReturnTypeAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using VSDiagnostics.Utilities; | ||
|
||
namespace VSDiagnostics.Diagnostics.Async.AsyncMethodWithVoidReturnType | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class AsyncMethodWithVoidReturnTypeAnalyzer : DiagnosticAnalyzer | ||
{ | ||
private const DiagnosticSeverity Severity = DiagnosticSeverity.Warning; | ||
|
||
private static readonly string Category = VSDiagnosticsResources.AsyncCategory; | ||
private static readonly string Message = VSDiagnosticsResources.AsyncMethodWithVoidReturnTypeMessage; | ||
private static readonly string Title = VSDiagnosticsResources.AsyncMethodWithVoidReturnTypeTitle; | ||
|
||
internal static DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId.AsyncMethodWithVoidReturnType, Title, Message, Category, Severity, isEnabledByDefault: true); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); | ||
|
||
public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.MethodDeclaration); | ||
|
||
private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context) | ||
{ | ||
var method = (MethodDeclarationSyntax) context.Node; | ||
|
||
// Method has to return void | ||
var returnType = context.SemanticModel.GetTypeInfo(method.ReturnType); | ||
if (returnType.Type == null || returnType.Type.SpecialType != SpecialType.System_Void) | ||
{ | ||
return; | ||
} | ||
|
||
var isAsync = false; | ||
foreach (var modifier in method.Modifiers) | ||
{ | ||
// Method has to be marked async | ||
if (modifier.IsKind(SyntaxKind.AsyncKeyword)) | ||
{ | ||
isAsync = true; | ||
} | ||
|
||
// Partial methods can only have a void return type | ||
if (modifier.IsKind(SyntaxKind.PartialKeyword)) | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
if (!isAsync) | ||
{ | ||
return; | ||
} | ||
|
||
// Event handlers can only have a void return type | ||
if (method.ParameterList?.Parameters.Count == 2) | ||
{ | ||
var parameters = method.ParameterList.Parameters; | ||
var firstArgumentType = context.SemanticModel.GetTypeInfo(parameters[0].Type); | ||
var isFirstArgumentObject = firstArgumentType.Type != null && | ||
firstArgumentType.Type.SpecialType == SpecialType.System_Object; | ||
|
||
|
||
var secondArgumentType = context.SemanticModel.GetTypeInfo(parameters[1].Type); | ||
var isSecondArgumentEventArgs = secondArgumentType.Type != null && | ||
secondArgumentType.Type.InheritsFrom(typeof(EventArgs)); | ||
|
||
if (isFirstArgumentObject && isSecondArgumentEventArgs) | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
context.ReportDiagnostic(Diagnostic.Create(Rule, method.GetLocation(), method.Identifier.ValueText)); | ||
} | ||
} | ||
} |
Oops, something went wrong.