diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 41d0bcf3a..7ee8f77e8 --- a/README.md +++ b/README.md @@ -169,7 +169,8 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0151](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0151.md)|Usage|DebuggerDisplay must contain valid members|⚠️|✔️|❌| |[MA0152](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0152.md)|Performance|Use Unwrap instead of using await twice|ℹ️|✔️|❌| |[MA0153](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0153.md)|Design|Do not log symbols decorated with DataClassificationAttribute directly|⚠️|✔️|❌| -|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|❌| +|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|✔️| +|[MA0155](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0155.md)|Design|Do not use async void methods|⚠️|❌|❌| diff --git a/docs/README.md b/docs/README.md old mode 100644 new mode 100755 index e332969cd..fd8e07f31 --- a/docs/README.md +++ b/docs/README.md @@ -153,7 +153,8 @@ |[MA0151](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0151.md)|Usage|DebuggerDisplay must contain valid members|⚠️|✔️|❌| |[MA0152](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0152.md)|Performance|Use Unwrap instead of using await twice|ℹ️|✔️|❌| |[MA0153](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0153.md)|Design|Do not log symbols decorated with DataClassificationAttribute directly|⚠️|✔️|❌| -|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|❌| +|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|✔️| +|[MA0155](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0155.md)|Design|Do not use async void methods|⚠️|❌|❌| |Id|Suppressed rule|Justification| |--|---------------|-------------| @@ -622,6 +623,9 @@ dotnet_diagnostic.MA0153.severity = warning # MA0154: Use langword in XML comment dotnet_diagnostic.MA0154.severity = suggestion + +# MA0155: Do not use async void methods +dotnet_diagnostic.MA0155.severity = none ``` # .editorconfig - all rules disabled @@ -1085,4 +1089,7 @@ dotnet_diagnostic.MA0153.severity = none # MA0154: Use langword in XML comment dotnet_diagnostic.MA0154.severity = none + +# MA0155: Do not use async void methods +dotnet_diagnostic.MA0155.severity = none ``` diff --git a/docs/Rules/MA0155.md b/docs/Rules/MA0155.md new file mode 100755 index 000000000..1018354e0 --- /dev/null +++ b/docs/Rules/MA0155.md @@ -0,0 +1,15 @@ +# MA0155 - Do not use async void methods + +```c# +// not-compliant +async void SomeMethod() +{ + await Task.Delay(1000); +} + +// ok +async Task SomeMethod() +{ + await Task.Delay(1000); +} +``` diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs new file mode 100755 index 000000000..4f7feb585 --- /dev/null +++ b/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs @@ -0,0 +1,38 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Meziantou.Analyzer.Rules; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class DoNotUseAsyncVoidAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor Rule = new( + RuleIdentifiers.DoNotUseAsyncVoid, + title: "Do not use async void methods", + messageFormat: "Do not use async void methods", + RuleCategories.Design, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: "", + helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.DoNotUseAsyncVoid)); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + var symbol = (IMethodSymbol)context.Symbol; + if (symbol is { ReturnsVoid: true, IsAsync: true }) + { + context.ReportDiagnostic(Rule, symbol); + } + } +} \ No newline at end of file diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs new file mode 100755 index 000000000..1afeb8da9 --- /dev/null +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; +using Meziantou.Analyzer.Rules; +using TestHelper; +using Xunit; + +namespace Meziantou.Analyzer.Test.Rules; +public sealed class DoNotUseAsyncVoidAnalyzerTests +{ + private static ProjectBuilder CreateProjectBuilder() + { + return new ProjectBuilder() + .WithAnalyzer() + .WithTargetFramework(TargetFramework.Net8_0); + } + + [Fact] + public async Task Method_Void() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + void A() => throw null; + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task Method_AsyncVoid() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + async void [|A|]() => throw null; + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task Method_AsyncTask() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + async System.Threading.Tasks.Task A() => throw null; + } + """) + .ValidateAsync(); + } + +}