diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index d02e32d22e28e..2cc61856e9e13 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -2060,12 +2060,12 @@ private AssemblySymbol GetForwardedToAssembly(string fullName, int arity, Diagno return null; } - internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID feature, DiagnosticBag diagnostics, Location locationOpt = null) + internal static bool CheckFeatureAvailability(SyntaxNode syntax, MessageID feature, DiagnosticBag diagnostics, Location locationOpt = null) { var options = (CSharpParseOptions)syntax.SyntaxTree.Options; if (options.IsFeatureEnabled(feature)) { - return; + return true; } var location = locationOpt ?? syntax.GetLocation(); @@ -2073,7 +2073,7 @@ internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID featu if (requiredFeature != null) { diagnostics.Add(ErrorCode.ERR_FeatureIsExperimental, location, feature.Localize(), requiredFeature); - return; + return false; } LanguageVersion availableVersion = options.LanguageVersion; @@ -2081,7 +2081,10 @@ internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID featu if (requiredVersion > availableVersion) { diagnostics.Add(availableVersion.GetErrorCode(), location, feature.Localize(), new CSharpRequiredLanguageVersion(requiredVersion)); + return false; } + + return true; } } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 1280b1141bdb8..de6e009a64bba 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -6882,7 +6882,18 @@ internal static string ERR_NonInvocableMemberCalled { return ResourceManager.GetString("ERR_NonInvocableMemberCalled", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Async Main methods must return Task or Task<int>. + /// + internal static string ERR_NonTaskMainCantBeAsync + { + get + { + return ResourceManager.GetString("ERR_NonTaskMainCantBeAsync", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot embed interop types from assembly '{0}' because it is missing the '{1}' attribute.. /// @@ -9852,22 +9863,24 @@ internal static string IDS_Covariantly { return ResourceManager.GetString("IDS_Covariantly", resourceCulture); } } - + /// /// Looks up a localized string similar to /// Visual C# Compiler Options /// /// - OUTPUT FILES - - /// /out:<file> Specify output file name (default: base name of + /// /out:<file> Specify output file name (default: base name of /// file with main class or first file) - /// /target:exe Build a console executable (default) (Short + /// /target:exe Build a console executable (default) (Short /// form: /t:exe) - /// /target:winexe Build a Windows executable (Short form: + /// /target:winexe Build a Windows executable (Short form: /// /t:winexe) - /// /target:library [rest of string was truncated]";. + /// /target:library [rest of string was truncated]";. /// - internal static string IDS_CSCHelp { - get { + internal static string IDS_CSCHelp + { + get + { return ResourceManager.GetString("IDS_CSCHelp", resourceCulture); } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 78bf4e6b5f7b5..82afbcaa4e07b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3596,9 +3596,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi Async methods are not allowed in an Interface, Class, or Structure which has the 'SecurityCritical' or 'SecuritySafeCritical' attribute. - - '{0}': an entry point cannot be marked with the 'async' modifier - The 'await' operator may only be used in a query expression within the first collection expression of the initial 'from' clause or within the collection expression of a 'join' clause @@ -5073,4 +5070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A tuple may not contain a value of type 'void'. + + A void or int returning entry point cannot be async + diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index c1dada07c57d0..9e078424051a2 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1453,42 +1453,101 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm } } - DiagnosticBag warnings = DiagnosticBag.GetInstance(); - var viableEntryPoints = ArrayBuilder.GetInstance(); - foreach (var candidate in entryPointCandidates) + // Validity and diagnostics are also tracked because they must be conditionally handled + // if there are not any "traditional" entrypoints found. + var taskEntryPoints = ArrayBuilder<(bool IsValid, MethodSymbol Candidate, DiagnosticBag SpecificDiagnostics)>.GetInstance(); + + // These diagnostics (warning only) are added to the compilation only if + // there were not any main methods found. + DiagnosticBag noMainFoundDiagnostics = DiagnosticBag.GetInstance(); + + bool CheckValid(MethodSymbol candidate, bool isCandidate, DiagnosticBag specificDiagnostics) { - if (!HasEntryPointSignature(candidate, warnings)) + if (!isCandidate) { - // a single error for partial methods - warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); - continue; + noMainFoundDiagnostics.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); + noMainFoundDiagnostics.AddRange(specificDiagnostics); + return false; } if (candidate.IsGenericMethod || candidate.ContainingType.IsGenericType) { // a single error for partial methods: - warnings.Add(ErrorCode.WRN_MainCantBeGeneric, candidate.Locations.First(), candidate); - continue; + noMainFoundDiagnostics.Add(ErrorCode.WRN_MainCantBeGeneric, candidate.Locations.First(), candidate); + return false; } + return true; + } + + var viableEntryPoints = ArrayBuilder.GetInstance(); + + foreach (var candidate in entryPointCandidates) + { + var perCandidateBag = DiagnosticBag.GetInstance(); + var (IsCandidate, IsTaskLike) = HasEntryPointSignature(candidate, perCandidateBag); - // PROTOTYPE(async-main): CheckFeatureAvailability should be called on non-async methods - // that return Task or Task - if (candidate.IsAsync) + if (IsTaskLike) { - // PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. - // PROTOTYPE(async-main): Switch diagnostics around if the type is not Task or Task - CheckFeatureAvailability(candidate.GetNonNullSyntaxNode(), MessageID.IDS_FeatureAsyncMain, diagnostics); + taskEntryPoints.Add((IsCandidate, candidate, perCandidateBag)); + } + else + { + if (CheckValid(candidate, IsCandidate, perCandidateBag)) + { + if (candidate.IsAsync) + { + diagnostics.Add(ErrorCode.ERR_NonTaskMainCantBeAsync, candidate.Locations.First(), candidate); + } + else + { + diagnostics.AddRange(perCandidateBag); + viableEntryPoints.Add(candidate); + } + } + perCandidateBag.Free(); } + } - viableEntryPoints.Add(candidate); + if (viableEntryPoints.Count == 0) + { + foreach (var (IsValid, Candidate, SpecificDiagnostics) in taskEntryPoints) + { + // PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. + if (CheckValid(Candidate, IsValid, SpecificDiagnostics) && + CheckFeatureAvailability(Candidate.GetNonNullSyntaxNode(), MessageID.IDS_FeatureAsyncMain, diagnostics)) + { + diagnostics.AddRange(SpecificDiagnostics); + viableEntryPoints.Add(Candidate); + } + } } - if ((object)mainType == null || viableEntryPoints.Count == 0) + foreach (var (_, _, SpecificDiagnostics) in taskEntryPoints) { - diagnostics.AddRange(warnings); + SpecificDiagnostics.Free(); } - warnings.Free(); + if (viableEntryPoints.Count == 0) + { + diagnostics.AddRange(noMainFoundDiagnostics); + } + else if ((object)mainType == null) + { + // Filters out diagnostics so that only InvalidMainSig and MainCant'BeGeneric are left. + // The reason that Error diagnostics can end up in `noMainFoundDiagnostics` is when + // HasEntryPointSignature yields some Error Diagnostics when people implement Task or Task incorrectly. + // + // We can't add those Errors to the general diagnostics bag because it would break previously-working programs. + // The fact that these warnings are not added when csc is invoked with /main is possibly a bug, and is tracked at + // https://github.com/dotnet/roslyn/issues/18964 + foreach (var diagnostic in noMainFoundDiagnostics.AsEnumerable()) + { + if (diagnostic.Code == (int)ErrorCode.WRN_InvalidMainSig || diagnostic.Code == (int)ErrorCode.WRN_MainCantBeGeneric) + { + diagnostics.Add(diagnostic); + } + } + } MethodSymbol entryPoint = null; if (viableEntryPoints.Count == 0) @@ -1518,7 +1577,9 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm entryPoint = viableEntryPoints[0]; } + taskEntryPoints.Free(); viableEntryPoints.Free(); + noMainFoundDiagnostics.Free(); return entryPoint; } finally @@ -1561,15 +1622,16 @@ internal bool ReturnsAwaitableToVoidOrInt(MethodSymbol method, DiagnosticBag dia /// /// Checks if the method has an entry point compatible signature, i.e. - /// - the return type is either void, int, , - /// or where T is an int. + /// - the return type is either void, int, or returns a , + /// or where the return type of GetAwaiter().GetResult() + /// is either void or int. /// - has either no parameter or a single parameter of type string[] /// - private bool HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) + private (bool IsCandidate, bool IsTaskLike) HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) { if (method.IsVararg) { - return false; + return (false, false); } TypeSymbol returnType = method.ReturnType; @@ -1580,45 +1642,38 @@ private bool HasEntryPointSignature(MethodSymbol method, DiagnosticBag bag) returnsTaskOrTaskOfInt = ReturnsAwaitableToVoidOrInt(method, bag); if (!returnsTaskOrTaskOfInt) { - return false; + return (false, false); } } - // Prior to 7.1, async methods were considered to "have the entrypoint signature". - // In order to keep back-compat, we need to let these through if compiling using a previous language version. - if (!returnsTaskOrTaskOfInt && method.IsAsync && this.LanguageVersion >= LanguageVersion.CSharp7_1) - { - return false; - } - if (method.RefKind != RefKind.None) { - return false; + return (false, returnsTaskOrTaskOfInt); } if (method.Parameters.Length == 0) { - return true; + return (true, returnsTaskOrTaskOfInt); } if (method.Parameters.Length > 1) { - return false; + return (false, returnsTaskOrTaskOfInt); } if (!method.ParameterRefKinds.IsDefault) { - return false; + return (false, returnsTaskOrTaskOfInt); } var firstType = method.Parameters[0].Type; if (firstType.TypeKind != TypeKind.Array) { - return false; + return (false, returnsTaskOrTaskOfInt); } var array = (ArrayTypeSymbol)firstType; - return array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String; + return (array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String, returnsTaskOrTaskOfInt); } internal override bool IsUnreferencedAssemblyIdentityDiagnosticCode(int code) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index cdaf5879c9e2a..ea7ce6b4a38fd 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1104,7 +1104,7 @@ internal enum ErrorCode ERR_VarargsAsync = 4006, ERR_ByRefTypeAndAwait = 4007, ERR_BadAwaitArgVoidCall = 4008, - // ERR_MainCantBeAsync = 4009, + ERR_NonTaskMainCantBeAsync = 4009, ERR_CantConvAsyncAnonFuncReturns = 4010, ERR_BadAwaiterPattern = 4011, ERR_BadSpecialByRefLocal = 4012, diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 58187a5827b59..826b6d3ae3d80 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -388,7 +388,6 @@ internal override BoundBlock CreateBody() { var syntax = _userMainReturnTypeSyntax; - if (ReturnsVoid) { return new BoundBlock( diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs index 3dd02d4548495..fbaae3a50786b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs @@ -3,14 +3,15 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Xunit; using System.Threading; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.AsyncMain)] public class CodeGenAsyncMainTests : EmitMetadataTestBase { - [Fact] - public void MultipleMainsOneOfWhichHasBadTaskType_WithMainType() + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp71_WithMainType() { var source = @" using System.Threading.Tasks; @@ -35,7 +36,7 @@ static void Main(string[] args) { } } [Fact] - public void MultipleMainsOneOfWhichHasBadTaskType() + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp7() { var source = @" using System.Threading.Tasks; @@ -52,21 +53,114 @@ static Task Main() { } static void Main(string[] args) { } }"; - var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); sourceCompilation.VerifyEmitDiagnostics( // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. // static Task Main() { Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12), - // (11,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(11, 22)); + } + + [Fact] + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp7_WithExplicitMain() + { + var source = @" +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } + static void Main(string[] args) { } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe.WithMainTypeName("Program"), parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + sourceCompilation.VerifyEmitDiagnostics( + // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. // static Task Main() { - Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(11, 12), + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12)); + } + + [Fact] + public void MultipleMainsOneOfWhichHasBadTaskType_CSharp71() + { + var source = @" +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } + static void Main(string[] args) { } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + sourceCompilation.VerifyEmitDiagnostics( + // (11,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(11, 12), // (11,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point // static Task Main() { Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(11, 22)); } [Fact] - public void GetResultReturnsSomethingElse() + public void GetResultReturnsSomethingElse_CSharp7() + { + var source = @" +using System.Threading.Tasks; +using System; + +namespace System.Runtime.CompilerServices { + public interface INotifyCompletion { + void OnCompleted(Action action); + } +} + +namespace System.Threading.Tasks { + public class Awaiter: System.Runtime.CompilerServices.INotifyCompletion { + public double GetResult() { return 0.0; } + public bool IsCompleted => true; + public void OnCompleted(Action action) {} + } + public class Task { + public Awaiter GetAwaiter() { + return new Awaiter(); + } + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + sourceCompilation.VerifyEmitDiagnostics( + // (25,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(25, 12), + // (25,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(25, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void GetResultReturnsSomethingElse_CSharp71() { var source = @" using System.Threading.Tasks; @@ -105,12 +199,48 @@ static Task Main() { // static Task Main() { Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(25, 22), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point - Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1) -); + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact] - public void TaskOfTGetAwaiterReturnsVoid() + public void TaskOfTGetAwaiterReturnsVoid_CSharp7() + { + var source = @" +using System; +using System.Threading.Tasks; + +namespace System.Threading.Tasks { + public class Task { + public void GetAwaiter() {} + } +} + +static class Program { + static Task Main() { + return null; + } +}"; + + var sourceCompilation = CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + sourceCompilation.VerifyDiagnostics( + // (12,12): warning CS0436: The type 'Task' in '' conflicts with the imported type 'Task' in 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Using the type defined in ''. + // static Task Main() { + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Task").WithArguments("", "System.Threading.Tasks.Task", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,12): error CS1986: 'await' requires that the type Task have a suitable GetAwaiter method + // static Task Main() { + Diagnostic(ErrorCode.ERR_BadAwaitArg, "Task").WithArguments("System.Threading.Tasks.Task").WithLocation(12, 12), + // (12,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point + // static Task Main() { + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("Program.Main()").WithLocation(12, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), + // (2,1): hidden CS8019: Unnecessary using directive. + // using System; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(2, 1)); + } + + [Fact] + public void TaskOfTGetAwaiterReturnsVoid_CSharp71() { var source = @" using System; @@ -145,6 +275,7 @@ static Task Main() { // using System; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(2, 1)); } + [Fact] public void TaskGetAwaiterReturnsVoid() { @@ -215,6 +346,8 @@ public void EmitTaskOfIntReturningMainWithoutInt() var corAssembly = @" namespace System { public class Object {} + public abstract class ValueType{} + public struct Int32{} }"; var corCompilation = CreateCompilation(corAssembly, options: TestOptions.DebugDll); corCompilation.VerifyDiagnostics(); @@ -241,9 +374,6 @@ static Task Main() { Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // (6,17): error CS0518: Predefined type 'System.Int32' is not defined or imported // static Task Main() { - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(6, 17), - // (6,12): error CS1061: 'Task' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly reference?) - // static Task Main() { Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Task").WithArguments("System.Threading.Tasks.Task", "GetAwaiter").WithLocation(6, 12), // (6,22): warning CS0028: 'Program.Main()' has the wrong signature to be an entry point // static Task Main() { @@ -292,26 +422,6 @@ static Task Main() { Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } - [Fact] - public void AsyncEmitMainOfIntTest() - { - var source = @" -using System; -using System.Threading.Tasks; - -class Program { - static async Task Main() { - Console.Write(""hello ""); - await Task.Factory.StartNew(() => 5); - Console.Write(""async main""); - return 10; - } -}"; - var c = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); - var verifier = CompileAndVerify(c, expectedOutput: "hello async main", expectedReturnCode: 10); - - } - [Fact] public void AsyncEmitMainTest() { @@ -548,6 +658,8 @@ async static Task Main(string[] args) }"; var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); compilation.VerifyDiagnostics( + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async static Task Main(string[] args) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) @@ -596,7 +708,7 @@ static Task Main() } [Fact] - public void MainCantBeAsyncVoid() + public void MainCantBeAsyncVoid_CSharp7() { var source = @" using System.Threading.Tasks; @@ -608,38 +720,13 @@ async static void Main() await Task.Factory.StartNew(() => { }); } }"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); compilation.VerifyDiagnostics( - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // (6,23): error CS9003: Async Main methods must return Task or Task // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(6, 23), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); - var entry = compilation.GetEntryPoint(CancellationToken.None); - Assert.Null(entry); - } - - [Fact] - public void MainCantBeAsyncVoid_CSharp7() - { - var source = @" -using System.Threading.Tasks; - -class A -{ - async static void Main() - { - await Task.Factory.StartNew(() => { }); - } -}"; - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); - compilation.VerifyDiagnostics( - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. - // async static void Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static void Main() - { - await Task.Factory.StartNew(() => { }); - }").WithArguments("async main", "7.1").WithLocation(6, 5)); } [Fact] @@ -660,9 +747,9 @@ async static int Main() // (6,22): error CS1983: The return type of an async method must be void, Task or Task // async static int Main() Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point - // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 22), + // (6,22): error CS4009: A void or int returning entry point cannot be async + // async static int Main() + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(6, 22), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); var entry = compilation.GetEntryPoint(CancellationToken.None); @@ -686,14 +773,12 @@ async static int Main() compilation.VerifyDiagnostics( // (6,22): error CS1983: The return type of an async method must be void, Task or Task // async static int Main() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main"), - // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(6, 22), + // (6,22): error CS9003: Async Main methods must return Task or Task // async static int Main() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static int Main() - { - return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) -); + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(6, 22), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact] @@ -751,13 +836,14 @@ async static Task Main(string[] args) }"; var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); compilation.VerifyDiagnostics( + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1), // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async static Task Main(string[] args) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main(string[] args) { return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) -); + }").WithArguments("async main", "7.1").WithLocation(6, 5)); } [Fact] @@ -779,6 +865,7 @@ async static Task Main() Assert.NotNull(entry); Assert.Equal("System.Threading.Tasks.Task A.Main()", entry.ToTestDisplayString()); } + [Fact] public void MainCanReturnTaskAndGenericOnInt_NoAsync() { @@ -819,8 +906,9 @@ async static Task Main() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() { return await Task.Factory.StartNew(() => 5); - }").WithArguments("async main", "7.1").WithLocation(6, 5) - ); + }").WithArguments("async main", "7.1").WithLocation(6, 5), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact] @@ -860,9 +948,9 @@ async static void Main() } }"; CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( - // (6,23): warning CS0028: 'A.Main()' has the wrong signature to be an entry point + // (6,23): warning CS0402: 'A.Main()': an entry point cannot be generic or in a generic type // async static void Main() - Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main()").WithLocation(6, 23), + Diagnostic(ErrorCode.WRN_MainCantBeGeneric, "Main").WithArguments("A.Main()").WithLocation(6, 23), // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } @@ -908,5 +996,220 @@ async static void Main(bool truth) // error CS5001: Program does not contain a static 'Main' method suitable for an entry point Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } + + [Fact] + public void TaskMainAndNonTaskMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void TaskMainAndNonTaskMain_CSharp71() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void AsyncVoidMain_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static void Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Async Void Main""); + } + async static int Main() + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Async Void Main""); + return 1; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + // (11,22): error CS1983: The return type of an async method must be void, Task or Task + // async static int Main() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "Main").WithLocation(11, 22), + // (11,22): error CS4009: A void or int returning entry point cannot be async + // async static int Main() + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main()").WithLocation(11, 22), + // (6,23): error CS4009: A void or int returning entry point cannot be async + // async static void Main(string[] args) + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main(string[])").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void AsyncVoidMain_CSharp71() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static async void Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Async Void Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( + // (6,23): error CS4009: A void or int returning entry point cannot be async + // static async void Main(string[] args) + Diagnostic(ErrorCode.ERR_NonTaskMainCantBeAsync, "Main").WithArguments("A.Main(string[])").WithLocation(6, 23), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact] + public void TaskMainAndNonTaskMain_WithExplicitMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe.WithMainTypeName("A")).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void TaskIntAndTaskFloat_CSharp7() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + async static Task Main() + { + System.Console.WriteLine(""Task""); + return 0; + } + + async static Task Main(string[] args) + { + System.Console.WriteLine(""Task""); + return 0.0F; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseDebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // async static Task Main() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(6, 28), + // (12,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // async static Task Main(string[] args) + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(12, 30), + // (6,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. + // async static Task Main() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async static Task Main() + { + System.Console.WriteLine(""Task""); + return 0; + }").WithArguments("async main", "7.1").WithLocation(6, 5), + // (12,30): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point + // async static Task Main(string[] args) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(12, 30), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + + [Fact] + public void TaskOfFloatMainAndNonTaskMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + return 0.0f; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics( + // (10,30): warning CS0028: 'A.Main(string[])' has the wrong signature to be an entry point + // async static Task Main(string[] args) + Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(string[])").WithLocation(11, 30)); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } + + [Fact] + public void TaskOfFloatMainAndNonTaskMain_WithExplicitMain() + { + var source = @" +using System.Threading.Tasks; + +class A +{ + static void Main() + { + System.Console.WriteLine(""Non Task Main""); + } + + async static Task Main(string[] args) + { + await Task.Factory.StartNew(() => { }); + System.Console.WriteLine(""Task Main""); + return 0.0f; + } +}"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe.WithMainTypeName("A")).VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "Non Task Main", expectedReturnCode: 0); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index ea6c34daf69c4..b5ceade1893ea 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -3403,7 +3403,7 @@ public static int Main() } [Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")] - public void Repro_17885() + public void Repro_17885_CSharp_71() { var source = @" using System.Threading.Tasks; @@ -3416,7 +3416,7 @@ async public static Task Main() CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( // (5,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // async public static Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main")); + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(5, 30)); } [Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")] @@ -3433,12 +3433,14 @@ async public static Task Main() CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( // (5,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // async public static Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main"), + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(5, 30), // (5,5): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async public static Task Main() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, @"async public static Task Main() { - }").WithArguments("async main", "7.1").WithLocation(5, 5)); + }").WithArguments("async main", "7.1").WithLocation(5, 5), + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); } [Fact, WorkItem(547088, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547088")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index e0b17fba31631..7b9c0c19250a6 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -5,6 +5,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; @@ -222,12 +223,10 @@ partial void M() {} var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree); - var voidTokens = tree.GetRoot().DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).ToList(); - var node = tree.GetRoot().FindNode(voidTokens[0].Span); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).First(); - var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList(); - var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[1].Span)); + var otherSymbol = m.PartialImplementationPart; + Assert.True(otherSymbol.IsPartialImplementation()); Assert.Equal(node, returnSyntax); Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax()); @@ -252,12 +251,10 @@ public partial class A { var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree); - var token = tree.GetRoot().DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).Skip(1).First(); - var node = tree.GetRoot().FindNode(token.Span); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Last(); - var mLocations = tree.GetRoot().DescendantTokens().Where(t => t.ValueText == "M").ToList(); - var otherSymbol = (MethodSymbol)model.GetDeclaredSymbolForNode(tree.GetRoot().FindNode(mLocations[0].Span)); + var otherSymbol = m.PartialImplementationPart; + Assert.True(otherSymbol.IsPartialImplementation()); Assert.Equal(node, returnSyntax); Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax()); @@ -279,8 +276,7 @@ partial void M() {} var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single().GetRoot(); - var token = tree.DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).First(); - var node = tree.FindNode(token.Span); + var node = tree.DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Single(); Assert.Equal(node, returnSyntax); } @@ -301,8 +297,7 @@ public void PartialExtractSyntaxLocation_OnlyDecl() var returnSyntax = m.ExtractReturnTypeSyntax(); var tree = comp.SyntaxTrees.Single().GetRoot(); - var token = tree.DescendantTokens().Where(t => t.Kind() == SyntaxKind.VoidKeyword).First(); - var node = tree.FindNode(token.Span); + var node = tree.DescendantNodes().OfType().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Single(); Assert.Equal(node, returnSyntax); } diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs index e3ac493c97775..0530fb6217152 100644 --- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs @@ -20,5 +20,6 @@ public enum CompilerFeature OutVar, Patterns, DefaultLiteral, + AsyncMain, } }