diff --git a/docs/features/local-functions.test.md b/docs/features/local-functions.test.md new file mode 100644 index 0000000000000000000000000000000000000000..ca556fda851ccb9b11635b32ebdecac8f9b576c6 --- /dev/null +++ b/docs/features/local-functions.test.md @@ -0,0 +1,67 @@ +# Interaction with other language features: + +General concerns: +- [ ] Error handling/recovery + - [ ] Errors in parsing + - [ ] Error handling for semantic errors (e.g. ambiguous lookup, inaccessible lookup, wrong kind of thing found, instance vs static thing found, wrong type for the context, value vs variable) +- [ ] Public interface of compiler APIs, backcompat scenarios +- [ ] Determinism +- [ ] Atomicity +- [ ] Edit-and-continue +- [ ] Completeness of the specification as a guide for testing (e.g. is the spec complete enough to suggest what the compiler should do in each scenario?) +- [ ] Other external documentation +- [ ] Performance + +Types and members: +- [ ] Access modifiers (public, protected, internal, protected internal, private), static modifier +- [ ] Parameter modifiers (ref, out, params) +- [ ] Attributes (including security attribute) +- [ ] Generics (type arguments, constraints, variance) +- [ ] default value +- [ ] partial classes +- [ ] literals +- [ ] enum (implicit vs. explicit underlying type) +- [ ] expression trees +- [ ] Iterators +- [ ] Initializers (object, collection, dictionary) +- [ ] array (single- or multi-dimensional, jagged, initilalizer) +- [ ] Expression-bodied methods/properties/... +- [ ] Extension methods +- [ ] Partial method +- [ ] Named and optional parameters +- [ ] String interpolation +- [ ] Properties (read-write, read-only, write-only, auto-property, expression-bodied) +- [ ] Interfaces (implicit vs. explicit interface member implementation) +- [ ] delegates +- [ ] Multi-declaration + +Code: +- [ ] Operators (see Eric's list) + - [ ] Operator overloading +- [ ] Lvalues: the synthesized fields are mutable + - [ ] Ref / out parameters + - [ ] Compound operators (+=, /=, etc ..) + - [ ] Assignment exprs +- [ ] lambdas (capture of parameters or locals, target typing) +- [ ] execution order +- [ ] Target typing (var, lambdas, integrals) +- [ ] Type inference +- [ ] Conversions + - [ ] Implicit (identity, implicit numeric, implicit enumeration, implicit nullable, null litaral, implicit reference, boxing, implicit dynamic, implicit constant, user-defined implicit conversion, anonymous function, method group) + - [ ] Explicit (numeric, enumeration, nullable, reference, unboxing, dynamic, user-defined) + - [ ] Anonymous functions +- [ ] nullable (wrapping, unwrapping) +- [ ] OHI + - [ ] inheritance (virtual, override, abstract, new) + - [ ] overload resolution +- [ ] Anonymous types +- [ ] Unsafe code +- [ ] LINQ +- [ ] constructors, properties, indexers, events, operators, and destructors. +- [X] Async +- [X] Var + +Misc: +- [ ] reserved keywords (sometimes contextual) +- [ ] pre-processing directives +- [ ] COM interop diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 1a21e8a84e3fd4ec361d13111f3ed0709be842eb..6029c08ae0ba8d35833e6041080e1fcc94616d2f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public static class LocalFunctionTestsExt + public static class LocalFunctionTestsUtil { public static IMethodSymbol FindLocalFunction(this CommonTestBase.CompilationVerifier verifier, string localFunctionName) { @@ -29,24 +29,23 @@ public static IMethodSymbol FindLocalFunction(this CommonTestBase.CompilationVer } } - [CompilerTrait(CompilerFeature.LocalFunctions)] - public class LocalFunctionTests : CSharpTestBase + public class LocalFunctionsTestBase : CSharpTestBase { - private readonly CSharpParseOptions _parseOptions = TestOptions.Regular.WithLocalFunctionsFeature(); + internal static readonly CSharpParseOptions DefaultParseOptions = TestOptions.Regular.WithLocalFunctionsFeature(); - CompilationVerifier VerifyOutput(string source, string output, CSharpCompilationOptions options) + internal CompilationVerifier VerifyOutput(string source, string output, CSharpCompilationOptions options) { - var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: options, parseOptions: _parseOptions); + var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: options, parseOptions: DefaultParseOptions); return CompileAndVerify(comp, expectedOutput: output).VerifyDiagnostics(); // no diagnostics } - CompilationVerifier VerifyOutput(string source, string output) + internal CompilationVerifier VerifyOutput(string source, string output) { - var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: TestOptions.ReleaseExe, parseOptions: _parseOptions); + var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: TestOptions.ReleaseExe, parseOptions: DefaultParseOptions); return CompileAndVerify(comp, expectedOutput: output).VerifyDiagnostics(); // no diagnostics } - CompilationVerifier VerifyOutputInMain(string methodBody, string output, params string[] usings) + internal CompilationVerifier VerifyOutputInMain(string methodBody, string output, params string[] usings) { for (var i = 0; i < usings.Length; i++) { @@ -64,18 +63,22 @@ static void Main() return VerifyOutput(source, output); } - private void VerifyDiagnostics(string source, params DiagnosticDescription[] expected) + internal void VerifyDiagnostics(string source, params DiagnosticDescription[] expected) { - var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: TestOptions.ReleaseExe, parseOptions: _parseOptions); + var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: TestOptions.ReleaseExe, parseOptions: DefaultParseOptions); comp.VerifyDiagnostics(expected); } - private void VerifyDiagnostics(string source, CSharpCompilationOptions options, params DiagnosticDescription[] expected) + internal void VerifyDiagnostics(string source, CSharpCompilationOptions options, params DiagnosticDescription[] expected) { - var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: options, parseOptions: _parseOptions); + var comp = CreateCompilationWithMscorlib45AndCSruntime(source, options: options, parseOptions: DefaultParseOptions); comp.VerifyDiagnostics(expected); } + } + [CompilerTrait(CompilerFeature.LocalFunctions)] + public class LocalFunctionTests : LocalFunctionsTestBase + { [Fact] public void EndToEnd() { @@ -1325,193 +1328,6 @@ IEnumerator LocalEnumerator() VerifyOutputInMain(source, "2", "System", "System.Collections"); } - [Fact] - [CompilerTrait(CompilerFeature.Async)] - public void AsyncBasic() - { - var source = @" -async Task Local() -{ - return await Task.FromResult(2); -} -Console.Write(Local().Result); -"; - VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); - } - - [Fact] - public void AsyncParam() - { - var source = @" -async Task LocalParam(int x) -{ - return await Task.FromResult(x); -} -Console.Write(LocalParam(2).Result); -"; - VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); - } - - [Fact] - [CompilerTrait(CompilerFeature.Async)] - public void AsyncGeneric() - { - var source = @" -async Task LocalGeneric(T x) -{ - return await Task.FromResult(x); -} -Console.Write(LocalGeneric(2).Result); -"; - VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); - } - - [Fact] - [CompilerTrait(CompilerFeature.Async)] - public void AsyncVoid() - { - var source = @" -// had bug with parser where 'async [keyword]' didn't parse. -async void LocalVoid() -{ - Console.Write(2); - await Task.Yield(); -} -LocalVoid(); -"; - VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); - } - - [Fact] - [CompilerTrait(CompilerFeature.Async)] - public void AsyncAwaitAwait() - { - var source = @" -Task Fun(int x) -{ - return Task.FromResult(x); -} -async Task AwaitAwait() -{ - var a = Fun(2); - await Fun(await a); - return await Fun(await a); -} -Console.WriteLine(AwaitAwait().Result); -"; - VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); - } - - [Fact] - [CompilerTrait(CompilerFeature.Async)] - public void AsyncKeyword() - { - var source = @" -using System; - -struct async -{ - public override string ToString() => ""2""; -} -struct await -{ - public override string ToString() => ""2""; -} - -class Program -{ - static string A() - { - async async() - { - return new async(); - } - return async().ToString(); - } - static string B() - { - string async() - { - return ""2""; - } - return async(); - } - static string C() - { - async Foo() - { - return new async(); - } - return Foo().ToString(); - } - static string D() - { - await Fun(await x) - { - return x; - } - return Fun(new await()).ToString(); - } - - static void Main(string[] args) - { - Console.WriteLine(A()); - Console.WriteLine(B()); - Console.WriteLine(C()); - Console.WriteLine(D()); - } -} -"; - var output = @" -2 -2 -2 -2 -"; - VerifyOutput(source, output); - } - - [Fact] - [CompilerTrait(CompilerFeature.Async)] - public void AsyncUnsafeKeyword() - { - var source = @" -using System; -using System.Threading.Tasks; - -class Program -{ - static string A() - { - async unsafe Task async() - { - return 2; - } - return async().Result.ToString(); - } - static string B() - { - unsafe async Task async() - { - return 2; - } - return async().Result.ToString(); - } - - static void Main(string[] args) - { - Console.WriteLine(A()); - Console.WriteLine(B()); - } -} -"; - var output = @" -2 -2 -"; - VerifyOutput(source, output, TestOptions.ReleaseExe.WithAllowUnsafe(true).WithWarningLevel(0)); - } - [Fact] public void Generic() { @@ -3733,5 +3549,390 @@ static void Main(string[] args) Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "int Add(int x, int y) => x + y;").WithLocation(7, 9) ); } + + [CompilerTrait(CompilerFeature.LocalFunctions, CompilerFeature.Var)] + public sealed class VarTests : LocalFunctionsTestBase + { + [Fact] + public void IllegalAsReturn() + { + var source = @" +using System; +class Program +{ + static void Main() + { + var f() => 42; + Console.WriteLine(f()); + } +}"; + var comp = CreateCompilationWithMscorlib45(source, parseOptions: DefaultParseOptions); + comp.VerifyDiagnostics( + // (7,9): error CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code + // var f() => 42; + Diagnostic(ErrorCode.ERR_TypeVarNotFound, "var").WithLocation(7, 9)); + } + + [Fact] + public void RealTypeAsReturn() + { + var source = @" +using System; +class var +{ + public override string ToString() => ""dog""; +} + +class Program +{ + static void Main() + { + var f() => new var(); + Console.WriteLine(f()); + } +}"; + + CompileAndVerify( + source, + parseOptions: DefaultParseOptions, + expectedOutput: "dog"); + } + + [Fact] + public void RealTypeParameterAsReturn() + { + var source = @" +using System; +class test +{ + public override string ToString() => ""dog""; +} + +class Program +{ + static void Test(var x) + { + var f() => x; + Console.WriteLine(f()); + } + + static void Main() + { + Test(new test()); + } +}"; + + CompileAndVerify( + source, + parseOptions: DefaultParseOptions, + expectedOutput: "dog"); + } + + [Fact] + public void IdentifierAndTypeNamedVar() + { + var source = @" +using System; +class var +{ + public override string ToString() => ""dog""; +} + +class Program +{ + static void Main() + { + int var = 42; + var f() => new var(); + Console.WriteLine($""{f()}-{var}""); + } +}"; + + CompileAndVerify( + source, + parseOptions: DefaultParseOptions, + expectedOutput: "dog-42"); + } + } + + [CompilerTrait(CompilerFeature.LocalFunctions, CompilerFeature.Async)] + public sealed class AsyncTests : LocalFunctionsTestBase + { + [Fact] + public void RealTypeAsReturn() + { + var source = @" +using System; +class async +{ + public override string ToString() => ""dog""; +} + +class Program +{ + static void Main() + { + async f() => new async(); + Console.WriteLine(f()); + } +}"; + + CompileAndVerify( + source, + parseOptions: DefaultParseOptions, + expectedOutput: "dog"); + } + + [Fact] + public void RealTypeParameterAsReturn() + { + var source = @" +using System; +class test +{ + public override string ToString() => ""dog""; +} + +class Program +{ + static void Test(async x) + { + async f() => x; + Console.WriteLine(f()); + } + + static void Main() + { + Test(new test()); + } +}"; + + CompileAndVerify( + source, + parseOptions: DefaultParseOptions, + expectedOutput: "dog"); + } + + [Fact] + public void ManyMeaningsType() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class async +{ + public override string ToString() => ""async""; +} + +class Program +{ + static void Main() + { + async Task Test(Task t) + { + async local = await t; + Console.WriteLine(local); + return local; + } + + Test(Task.FromResult(new async())).Wait(); + } +}"; + + var comp = CreateCompilationWithMscorlib46(source, parseOptions: DefaultParseOptions, options: TestOptions.DebugExe); + CompileAndVerify( + comp, + expectedOutput: "async"); + } + + [Fact] + public void Basic() + { + var source = @" +async Task Local() +{ + return await Task.FromResult(2); +} +Console.Write(Local().Result); +"; + VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); + } + + [Fact] + public void Param() + { + var source = @" +async Task LocalParam(int x) +{ + return await Task.FromResult(x); +} +Console.Write(LocalParam(2).Result); +"; + VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); + } + + [Fact] + [CompilerTrait(CompilerFeature.Async)] + public void Generic() + { + var source = @" +async Task LocalGeneric(T x) +{ + return await Task.FromResult(x); +} +Console.Write(LocalGeneric(2).Result); +"; + VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); + } + + [Fact] + [CompilerTrait(CompilerFeature.Async)] + public void Void() + { + var source = @" +// had bug with parser where 'async [keyword]' didn't parse. +async void LocalVoid() +{ + Console.Write(2); + await Task.Yield(); +} +LocalVoid(); +"; + VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); + } + + [Fact] + [CompilerTrait(CompilerFeature.Async)] + public void AwaitAwait() + { + var source = @" +Task Fun(int x) +{ + return Task.FromResult(x); +} +async Task AwaitAwait() +{ + var a = Fun(2); + await Fun(await a); + return await Fun(await a); +} +Console.WriteLine(AwaitAwait().Result); +"; + VerifyOutputInMain(source, "2", "System", "System.Threading.Tasks"); + } + + [Fact] + [CompilerTrait(CompilerFeature.Async)] + public void Keyword() + { + var source = @" +using System; + +struct async +{ + public override string ToString() => ""2""; +} +struct await +{ + public override string ToString() => ""2""; +} + +class Program +{ + static string A() + { + async async() + { + return new async(); + } + return async().ToString(); + } + static string B() + { + string async() + { + return ""2""; + } + return async(); + } + static string C() + { + async Foo() + { + return new async(); + } + return Foo().ToString(); + } + static string D() + { + await Fun(await x) + { + return x; + } + return Fun(new await()).ToString(); + } + + static void Main(string[] args) + { + Console.WriteLine(A()); + Console.WriteLine(B()); + Console.WriteLine(C()); + Console.WriteLine(D()); + } +} +"; + var output = @" +2 +2 +2 +2 +"; + VerifyOutput(source, output); + } + + [Fact] + [CompilerTrait(CompilerFeature.Async)] + public void UnsafeKeyword() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Program +{ + static string A() + { + async unsafe Task async() + { + return 2; + } + return async().Result.ToString(); + } + static string B() + { + unsafe async Task async() + { + return 2; + } + return async().Result.ToString(); + } + + static void Main(string[] args) + { + Console.WriteLine(A()); + Console.WriteLine(B()); + } +} +"; + var output = @" +2 +2 +"; + VerifyOutput(source, output, TestOptions.ReleaseExe.WithAllowUnsafe(true).WithWarningLevel(0)); + } + + } } } diff --git a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs index b4c04523dd1857583208c039d55b20fceb60413e..1388e94efc7c2df8c8db9e8b91867fc2286aaf9b 100644 --- a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpTestBase.cs @@ -328,6 +328,23 @@ public static SyntaxTree ParseWithRoundTripCheck(string text, CSharpParseOptions return CreateCompilation(source, refs, options, assemblyName); } + public static CSharpCompilation CreateCompilationWithMscorlib46( + string source, + IEnumerable references = null, + CSharpCompilationOptions options = null, + CSharpParseOptions parseOptions = null, + string sourceFileName = "", + string assemblyName = "") + { + return CreateCompilationWithMscorlib46( + new[] { source }, + references, + options, + parseOptions, + sourceFileName, + assemblyName); + } + public static CSharpCompilation CreateCompilationWithMscorlib46( string[] sources, IEnumerable references = null, diff --git a/src/Test/Utilities/Shared/Traits/CompilerFeature.cs b/src/Test/Utilities/Shared/Traits/CompilerFeature.cs index 077178fa5705a2ca2595b81e1b953df775a18295..0d4ed9249042f74b20bf0b3cf6fe54bcc41d5788 100644 --- a/src/Test/Utilities/Shared/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Shared/Traits/CompilerFeature.cs @@ -13,6 +13,7 @@ public enum CompilerFeature ExpressionBody, Iterator, LocalFunctions, - Params + Params, + Var } }