提交 902da979 编写于 作者: C CyrusNajmabadi

Merge remote-tracking branch 'upstream/master' into blockSimplification

# Async Task Main
## [dotnet/csharplang proposal](https://github.com/dotnet/csharplang/blob/master/proposals/async-main.md)
## Technical Details
* The compiler must recognize `Task` and `Task<int>` as valid entrypoint return types in addition to `void` and `int`.
* The compiler must allow `async` to be placed on a main method that returns a `Task` or a `Task<T>` (but not void).
* The compiler must generate a shim method `$EntrypointMain` that mimics the arguments of the user-defined main.
* `static async Task Main(...)` -> `static void $EntrypointMain(...)`
* `static async Task<int> Main(...)` -> `static int $EntrypointMain(...)`
* The parameters between the user-defined main and the generated main should match exactly.
* The body of the generated main should be `return Main(args...).GetAwaiter().GetResult();`
\ No newline at end of file
Main areas to test:
* Signature acceptance / rejection
* Method body creation
# Signature acceptance / rejection
## Single Mains
Classes that contain only a single "main" method
### Single legal main
* Past: Ok
* New 7: Ok
* New 7.1 Ok
### Single async (void) main
* Past: ERR (Async can't be main)
* New 7: ERR (Update to get this to work)
* New 7.1: Ok
### Single async (Task) main
* Past: ERR (No entrypoints found), WARN (has the wrong signature to be an entry point)
* New 7: ERR (Update to get this to work)
* New 7.1 Ok
### Single async (Task<int>) main
* Past: ERR (No entrypoints found), WARN (has the wrong signature)
* New 7: ERR (Update to get this to work)
* New 7.1: Ok
## Multiple Mains
Classes that contain more than one main
### Multiple legal mains
* Past: Err
* New 7: Err
* New 7.1: Err
### Single legal main, single async (void) main
* Past: Err (an entrypoint cannot be marked with async)
* New 7: Err (new error here?)
* New 7.1: Err (new error here? "void is not an acceptable async return type")
### Single legal main, single legal Task main
* Past: Ok (warning: task main has wrong signature)
* New 7: Ok (new warning here)
* New 7.1: Ok (new warning here?)
### Single legal main, single legal Task<int> main
* Past: Ok (warning: task main has wrong signature)
* New 7: Ok (new warning here)
* New 7.1: Ok (new warning here?)
# Method body creation
* Inspect IL for correct codegen.
* Make sure that attributes are correctly applied to the synthesized mains.
## Broken methods on Task and Task<T>
* GetAwatier or GetResult are missing
* GetAwaiter or GetResult don't have the required signature
* Has the right shape, but types are missing
## Task or Task<T> is missing
This will be caught during entrypoint detection, should be a binding error.
## Void or int is missing
If Task can be found, but void or int can't be found, then the compiler should behave gracefully.
# Vlad Test Plan
## Public interface of compiler APIs
-N/A?
## General functionality
- `Task` and `Task<int>` returns are allowed. Do we require `async`.
- Multiple valid/invalid async candidates.
- process exit code on success (void and int) and on exception
- `/main:<type>` cmd switch is still functional
## Old versions, compat
- langver
- when both async and regular Main avaialble. With old/new compiler versions.
- async void Main. With old.new langver. With another applicable Main present.
## Type and members
- Access modifiers (public, protected, internal, protected internal, private), static modifier
- Parameter modifiers (ref, out, params)
- STA attribute
- Partial method
- Named and optional parameters
- `Task<dynamic>`
- Task-Like types
## Debug
- F11 works
- stepping through Main
......@@ -29,7 +29,7 @@ private BoundExpression BindAwait(BoundExpression expression, SyntaxNode node, D
bool hasErrors =
ReportBadAwaitWithoutAsync(node, diagnostics) |
ReportBadAwaitContext(node, diagnostics) |
!GetAwaitableExpressionInfo(expression, out getAwaiter, out isCompleted, out getResult, node, diagnostics);
!GetAwaitableExpressionInfo(expression, out getAwaiter, out isCompleted, out getResult, out _, node, diagnostics);
// Spec 7.7.7.2:
// The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). Thus,
......@@ -209,17 +209,19 @@ private bool ReportBadAwaitContext(SyntaxNode node, DiagnosticBag diagnostics)
/// Finds and validates the required members of an awaitable expression, as described in spec 7.7.7.1.
/// </summary>
/// <returns>True if the expression is awaitable; false otherwise.</returns>
private bool GetAwaitableExpressionInfo(
internal bool GetAwaitableExpressionInfo(
BoundExpression expression,
out MethodSymbol getAwaiter,
out PropertySymbol isCompleted,
out MethodSymbol getResult,
out BoundExpression getAwaiterGetResultCall,
SyntaxNode node,
DiagnosticBag diagnostics)
{
getAwaiter = null;
isCompleted = null;
getResult = null;
getAwaiterGetResultCall = null;
if (!ValidateAwaitedExpression(expression, node, diagnostics))
{
......@@ -231,7 +233,8 @@ private bool ReportBadAwaitContext(SyntaxNode node, DiagnosticBag diagnostics)
return true;
}
if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter))
BoundExpression getAwaiterCall = null;
if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter, out getAwaiterCall))
{
return false;
}
......@@ -239,7 +242,7 @@ private bool ReportBadAwaitContext(SyntaxNode node, DiagnosticBag diagnostics)
TypeSymbol awaiterType = getAwaiter.ReturnType;
return GetIsCompletedProperty(awaiterType, node, expression.Type, diagnostics, out isCompleted)
&& AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics)
&& GetGetResultMethod(awaiterType, node, expression.Type, diagnostics, out getResult);
&& GetGetResultMethod(getAwaiterCall, node, expression.Type, diagnostics, out getResult, out getAwaiterGetResultCall);
}
/// <summary>
......@@ -273,19 +276,21 @@ private static bool ValidateAwaitedExpression(BoundExpression expression, Syntax
/// NOTE: this is an error in the spec. An extension method of the form
/// Awaiter&lt;T&gt; GetAwaiter&lt;T&gt;(this Task&lt;T&gt;) may be used.
/// </remarks>
private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, DiagnosticBag diagnostics, out MethodSymbol getAwaiterMethod)
private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, DiagnosticBag diagnostics, out MethodSymbol getAwaiterMethod, out BoundExpression getAwaiterCall)
{
if (expression.Type.SpecialType == SpecialType.System_Void)
{
Error(diagnostics, ErrorCode.ERR_BadAwaitArgVoidCall, node);
getAwaiterMethod = null;
getAwaiterCall = null;
return false;
}
var getAwaiterCall = MakeInvocationExpression(node, expression, WellKnownMemberNames.GetAwaiter, ImmutableArray<BoundExpression>.Empty, diagnostics);
getAwaiterCall = MakeInvocationExpression(node, expression, WellKnownMemberNames.GetAwaiter, ImmutableArray<BoundExpression>.Empty, diagnostics);
if (getAwaiterCall.HasAnyErrors) // && !expression.HasAnyErrors?
{
getAwaiterMethod = null;
getAwaiterCall = null;
return false;
}
......@@ -293,6 +298,7 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Di
{
Error(diagnostics, ErrorCode.ERR_BadAwaitArg, node, expression.Type);
getAwaiterMethod = null;
getAwaiterCall = null;
return false;
}
......@@ -303,6 +309,7 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Di
{
Error(diagnostics, ErrorCode.ERR_BadAwaitArg, node, expression.Type);
getAwaiterMethod = null;
getAwaiterCall = null;
return false;
}
......@@ -383,28 +390,31 @@ private bool AwaiterImplementsINotifyCompletion(TypeSymbol awaiterType, SyntaxNo
/// Spec 7.7.7.1:
/// An Awaiter A has an accessible instance method GetResult with no parameters and no type parameters.
/// </remarks>
private bool GetGetResultMethod(TypeSymbol awaiterType, SyntaxNode node, TypeSymbol awaitedExpressionType, DiagnosticBag diagnostics, out MethodSymbol getResultMethod)
private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode node, TypeSymbol awaitedExpressionType, DiagnosticBag diagnostics, out MethodSymbol getResultMethod, out BoundExpression getAwaiterGetResultCall)
{
var receiver = new BoundLiteral(node, ConstantValue.Null, awaiterType);
var getResultCall = MakeInvocationExpression(node, receiver, WellKnownMemberNames.GetResult, ImmutableArray<BoundExpression>.Empty, diagnostics);
if (getResultCall.HasAnyErrors)
var awaiterType = awaiterExpression.Type;
getAwaiterGetResultCall = MakeInvocationExpression(node, awaiterExpression, WellKnownMemberNames.GetResult, ImmutableArray<BoundExpression>.Empty, diagnostics);
if (getAwaiterGetResultCall.HasAnyErrors)
{
getResultMethod = null;
getAwaiterGetResultCall = null;
return false;
}
if (getResultCall.Kind != BoundKind.Call)
if (getAwaiterGetResultCall.Kind != BoundKind.Call)
{
Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.GetResult);
getResultMethod = null;
getAwaiterGetResultCall = null;
return false;
}
getResultMethod = ((BoundCall)getResultCall).Method;
getResultMethod = ((BoundCall)getAwaiterGetResultCall).Method;
if (getResultMethod.IsExtensionMethod)
{
Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.GetResult);
getResultMethod = null;
getAwaiterGetResultCall = null;
return false;
}
......@@ -412,6 +422,7 @@ private bool GetGetResultMethod(TypeSymbol awaiterType, SyntaxNode node, TypeSym
{
Error(diagnostics, ErrorCode.ERR_BadAwaiterPattern, node, awaiterType, awaitedExpressionType);
getResultMethod = null;
getAwaiterGetResultCall = null;
return false;
}
......
......@@ -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
......@@ -6882,7 +6882,18 @@ internal class CSharpResources {
return ResourceManager.GetString("ERR_NonInvocableMemberCalled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Async Main methods must return Task or Task&lt;int&gt;.
/// </summary>
internal static string ERR_NonTaskMainCantBeAsync
{
get
{
return ResourceManager.GetString("ERR_NonTaskMainCantBeAsync", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot embed interop types from assembly &apos;{0}&apos; because it is missing the &apos;{1}&apos; attribute..
/// </summary>
......@@ -9870,22 +9881,24 @@ internal class CSharpResources {
return ResourceManager.GetString("IDS_Covariantly", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to
/// Visual C# Compiler Options
///
/// - OUTPUT FILES -
/// /out:&lt;file&gt; Specify output file name (default: base name of
/// /out:&lt;file&gt; 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]&quot;;.
/// /target:library [rest of string was truncated]&quot;;.
/// </summary>
internal static string IDS_CSCHelp {
get {
internal static string IDS_CSCHelp
{
get
{
return ResourceManager.GetString("IDS_CSCHelp", resourceCulture);
}
}
......@@ -9935,6 +9948,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to async main.
/// </summary>
internal static string IDS_FeatureAsyncMain {
get {
return ResourceManager.GetString("IDS_FeatureAsyncMain", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to automatically implemented properties.
/// </summary>
......
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
......@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
......@@ -3599,9 +3599,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi
<data name="ERR_SecurityCriticalOrSecuritySafeCriticalOnAsyncInClassOrStruct" xml:space="preserve">
<value>Async methods are not allowed in an Interface, Class, or Structure which has the 'SecurityCritical' or 'SecuritySafeCritical' attribute.</value>
</data>
<data name="ERR_MainCantBeAsync" xml:space="preserve">
<value>'{0}': an entry point cannot be marked with the 'async' modifier</value>
</data>
<data name="ERR_BadAwaitInQuery" xml:space="preserve">
<value>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</value>
</data>
......@@ -4391,34 +4388,34 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Visual C# Compiler Options
- OUTPUT FILES -
/out:&lt;file&gt; Specify output file name (default: base name of
/out:&lt;file&gt; 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 Build a library (Short form: /t:library)
/target:module Build a module that can be added to another
/target:module Build a module that can be added to another
assembly (Short form: /t:module)
/target:appcontainerexe Build an Appcontainer executable (Short form:
/target:appcontainerexe Build an Appcontainer executable (Short form:
/t:appcontainerexe)
/target:winmdobj Build a Windows Runtime intermediate file that
/target:winmdobj Build a Windows Runtime intermediate file that
is consumed by WinMDExp (Short form: /t:winmdobj)
/doc:&lt;file&gt; XML Documentation file to generate
/platform:&lt;string&gt; Limit which platforms this code can run on: x86,
Itanium, x64, arm, anycpu32bitpreferred, or
Itanium, x64, arm, anycpu32bitpreferred, or
anycpu. The default is anycpu.
- INPUT FILES -
/recurse:&lt;wildcard&gt; Include all files in the current directory and
subdirectories according to the wildcard
/recurse:&lt;wildcard&gt; Include all files in the current directory and
subdirectories according to the wildcard
specifications
/reference:&lt;alias&gt;=&lt;file&gt; Reference metadata from the specified assembly
/reference:&lt;alias&gt;=&lt;file&gt; Reference metadata from the specified assembly
file using the given alias (Short form: /r)
/reference:&lt;file list&gt; Reference metadata from the specified assembly
/reference:&lt;file list&gt; Reference metadata from the specified assembly
files (Short form: /r)
/addmodule:&lt;file list&gt; Link the specified modules into this assembly
/link:&lt;file list&gt; Embed metadata from the specified interop
/link:&lt;file list&gt; Embed metadata from the specified interop
assembly files (Short form: /l)
/analyzer:&lt;file list&gt; Run the analyzers from this assembly
(Short form: /a)
......@@ -4434,16 +4431,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
/win32manifest:&lt;file&gt; Specify a Win32 manifest file (.xml)
/nowin32manifest Do not include the default Win32 manifest
/resource:&lt;resinfo&gt; Embed the specified resource (Short form: /res)
/linkresource:&lt;resinfo&gt; Link the specified resource to this assembly
(Short form: /linkres) Where the resinfo format
/linkresource:&lt;resinfo&gt; Link the specified resource to this assembly
(Short form: /linkres) Where the resinfo format
is &lt;file&gt;[,&lt;string name&gt;[,public|private]]
- CODE GENERATION -
/debug[+|-] Emit debugging information
/debug:{full|pdbonly|portable|embedded}
Specify debugging type ('full' is default,
Specify debugging type ('full' is default,
'portable' is a cross-platform format,
'embedded' is a cross-platform format embedded into
'embedded' is a cross-platform format embedded into
the target .dll or .exe)
/optimize[+|-] Enable optimizations (Short form: /o)
/deterministic Produce a deterministic assembly
......@@ -4463,17 +4460,17 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
diagnostics.
/reportanalyzer Report additional analyzer information, such as
execution time.
- LANGUAGE -
/checked[+|-] Generate overflow checks
/unsafe[+|-] Allow 'unsafe' code
/define:&lt;symbol list&gt; Define conditional compilation symbol(s) (Short
/define:&lt;symbol list&gt; Define conditional compilation symbol(s) (Short
form: /d)
/langversion:&lt;string&gt; Specify language version mode: ISO-1, ISO-2, 3,
/langversion:&lt;string&gt; Specify language version mode: ISO-1, ISO-2, 3,
4, 5, 6, 7, 7.1, Default, or Latest
- SECURITY -
/delaysign[+|-] Delay-sign the assembly using only the public
/delaysign[+|-] Delay-sign the assembly using only the public
portion of the strong name key
/publicsign[+|-] Public-sign the assembly using only the public
portion of the strong name key
......@@ -4491,36 +4488,36 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
- ADVANCED -
/baseaddress:&lt;address&gt; Base address for the library to be built
/checksumalgorithm:&lt;alg&gt; Specify algorithm for calculating source file
/checksumalgorithm:&lt;alg&gt; Specify algorithm for calculating source file
checksum stored in PDB. Supported values are:
SHA1 (default) or SHA256.
/codepage:&lt;n&gt; Specify the codepage to use when opening source
/codepage:&lt;n&gt; Specify the codepage to use when opening source
files
/utf8output Output compiler messages in UTF-8 encoding
/main:&lt;type&gt; Specify the type that contains the entry point
(ignore all other possible entry points) (Short
/main:&lt;type&gt; Specify the type that contains the entry point
(ignore all other possible entry points) (Short
form: /m)
/fullpaths Compiler generates fully qualified paths
/filealign:&lt;n&gt; Specify the alignment used for output file
/filealign:&lt;n&gt; Specify the alignment used for output file
sections
/pathmap:&lt;K1&gt;=&lt;V1&gt;,&lt;K2&gt;=&lt;V2&gt;,...
Specify a mapping for source path names output by
the compiler.
/pdb:&lt;file&gt; Specify debug information file name (default:
/pdb:&lt;file&gt; Specify debug information file name (default:
output file name with .pdb extension)
/errorendlocation Output line and column of the end location of
/errorendlocation Output line and column of the end location of
each error
/preferreduilang Specify the preferred output language name.
/nostdlib[+|-] Do not reference standard library (mscorlib.dll)
/subsystemversion:&lt;string&gt; Specify subsystem version of this assembly
/lib:&lt;file list&gt; Specify additional directories to search in for
/lib:&lt;file list&gt; Specify additional directories to search in for
references
/errorreport:&lt;string&gt; Specify how to handle internal compiler errors:
prompt, send, queue, or none. The default is
/errorreport:&lt;string&gt; Specify how to handle internal compiler errors:
prompt, send, queue, or none. The default is
queue.
/appconfig:&lt;file&gt; Specify an application configuration file
/appconfig:&lt;file&gt; Specify an application configuration file
containing assembly binding settings
/moduleassemblyname:&lt;string&gt; Name of the assembly which this module will be
/moduleassemblyname:&lt;string&gt; Name of the assembly which this module will be
a part of
/modulename:&lt;string&gt; Specify the name of the source module
</value>
......@@ -5061,6 +5058,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_CompilerAndLanguageVersion" xml:space="preserve">
<value>Compiler version: '{0}'. Language version: {1}.</value>
</data>
<data name="IDS_FeatureAsyncMain" xml:space="preserve">
<value>async main</value>
</data>
<data name="ERR_TupleInferredNamesNotAvailable" xml:space="preserve">
<value>Tuple element name '{0}' is inferred. Please use language version {1} or greater to access an element by its inferred name.</value>
</data>
......@@ -5073,6 +5073,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_VoidInTuple" xml:space="preserve">
<value>A tuple may not contain a value of type 'void'.</value>
</data>
<data name="ERR_NonTaskMainCantBeAsync" xml:space="preserve">
<value>A void or int returning entry point cannot be async</value>
</data>
<data name="ERR_PatternWrongGenericTypeInVersion" xml:space="preserve">
<value>An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater.</value>
</data>
......
......@@ -184,6 +184,8 @@ private void SetGlobalErrorIfTrue(bool arg)
}
}
// Returns the MethodSymbol for the assembly entrypoint. If the user has a Task returning main,
// this function returns the synthesized Main MethodSymbol.
private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, DiagnosticBag diagnostics, CancellationToken cancellationToken)
{
var entryPointAndDiagnostics = compilation.GetEntryPointAndDiagnostics(cancellationToken);
......@@ -194,21 +196,69 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul
Debug.Assert(!entryPointAndDiagnostics.Diagnostics.IsDefault);
diagnostics.AddRange(entryPointAndDiagnostics.Diagnostics);
var entryPoint = entryPointAndDiagnostics.MethodSymbol;
var synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol;
if ((object)entryPoint == null)
{
Debug.Assert(entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty);
return null;
}
// entryPoint can be a SynthesizedEntryPointSymbol if a script is being compiled.
SynthesizedEntryPointSymbol synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol;
if ((object)synthesizedEntryPoint == null && (entryPoint.ReturnType.IsGenericTaskType(compilation) || entryPoint.ReturnType.IsNonGenericTaskType(compilation)))
{
synthesizedEntryPoint = new SynthesizedEntryPointSymbol.AsyncForwardEntryPoint(compilation, entryPoint.ContainingType, entryPoint);
entryPoint = synthesizedEntryPoint;
if ((object)moduleBeingBuilt != null)
{
moduleBeingBuilt.AddSynthesizedDefinition(entryPoint.ContainingType, synthesizedEntryPoint);
}
}
if (((object)synthesizedEntryPoint != null) &&
(moduleBeingBuilt != null) &&
!hasDeclarationErrors &&
!diagnostics.HasAnyErrors())
{
var body = synthesizedEntryPoint.CreateBody();
BoundStatement body = synthesizedEntryPoint.CreateBody();
var dynamicAnalysisSpans = ImmutableArray<SourceSpan>.Empty;
VariableSlotAllocator lazyVariableSlotAllocator = null;
var lambdaDebugInfoBuilder = ArrayBuilder<LambdaDebugInfo>.GetInstance();
var closureDebugInfoBuilder = ArrayBuilder<ClosureDebugInfo>.GetInstance();
StateMachineTypeSymbol stateMachineTypeOpt = null;
const int methodOrdinal = -1;
var loweredBody = LowerBodyOrInitializer(
synthesizedEntryPoint,
methodOrdinal,
body,
null,
new TypeCompilationState(synthesizedEntryPoint.ContainingType, compilation, moduleBeingBuilt),
false,
null,
ref dynamicAnalysisSpans,
diagnostics,
ref lazyVariableSlotAllocator,
lambdaDebugInfoBuilder,
closureDebugInfoBuilder,
out stateMachineTypeOpt);
Debug.Assert((object)lazyVariableSlotAllocator == null);
Debug.Assert((object)stateMachineTypeOpt == null);
Debug.Assert(dynamicAnalysisSpans.IsEmpty);
Debug.Assert(lambdaDebugInfoBuilder.IsEmpty());
Debug.Assert(closureDebugInfoBuilder.IsEmpty());
lambdaDebugInfoBuilder.Free();
closureDebugInfoBuilder.Free();
var emittedBody = GenerateMethodBody(
moduleBeingBuilt,
synthesizedEntryPoint,
methodOrdinal,
body,
loweredBody,
ImmutableArray<LambdaDebugInfo>.Empty,
ImmutableArray<ClosureDebugInfo>.Empty,
stateMachineTypeOpt: null,
......@@ -221,7 +271,6 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul
moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody);
}
Debug.Assert((object)entryPoint != null || entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty);
return entryPoint;
}
......
......@@ -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,
......
......@@ -130,6 +130,7 @@ internal enum MessageID
IDS_FeatureDefaultLiteral = MessageBase + 12718,
IDS_FeatureInferredTupleNames = MessageBase + 12719,
IDS_FeatureGenericPatternMatching = MessageBase + 12720,
IDS_FeatureAsyncMain = MessageBase + 12721,
}
// Message IDs may refer to strings that need to be localized.
......@@ -187,6 +188,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
switch (feature)
{
// C# 7.1 features.
case MessageID.IDS_FeatureAsyncMain:
case MessageID.IDS_FeatureDefaultLiteral:
case MessageID.IDS_FeatureInferredTupleNames:
case MessageID.IDS_FeatureGenericPatternMatching:
......
......@@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
......@@ -84,7 +85,7 @@ public virtual bool IsGenericMethod
internal virtual bool IsDirectlyExcludedFromCodeCoverage { get => false; }
/// <summary>
/// Returns true if this method is an extension method.
/// Returns true if this method is an extension method.
/// </summary>
public abstract bool IsExtensionMethod { get; }
......@@ -114,7 +115,7 @@ public virtual bool IsGenericMethod
internal abstract IEnumerable<Microsoft.Cci.SecurityAttribute> GetSecurityInformation();
/// <summary>
/// Marshalling information for return value (FieldMarshal in metadata).
/// Marshalling information for return value (FieldMarshal in metadata).
/// </summary>
internal abstract MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation { get; }
......@@ -133,7 +134,7 @@ public virtual bool IsGenericMethod
/// <summary>
/// Returns true if this method hides base methods by name. This cannot be specified directly
/// in the C# language, but can be true for methods defined in other languages imported from
/// metadata. The equivalent of the "hidebyname" flag in metadata.
/// metadata. The equivalent of the "hidebyname" flag in metadata.
/// </summary>
public abstract bool HidesBaseMethodsByName { get; }
......@@ -184,7 +185,7 @@ public virtual bool IsCheckedBuiltin
public abstract TypeSymbol ReturnType { get; }
/// <summary>
/// Returns the type arguments that have been substituted for the type parameters.
/// Returns the type arguments that have been substituted for the type parameters.
/// If nothing has been substituted for a given type parameter,
/// then the type parameter itself is consider the type argument.
/// </summary>
......@@ -275,13 +276,13 @@ internal virtual bool IsExplicitInterfaceImplementation
/// Returns interface methods explicitly implemented by this method.
/// </summary>
/// <remarks>
/// Methods imported from metadata can explicitly implement more than one method,
/// Methods imported from metadata can explicitly implement more than one method,
/// that is why return type is ImmutableArray.
/// </remarks>
public abstract ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations { get; }
/// <summary>
/// Returns the list of custom modifiers, if any, associated with the return type.
/// Returns the list of custom modifiers, if any, associated with the return type.
/// </summary>
public abstract ImmutableArray<CustomModifier> ReturnTypeCustomModifiers { get; }
......@@ -309,7 +310,7 @@ public virtual ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes()
/// returns the property that this method is the getter or setter for.
/// If this method has MethodKind of MethodKind.EventAdd or MethodKind.EventRemove,
/// returns the event that this method is the adder or remover for.
/// Note, the set of possible associated symbols might be expanded in the future to
/// Note, the set of possible associated symbols might be expanded in the future to
/// reflect changes in the languages.
/// </summary>
public abstract Symbol AssociatedSymbol { get; }
......@@ -327,19 +328,19 @@ internal MethodSymbol GetLeastOverriddenMethod(NamedTypeSymbol accessingTypeOpt)
while (m.IsOverride && !m.HidesBaseMethodsByName)
{
// We might not be able to access the overridden method. For example,
//
//
// .assembly A
// {
// InternalsVisibleTo("B")
// public class A { internal virtual void M() { } }
// }
//
//
// .assembly B
// {
// InternalsVisibleTo("C")
// public class B : A { internal override void M() { } }
// }
//
//
// .assembly C
// {
// public class C : B { ... new B().M ... } // A.M is not accessible from here
......@@ -374,9 +375,9 @@ internal MethodSymbol GetConstructedLeastOverriddenMethod(NamedTypeSymbol access
/// <summary>
/// If this method overrides another method (because it both had the override modifier
/// and there correctly was a method to override), returns the overridden method.
/// Note that if an overriding method D.M overrides C.M, which in turn overrides
/// Note that if an overriding method D.M overrides C.M, which in turn overrides
/// virtual method A.M, the "overridden method" of D.M is C.M, not the original virtual
/// method A.M. Note also that constructed generic methods are not considered to
/// method A.M. Note also that constructed generic methods are not considered to
/// override anything.
/// </summary>
public MethodSymbol OverriddenMethod
......@@ -587,7 +588,7 @@ internal bool IsSubmissionInitializer
}
/// <summary>
/// Determines whether this method is a candidate for a default assembly entry point
/// Determines whether this method is a candidate for a default assembly entry point
/// (i.e. it is a static method called "Main").
/// </summary>
internal bool IsEntryPointCandidate
......@@ -595,53 +596,6 @@ internal bool IsEntryPointCandidate
get { return IsStatic && Name == WellKnownMemberNames.EntryPointMethodName; }
}
/// <summary>
/// Checks if the method has an entry point compatible signature, i.e.
/// - the return type is either void or int
/// - has either no parameter or a single parameter of type string[]
/// </summary>
internal bool HasEntryPointSignature()
{
if (this.IsVararg)
{
return false;
}
TypeSymbol returnType = ReturnType;
if (returnType.SpecialType != SpecialType.System_Int32 && returnType.SpecialType != SpecialType.System_Void)
{
return false;
}
if (RefKind != RefKind.None)
{
return false;
}
if (Parameters.Length == 0)
{
return true;
}
if (Parameters.Length > 1)
{
return false;
}
if (!ParameterRefKinds.IsDefault)
{
return false;
}
var firstType = Parameters[0].Type;
if (firstType.TypeKind != TypeKind.Array)
{
return false;
}
var array = (ArrayTypeSymbol)firstType;
return array.IsSZArray && array.ElementType.SpecialType == SpecialType.System_String;
}
internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument argument)
{
......@@ -738,7 +692,7 @@ public virtual TypeSymbol ReceiverType
}
/// <summary>
/// If this method is a reduced extension method, returns a type inferred during reduction process for the type parameter.
/// If this method is a reduced extension method, returns a type inferred during reduction process for the type parameter.
/// </summary>
/// <param name="reducedFromTypeParameter">Type parameter of the corresponding <see cref="ReducedFrom"/> method.</param>
/// <returns>Inferred type or Nothing if nothing was inferred.</returns>
......@@ -888,7 +842,7 @@ internal bool CalculateUseSiteDiagnostic(ref DiagnosticInfo result)
return true;
}
// If the member is in an assembly with unified references,
// If the member is in an assembly with unified references,
// we check if its definition depends on a type from a unified reference.
if (this.ContainingModule.HasUnifiedReferences)
{
......@@ -908,7 +862,7 @@ internal bool CalculateUseSiteDiagnostic(ref DiagnosticInfo result)
}
/// <summary>
/// Return error code that has highest priority while calculating use site error for this symbol.
/// Return error code that has highest priority while calculating use site error for this symbol.
/// </summary>
protected override int HighestPriorityUseSiteError
{
......@@ -949,7 +903,7 @@ internal virtual TypeSymbol IteratorElementType
/// <summary>
/// Generates bound block representing method's body for methods in lowered form and adds it to
/// a collection of method bodies of the current module. This method is supposed to only be
/// a collection of method bodies of the current module. This method is supposed to only be
/// called for method symbols which return SynthesizesLoweredBoundBody == true.
/// </summary>
internal virtual void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
......@@ -980,7 +934,7 @@ internal virtual bool SynthesizesLoweredBoundBody
/// <remarks>
/// Syntax offset is a unique identifier for the local within the emitted method body.
/// It's based on position of the local declarator. In single-part method bodies it's simply the distance
/// from the start of the method body syntax span. If a method body has multiple parts (such as a constructor
/// from the start of the method body syntax span. If a method body has multiple parts (such as a constructor
/// comprising of code for member initializers and constructor initializer calls) the offset is calculated
/// as if all source these parts were concatenated together and prepended to the constructor body.
/// The resulting syntax offset is then negative for locals defined outside of the constructor body.
......@@ -1232,7 +1186,7 @@ public virtual bool IsTupleMethod
/// <summary>
/// If this is a method of a tuple type, return corresponding underlying method from the
/// tuple underlying type. Otherwise, null.
/// tuple underlying type. Otherwise, null.
/// </summary>
public virtual MethodSymbol TupleUnderlyingMethod
{
......
......@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
......@@ -300,5 +301,19 @@ public static bool IsGenericTaskReturningAsync(this MethodSymbol method, CSharpC
return method.IsAsync
&& method.ReturnType.IsGenericTaskType(compilation);
}
internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol method)
{
method = method.PartialDefinitionPart ?? method;
foreach (var reference in method.DeclaringSyntaxReferences)
{
if (reference.GetSyntax() is MethodDeclarationSyntax methodDeclaration)
{
return methodDeclaration.ReturnType;
}
}
return (CSharpSyntaxNode)CSharpSyntaxTree.Dummy.GetRoot();
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
......@@ -17,7 +19,6 @@ internal abstract class SynthesizedEntryPointSymbol : MethodSymbol
internal const string FactoryName = "<Factory>";
private readonly NamedTypeSymbol _containingType;
private readonly TypeSymbol _returnType;
internal static SynthesizedEntryPointSymbol Create(SynthesizedInteractiveInitializerMethod initializerMethod, DiagnosticBag diagnostics)
{
......@@ -54,13 +55,11 @@ internal static SynthesizedEntryPointSymbol Create(SynthesizedInteractiveInitial
}
}
private SynthesizedEntryPointSymbol(NamedTypeSymbol containingType, TypeSymbol returnType)
private SynthesizedEntryPointSymbol(NamedTypeSymbol containingType)
{
Debug.Assert((object)containingType != null);
Debug.Assert((object)returnType != null);
_containingType = containingType;
_returnType = returnType;
}
internal override bool GenerateDebugInfo
......@@ -128,11 +127,6 @@ internal override RefKind RefKind
get { return RefKind.None; }
}
public override TypeSymbol ReturnType
{
get { return _returnType; }
}
public override ImmutableArray<CustomModifier> ReturnTypeCustomModifiers
{
get { return ImmutableArray<CustomModifier>.Empty; }
......@@ -160,7 +154,7 @@ public override int Arity
public override bool ReturnsVoid
{
get { return _returnType.SpecialType == SpecialType.System_Void; }
get { return ReturnType.SpecialType == SpecialType.System_Void; }
}
public override MethodKind MethodKind
......@@ -286,7 +280,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l
throw ExceptionUtilities.Unreachable;
}
private CSharpSyntaxNode GetSyntax()
private static CSharpSyntaxNode DummySyntax()
{
var syntaxTree = CSharpSyntaxTree.Dummy;
return (CSharpSyntaxNode)syntaxTree.GetRoot();
......@@ -329,30 +323,127 @@ private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundE
{ WasCompilerGenerated = true };
}
/// <summary> A synthesized entrypoint that forwards all calls to an async Main Method </summary>
internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol
{
/// <summary> The user-defined asynchronous main method. </summary>
private readonly CSharpSyntaxNode _userMainReturnTypeSyntax;
private readonly BoundExpression _getAwaiterGetResultCall;
private readonly ImmutableArray<ParameterSymbol> _parameters;
internal AsyncForwardEntryPoint(CSharpCompilation compilation, NamedTypeSymbol containingType, MethodSymbol userMain) :
base(containingType)
{
// There should be no way for a userMain to be passed in unless it already passed the
// parameter checks for determining entrypoint validity.
Debug.Assert(userMain.ParameterCount == 0 || userMain.ParameterCount == 1);
_userMainReturnTypeSyntax = userMain.ExtractReturnTypeSyntax();
var binder = compilation.GetBinder(_userMainReturnTypeSyntax);
_parameters = SynthesizedParameterSymbol.DeriveParameters(userMain, this);
var arguments = Parameters.SelectAsArray((p, s) => (BoundExpression)new BoundParameter(s, p, p.Type), _userMainReturnTypeSyntax);
// Main(args) or Main()
BoundCall userMainInvocation = new BoundCall(
syntax: _userMainReturnTypeSyntax,
receiverOpt: null,
method: userMain,
arguments: arguments,
argumentNamesOpt: default(ImmutableArray<string>),
argumentRefKindsOpt: default(ImmutableArray<RefKind>),
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
argsToParamsOpt: default(ImmutableArray<int>),
resultKind: LookupResultKind.Viable,
type: userMain.ReturnType)
{ WasCompilerGenerated = true };
// The diagnostics that would be produced here will already have been captured and returned.
var droppedBag = DiagnosticBag.GetInstance();
var success = binder.GetAwaitableExpressionInfo(userMainInvocation, out _, out _, out _, out _getAwaiterGetResultCall, _userMainReturnTypeSyntax, droppedBag);
droppedBag.Free();
Debug.Assert(
ReturnType.SpecialType == SpecialType.System_Void ||
ReturnType.SpecialType == SpecialType.System_Int32);
}
public override string Name => MainName;
public override ImmutableArray<ParameterSymbol> Parameters => _parameters;
public override TypeSymbol ReturnType => _getAwaiterGetResultCall.Type;
internal override BoundBlock CreateBody()
{
var syntax = _userMainReturnTypeSyntax;
if (ReturnsVoid)
{
return new BoundBlock(
syntax: syntax,
locals: ImmutableArray<LocalSymbol>.Empty,
statements: ImmutableArray.Create<BoundStatement>(
new BoundExpressionStatement(
syntax: syntax,
expression: _getAwaiterGetResultCall
)
{ WasCompilerGenerated = true },
new BoundReturnStatement(
syntax: syntax,
refKind: RefKind.None,
expressionOpt: null
)
{ WasCompilerGenerated = true }
)
)
{ WasCompilerGenerated = true };
}
else
{
return new BoundBlock(
syntax: syntax,
locals: ImmutableArray<LocalSymbol>.Empty,
statements: ImmutableArray.Create<BoundStatement>(
new BoundReturnStatement(
syntax: syntax,
refKind: RefKind.None,
expressionOpt: _getAwaiterGetResultCall
)
)
)
{ WasCompilerGenerated = true };
}
}
}
private sealed class ScriptEntryPoint : SynthesizedEntryPointSymbol
{
private readonly MethodSymbol _getAwaiterMethod;
private readonly MethodSymbol _getResultMethod;
private readonly TypeSymbol _returnType;
internal ScriptEntryPoint(NamedTypeSymbol containingType, TypeSymbol returnType, MethodSymbol getAwaiterMethod, MethodSymbol getResultMethod) :
base(containingType, returnType)
base(containingType)
{
Debug.Assert(containingType.IsScriptClass);
Debug.Assert(returnType.SpecialType == SpecialType.System_Void);
_getAwaiterMethod = getAwaiterMethod;
_getResultMethod = getResultMethod;
_returnType = returnType;
}
public override string Name
{
get { return MainName; }
}
public override string Name => MainName;
public override ImmutableArray<ParameterSymbol> Parameters
{
get { return ImmutableArray<ParameterSymbol>.Empty; }
}
public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray<ParameterSymbol>.Empty;
public override TypeSymbol ReturnType => _returnType;
// private static void <Main>()
// {
......@@ -365,7 +456,7 @@ internal override BoundBlock CreateBody()
Debug.Assert((object)_getAwaiterMethod != null);
Debug.Assert((object)_getResultMethod != null);
var syntax = this.GetSyntax();
var syntax = DummySyntax();
var ctor = _containingType.GetScriptConstructor();
Debug.Assert(ctor.ParameterCount == 0);
......@@ -423,13 +514,15 @@ internal override BoundBlock CreateBody()
private sealed class SubmissionEntryPoint : SynthesizedEntryPointSymbol
{
private readonly ImmutableArray<ParameterSymbol> _parameters;
private readonly TypeSymbol _returnType;
internal SubmissionEntryPoint(NamedTypeSymbol containingType, TypeSymbol returnType, TypeSymbol submissionArrayType) :
base(containingType, returnType)
base(containingType)
{
Debug.Assert(containingType.IsSubmissionClass);
Debug.Assert(returnType.SpecialType != SpecialType.System_Void);
_parameters = ImmutableArray.Create<ParameterSymbol>(SynthesizedParameterSymbol.Create(this, submissionArrayType, 0, RefKind.None, "submissionArray"));
_returnType = returnType;
}
public override string Name
......@@ -442,6 +535,8 @@ public override ImmutableArray<ParameterSymbol> Parameters
get { return _parameters; }
}
public override TypeSymbol ReturnType => _returnType;
// private static T <Factory>(object[] submissionArray)
// {
// var submission = new Submission#N(submissionArray);
......@@ -449,7 +544,7 @@ public override ImmutableArray<ParameterSymbol> Parameters
// }
internal override BoundBlock CreateBody()
{
var syntax = this.GetSyntax();
var syntax = DummySyntax();
var ctor = _containingType.GetScriptConstructor();
Debug.Assert(ctor.ParameterCount == 1);
......
......@@ -70,6 +70,7 @@
<Compile Include="Attributes\AttributeTests_Synthesized.cs" />
<Compile Include="Attributes\AttributeTests_Tuples.cs" />
<Compile Include="Attributes\AttributeTests_WellKnownAttributes.cs" />
<Compile Include="CodeGen\CodeGenAsyncMainTests.cs" />
<Compile Include="CodeGen\CodeGenCapturing.cs" />
<Compile Include="CodeGen\PatternTests.cs" />
<Compile Include="Emit\BinaryCompatibility.cs" />
......
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
......@@ -88,7 +89,7 @@ public void BadAsyncConstructor()
{
var source = @"
class C {
async public C() { }
async public C() { }
}";
CreateCompilationWithMscorlib45(source).VerifyDiagnostics(
Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("async"));
......@@ -215,7 +216,7 @@ public void TaskRetNoObjectRequired()
class C
{
static void InferTask(Func<Task> x) { }
static void InferTaskOrTaskT(Func<Task> x) { }
static void InferTaskOrTaskT(Func<Task<int>> x) { }
......@@ -939,7 +940,7 @@ public void BadAwaitInCatchFilter()
class Test
{
async static Task M1()
async static Task M1()
{
try
{
......@@ -1067,7 +1068,7 @@ public static async void F()
object o = new object();
lock(await Task.Factory.StartNew(() => o))
{
}
}
......@@ -1188,7 +1189,7 @@ public void AsyncExplicitInterfaceImplementation()
interface IInterface
{
void F();
void F();
}
class C : IInterface
......@@ -1200,7 +1201,7 @@ async void IInterface.F()
static void Main()
{
}
}";
CreateCompilationWithMscorlib45(source).VerifyDiagnostics();
......@@ -1212,97 +1213,16 @@ public void AsyncInterfaceMember()
var source = @"
interface IInterface
{
async void F();
async void F();
}";
CreateCompilationWithMscorlib45(source).VerifyDiagnostics(
// (4,16): error CS0106: The modifier 'async' is not valid for this item
// async void F();
// async void F();
Diagnostic(ErrorCode.ERR_BadMemberFlag, "F").WithArguments("async"));
}
[Fact]
public void MainCantBeAsync()
{
var source = @"
using System.Threading.Tasks;
class A
{
async static void Main()
{
await Task.Factory.StartNew(() => { });
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (4,23): error CS4009: 'A.Main()': an entry point cannot be marked with the 'async' modifier
// async static void Main()
Diagnostic(ErrorCode.ERR_MainCantBeAsync, "Main").WithArguments("A.Main()"));
}
[Fact]
public void MainCantBeAsync_AndGeneric()
{
var source = @"
using System.Threading.Tasks;
class A
{
async static void Main<T>()
{
await Task.Factory.StartNew(() => { });
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (4,23): warning CS0402: 'A.Main<T>()': an entry point cannot be generic or in a generic type
// async static void Main<T>()
Diagnostic(ErrorCode.WRN_MainCantBeGeneric, "Main").WithArguments("A.Main<T>()"),
// error CS5001: Program does not contain a static 'Main' method suitable for an entry point
Diagnostic(ErrorCode.ERR_NoEntryPoint));
}
[Fact]
public void MainCantBeAsync_AndBadSig()
{
var source = @"
using System.Threading.Tasks;
class A
{
async static void Main(bool truth)
{
await Task.Factory.StartNew(() => { });
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (4,23): warning CS0028: 'A.Main(bool)' has the wrong signature to be an entry point
// async static void Main(bool truth)
Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main(bool)"),
// error CS5001: Program does not contain a static 'Main' method suitable for an entry point
Diagnostic(ErrorCode.ERR_NoEntryPoint));
}
[Fact]
public void MainCantBeAsync_AndGeneric_AndBadSig()
{
var source = @"
using System.Threading.Tasks;
class A
{
async static void Main<T>(bool truth)
{
await Task.Factory.StartNew(() => { });
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (4,23): warning CS0028: 'A.Main<T>(bool)' has the wrong signature to be an entry point
// async static void Main<T>(bool truth)
Diagnostic(ErrorCode.WRN_InvalidMainSig, "Main").WithArguments("A.Main<T>(bool)"),
// error CS5001: Program does not contain a static 'Main' method suitable for an entry point
Diagnostic(ErrorCode.ERR_NoEntryPoint));
}
[Fact]
public void AwaitInQuery_FirstCollectionExpressionOfInitialFrom()
{
var source = @"
......@@ -1829,7 +1749,7 @@ static Task<T> Foo<T>(T t)
{
return Task.Run(async () => { return t; });
}
static int Main()
{
return 0;
......@@ -1865,7 +1785,7 @@ class Test
static async Task<dynamic> Meth1()
{
throw new EntryPointNotFoundException();
Foo();
Foo();
return """";
}
......@@ -1891,10 +1811,10 @@ static int Main()
// static async Task<dynamic> Meth1()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth1"),
// (23,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
// Foo();
// Foo();
Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Foo()"),
// (23,9): warning CS0162: Unreachable code detected
// Foo();
// Foo();
Diagnostic(ErrorCode.WRN_UnreachableCode, "Foo"),
// (27,33): 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.
// static async Task<decimal?> Meth2()
......@@ -2908,7 +2828,7 @@ public void Meth()
Foo();
});
};
});
});
}
static int Main()
......@@ -2950,7 +2870,7 @@ public void Meth()
return """";
};
del3();
};
}
......@@ -2981,7 +2901,7 @@ public static Task<int> ExMeth(this int i)
return (Task<int>) Foo();
}
}
class Test
class Test
{
public static int amount=0;
static int Main()
......@@ -3066,7 +2986,7 @@ public Task Foo2()
return Task.Run(() => { });
}
}
class Test
class Test
{
static int Main()
{
......@@ -3097,7 +3017,7 @@ public async Task Foo()
{
await Task.Delay(10);
}
public void Dispose()
public void Dispose()
{
Foo();
}
......@@ -3483,22 +3403,42 @@ 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;
class Test
{
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").WithLocation(5, 30));
}
[Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")]
public void Repro_17885_CSharp7()
{
var source = @"
using System.Threading.Tasks;
class Test
{
async public static void Main()
async public static Task Main()
{
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (4,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 void Main()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main"),
// (4,30): error CS4009: 'Test.Main()': an entry point cannot be marked with the 'async' modifier
// async public static void Main()
Diagnostic(ErrorCode.ERR_MainCantBeAsync, "Main").WithArguments("Test.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").WithLocation(5, 30),
// (5,25): 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, "Task").WithArguments("async main", "7.1").WithLocation(5, 25),
// 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")]
......@@ -3511,7 +3451,7 @@ static int Main()
{
return 1;
}
public async void Foo(ref int x)
{ }
}";
......@@ -3582,7 +3522,7 @@ public void GetAwaiterIsExtension()
var source =
@"using System;
using A;
namespace A
{
public class IAS<T>
......@@ -3716,7 +3656,7 @@ public void ReturnExpressionNotConvertible()
{
string source = @"
using System.Threading.Tasks;
class Program
{
static async Task<T> Foo<T>()
......@@ -3739,9 +3679,9 @@ public void RefParameterOnAsyncLambda()
string source = @"
using System;
using System.Threading.Tasks;
delegate Task D(ref int x);
class C
{
static void Main()
......@@ -3751,7 +3691,7 @@ static void Main()
await Task.Delay(500);
Console.WriteLine(i++);
};
int x = 5;
d(ref x).Wait();
Console.WriteLine(x);
......@@ -3828,7 +3768,7 @@ public void DelegateTypeWithNoInvokeMethod()
@".class public auto ansi sealed D`1<T>
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
......
......@@ -5,8 +5,10 @@
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;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
......@@ -202,6 +204,104 @@ public partial class A {
Assert.Equal(1, m.First().Locations.Length);
}
[Fact]
public void PartialExtractSyntaxLocation_DeclBeforeDef()
{
var text =
@"public partial class A {
partial void M();
}
public partial class A {
partial void M() {}
}
";
var comp = CreateStandardCompilation(text);
var global = comp.GlobalNamespace;
var a = global.GetTypeMembers("A", 0).Single();
var m = (MethodSymbol) a.GetMembers("M").Single();
Assert.True(m.IsPartialDefinition());
var returnSyntax = m.ExtractReturnTypeSyntax();
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<PredefinedTypeSyntax>().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).First();
var otherSymbol = m.PartialImplementationPart;
Assert.True(otherSymbol.IsPartialImplementation());
Assert.Equal(node, returnSyntax);
Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax());
}
[Fact]
public void PartialExtractSyntaxLocation_DefBeforeDecl()
{
var text =
@"public partial class A {
partial void M() {}
}
public partial class A {
partial void M();
}
";
var comp = CreateStandardCompilation(text);
var global = comp.GlobalNamespace;
var a = global.GetTypeMembers("A", 0).Single();
var m = (MethodSymbol) a.GetMembers("M").Single();
Assert.True(m.IsPartialDefinition());
var returnSyntax = m.ExtractReturnTypeSyntax();
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<PredefinedTypeSyntax>().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Last();
var otherSymbol = m.PartialImplementationPart;
Assert.True(otherSymbol.IsPartialImplementation());
Assert.Equal(node, returnSyntax);
Assert.Equal(node, otherSymbol.ExtractReturnTypeSyntax());
}
[Fact]
public void PartialExtractSyntaxLocation_OnlyDef()
{
var text =
@"public partial class A {
partial void M() {}
}
";
var comp = CreateStandardCompilation(text);
var global = comp.GlobalNamespace;
var a = global.GetTypeMembers("A", 0).Single();
var m = (MethodSymbol) a.GetMembers("M").Single();
Assert.True(m.IsPartialImplementation());
var returnSyntax = m.ExtractReturnTypeSyntax();
var tree = comp.SyntaxTrees.Single().GetRoot();
var node = tree.DescendantNodes().OfType<PredefinedTypeSyntax>().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Single();
Assert.Equal(node, returnSyntax);
}
[Fact]
public void PartialExtractSyntaxLocation_OnlyDecl()
{
var text =
@"public partial class A {
partial void M();
}
";
var comp = CreateStandardCompilation(text);
var global = comp.GlobalNamespace;
var a = global.GetTypeMembers("A", 0).Single();
var m = (MethodSymbol) a.GetMembers("M").Single();
Assert.True(m.IsPartialDefinition());
var returnSyntax = m.ExtractReturnTypeSyntax();
var tree = comp.SyntaxTrees.Single().GetRoot();
var node = tree.DescendantNodes().OfType<PredefinedTypeSyntax>().Where(n => n.Keyword.Kind() == SyntaxKind.VoidKeyword).Single();
Assert.Equal(node, returnSyntax);
}
[Fact]
public void FullName()
{
......
......@@ -105,6 +105,8 @@ private Action<IModuleSymbol> Translate(Action<ModuleSymbol> action)
Action<ModuleSymbol> symbolValidator = null,
SignatureDescription[] expectedSignatures = null,
string expectedOutput = null,
int? expectedReturnCode = null,
string[] args = null,
CompilationOptions options = null,
ParseOptions parseOptions = null,
EmitOptions emitOptions = null,
......@@ -119,6 +121,8 @@ private Action<IModuleSymbol> Translate(Action<ModuleSymbol> action)
Translate2(symbolValidator),
expectedSignatures,
expectedOutput,
expectedReturnCode,
args,
options,
parseOptions,
emitOptions,
......@@ -185,6 +189,8 @@ private Action<IModuleSymbol> Translate(Action<ModuleSymbol> action)
Action<ModuleSymbol> symbolValidator = null,
SignatureDescription[] expectedSignatures = null,
string expectedOutput = null,
int? expectedReturnCode = null,
string[] args = null,
EmitOptions emitOptions = null,
bool verify = true)
{
......@@ -197,6 +203,8 @@ private Action<IModuleSymbol> Translate(Action<ModuleSymbol> action)
Translate2(symbolValidator),
expectedSignatures,
expectedOutput,
expectedReturnCode,
args,
emitOptions,
verify);
}
......
......@@ -37,6 +37,8 @@ Public MustInherit Class BasicTestBase
Friend Shadows Function CompileAndVerify(
source As XElement,
expectedOutput As XCData,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional additionalRefs As MetadataReference() = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
......@@ -52,6 +54,8 @@ Public MustInherit Class BasicTestBase
Return CompileAndVerify(
source,
XCDataToString(expectedOutput),
expectedReturnCode,
args,
additionalRefs,
dependencies,
sourceSymbolValidator,
......@@ -73,6 +77,8 @@ Public MustInherit Class BasicTestBase
Optional symbolValidator As Action(Of ModuleSymbol) = Nothing,
Optional expectedSignatures As SignatureDescription() = Nothing,
Optional expectedOutput As String = Nothing,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional emitOptions As EmitOptions = Nothing,
Optional verify As Boolean = True) As CompilationVerifier
......@@ -85,6 +91,8 @@ Public MustInherit Class BasicTestBase
Translate(symbolValidator),
expectedSignatures,
expectedOutput,
expectedReturnCode,
args,
emitOptions,
verify)
End Function
......@@ -92,6 +100,7 @@ Public MustInherit Class BasicTestBase
Friend Shadows Function CompileAndVerify(
compilation As Compilation,
expectedOutput As XCData,
Optional args As String() = Nothing,
Optional manifestResources As IEnumerable(Of ResourceDescription) = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
......@@ -110,6 +119,8 @@ Public MustInherit Class BasicTestBase
symbolValidator,
expectedSignatures,
XCDataToString(expectedOutput),
Nothing,
args,
emitOptions,
verify)
End Function
......@@ -117,6 +128,8 @@ Public MustInherit Class BasicTestBase
Friend Shadows Function CompileAndVerify(
source As XElement,
Optional expectedOutput As String = Nothing,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional additionalRefs As MetadataReference() = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
......@@ -136,6 +149,8 @@ Public MustInherit Class BasicTestBase
Return Me.CompileAndVerify(source,
allReferences,
expectedOutput,
expectedReturnCode,
args,
dependencies,
sourceSymbolValidator,
validator,
......@@ -152,6 +167,8 @@ Public MustInherit Class BasicTestBase
source As XElement,
allReferences As IEnumerable(Of MetadataReference),
Optional expectedOutput As String = Nothing,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
Optional validator As Action(Of PEAssembly) = Nothing,
......@@ -180,6 +197,8 @@ Public MustInherit Class BasicTestBase
Translate(symbolValidator),
expectedSignatures,
expectedOutput,
expectedReturnCode,
args,
emitOptions,
verify)
End Function
......@@ -188,6 +207,8 @@ Public MustInherit Class BasicTestBase
source As String,
allReferences As IEnumerable(Of MetadataReference),
Optional expectedOutput As String = Nothing,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
Optional validator As Action(Of PEAssembly) = Nothing,
......@@ -215,6 +236,8 @@ Public MustInherit Class BasicTestBase
Translate(symbolValidator),
expectedSignatures,
expectedOutput,
expectedReturnCode,
args,
emitOptions,
verify)
End Function
......@@ -223,6 +246,8 @@ Public MustInherit Class BasicTestBase
source As XElement,
allReferences As IEnumerable(Of MetadataReference),
Optional expectedOutput As String = Nothing,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
Optional validator As Action(Of PEAssembly) = Nothing,
......@@ -236,6 +261,8 @@ Public MustInherit Class BasicTestBase
source,
allReferences,
If(OSVersion.IsWin8, expectedOutput, Nothing),
If(OSVersion.IsWin8, expectedReturnCode, Nothing),
args,
dependencies,
sourceSymbolValidator,
validator,
......@@ -249,6 +276,8 @@ Public MustInherit Class BasicTestBase
Friend Shadows Function CompileAndVerifyOnWin8Only(
source As XElement,
expectedOutput As XCData,
Optional expectedReturnCode As Integer? = Nothing,
Optional args As String() = Nothing,
Optional allReferences() As MetadataReference = Nothing,
Optional dependencies As IEnumerable(Of ModuleData) = Nothing,
Optional sourceSymbolValidator As Action(Of ModuleSymbol) = Nothing,
......@@ -263,6 +292,8 @@ Public MustInherit Class BasicTestBase
source,
allReferences,
XCDataToString(expectedOutput),
expectedReturnCode,
args,
dependencies,
sourceSymbolValidator,
validator,
......
......@@ -72,23 +72,18 @@ public CoreCLRRuntimeEnvironment(IEnumerable<ModuleData> additionalDependencies
}
}
public (int ExitCode, string Output) Execute(string moduleName, int expectedOutputLength)
public int Execute(string moduleName, string[] args, string expectedOutput)
{
var emitData = GetEmitData();
emitData.RuntimeData.ExecuteRequested = true;
return emitData.LoadContext.Execute(GetMainImage(), expectedOutputLength);
}
public int Execute(string moduleName, string expectedOutput)
{
var (exitCode, actualOutput) = Execute(moduleName, expectedOutput.Length);
var (ExitCode, Output) = emitData.LoadContext.Execute(GetMainImage(), args, expectedOutput?.Length);
if (expectedOutput.Trim() != actualOutput.Trim())
if (expectedOutput != null && expectedOutput.Trim() != Output.Trim())
{
throw new ExecutionException(expectedOutput, actualOutput, moduleName);
throw new ExecutionException(expectedOutput, Output, moduleName);
}
return exitCode;
return ExitCode;
}
private EmitData GetEmitData() => _emitData ?? throw new InvalidOperationException("Must call Emit before calling this method");
......
......@@ -95,7 +95,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray<byte> mainImage)
}
}
internal (int ExitCode, string Output) Execute(ImmutableArray<byte> mainImage, int expectedOutputLength)
internal (int ExitCode, string Output) Execute(ImmutableArray<byte> mainImage, string[] mainArgs, int? expectedOutputLength)
{
var mainAssembly = LoadImageAsAssembly(mainImage);
var entryPoint = mainAssembly.EntryPoint;
......@@ -113,7 +113,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray<byte> mainImage)
}
else if (count == 1)
{
args = new[] { Array.Empty<string>() };
args = new[] { mainArgs ?? Array.Empty<string>() };
}
else
{
......@@ -121,7 +121,7 @@ private Assembly LoadImageAsAssembly(ImmutableArray<byte> mainImage)
}
exitCode = entryPoint.Invoke(null, args) is int exit ? exit : 0;
}, expectedOutputLength, out var stdOut, out var stdErr);
}, expectedOutputLength ?? 0, out var stdOut, out var stdErr);
var output = stdOut + stdErr;
return (exitCode, output);
......
......@@ -236,13 +236,22 @@ private static RuntimeData CreateRuntimeData()
}
}
public int Execute(string moduleName, int expectedOutputLength, out string processOutput)
public int Execute(string moduleName, string[] args, string expectedOutput)
{
try
{
var emitData = GetEmitData();
emitData.RuntimeData.ExecuteRequested = true;
return emitData.Manager.Execute(moduleName, expectedOutputLength, out processOutput);
var resultCode = emitData.Manager.Execute(moduleName, args, expectedOutput?.Length, out var output);
if (expectedOutput != null && expectedOutput.Trim() != output.Trim())
{
string dumpDir;
GetEmitData().Manager.DumpAssemblyData(out dumpDir);
throw new ExecutionException(expectedOutput, output, dumpDir);
}
return resultCode;
}
catch (TargetInvocationException tie)
{
......@@ -257,21 +266,6 @@ public int Execute(string moduleName, int expectedOutputLength, out string proce
}
}
public int Execute(string moduleName, string expectedOutput)
{
string actualOutput;
int exitCode = Execute(moduleName, expectedOutput.Length, out actualOutput);
if (expectedOutput.Trim() != actualOutput.Trim())
{
string dumpDir;
GetEmitData().Manager.DumpAssemblyData(out dumpDir);
throw new ExecutionException(expectedOutput, actualOutput, dumpDir);
}
return exitCode;
}
private EmitData GetEmitData()
{
if (_emitData == null)
......
......@@ -367,7 +367,7 @@ private SortedSet<string> GetFullyQualifiedTypeNames(string assemblyName)
return typeNames;
}
public int Execute(string moduleName, int expectedOutputLength, out string output)
public int Execute(string moduleName, string[] mainArgs, int? expectedOutputLength, out string output)
{
ImmutableArray<byte> bytes = GetModuleBytesByName(moduleName);
Assembly assembly = DesktopRuntimeUtil.LoadAsAssembly(moduleName, bytes);
......@@ -386,7 +386,7 @@ public int Execute(string moduleName, int expectedOutputLength, out string outpu
}
else if (count == 1)
{
args = new object[] { new string[0] };
args = new object[] { mainArgs ?? new string[0] };
}
else
{
......@@ -394,7 +394,7 @@ public int Execute(string moduleName, int expectedOutputLength, out string outpu
}
result = entryPoint.Invoke(null, args);
}, expectedOutputLength, out stdOut, out stdErr);
}, expectedOutputLength ?? 0, out stdOut, out stdErr);
output = stdOut + stdErr;
return result is int ? (int)result : 0;
......
......@@ -88,7 +88,7 @@ internal ImmutableArray<ModuleMetadata> GetAllModuleMetadata()
return modules;
}
public void Emit(string expectedOutput, IEnumerable<ResourceDescription> manifestResources, EmitOptions emitOptions, bool peVerify, SignatureDescription[] expectedSignatures)
public void Emit(string expectedOutput, int? expectedReturnCode, string[] args, IEnumerable<ResourceDescription> manifestResources, EmitOptions emitOptions, bool peVerify, SignatureDescription[] expectedSignatures)
{
using (var testEnvironment = RuntimeEnvironmentFactory.Create(_dependencies))
{
......@@ -105,9 +105,14 @@ public void Emit(string expectedOutput, IEnumerable<ResourceDescription> manifes
MetadataSignatureUnitTestHelper.VerifyMemberSignatures(testEnvironment, expectedSignatures);
}
if (expectedOutput != null)
if (expectedOutput != null || expectedReturnCode != null)
{
testEnvironment.Execute(mainModuleName, expectedOutput);
var returnCode = testEnvironment.Execute(mainModuleName, args, expectedOutput);
if (expectedReturnCode is int exCode)
{
Assert.Equal(exCode, returnCode);
}
}
}
}
......
......@@ -73,6 +73,8 @@ public abstract partial class CommonTestBase : TestBase
Action<IModuleSymbol> symbolValidator = null,
SignatureDescription[] expectedSignatures = null,
string expectedOutput = null,
int? expectedReturnCode = null,
string[] args = null,
CompilationOptions options = null,
ParseOptions parseOptions = null,
EmitOptions emitOptions = null,
......@@ -94,6 +96,8 @@ public abstract partial class CommonTestBase : TestBase
symbolValidator,
expectedSignatures,
expectedOutput,
expectedReturnCode,
args,
emitOptions,
verify);
}
......@@ -107,6 +111,8 @@ public abstract partial class CommonTestBase : TestBase
Action<IModuleSymbol> symbolValidator = null,
SignatureDescription[] expectedSignatures = null,
string expectedOutput = null,
int? expectedReturnCode = null,
string[] args = null,
EmitOptions emitOptions = null,
bool verify = true)
{
......@@ -136,6 +142,8 @@ public abstract partial class CommonTestBase : TestBase
manifestResources,
expectedSignatures,
expectedOutput,
expectedReturnCode,
args ?? Array.Empty<string>(),
assemblyValidator,
symbolValidator,
emitOptions,
......@@ -199,6 +207,8 @@ static internal void RunValidators(CompilationVerifier verifier, Action<PEAssemb
IEnumerable<ResourceDescription> manifestResources,
SignatureDescription[] expectedSignatures,
string expectedOutput,
int? expectedReturnCode,
string[] args,
Action<PEAssembly> assemblyValidator,
Action<IModuleSymbol> symbolValidator,
EmitOptions emitOptions,
......@@ -208,7 +218,7 @@ static internal void RunValidators(CompilationVerifier verifier, Action<PEAssemb
verifier = new CompilationVerifier(this, compilation, dependencies);
verifier.Emit(expectedOutput, manifestResources, emitOptions, verify, expectedSignatures);
verifier.Emit(expectedOutput, expectedReturnCode, args, manifestResources, emitOptions, verify, expectedSignatures);
// We're dual-purposing emitters here. In this context, it
// tells the validator the version of Emit that is calling it.
......
......@@ -367,7 +367,7 @@ public interface IRuntimeEnvironmentFactory
public interface IRuntimeEnvironment : IDisposable
{
void Emit(Compilation mainCompilation, IEnumerable<ResourceDescription> manifestResources, EmitOptions emitOptions, bool usePdbForDebugging = false);
int Execute(string moduleName, string expectedOutput);
int Execute(string moduleName, string[] args, string expectedOutput);
ImmutableArray<byte> GetMainImage();
ImmutableArray<byte> GetMainPdb();
ImmutableArray<Diagnostic> GetDiagnostics();
......
......@@ -20,5 +20,6 @@ public enum CompilerFeature
OutVar,
Patterns,
DefaultLiteral,
AsyncMain,
}
}
......@@ -214,7 +214,7 @@ public void AddAssemblyReferenceAndTypesToInteractive()
VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.SolutionCrawler);
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void ResetInteractiveFromProjectAndVerify()
{
var assembly = new ProjectUtils.AssemblyReference("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
......
......@@ -54,7 +54,7 @@ End Module
VisualStudio.Debugger.CheckExpression("names(1)", "String", "\"bar\"");
}
[Fact]
[Fact (Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void AddTryCatchAroundActiveStatement()
{
VisualStudio.Editor.SetText(@"
......@@ -294,7 +294,7 @@ End Module
VisualStudio.LocalsWindow.Verify.CheckEntry("lLng", "Long", "444");
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")]
public void WatchWindowUpdatesCorrectlyDuringEnC()
{
VisualStudio.Editor.SetText(@"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册