Skip to content

Commit

Permalink
Support async Main methods
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Aug 6, 2018
1 parent a6e2f51 commit 9a4f3fa
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,51 @@ class ClassName
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Theory]
[InlineData("static Task Main()")]
[InlineData("static Task<int> Main()")]
[InlineData("static Task Main(string[] args)")]
[InlineData("static Task Main(params string[] args)")]
[InlineData("static Task<int> Main(string[] args)")]
[InlineData("static Task<int> Main(params string[] args)")]
[InlineData("public static Task Main(string[] args)")]
[InlineData("public static Task<int> Main(params string[] args)")]
public async Task TestAsyncMainAsync(string signature)
{
string testCode = $@"
using System.Threading.Tasks;
class ClassName
{{
{signature} {{ throw null; }}
}}
";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Theory]
[InlineData(" Task Main()")]
[InlineData("static Task<uint> Main()")]
[InlineData("static Task Main(string[] args, CancellationToken cancellationToken)")]
[InlineData("static Task Main(string args)")]
public async Task TestAsyncMainNonMatchingSignatureAsync(string signature)
{
string testCode = $@"
using System.Threading;
using System.Threading.Tasks;
class ClassName
{{
{signature} {{ throw null; }}
}}
";
string fixedCode = testCode.Replace("Main", "MainAsync");

DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("Main").WithLocation(6, 23);
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}

protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
{
yield return new UseAsyncSuffixAnalyzer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace AsyncUsageAnalyzers.Helpers

internal static class MethodSymbolExtensions
{
public static bool HasAsyncSignature(this IMethodSymbol symbol, bool treatAsyncVoidAsAsync = false)
public static bool HasAsyncSignature(this IMethodSymbol symbol, bool treatAsyncVoidAsAsync = false, bool treatValueTaskAsAsync = true)
{
// void-returning methods are not asynchronous according to their signature, even if they use `async`
if (symbol.ReturnsVoid)
Expand All @@ -26,7 +26,7 @@ public static bool HasAsyncSignature(this IMethodSymbol symbol, bool treatAsyncV
{
// This check conveniently covers Task and Task<T> by ignoring the `1 in Task<T>.
if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal)
&& !string.Equals("ValueTask", symbol.ReturnType?.Name, StringComparison.Ordinal))
&& !(treatValueTaskAsAsync && string.Equals("ValueTask", symbol.ReturnType?.Name, StringComparison.Ordinal)))
{
return false;
}
Expand Down Expand Up @@ -92,5 +92,52 @@ public static bool IsOverrideOrImplementation(this IMethodSymbol symbol)

return false;
}

public static bool IsAsyncMain(this IMethodSymbol symbol)
{
// The following signatures are allowed:
//
// static Task Main()
// static Task<int> Main()
// static Task Main(string[])
// static Task<int> Main(string[])
if (!symbol.IsStatic)
{
return false;
}

if (!string.Equals(symbol.Name, "Main", StringComparison.Ordinal))
{
return false;
}

if (!symbol.HasAsyncSignature(treatAsyncVoidAsAsync: false, treatValueTaskAsAsync: false))
{
return false;
}

var returnType = (INamedTypeSymbol)symbol.ReturnType;
if (returnType.IsGenericType)
{
if (returnType.TypeArguments.Length != 1
|| returnType.TypeArguments[0].SpecialType != SpecialType.System_Int32)
{
return false;
}
}

switch (symbol.Parameters.Length)
{
case 0:
return true;

case 1:
return symbol.Parameters[0].Type is IArrayTypeSymbol arrayType
&& arrayType.ElementType.SpecialType == SpecialType.System_String;

default:
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ private static void HandleMethodDeclaration(SymbolAnalysisContext context)
return;
}

if (symbol.IsAsyncMain())
{
return;
}

context.ReportDiagnostic(Diagnostic.Create(Descriptor, symbol.Locations[0], symbol.Name));
}
}
Expand Down

0 comments on commit 9a4f3fa

Please sign in to comment.