/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLocalFunctionTests.cs
6 368 строк
148 KB
Julien Couvreur
Extensions: propagate return-targeted and local function parameter attributes (#80070)
29 авг 2025, 23:32
Не верифицирован
29 авг 2025, 23:32
7a96051
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public static class LocalFunctionTestsUtil { public static IMethodSymbol FindLocalFunction(this CompilationVerifier verifier, string localFunctionName) { localFunctionName = (char)GeneratedNameKind.LocalFunction + "__" + localFunctionName; var methods = verifier.TestData.GetMethodsByName(); IMethodSymbol result = null; foreach (var kvp in methods) { if (kvp.Key.Contains(localFunctionName)) { Assert.Null(result); // more than one name matched result = ((MethodSymbol)kvp.Value.Method).GetPublicSymbol(); } } Assert.NotNull(result); // no methods matched return result; } } [CompilerTrait(CompilerFeature.LocalFunctions)] public class CodeGenLocalFunctionTests : CSharpTestBase { [Fact] [WorkItem(37459, "https://github.com/dotnet/roslyn/pull/37459")] public void StaticLocalFunctionCaptureConstants() { var src = @" using System; class C { const int X = 1; void M() { const int Y = 5; local(); return; static void local() { Console.WriteLine(X); Console.WriteLine(Y); } } public static void Main() { (new C()).M(); } } "; var verifier = CompileAndVerify(src, expectedOutput: @" 1 5"); verifier.VerifyIL("C.<M>g__local|1_0", @" { // Code size 13 (0xd) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: call ""void System.Console.WriteLine(int)"" IL_0006: ldc.i4.5 IL_0007: call ""void System.Console.WriteLine(int)"" IL_000c: ret }"); } [Fact] [WorkItem(481125, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=481125")] public void Repro481125() { var comp = CreateCompilation(@" using System; using System.Linq; public class C { static void Main() { var c = new C(); Console.WriteLine(c.M(0).Count()); Console.WriteLine(c.M(1).Count()); } public IQueryable<E> M(int salesOrderId) { using (var uow = new D()) { return Local(); IQueryable<E> Local() => uow.ES.Where(so => so.Id == salesOrderId); } } } internal class D : IDisposable { public IQueryable<E> ES => new[] { new E() }.AsQueryable(); public void Dispose() { } } public class E { public int Id; }", options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @"1 0"); } [Fact] [CompilerTrait(CompilerFeature.IOperation)] [WorkItem(24647, "https://github.com/dotnet/roslyn/issues/24647")] public void Repro24647() { var comp = CreateCompilation(@" class Program { static void Main(string[] args) { void local() { } => new object(); } }"); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); var localFunction = tree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single(); var creation = localFunction.DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single(); var objectCreationOperation = model.GetOperation(creation); var localFunctionOperation = (ILocalFunctionOperation)model.GetOperation(localFunction); Assert.NotNull(objectCreationOperation); comp.VerifyOperationTree(creation, expectedOperationTree: @" IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object, IsInvalid) (Syntax: 'new object()') Arguments(0) Initializer: null "); Assert.Equal(OperationKind.ExpressionStatement, objectCreationOperation.Parent.Kind); Assert.Equal(OperationKind.Block, objectCreationOperation.Parent.Parent.Kind); Assert.Same(localFunctionOperation.IgnoredBody, objectCreationOperation.Parent.Parent); var info = model.GetTypeInfo(creation); Assert.Equal("System.Object", info.Type.ToTestDisplayString()); Assert.Equal("System.Object", info.ConvertedType.ToTestDisplayString()); } [Fact] [WorkItem(22027, "https://github.com/dotnet/roslyn/issues/22027")] public void Repro22027() { CompileAndVerify(@" class Program { static void Main(string[] args) { } public object TestLocalFn(object inp) { try { var sr = new object(); return sr; void Local1() { var copy = inp; Local2(); } void Local2() { } } catch { throw; } } }"); } [Fact] [WorkItem(21768, "https://github.com/dotnet/roslyn/issues/21768")] public void Repro21768() { var comp = CreateCompilation(@" using System; using System.Linq; class C { void Function(int someField) //necessary to have a parameter { using (IInterface db = null) //necessary to have this using statement { void LocalFunction() //necessary { var results = db.Query<Class1>() //need to call this method. using a constant array does not reproduce the bug. .Where(cje => cje.SomeField >= someField) //need expression tree here referencing parameter ; } } } interface IInterface : IDisposable { IQueryable<T> Query<T>(); } class Class1 { public int SomeField { get; set; } } }"); CompileAndVerify(comp); } [Fact] [WorkItem(21811, "https://github.com/dotnet/roslyn/issues/21811")] public void Repro21811() { var comp = CreateCompilation(@" using System.Collections.Generic; using System.Linq; class Program { static void Main(string[] args) { var history = new List<long>(); Enumerable.Range(0, 5) .Select(i => { history.Insert(0, i); return Test(i); bool Test(int v) { history.Remove(0); return Square(v) > 5; } int Square(int w) { return w * w; } }); } }"); } [Fact] [WorkItem(21645, "https://github.com/dotnet/roslyn/issues/21645")] public void Repro21645() { CompileAndVerify(@" public class Class1 { private void Test() { bool outside = true; void Inner() //This can also be a lambda (ie. Action action = () => { ... };) { void Bar() { } void Foo() { Bar(); bool captured = outside; } } } }"); } [Fact] [WorkItem(21543, "https://github.com/dotnet/roslyn/issues/21543")] public void Repro21543() { CompileAndVerify(@" using System; class Program { static void Method(Action action) { } static void Main() { int value = 0; Method(() => { local(); void local() { Console.WriteLine(value); Method(() => { local(); }); } }); } }"); } [Fact] [WorkItem(472056, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=472056")] public void Repro472056() { var comp = CreateCompilationWithMscorlib46(@" using System; using System.Collections.Generic; using System.Threading.Tasks; namespace ConsoleApp2 { class Program { static void Main(string[] args) { var task = WhyYouBreaky(new List<string>()); Console.WriteLine(task.Result); } static async Task<string> WhyYouBreaky(List<string> words) { await Task.Delay(1); var word = """"; // moving me before the 'await' will make it work words.Add(""Oh No!""); // I will crash here :( return ""Great success!""; // Not so much. void IDontEvenGetCalled() { // commenting out either of these lines will make it work var a = word; var b = words[0]; } } } }", options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Great success!"); } [Fact] public void AsyncStructClosure() { var comp = CreateCompilationWithMscorlib46(@" using System; using System.Threading.Tasks; class C { static void Main() => M().Wait(); static async Task M() { int x = 2; int y = 3; int L() => x + y; Console.WriteLine(L()); await Task.FromResult(false); } }", options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: "5"); // No field captures verifier.VerifySynthesizedFields("C.<M>d__1", "int <>1__state", "System.Runtime.CompilerServices.AsyncTaskMethodBuilder <>t__builder", "System.Runtime.CompilerServices.TaskAwaiter<bool> <>u__1"); comp = CreateCompilationWithMscorlib46(@" using System; using System.Threading.Tasks; class C { static void Main() => M().Wait(); static async Task M() { int x = 2; int y = 3; int L() => x + y; Console.WriteLine(L()); await Task.FromResult(false); x++; Console.WriteLine(x); } }", options: TestOptions.ReleaseExe); verifier = CompileAndVerify(comp, expectedOutput: @"5 3"); verifier.VerifySynthesizedFields("C.<M>d__1", "int <>1__state", "System.Runtime.CompilerServices.AsyncTaskMethodBuilder <>t__builder", // Display class capture "C.<>c__DisplayClass1_0 <>8__1", "System.Runtime.CompilerServices.TaskAwaiter<bool> <>u__1"); verifier.VerifySynthesizedFields("C.<>c__DisplayClass1_0", "int x", "int y"); comp = CreateCompilationWithMscorlib46(@" using System; using System.Threading.Tasks; class C { static void Main() => M().Wait(); static async Task M() { int x = 2; int y = 3; int L() => x + y; Console.WriteLine(L()); await Task.FromResult(false); x = 5; y = 7; Console.WriteLine(L()); } }", options: TestOptions.ReleaseExe); verifier = CompileAndVerify(comp, expectedOutput: @"5 12"); // Nothing captured across await verifier.VerifySynthesizedFields("C.<M>d__1", "int <>1__state", "System.Runtime.CompilerServices.AsyncTaskMethodBuilder <>t__builder", "System.Runtime.CompilerServices.TaskAwaiter<bool> <>u__1"); } [Fact] public void IteratorStructClosure() { var verifier = CompileAndVerify(@" using System; using System.Collections.Generic; class C { static void Main() { foreach (var m in M()) { Console.WriteLine(m); } } static IEnumerable<int> M() { int x = 2; int y = 3; int L() => x + y; yield return L(); } }", expectedOutput: "5"); // No field captures verifier.VerifySynthesizedFields("C.<M>d__1", "int <>1__state", "int <>2__current", "int <>l__initialThreadId"); verifier = CompileAndVerify(@" using System; using System.Collections.Generic; class C { static void Main() { foreach (var m in M()) { Console.WriteLine(m); } } static IEnumerable<int> M() { int x = 2; int y = 3; int L() => x + y; yield return L(); x++; yield return x; } }", expectedOutput: @"5 3"); verifier.VerifySynthesizedFields("C.<M>d__1", "int <>1__state", "int <>2__current", "int <>l__initialThreadId", // Display class capture "C.<>c__DisplayClass1_0 <>8__1"); verifier.VerifySynthesizedFields("C.<>c__DisplayClass1_0", "int x", "int y"); verifier = CompileAndVerify(@" using System; using System.Collections.Generic; class C { static void Main() { foreach (var m in M()) { Console.WriteLine(m); } } static IEnumerable<int> M() { int x = 2; int y = 3; int L() => x + y; yield return L(); x = 5; y = 7; yield return L(); } }", expectedOutput: @"5 12"); // No captures verifier.VerifySynthesizedFields("C.<M>d__1", "int <>1__state", "int <>2__current", "int <>l__initialThreadId"); } [Fact] [WorkItem(21409, "https://github.com/dotnet/roslyn/issues/21409")] public void Repro21409() { CompileAndVerify( @" using System; using System.Collections.Generic; namespace Buggles { class Program { private static IEnumerable<int> Problem(IEnumerable<int> chunks) { var startOfChunk = 0; var pendingChunks = new List<int>(); int GenerateChunk() { if (pendingChunks == null) { Console.WriteLine(""impossible in local function""); return -1; } while (pendingChunks.Count > 0) { pendingChunks.RemoveAt(0); } return startOfChunk; } foreach (var chunk in chunks) { if (chunk - startOfChunk <= 0) { pendingChunks.Insert(0, chunk); } else { yield return GenerateChunk(); } startOfChunk = chunk; if (pendingChunks == null) { Console.WriteLine(""impossible in outer function""); } else { pendingChunks.Insert(0, chunk); } } } private static void Main() { var xs = Problem(new[] { 0, 1, 2, 3 }); foreach (var x in xs) { Console.WriteLine(x); } } } } ", expectedOutput: @" 0 1 2"); } [Fact] [WorkItem(294554, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=294554")] public void ThisOnlyClosureBetweenStructCaptures() { CompileAndVerify(@" using System; class C { int _x = 0; void M() { void L1() { int x = 0; _x++; void L2() { Action a2 = L2; int y = 0; L3(); void L3() { _x++; y++; } } L2(); void L5() => x++; L5(); } L1(); } }"); } [Fact] public void CaptureThisInDifferentScopes() { CompileAndVerify(@" using System; class C { int _x; void M() { { int y = 0; Func<int> f1 = () => _x + y; } { int y = 0; Func<int> f2 = () => _x + y; } } }"); } [Fact] public void CaptureThisInDifferentScopes2() { CompileAndVerify(@" using System; class C { int _x; void M() { { int y = 0; int L1() => _x + y; } { int y = 0; int L2() => _x + y; } } }"); } [Fact] public void CaptureFramePointerInDifferentScopes() { CompileAndVerify(@" using System; class C { void M(int x) { Func<int> f1 = () => x; { int z = 0; Func<int> f2 = () => x + z; } { int z = 0; Func<int> f3 = () => x + z; } } }"); } [Fact] public void EnvironmentChainContainsStructEnvironment() { CompileAndVerify(@" using System; class C { void M(int x) { { int y = 10; void L() => Console.WriteLine(y); { int z = 5; Action f2 = () => Console.WriteLine(z + x); f2(); } L(); } } public static void Main() => new C().M(3); }", expectedOutput: @"8 10"); } [Fact] public void Repro20577() { var comp = CreateCompilation(@" using System.Linq; public class Program { public static void Main(string[] args) { object v; void AAA() { object BBB(object v2) { var a = v; ((object[])v2).Select(i => BBB(i)); return null; } } } }"); CompileAndVerify(comp); } [Fact] public void Repro19033() { CompileAndVerify(@" using System; class Program { void Q(int n = 0) { { object mc; string B(object map) { Action<int> a = _ => B(new object()); return n.ToString(); } } } }"); } [Fact] public void Repro19033_2() { CompileAndVerify(@" using System; class C { static void F(Action a) { object x = null; { object y = null; void G(object z) { F(() => G(x)); } } } }"); } [Fact] [WorkItem(18814, "https://github.com/dotnet/roslyn/issues/18814")] [WorkItem(18918, "https://github.com/dotnet/roslyn/issues/18918")] public void IntermediateStructClosures1() { var verifier = CompileAndVerify(@" using System; class C { int _x = 0; public static void Main() => new C().M(); public void M() { int var1 = 0; void L1() { void L2() { void L3() { void L4() { int var2 = 0; void L5() { int L6() => var2 + _x++; L6(); } L5(); } L4(); } L3(); } L2(); int L8() => var1; } Console.WriteLine(_x); L1(); Console.WriteLine(_x); } }", expectedOutput: @"0 1"); verifier.VerifyIL("C.M()", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (C.<>c__DisplayClass2_0 V_0) //CS$<>8__locals0 IL_0000: ldloca.s V_0 IL_0002: ldarg.0 IL_0003: stfld ""C C.<>c__DisplayClass2_0.<>4__this"" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: stfld ""int C.<>c__DisplayClass2_0.var1"" IL_0010: ldarg.0 IL_0011: ldfld ""int C._x"" IL_0016: call ""void System.Console.WriteLine(int)"" IL_001b: ldarg.0 IL_001c: ldloca.s V_0 IL_001e: call ""void C.<M>g__L1|2_0(ref C.<>c__DisplayClass2_0)"" IL_0023: ldarg.0 IL_0024: ldfld ""int C._x"" IL_0029: call ""void System.Console.WriteLine(int)"" IL_002e: ret }"); // L1 verifier.VerifyIL("C.<M>g__L1|2_0(ref C.<>c__DisplayClass2_0)", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call ""void C.<M>g__L2|2_1(ref C.<>c__DisplayClass2_0)"" IL_0007: ret }"); // L2 verifier.VerifyIL("C.<M>g__L2|2_1(ref C.<>c__DisplayClass2_0)", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call ""void C.<M>g__L3|2_3(ref C.<>c__DisplayClass2_0)"" IL_0007: ret }"); // Skip some... L5 verifier.VerifyIL("C.<M>g__L5|2_5(ref C.<>c__DisplayClass2_0, ref C.<>c__DisplayClass2_1)", @" { // Code size 10 (0xa) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: ldarg.2 IL_0003: call ""int C.<M>g__L6|2_6(ref C.<>c__DisplayClass2_0, ref C.<>c__DisplayClass2_1)"" IL_0008: pop IL_0009: ret }"); // L6 verifier.VerifyIL("C.<M>g__L6|2_6(ref C.<>c__DisplayClass2_0, ref C.<>c__DisplayClass2_1)", @" { // Code size 25 (0x19) .maxstack 4 .locals init (int V_0) IL_0000: ldarg.2 IL_0001: ldfld ""int C.<>c__DisplayClass2_1.var2"" IL_0006: ldarg.0 IL_0007: ldarg.0 IL_0008: ldfld ""int C._x"" IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: ldc.i4.1 IL_0010: add IL_0011: stfld ""int C._x"" IL_0016: ldloc.0 IL_0017: add IL_0018: ret }"); } [Fact] [WorkItem(18814, "https://github.com/dotnet/roslyn/issues/18814")] [WorkItem(18918, "https://github.com/dotnet/roslyn/issues/18918")] public void IntermediateStructClosures2() { CompileAndVerify(@" class C { int _x; void M() { int y = 0; void L1() { void L2() { int z = 0; int L3() => z + _x; } y++; } } }"); } [Fact] [WorkItem(18814, "https://github.com/dotnet/roslyn/issues/18814")] public void Repro18814() { CompileAndVerify(@" class Program { private void ResolvingPackages() { string outerScope(int a) => """"; void C1(int cabinetIdx) { void modifyState() { var no = outerScope(cabinetIdx); } modifyState(); } } }"); } [Fact] [WorkItem(18918, "https://github.com/dotnet/roslyn/issues/18918")] public void Repro18918() { CompileAndVerify(@" public class Test { private int _field; public void OuterMethod(int outerParam) { void InnerMethod1() { void InnerInnerMethod(int innerInnerParam) { InnerInnerInnerMethod(); bool InnerInnerInnerMethod() { return innerInnerParam != _field; } } void InnerMethod2() { var temp = outerParam; } } } }"); } [Fact] [WorkItem(17719, "https://github.com/dotnet/roslyn/issues/17719")] public void Repro17719() { var comp = CompileAndVerify(@" using System; class C { public static void Main() { T GetField<T>(string name, T @default = default(T)) { return @default; } Console.WriteLine(GetField<int>(string.Empty)); } }", expectedOutput: "0"); } [Fact] [WorkItem(17890, "https://github.com/dotnet/roslyn/issues/17890")] public void Repro17890() { var comp = CreateCompilationWithMscorlib46(@" using System; using System.Collections.Generic; using System.Linq; public class Class { public class Item { public int Id { get; set; } } public class ItemsContainer : IDisposable { public List<Item> Items { get; set; } public void Dispose() { } } public static void CompilerError() { using (var itemsContainer = new ItemsContainer()) { Item item = null; itemsContainer.Items.Where(x => x.Id == item.Id); void Local1() { itemsContainer.Items = null; } void Local2() { Local1(); } } } }", references: new[] { LinqAssemblyRef }); CompileAndVerify(comp); } [Fact] [WorkItem(16783, "https://github.com/dotnet/roslyn/issues/16783")] public void GenericDefaultParams() { CompileAndVerify(@" using System; class C { public void M() { void Local<T>(T t = default(T)) { Console.WriteLine(t); } Local<int>(); } } class C2 { public static void Main() { new C().M(); } }", expectedOutput: "0"); } [Fact] public void GenericCaptureDefaultParams() { CompileAndVerify(@" using System; class C<T> { public void M() { void Local(T t = default(T)) { Console.WriteLine(t); } Local(); } } class C2 { public static void Main() { new C<int>().M(); } }", expectedOutput: "0"); } [Fact] public void NameofRecursiveDefaultParameter() { var comp = CreateCompilation(@" using System; class C { public static void Main() { void Local(string s = nameof(Local)) { Console.WriteLine(s); } Local(); } }", options: TestOptions.ReleaseExe); comp.VerifyDiagnostics(); comp.DeclarationDiagnostics.Verify(); CompileAndVerify(comp, expectedOutput: "Local"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope() { var src = @" using System; class C { public static void Main() { var d = """"; { int x = 0; void M() { if (d != null) { Action a = () => x++; a(); } } M(); Console.WriteLine(x); } } }"; CompileAndVerify(src, expectedOutput: "1"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope2() { var src = @" using System; class C { class D : IDisposable { public void Dispose() {} } public static void Main() { using (var d = new D()) { int x = 0; void M() { if (d != null) { Action a = () => x++; a(); } } M(); Console.WriteLine(x); } } }"; CompileAndVerify(src, expectedOutput: "1"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope3() { var src = @" using System; class C { public static void Main() { var d = """"; { int x = 0; void M() { if (d != null) { void Local() => x++; Action a = Local; a(); } } M(); Console.WriteLine(x); } } }"; CompileAndVerify(src, expectedOutput: "1"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope4() { var src = @" using System; class C { public static void Main() { var d = """"; { int y = 0; { int x = 0; void M() { if (d != null) { Action a = () => x++;; a(); } } M(); Console.WriteLine(x); } y++; } } }"; CompileAndVerify(src, expectedOutput: "1"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope5() { var src = @" using System; class C { public static void Main() { int x = 0; { int y = 0; void L() { int z = 0; void L2() { if (x == 0 && z == 0) { Action a = () => y++; a(); } } L2(); } L(); Console.WriteLine(y); } } }"; CompileAndVerify(src, expectedOutput: "1"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope6() { var src = @" using System; class C { public static void Main() { int x = 0; { int y = 0; void L() { int z = 0; void L2() { if (x == 0 && y == 0) { Action a = () => z++; a(); } y++; } L2(); Console.WriteLine(z); } L(); Console.WriteLine(y); } ((Action)(() => x++))(); Console.WriteLine(x); } }"; CompileAndVerify(src, expectedOutput: @"1 1 1"); } [ConditionalFact(typeof(DesktopOnly))] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope7() { var src = @" using System; using System.Threading.Tasks; class C { public static void Main() { int x = 0; { int y = 0; void L() { if (x == 0) { async Task L2() { await Task.Delay(1); y++; } L2().Wait(); } } L(); Console.WriteLine(y); } Console.WriteLine(x); } }"; CompileAndVerify(src, targetFramework: TargetFramework.Mscorlib46, expectedOutput: @"1 0"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void CaptureVarNestedLambdaSkipScope8() { var src = @" using System; using System.Collections.Generic; class C { public static void Main() { int x = 0; { int y = 0; void L() { if (x == 0) { IEnumerable<int> L2() { yield return 0; y++; } foreach (var i in L2()) { } } } L(); Console.WriteLine(y); } Console.WriteLine(x); } }"; CompileAndVerifyWithMscorlib46(src, expectedOutput: @"1 0"); } [Fact] [WorkItem(16895, "https://github.com/dotnet/roslyn/issues/16895")] public void LocalFunctionCaptureSkipScope() { var src = @" using System; class C { public static void Main(string[] args) { { int uncaptured = 0; uncaptured++; { int x = 0; bool Local(int y) => x == 0 && args == null && y == 0; Local(0); } } } }"; CompileAndVerify(src); } [Fact] [WorkItem(16399, "https://github.com/dotnet/roslyn/issues/16399")] public void RecursiveGenericLocalFunctionIterator() { var src = @" using System; using System.Collections.Generic; using System.Linq; public static class EnumerableExtensions { static void Main(string[] args) { GetLeaves<object>(new List<object>(), list => null); var results = GetLeaves<object>( new object[] { new[] { ""a"", ""b""}, new[] { ""c"" }, new[] { new[] { ""d"" } } }, node => node is string ? null : (IEnumerable<object>)node); foreach (var i in results) { Console.WriteLine(i); } } public static IEnumerable<T> GetLeaves<T>(T root, Func<T, IEnumerable<T>> getChildren) { return GetLeaves(root); IEnumerable<T> GetLeaves(T node) { var children = getChildren(node); if (children == null) { return new[] { node }; } else { return children.SelectMany(GetLeaves); } } } }"; VerifyOutput(src, @"a b c d"); } [Fact] [WorkItem(243633, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems/edit/243633")] public void CaptureGenericFieldAndParameter() { var src = @" using System; using System.Collections.Generic; class Test<T> { T Value; public bool Goo(IEqualityComparer<T> comparer) { bool local(T tmp) { return comparer.Equals(tmp, this.Value); } return local(this.Value); } } "; var comp = CompileAndVerify(src); } [Fact] public void CaptureGenericField() { var src = @" using System; class C<T> { T Value = default(T); public void M() { void L() { Console.WriteLine(Value); } var f = (Action)(() => L()); f(); } } class C2 { public static void Main(string[] args) => new C<int>().M(); } "; VerifyOutput(src, "0"); } [Fact] public void CaptureGenericParam() { var src = @" using System; class C<T> { T Value = default(T); public void M<U>(U val2) { void L() { Console.WriteLine(Value); Console.WriteLine(val2); } var f = (Action)(() => L()); f(); } } class C2 { public static void Main(string[] args) => new C<int>().M(10); } "; VerifyOutput(src, @"0 10"); } [Fact] public void CaptureGenericParamInGenericLocalFunc() { var src = @" using System; class C<T> { T Value = default(T); public void M<U>(U v1) { void L<V>(V v2) where V : T { Console.WriteLine(Value); Console.WriteLine(v1); Console.WriteLine(v2); } var f = (Action)(() => L<T>(Value)); f(); } } class C2 { public static void Main(string[] args) => new C<int>().M(10); } "; VerifyOutput(src, @"0 10 0"); } [Fact] public void DeepNestedLocalFuncsWithDifferentCaptures() { var src = @" using System; class C { int P = 100000; void M() { C Local1() => this; int capture1 = 1; Func<int> f1 = () => capture1 + Local1().P; Console.WriteLine(f1()); { C Local2() => Local1(); int capture2 = 10; Func<int> f2 = () => capture2 + Local2().P; Console.WriteLine(f2()); { C Local3() => Local2(); int capture3 = 100; Func<int> f3 = () => capture1 + capture2 + capture3 + Local3().P; Console.WriteLine(f3()); Console.WriteLine(Local3().P); } } } public static void Main() => new C().M(); }"; VerifyOutput(src, @"100001 100010 100111 100000"); } [Fact] public void LotsOfMutuallyRecursiveLocalFunctions() { var src = @" class C { int P = 0; public void M() { int Local1() => this.P; int Local2() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local3() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local4() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local5() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local6() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local7() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local8() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local9() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local10() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local11() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); int Local12() => Local12() + Local11() + Local10() + Local9() + Local8() + Local7() + Local6() + Local5() + Local4() + Local3() + Local2() + Local1(); Local1(); Local2(); Local3(); Local4(); Local5(); Local6(); Local7(); Local8(); Local9(); Local10(); Local11(); Local12(); } } "; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); } [Fact] public void LocalFuncAndLambdaWithDifferentThis() { var src = @" using System; class C { private int P = 1; public void M() { int Local(int x) => x + this.P; int y = 10; var a = new Func<int>(() => Local(y)); Console.WriteLine(a()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "11"); } [Fact] public void LocalFuncAndLambdaWithDifferentThis2() { var src = @" using System; class C { private int P = 1; public void M() { int Local() => 10 + this.P; int Local2(int x) => x + Local(); int y = 100; var a = new Func<int>(() => Local2(y)); Console.WriteLine(a()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "111"); } [Fact] public void LocalFuncAndLambdaWithDifferentThis3() { var src = @" using System; class C { private int P = 1; public void M() { int Local() { if (this.P < 5) { return Local2(this.P++); } else { return 1; } } int Local2(int x) => x + Local(); int y = 100; var a = new Func<int>(() => Local2(y)); Console.WriteLine(a()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "111"); } [Fact] public void LocalFuncAndLambdaWithDifferentThis4() { var src = @" using System; class C { private int P = 1; public void M() { int Local(int x) => x + this.P; int y = 10; var a = new Func<int>(() => { var b = (Func<int, int>)Local; return b(y); }); Console.WriteLine(a()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "11"); } [Fact] public void LocalFuncAndLambdaWithDifferentThis5() { var src = @" using System; class C { private int P = 1; public void M() { int Local(int x) => x + this.P; int y = 10; var a = new Func<int>(() => { var b = new Func<int, int>(Local); return b(y); }); Console.WriteLine(a()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "11"); } [Fact] public void TwoFrames() { var src = @" using System; class C { private int P = 0; public void M() { int x = 0; var a = new Func<int>(() => { int Local() => x + this.P; int z = 0; int Local3() => z + Local(); return Local3(); }); Console.WriteLine(a()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "0"); } [Fact] public void SameFrame() { var src = @" using System; class C { private int P = 1; public void M() { int x = 10; int Local() => x + this.P; int y = 100; int Local2() => y + Local(); Console.WriteLine(Local2()); } public static void Main(string[] args) { var c = new C(); c.M(); } }"; VerifyOutput(src, "111"); } [Fact] public void MutuallyRecursiveThisCapture() { var src = @" using System; class C { private int P = 1; public void M() { int Local() { if (this.P < 5) { return Local2(this.P++); } else { return 1; } } int Local2(int x) => x + Local(); Console.WriteLine(Local()); } public static void Main() => new C().M(); }"; VerifyOutput(src, "11"); } [Fact] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicParameterLocalFunction() { var src = @" using System; class C { static void Main(string[] args) => M(0); static void M(int x) { dynamic y = x + 1; Action a; Action local(dynamic z) { Console.Write(z); Console.Write(y); return () => Console.Write(y + z + 1); } a = local(x); a(); } }"; VerifyOutput(src, "012"); } [Fact] public void EndToEnd() { var source = @" using System; class Program { static void Main(string[] args) { void Local() { Console.WriteLine(""Hello, world!""); } Local(); } } "; VerifyOutput(source, "Hello, world!"); } [Fact] [CompilerTrait(CompilerFeature.ExpressionBody)] public void ExpressionBody() { var source = @" int Local() => 2; Console.Write(Local()); Console.Write(' '); void VoidLocal() => Console.Write(2); VoidLocal(); "; VerifyOutputInMain(source, "2 2", "System"); } [Fact] public void EmptyStatementAfter() { var source = @" void Local() { Console.Write(2); }; Local(); "; VerifyOutputInMain(source, "2", "System"); } [Fact] [CompilerTrait(CompilerFeature.Params)] public void Params() { var source = @" void Params(params int[] x) { Console.WriteLine(string.Join("","", x)); } Params(2); "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void RefAndOut() { var source = @" void RefOut(ref int x, out int y) { y = ++x; } int a = 1; int b; RefOut(ref a, out b); Console.Write(a); Console.Write(' '); Console.Write(b); "; VerifyOutputInMain(source, "2 2", "System"); } [Fact] public void NamedAndOptional() { var source = @" void NamedOptional(int x = 2) { Console.Write(x); } NamedOptional(x: 3); Console.Write(' '); NamedOptional(); "; VerifyOutputInMain(source, "3 2", "System"); } [Fact, WorkItem(51518, "https://github.com/dotnet/roslyn/issues/51518")] public void OptionalParameterCodeGen() { var source = @" public class C { public static void Main() { LocalFunc(); void LocalFunc(int a = 2) { } } } "; var comp = CreateCompilationWithMscorlib461AndCSharp(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedSignatures: new SignatureDescription[] { Signature("C", "Main", ".method public hidebysig static System.Void Main() cil managed"), Signature("C", "<Main>g__LocalFunc|0_0", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] assembly hidebysig static System.Void <Main>g__LocalFunc|0_0([opt] System.Int32 a = 2) cil managed") }); } [Fact, WorkItem(53478, "https://github.com/dotnet/roslyn/issues/53478")] public void OptionalParameterCodeGen_Reflection() { VerifyOutputInMain(@"void TestAction(int i = 5) { } var d = (Action<int>)TestAction; var p2 = d.Method.GetParameters(); Console.WriteLine(p2[0].HasDefaultValue); Console.WriteLine(p2[0].DefaultValue);", @"True 5", new[] { "System" }); } [Fact] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicArgShadowing() { var src = @" using System; class C { static void Shadow(int x) => Console.Write(x + 1); static void Main() { void Shadow(int x) => Console.Write(x); dynamic val = 2; Shadow(val); } }"; VerifyOutput(src, "2"); } [Fact] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicParameter() { var source = @" using System; class Program { static void Main() { void Local(dynamic x) { Console.Write(x); } Local(2); } } "; VerifyOutput(source, "2"); } [Fact] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicReturn() { var source = @" dynamic RetDyn() { return 2; } Console.Write(RetDyn()); "; VerifyOutputInMain(source, "2", "System"); } [Fact] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicDelegate() { var source = @" using System; class Program { static void Main() { dynamic Local(dynamic x) { return x; } dynamic L2(int x) { void L2_1(int y) { Console.Write(x); Console.Write(y); } dynamic z = x + 1; void L2_2() => L2_1(z); return (Action)L2_2; } dynamic local = (Func<dynamic, dynamic>)Local; Console.Write(local(2)); L2(3)(); } } "; VerifyOutput(source, "234"); } [Fact] public void Nameof() { var source = @" void Local() { } Console.Write(nameof(Local)); "; VerifyOutputInMain(source, "Local", "System"); } [Fact] public void ExpressionTreeParameter() { var source = @" Expression<Func<int, int>> Local(Expression<Func<int, int>> f) { return f; } Console.Write(Local(x => x)); "; VerifyOutputInMain(source, "x => x", "System", "System.Linq.Expressions"); } [Fact] public void LinqInLocalFunction() { var source = @" IEnumerable<int> Query(IEnumerable<int> values) { return from x in values where x < 5 select x * x; } Console.Write(string.Join("","", Query(Enumerable.Range(0, 10)))); "; VerifyOutputInMain(source, "0,1,4,9,16", "System", "System.Linq", "System.Collections.Generic"); } [Fact] public void ConstructorWithoutArg() { var source = @" using System; class Base { public int x; public Base(int x) { this.x = x; } } class Program : Base { Program() : base(2) { void Local() { Console.Write(x); } Local(); } public static void Main() { new Program(); } } "; VerifyOutput(source, "2"); } [Fact] public void ConstructorWithArg() { var source = @" using System; class Base { public int x; public Base(int x) { this.x = x; } } class Program : Base { Program(int x) : base(x + 2) { void Local() { Console.Write(x); Console.Write(' '); Console.Write(base.x); } Local(); } public static void Main() { new Program(2); } } "; VerifyOutput(source, "2 4"); } [Fact] public void IfDef() { var source = @" using System; class Program { public static void Main() { #if LocalFunc void Local() { Console.Write(2); Console.Write(' '); #endif Console.Write(4); #if LocalFunc } Local(); #endif } } "; VerifyOutput(source, "4"); source = "#define LocalFunc" + source; VerifyOutput(source, "2 4"); } [Fact] public void PragmaWarningDisableEntersLocfunc() { var source = @" #pragma warning disable CS0168 void Local() { int x; // unused Console.Write(2); } #pragma warning restore CS0168 Local(); "; // No diagnostics is asserted in VerifyOutput, so if the warning happens, then we'll catch it VerifyOutputInMain(source, "2", "System"); } [Fact] public void ObsoleteAttributeRecursion() { var source = @" using System; class Program { [Obsolete] public void Obs() { void Local() { Obs(); // shouldn't emit warning } Local(); } public static void Main() { Console.Write(2); } } "; VerifyOutput(source, "2"); } [Fact] public void MainLocfuncIsntEntry() { var source = @" void Main() { Console.Write(4); } Console.Write(2); Console.Write(' '); Main(); "; VerifyOutputInMain(source, "2 4", "System"); } [Fact] public void Shadows() { var source = @" using System; class Program { static void Local() { Console.WriteLine(""bad""); } static void Main(string[] args) { void Local() { Console.Write(2); } Local(); } } "; VerifyOutput(source, "2"); } [Fact] public void ExtensionMethodClosure() { var source = @" using System; static class Program { public static void Ext(this int x) { void Local() { Console.Write(x); } Local(); } public static void Main() { 2.Ext(); } } "; // warning level 0 because extension method generates CS1685 (predefined type multiple definition) for ExtensionAttribute in System.Core and mscorlib VerifyOutput(source, "2", TestOptions.ReleaseExe.WithWarningLevel(0)); } [Fact] public void Scoping() { var source = @" void Local() { Console.Write(2); } if (true) { Local(); } "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void Property() { var source = @" using System; class Program { static int Goo { get { int Local() { return 2; } return Local(); } } static void Main(string[] args) { Console.Write(Goo); } }"; VerifyOutput(source, "2"); } [Fact] public void PropertyIterator() { var source = @" using System; using System.Collections.Generic; class Program { static int Goo { get { int a = 2; IEnumerable<int> Local() { yield return a; } foreach (var x in Local()) { return x; } return 0; } } static void Main(string[] args) { Console.Write(Goo); } }"; VerifyOutput(source, "2"); } [Fact] public void DelegateFunc() { var source = @" int Local(int x) => x; Func<int, int> local = Local; Console.Write(local(2)); "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void DelegateFuncGenericImplicit() { var source = @" T Local<T>(T x) => x; Func<int, int> local = Local; Console.Write(local(2)); "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void DelegateFuncGenericExplicit() { var source = @" T Local<T>(T x) => x; Func<int, int> local = Local<int>; Console.Write(local(2)); "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void DelegateAction() { var source = @" void Local() { Console.Write(2); } var local = new Action(Local); local(); Console.Write(' '); local = (Action)Local; local(); "; VerifyOutputInMain(source, "2 2", "System"); } [Fact] public void InterpolatedString() { var source = @" int x = 1; int Bar() => ++x; var str = $@""{((Func<int>)(() => { int Goo() => Bar(); return Goo(); }))()}""; Console.Write(str + ' ' + x); "; VerifyOutputInMain(source, "2 2", "System"); } // StaticNoClosure*() are generic because the reference to the locfunc is constructed, and actual local function is not // (i.e. testing to make sure we use MethodSymbol.OriginalDefinition in LambdaRewriter.Analysis) [Fact] public void StaticNoClosure() { var source = @" T Goo<T>(T x) { return x; } Console.Write(Goo(2)); "; var verify = VerifyOutputInMain(source, "2", "System"); var goo = verify.FindLocalFunction("Goo"); Assert.True(goo.IsStatic); Assert.Equal(verify.Compilation.GetTypeByMetadataName("Program"), goo.ContainingType); } [Fact] public void StaticNoClosureDelegate() { var source = @" T Goo<T>(T x) { return x; } Func<int, int> goo = Goo; Console.Write(goo(2)); "; var verify = VerifyOutputInMain(source, "2", "System"); var goo = verify.FindLocalFunction("Goo"); var program = verify.Compilation.GetTypeByMetadataName("Program"); Assert.True(goo.IsStatic); Assert.Equal(program, goo.ContainingType); } [Fact] public void ClosureBasic() { var source = @" using System; class Program { static void Print(int a) { Console.Write(' '); Console.Write(a); } static void A(int y) { int x = 1; void Local() { Print(x); Print(y); } Local(); Print(x); Print(y); x = 3; y = 4; Local(); Print(x); Print(y); void Local2() { Print(x); Print(y); x += 2; y += 2; Print(x); Print(y); } Local2(); Print(x); Print(y); } static void Main(string[] args) { A(2); } } "; VerifyOutput(source, "1 2 1 2 3 4 3 4 3 4 5 6 5 6"); } [Fact] public void ClosureThisOnly() { var source = @" using System; class Program { int _a; static void Print(int a) { Console.Write(' '); Console.Write(a); } void B() { _a = 2; void Local() { Print(_a); _a++; Print(_a); } Print(_a); Local(); Print(_a); } static void Main(string[] args) { new Program().B(); } }"; VerifyOutput(source, "2 2 3 3"); } [Fact] public void ClosureGeneralThisOnly() { var source = @" var x = 0; void Outer() { if (++x == 2) { Console.Write(x); return; } void Inner() { Outer(); } Inner(); } Outer(); "; var verify = VerifyOutputInMain(source, "2", "System"); var outer = verify.FindLocalFunction("Outer"); var inner = verify.FindLocalFunction("Inner"); Assert.Equal(outer.ContainingType, inner.ContainingType); } [Fact] public void ClosureStaticInInstance() { var source = @" using System; class Program { static int _sa; static void Print(int a) { Console.Write(' '); Console.Write(a); } void C() { _sa = 2; void Local() { Print(_sa); _sa++; Print(_sa); } Print(_sa); Local(); Print(_sa); } static void Main(string[] args) { new Program().C(); } }"; VerifyOutput(source, "2 2 3 3"); } [Fact] public void ClosureGeneric() { var source = @" using System; class Program { static void Print(object a) { Console.Write(' '); Console.Write(a); } class Gen<T1> { T1 t1; public Gen(T1 t1) { this.t1 = t1; } public void D<T2>(T2 t2) { T2 Local(T1 x) { Print(x); Print(t1); t1 = (T1)(object)((int)(object)x + 2); t2 = (T2)(object)x; return (T2)(object)((int)(object)t2 + 4); } Print(t1); Print(t2); Print(Local(t1)); Print(t1); Print(t2); } } static void Main(string[] args) { new Gen<int>(2).D<int>(3); } }"; VerifyOutput(source, "2 3 2 2 6 4 2"); } [Fact] public void ClosureLambdasAndLocfuncs() { var source = @" using System; class Program { static void Print(int a) { Console.Write(' '); Console.Write(a); } static void E() { int a = 2; void M1() { int b = a; Action M2 = () => { int c = b; void M3() { int d = c; Print(d + b); } M3(); }; M2(); } M1(); } static void Main(string[] args) { E(); } }"; VerifyOutput(source, "4"); } [Fact] public void ClosureTripleNested() { var source = @" using System; class Program { static void Print(int a) { Console.Write(' '); Console.Write(a); } static void A() { int a = 0; void M1() { int b = a; void M2() { int c = b; void M3() { Print(c); c = 2; } Print(b); M3(); Print(c); b = 2; } Print(a); M2(); Print(b); a = 2; } M1(); Print(a); } static void B() { int a = 0; void M1() { int b = a; void M2() { void M3() { Print(b); b = 2; } M3(); Print(b); } Print(a); M2(); Print(b); a = 2; } M1(); Print(a); } static void C() { int a = 0; void M1() { void M2() { int c = a; void M3() { Print(c); c = 2; } Print(a); M3(); Print(c); a = 2; } M2(); Print(a); } M1(); Print(a); } static void D() { void M1() { int b = 0; void M2() { int c = b; void M3() { Print(c); c = 2; } Print(b); M3(); Print(c); b = 2; } M2(); Print(b); } M1(); } static void E() { int a = 0; void M1() { void M2() { void M3() { Print(a); a = 2; } M3(); Print(a); } M2(); Print(a); } M1(); Print(a); } static void F() { void M1() { int b = 0; void M2() { void M3() { Print(b); b = 2; } M3(); Print(b); } M2(); Print(b); } M1(); } static void G() { void M1() { void M2() { int c = 0; void M3() { Print(c); c = 2; } M3(); Print(c); } M2(); } M1(); } static void Main(string[] args) { A(); Console.WriteLine(); B(); Console.WriteLine(); C(); Console.WriteLine(); D(); Console.WriteLine(); E(); Console.WriteLine(); F(); Console.WriteLine(); G(); Console.WriteLine(); } } "; VerifyOutput(source, @" 0 0 0 2 2 2 0 0 2 2 2 0 0 2 2 2 0 0 2 2 0 2 2 2 0 2 2 0 2 "); } [Fact] public void InstanceClosure() { var source = @" using System; class Program { int w; int A(int y) { int x = 1; int Local1(int z) { int Local2() { return Local1(x + y + w); } return z != -1 ? z : Local2(); } return Local1(-1); } static void Main(string[] args) { var prog = new Program(); prog.w = 3; Console.WriteLine(prog.A(2)); } } "; VerifyOutput(source, "6"); } [Fact] public void SelfClosure() { var source = @" using System; class Program { static int Test() { int x = 2; int Local1(int y) { int Local2() { return Local1(x); } return y != 0 ? y : Local2(); } return Local1(0); } static void Main(string[] args) { Console.WriteLine(Test()); } } "; VerifyOutput(source, "2"); } [Fact] public void StructClosure() { var source = @" int x = 2; void Goo() { Console.Write(x); } Goo(); "; var verify = VerifyOutputInMain(source, "2", "System"); var goo = verify.FindLocalFunction("Goo"); var program = verify.Compilation.GetTypeByMetadataName("Program"); Assert.Equal(program, goo.ContainingType); Assert.True(goo.IsStatic); Assert.Equal(RefKind.Ref, goo.Parameters[0].RefKind); Assert.True(goo.Parameters[0].Type.IsValueType); } [Fact] public void StructClosureGeneric() { var source = @" int x = 2; void Goo<T1>() { int y = x; void Bar<T2>() { Console.Write(x + y); } Bar<T1>(); } Goo<int>(); "; var verify = VerifyOutputInMain(source, "4", "System"); var goo = verify.FindLocalFunction("Goo"); var bar = verify.FindLocalFunction("Bar"); Assert.Equal(1, goo.Parameters.Length); Assert.Equal(2, bar.Parameters.Length); Assert.Equal(RefKind.Ref, goo.Parameters[0].RefKind); Assert.Equal(RefKind.Ref, bar.Parameters[0].RefKind); Assert.Equal(RefKind.Ref, bar.Parameters[1].RefKind); Assert.True(goo.Parameters[0].Type.IsValueType); Assert.True(bar.Parameters[0].Type.IsValueType); Assert.True(bar.Parameters[1].Type.IsValueType); Assert.Equal(goo.Parameters[0].Type.OriginalDefinition, bar.Parameters[0].Type.OriginalDefinition); var gooFrame = (INamedTypeSymbol)goo.Parameters[0].Type; var barFrame = (INamedTypeSymbol)bar.Parameters[1].Type; Assert.Equal(0, gooFrame.Arity); Assert.Equal(1, barFrame.Arity); } [Fact] public void ClosureOfStructClosure() { var source = @" void Outer() { int a = 0; void Middle() { int b = 0; void Inner() { a++; b++; } a++; Inner(); } Middle(); Console.WriteLine(a); } Outer(); "; var verify = VerifyOutputInMain(source, "2", "System"); var inner = verify.FindLocalFunction("Inner"); var middle = verify.FindLocalFunction("Middle"); var outer = verify.FindLocalFunction("Outer"); var program = verify.Compilation.GetTypeByMetadataName("Program"); Assert.Equal(program, inner.ContainingType); Assert.Equal(program, middle.ContainingType); Assert.Equal(program, outer.ContainingType); Assert.True(inner.IsStatic); Assert.True(middle.IsStatic); Assert.True(outer.IsStatic); Assert.Equal(2, inner.Parameters.Length); Assert.Equal(1, middle.Parameters.Length); Assert.Equal(0, outer.Parameters.Length); Assert.Equal(RefKind.Ref, inner.Parameters[0].RefKind); Assert.Equal(RefKind.Ref, inner.Parameters[1].RefKind); Assert.Equal(RefKind.Ref, middle.Parameters[0].RefKind); Assert.True(inner.Parameters[0].Type.IsValueType); Assert.True(inner.Parameters[1].Type.IsValueType); Assert.True(middle.Parameters[0].Type.IsValueType); } [Fact] public void ThisClosureCallingOtherClosure() { var source = @" using System; class Program { int _x; int Test() { int First() { return ++_x; } int Second() { return First(); } return Second(); } static void Main() { Console.Write(new Program() { _x = 1 }.Test()); } } "; var verify = VerifyOutput(source, "2"); var program = verify.Compilation.GetTypeByMetadataName("Program"); Assert.Equal(program, verify.FindLocalFunction("First").ContainingType); Assert.Equal(program, verify.FindLocalFunction("Second").ContainingType); } [Fact] public void RecursiveStructClosure() { var source = @" int x = 0; void Goo() { if (x != 2) { x++; Goo(); } else { Console.Write(x); } } Goo(); "; var verify = VerifyOutputInMain(source, "2", "System"); var goo = verify.FindLocalFunction("Goo"); var program = verify.Compilation.GetTypeByMetadataName("Program"); Assert.Equal(program, goo.ContainingType); Assert.True(goo.IsStatic); Assert.Equal(RefKind.Ref, goo.Parameters[0].RefKind); Assert.True(goo.Parameters[0].Type.IsValueType); } [Fact] public void MutuallyRecursiveStructClosure() { var source = @" int x = 0; void Goo(int depth) { int dummy = 0; void Bar(int depth2) { dummy++; Goo(depth2); } if (depth != 2) { x++; Bar(depth + 1); } else { Console.Write(x); } } Goo(0); "; var verify = VerifyOutputInMain(source, "2", "System"); var program = verify.Compilation.GetTypeByMetadataName("Program"); var goo = verify.FindLocalFunction("Goo"); var bar = verify.FindLocalFunction("Bar"); Assert.Equal(program, goo.ContainingType); Assert.Equal(program, bar.ContainingType); Assert.True(goo.IsStatic); Assert.True(bar.IsStatic); Assert.Equal(2, goo.Parameters.Length); Assert.Equal(3, bar.Parameters.Length); Assert.Equal(RefKind.Ref, goo.Parameters[1].RefKind); Assert.Equal(RefKind.Ref, bar.Parameters[1].RefKind); Assert.Equal(RefKind.Ref, bar.Parameters[2].RefKind); Assert.True(goo.Parameters[1].Type.IsValueType); Assert.True(bar.Parameters[1].Type.IsValueType); Assert.True(bar.Parameters[2].Type.IsValueType); } [Fact] public void Recursion() { var source = @" void Goo(int depth) { if (depth > 10) { Console.WriteLine(2); return; } Goo(depth + 1); } Goo(0); "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void MutualRecursion() { var source = @" void Goo(int depth) { if (depth > 10) { Console.WriteLine(2); return; } void Bar(int depth2) { Goo(depth2 + 1); } Bar(depth + 1); } Goo(0); "; VerifyOutputInMain(source, "2", "System"); } [Fact] public void RecursionThisOnlyClosure() { var source = @" using System; class Program { int _x; void Outer() { void Inner() { if (_x == 0) { return; } Console.Write(_x); Console.Write(' '); _x = 0; Inner(); } Inner(); } public static void Main() { new Program() { _x = 2 }.Outer(); } } "; var verify = VerifyOutput(source, "2"); var program = verify.Compilation.GetTypeByMetadataName("Program"); Assert.Equal(program, verify.FindLocalFunction("Inner").ContainingType); } [Fact] public void RecursionFrameCaptureTest() { // ensures that referring to a local function in an otherwise noncapturing Inner captures the frame of Outer. var source = @" int x = 0; int Outer(bool isRecursive) { if (isRecursive) { return x; } x++; int Middle() { int Inner() { return Outer(true); } return Inner(); } return Middle(); } Console.Write(Outer(false)); Console.Write(' '); Console.Write(x); "; VerifyOutputInMain(source, "1 1", "System"); } [Fact] [CompilerTrait(CompilerFeature.Iterator)] public void IteratorBasic() { var source = @" IEnumerable<int> Local() { yield return 2; } Console.Write(string.Join("","", Local())); "; VerifyOutputInMain(source, "2", "System", "System.Collections.Generic"); } [Fact] [CompilerTrait(CompilerFeature.Iterator)] public void IteratorGeneric() { var source = @" IEnumerable<T> LocalGeneric<T>(T val) { yield return val; } Console.Write(string.Join("","", LocalGeneric(2))); "; VerifyOutputInMain(source, "2", "System", "System.Collections.Generic"); } [Fact] [CompilerTrait(CompilerFeature.Iterator)] public void IteratorNonGeneric() { var source = @" IEnumerable LocalNongen() { yield return 2; } foreach (int x in LocalNongen()) { Console.Write(x); } "; VerifyOutputInMain(source, "2", "System", "System.Collections"); } [Fact] [CompilerTrait(CompilerFeature.Iterator)] public void IteratorEnumerator() { var source = @" IEnumerator LocalEnumerator() { yield return 2; } var y = LocalEnumerator(); y.MoveNext(); Console.Write(y.Current); "; VerifyOutputInMain(source, "2", "System", "System.Collections"); } [Fact] public void Generic() { var source = @" using System; class Program { // No closure. Return 'valu'. static T A1<T>(T val) { T Local(T valu) { return valu; } return Local(val); } static int B1(int val) { T Local<T>(T valu) { return valu; } return Local(val); } static T1 C1<T1>(T1 val) { T2 Local<T2>(T2 valu) { return valu; } return Local<T1>(val); } // General closure. Return 'val'. static T A2<T>(T val) { T Local(T valu) { return val; } return Local(val); } static int B2(int val) { T Local<T>(T valu) { return (T)(object)val; } return Local(val); } static T1 C2<T1>(T1 val) { T2 Local<T2>(T2 valu) { return (T2)(object)val; } return Local<T1>(val); } // This-only closure. Return 'field'. int field = 2; T A3<T>(T val) { T Local(T valu) { return (T)(object)field; } return Local(val); } int B3(int val) { T Local<T>(T valu) { return (T)(object)field; } return Local(val); } T1 C3<T1>(T1 val) { T2 Local<T2>(T2 valu) { return (T2)(object)field; } return Local<T1>(val); } static void Main(string[] args) { var program = new Program(); Console.WriteLine(Program.A1(2)); Console.WriteLine(Program.B1(2)); Console.WriteLine(Program.C1(2)); Console.WriteLine(Program.A2(2)); Console.WriteLine(Program.B2(2)); Console.WriteLine(Program.C2(2)); Console.WriteLine(program.A3(2)); Console.WriteLine(program.B3(2)); Console.WriteLine(program.C3(2)); } } "; var output = @" 2 2 2 2 2 2 2 2 2 "; VerifyOutput(source, output); } [Fact] public void GenericConstraint() { var source = @" using System; class Program { static T A<T>(T val) where T : struct { T Local(T valu) { return valu; } return Local(val); } static int B(int val) { T Local<T>(T valu) where T : struct { return valu; } return Local(val); } static T1 C<T1>(T1 val) where T1 : struct { T2 Local<T2>(T2 valu) where T2 : struct { return valu; } return Local(val); } static object D(object val) { T Local<T>(T valu) where T : class { return valu; } return Local(val); } static void Main(string[] args) { Console.WriteLine(A(2)); Console.WriteLine(B(2)); Console.WriteLine(C(2)); Console.WriteLine(D(2)); } } "; var output = @" 2 2 2 2 "; VerifyOutput(source, output); } [Fact] public void GenericTripleNestedNoClosure() { var source = @" using System; class Program { // Name of method is T[outer][middle][inner] where brackets are g=generic n=nongeneric // One generic static T1 Tgnn<T1>(T1 a) { T1 Local(T1 aa) { T1 Local2(T1 aaa) { return aaa; } return Local2(aa); } return Local(a); } static int Tngn(int a) { T1 Local<T1>(T1 aa) { T1 Local2(T1 aaa) { return aaa; } return Local2(aa); } return Local(a); } static int Tnng(int a) { int Local(int aa) { T1 Local2<T1>(T1 aaa) { return aaa; } return Local2(aa); } return Local(a); } // Two generic static T1 Tggn<T1>(T1 a) { T2 Local<T2>(T2 aa) { T2 Local2(T2 aaa) { return aaa; } return Local2(aa); } return Local(a); } static T1 Tgng<T1>(T1 a) { T1 Local(T1 aa) { T2 Local2<T2>(T2 aaa) { return aaa; } return Local2(aa); } return Local(a); } static int Tngg(int a) { T1 Local<T1>(T1 aa) { T2 Local2<T2>(T2 aaa) { return aaa; } return Local2(aa); } return Local(a); } // Three generic static T1 Tggg<T1>(T1 a) { T2 Local<T2>(T2 aa) { T3 Local2<T3>(T3 aaa) { return aaa; } return Local2(aa); } return Local(a); } static void Main(string[] args) { Console.WriteLine(Program.Tgnn(2)); Console.WriteLine(Program.Tngn(2)); Console.WriteLine(Program.Tnng(2)); Console.WriteLine(Program.Tggn(2)); Console.WriteLine(Program.Tgng(2)); Console.WriteLine(Program.Tngg(2)); Console.WriteLine(Program.Tggg(2)); } } "; var output = @" 2 2 2 2 2 2 2 "; VerifyOutput(source, output); } [Fact] public void GenericTripleNestedMiddleClosure() { var source = @" using System; class Program { // Name of method is T[outer][middle][inner] where brackets are g=generic n=nongeneric // One generic static T1 Tgnn<T1>(T1 a) { T1 Local(T1 aa) { T1 Local2(T1 aaa) { return (T1)(object)aa; } return Local2(aa); } return Local(a); } static int Tngn(int a) { T1 Local<T1>(T1 aa) { T1 Local2(T1 aaa) { return (T1)(object)aa; } return Local2(aa); } return Local(a); } static int Tnng(int a) { int Local(int aa) { T1 Local2<T1>(T1 aaa) { return (T1)(object)aa; } return Local2(aa); } return Local(a); } // Two generic static T1 Tggn<T1>(T1 a) { T2 Local<T2>(T2 aa) { T2 Local2(T2 aaa) { return (T2)(object)aa; } return Local2(aa); } return Local(a); } static T1 Tgng<T1>(T1 a) { T1 Local(T1 aa) { T2 Local2<T2>(T2 aaa) { return (T2)(object)aa; } return Local2(aa); } return Local(a); } static int Tngg(int a) { T1 Local<T1>(T1 aa) { T2 Local2<T2>(T2 aaa) { return (T2)(object)aa; } return Local2(aa); } return Local(a); } // Three generic static T1 Tggg<T1>(T1 a) { T2 Local<T2>(T2 aa) { T3 Local2<T3>(T3 aaa) { return (T3)(object)aa; } return Local2(aa); } return Local(a); } static void Main(string[] args) { Console.WriteLine(Program.Tgnn(2)); Console.WriteLine(Program.Tngn(2)); Console.WriteLine(Program.Tnng(2)); Console.WriteLine(Program.Tggn(2)); Console.WriteLine(Program.Tgng(2)); Console.WriteLine(Program.Tngg(2)); Console.WriteLine(Program.Tggg(2)); } } "; var output = @" 2 2 2 2 2 2 2 "; VerifyOutput(source, output); } [Fact] public void GenericTripleNestedOuterClosure() { var source = @" using System; class Program { // Name of method is T[outer][middle][inner] where brackets are g=generic n=nongeneric // One generic static T1 Tgnn<T1>(T1 a) { T1 Local(T1 aa) { T1 Local2(T1 aaa) { return (T1)(object)a; } return Local2(aa); } return Local(a); } static int Tngn(int a) { T1 Local<T1>(T1 aa) { T1 Local2(T1 aaa) { return (T1)(object)a; } return Local2(aa); } return Local(a); } static int Tnng(int a) { int Local(int aa) { T1 Local2<T1>(T1 aaa) { return (T1)(object)a; } return Local2(aa); } return Local(a); } // Two generic static T1 Tggn<T1>(T1 a) { T2 Local<T2>(T2 aa) { T2 Local2(T2 aaa) { return (T2)(object)a; } return Local2(aa); } return Local(a); } static T1 Tgng<T1>(T1 a) { T1 Local(T1 aa) { T2 Local2<T2>(T2 aaa) { return (T2)(object)a; } return Local2(aa); } return Local(a); } static int Tngg(int a) { T1 Local<T1>(T1 aa) { T2 Local2<T2>(T2 aaa) { return (T2)(object)a; } return Local2(aa); } return Local(a); } // Three generic static T1 Tggg<T1>(T1 a) { T2 Local<T2>(T2 aa) { T3 Local2<T3>(T3 aaa) { return (T3)(object)a; } return Local2(aa); } return Local(a); } static void Main(string[] args) { Console.WriteLine(Program.Tgnn(2)); Console.WriteLine(Program.Tngn(2)); Console.WriteLine(Program.Tnng(2)); Console.WriteLine(Program.Tggn(2)); Console.WriteLine(Program.Tgng(2)); Console.WriteLine(Program.Tngg(2)); Console.WriteLine(Program.Tggg(2)); } } "; var output = @" 2 2 2 2 2 2 2 "; VerifyOutput(source, output); } [Fact] public void GenericTripleNestedNoClosureLambda() { var source = @" using System; class Program { // Name of method is T[outer][middle][inner] where brackets are g=generic n=nongeneric // One generic static T1 Tgnn<T1>(T1 a) { Func<T1, T1> Local = aa => { Func<T1, T1> Local2 = aaa => { return aaa; }; return Local2(aa); }; return Local(a); } static int Tngn(int a) { T1 Local<T1>(T1 aa) { Func<T1, T1> Local2 = aaa => { return aaa; }; return Local2(aa); } return Local(a); } static int Tnng(int a) { Func<int, int> Local = aa => { T1 Local2<T1>(T1 aaa) { return aaa; } return Local2(aa); }; return Local(a); } // Two generic static T1 Tggn<T1>(T1 a) { T2 Local<T2>(T2 aa) { Func<T2, T2> Local2 = aaa => { return aaa; }; return Local2(aa); } return Local(a); } static T1 Tgng<T1>(T1 a) { Func<T1, T1> Local = aa => { T2 Local2<T2>(T2 aaa) { return aaa; } return Local2(aa); }; return Local(a); } // Tngg and Tggg are impossible with lambdas static void Main(string[] args) { Console.WriteLine(Program.Tgnn(2)); Console.WriteLine(Program.Tngn(2)); Console.WriteLine(Program.Tnng(2)); Console.WriteLine(Program.Tggn(2)); Console.WriteLine(Program.Tgng(2)); } } "; var output = @" 2 2 2 2 2 "; VerifyOutput(source, output); } [Fact] public void GenericUpperCall() { var source = @" using System; class Program { static T1 InnerToOuter<T1>(T1 a) { T2 Local<T2>(T2 aa) { T3 Local2<T3>(T3 aaa) { if ((object)aaa == null) return InnerToOuter((T3)new object()); return aaa; } return Local2(aa); } return Local(a); } static T1 InnerToMiddle<T1>(T1 a) { T2 Local<T2>(T2 aa) { T3 Local2<T3>(T3 aaa) { if ((object)aaa == null) return InnerToMiddle((T3)new object()); return aaa; } return Local2(aa); } return Local(a); } static T1 InnerToOuterScoping<T1>(T1 a) { T2 Local<T2>(T2 aa) { T3 Local2<T3>(T3 aaa) { if ((object)aaa == null) return (T3)(object)InnerToOuter((T1)new object()); return aaa; } return Local2(aa); } return Local(a); } static T1 M1<T1>(T1 a) { T2 M2<T2>(T2 aa) { T2 x = aa; T3 M3<T3>(T3 aaa) { T4 M4<T4>(T4 aaaa) { return (T4)(object)x; } return M4(aaa); } return M3(aa); } return M2(a); } static void Main(string[] args) { Console.WriteLine(Program.InnerToOuter((object)null)); Console.WriteLine(Program.InnerToMiddle((object)null)); Console.WriteLine(Program.InnerToOuterScoping((object)null)); Console.WriteLine(Program.M1(2)); } } "; var output = @" System.Object System.Object System.Object 2 "; VerifyOutput(source, output); } [Fact] public void CompoundOperatorExecutesOnce() { var source = @" using System; class Program { int _x = 2; public static void Main() { var prog = new Program(); Program SideEffect() { Console.Write(prog._x); return prog; } SideEffect()._x += 2; Console.Write(' '); SideEffect(); } } "; VerifyOutput(source, "2 4"); } [Fact] public void ConstValueDoesntMakeClosure() { var source = @" const int x = 2; void Local() { Console.Write(x); } Local(); "; // Should be a static method on "Program" itself, not a display class like "Program+<>c__DisplayClass0_0" var verify = VerifyOutputInMain(source, "2", "System"); var goo = verify.FindLocalFunction("Local"); Assert.True(goo.IsStatic); Assert.Equal(verify.Compilation.GetTypeByMetadataName("Program"), goo.ContainingType); } [Fact] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicArgument() { var source = @" using System; class Program { static void Main() { int capture1 = 0; void L1(int x) => Console.Write(x); void L2(int x) { Console.Write(capture1); Console.Write(x); } dynamic L4(int x) { Console.Write(capture1); return x; } Action<int> L5(int x) { Console.Write(x); return L1; } dynamic val = 2; Console.WriteLine(); L1(val); L2(val); Console.WriteLine(); L2(L4(val)); L5(val)(val); } } "; VerifyOutput(source, output: @"202 00222"); } [Fact] [WorkItem(21317, "https://github.com/dotnet/roslyn/issues/21317")] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicGenericArg() { var src = @" void L1<T>(T x) { Console.WriteLine($""{x}: {typeof(T)}""); } dynamic val = 2; L1<object>(val); L1<int>(val); L1<dynamic>(val); L1<dynamic>(4); void L2<T>(int x, T y) => Console.WriteLine($""{x}, {y}: {typeof(T)}""); L2<float>(val, 3.0f); List<dynamic> listOfDynamic = new List<dynamic> { 1, 2, 3 }; void L3<T>(List<T> x) => Console.WriteLine($""{string.Join("", "", x)}: {typeof(T)}""); L3(listOfDynamic); void L4<T>(T x, params int[] y) => Console.WriteLine($""{x}, {string.Join("", "", y)}: {typeof(T)}""); L4<dynamic>(val, 3, 4); L4<int>(val, 3, 4); L4<int>(1, 3, val); void L5<T>(int x, params T[] y) => Console.WriteLine($""{x}, {string.Join("", "", y)}: {typeof(T)}""); L5<int>(val, 3, 4); L5<int>(1, 3, val); L5<dynamic>(1, 3, val); "; var output = @" 2: System.Object 2: System.Int32 2: System.Object 4: System.Object 2, 3: System.Single 1, 2, 3: System.Object 2, 3, 4: System.Object 2, 3, 4: System.Int32 1, 3, 2: System.Int32 2, 3, 4: System.Int32 1, 3, 2: System.Int32 1, 3, 2: System.Object "; VerifyOutputInMain(src, output, "System", "System.Collections.Generic"); } [Fact] [WorkItem(21317, "https://github.com/dotnet/roslyn/issues/21317")] [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicGenericClassMethod() { var src = @" using System; class C1<T1> { public static void M1<T2>() { void F(int x) { Console.WriteLine($""C1<{typeof(T1)}>.M1<{typeof(T2)}>.F({x})""); } F((dynamic)2); } public static void M2() { void F(int x) { Console.WriteLine($""C1<{typeof(T1)}>.M2.F({x})""); } F((dynamic)2); } } class C2 { public static void M1<T2>() { void F(int x) { Console.WriteLine($""C2.M1<{typeof(T2)}>.F({x})""); } F((dynamic)2); } public static void M2() { void F(int x) { Console.WriteLine($""C2.M2.F({x})""); } F((dynamic)2); } } class Program { static void Main() { C1<int>.M1<float>(); C1<int>.M2(); C2.M1<float>(); C2.M2(); } } "; var output = @" C1<System.Int32>.M1<System.Single>.F(2) C1<System.Int32>.M2.F(2) C2.M1<System.Single>.F(2) C2.M2.F(2) "; VerifyOutput(src, output); } [Fact] [CompilerTrait(CompilerFeature.Dynamic, CompilerFeature.Params)] public void DynamicArgsAndParams() { var src = @" int capture1 = 0; void L1(int x, params int[] ys) { Console.Write(capture1); Console.Write(x); foreach (var y in ys) { Console.Write(y); } } dynamic val = 2; int val2 = 3; L1(val, val2); L1(val); L1(val, val, val); "; VerifyOutputInMain(src, "023020222", "System"); } [Fact] public void Basic() { var source = @" async Task<int> 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<int> 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 GenericAsync() { var source = @" async Task<T> LocalGeneric<T>(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<int> Fun(int x) { return Task.FromResult(x); } async Task<int> 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 Goo() { return new async(); } return Goo().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<int> async() { return 2; } return async().Result.ToString(); } static string B() { unsafe async Task<int> 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), verify: Verification.Passes); } [Fact] public void UnsafeBasic() { var source = @" using System; class Program { static void A() { unsafe void Local() { int x = 2; Console.Write(*&x); } Local(); } static void Main(string[] args) { A(); } } "; VerifyOutput(source, "2", TestOptions.ReleaseExe.WithAllowUnsafe(true), verify: Verification.Fails); } [Fact] public void UnsafeParameter() { var source = @" using System; class Program { static unsafe void B() { int x = 2; unsafe void Local(int* y) { Console.Write(*y); } Local(&x); } static void Main(string[] args) { B(); } } "; VerifyOutput(source, "2", TestOptions.ReleaseExe.WithAllowUnsafe(true), verify: Verification.Fails); } [Fact] public void UnsafeClosure() { var source = @" using System; class Program { static unsafe void C() { int y = 2; int* x = &y; unsafe void Local() { Console.Write(*x); } Local(); } static void Main(string[] args) { C(); } } "; VerifyOutput(source, "2", TestOptions.ReleaseExe.WithAllowUnsafe(true), verify: Verification.Fails); } [Fact] public void UnsafeCalls() { var src = @" using System; class C { public static void Main(string[] args) { int x = 2; int y = 0; unsafe void Local(ref int x2) { fixed (int* ptr = &x2) { Local2(ptr); } } unsafe int* Local2(int* ptr) { (*ptr)++; y++; return null; } while (x < 10) { Local(ref x); x++; } Console.WriteLine(x); Console.WriteLine(y); } }"; VerifyOutput(src, $"10{Environment.NewLine}4", TestOptions.ReleaseExe.WithAllowUnsafe(true), verify: Verification.Fails); } [Fact] [WorkItem(15322, "https://github.com/dotnet/roslyn/issues/15322")] public void UseBeforeDeclaration() { var src = @" Assign(); Local(); int x; void Local() => System.Console.WriteLine(x); void Assign() { x = 5; }"; VerifyOutputInMain(src, "5"); } [Fact] [WorkItem(15558, "https://github.com/dotnet/roslyn/issues/15558")] public void CapturingSharesVar() { var src = @" int i = 0; int oldi<T>() where T : struct => (i += @sizeof<T>()) - @sizeof<T>(); int @sizeof<T>() where T : struct => typeof(T).IsAssignableFrom(typeof(long)) ? sizeof(long) : 1; while (i < 10) System.Console.WriteLine(oldi<byte>());"; VerifyOutputInMain(src, @"0 1 2 3 4 5 6 7 8 9"); } [Fact] [WorkItem(15599, "https://github.com/dotnet/roslyn/issues/15599")] public void NestedLocalFuncCapture() { var src = @" using System; public class C { int instance = 11; public void M() { int M() => instance; { int local = 11; bool M2() => local == M(); Console.WriteLine(M2()); } } public static void Main() => new C().M(); }"; VerifyOutput(src, "True"); } [Fact] [WorkItem(15599, "https://github.com/dotnet/roslyn/issues/15599")] public void NestedLocalFuncCapture2() { var src = @" using System; public class C { int instance = 0b1; public void M() { int var1 = 0b10; int M() => var1 + instance; { int local = 0b100; int M2() => local + M(); Console.WriteLine(M2()); } } public static void Main() => new C().M(); }"; VerifyOutput(src, "7"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction() { var src = @" void Local<T>(T t, int count) { if (count > 0) { Console.Write(t); Local(t, count - 1); } } Local(""A"", 5); "; VerifyOutputInMain(src, "AAAAA", "System"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction2() { var src = @" void Local<T>(T t, int count) { if (count > 0) { Console.Write(t); var action = new Action<T, int>(Local); action(t, count - 1); } } Local(""A"", 5); "; VerifyOutputInMain(src, "AAAAA", "System"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction3() { var src = @" void Local<T>(T t, int count) { if (count > 0) { Console.Write(t); var action = (Action<T, int>)Local; action(t, count - 1); } } Local(""A"", 5); "; VerifyOutputInMain(src, "AAAAA", "System"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction4() { var src = @" using System; class C { public static void M<T>(T t) { void Local<U>(U u, int count) { if (count > 0) { Console.Write(t); Console.Write(u); Local(u, count - 1); } } Local(""A"", 5); } public static void Main() { C.M(""B""); } }"; VerifyOutput(src, "BABABABABA"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction5() { var src = @" using System; class C<T1> { T1 t1; public C(T1 t1) { this.t1 = t1; } public void M<T2>(T2 t2) { void L1<T3>(T3 t3) { void L2<T4>(T4 t4) { void L3<U>(U u, int count) { if (count > 0) { Console.Write(t1); Console.Write(t2); Console.Write(t3); Console.Write(t4); Console.Write(u); L3(u, count - 1); } } L3(""A"", 5); } L2(""B""); } L1(""C""); } } class Program { public static void Main() { var c = new C<string>(""D""); c.M(""E""); } }"; VerifyOutput(src, "DECBADECBADECBADECBADECBA"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction6() { var src = @" using System; class C<T1> { T1 t1; public C(T1 t1) { this.t1 = t1; } public void M<T2>(T2 t2) { void L1<T3>(T3 t3) { void L2<T4>(T4 t4) { void L3<U>(U u, int count) { if (count > 0) { Console.Write(t1); Console.Write(t2); Console.Write(t3); Console.Write(t4); Console.Write(u); var a = new Action<U, int>(L3); a(u, count - 1); } } var b = new Action<string, int>(L3); b(""A"", 5); } var c = new Action<string>(L2); c(""B""); } var d = new Action<string>(L1); d(""C""); } } class Program { public static void Main() { var c = new C<string>(""D""); c.M(""E""); } }"; VerifyOutput(src, "DECBADECBADECBADECBADECBA"); } [Fact] [WorkItem(15751, "https://github.com/dotnet/roslyn/issues/15751")] public void RecursiveGenericLocalFunction7() { var src = @" using System; class C<T1> { T1 t1; public C(T1 t1) { this.t1 = t1; } public void M<T2>(T2 t2) { void L1<T3>(T3 t3) { void L2<T4>(T4 t4) { void L3<U>(U u, int count) { if (count > 0) { Console.Write(t1); Console.Write(t2); Console.Write(t3); Console.Write(t4); Console.Write(u); var a = (Action<U, int>)(L3); a(u, count - 1); } } var b = (Action<string, int>)(L3); b(""A"", 5); } var c = (Action<string>)(L2); c(""B""); } var d = (Action<string>)(L1); d(""C""); } } class Program { public static void Main() { var c = new C<string>(""D""); c.M(""E""); } }"; VerifyOutput(src, "DECBADECBADECBADECBADECBA"); } [Fact] [WorkItem(16038, "https://github.com/dotnet/roslyn/issues/16038")] public void RecursiveGenericLocalFunction8() { var src = @" using System; class C<T0> { T0 t0; public C(T0 t0) { this.t0 = t0; } public void M<T1>(T1 t1) { (T0, T1, T2) L1<T2>(T2 t2) { (T0, T1, T2, T3) L2<T3>(T3 t3, int count) { if (count > 0) { Console.Write(t0); Console.Write(t1); Console.Write(t2); Console.Write(t3); return L2(t3, count - 1); } return (t0, t1, t2, t3); } var (t4, t5, t6, t7) = L2(""A"", 5); return (t4, t5, t6); } L1(""B""); } } class Program { public static void Main() { var c = new C<string>(""C""); c.M(""D""); } }"; CompileAndVerify(src, expectedOutput: "CDBACDBACDBACDBACDBA"); } [Fact] [WorkItem(19119, "https://github.com/dotnet/roslyn/issues/19119")] public void StructFrameInitUnnecessary() { var c = CompileAndVerify(@" class Program { static void Main(string[] args) { int q = 1; if (q > 0) { int w = 2; if (w > 0) { int e = 3; if (e > 0) { void Print() => System.Console.WriteLine(q + w + e); Print(); } } } } }", expectedOutput: "6"); //NOTE: the following code should not have "initobj" instructions. c.VerifyIL("Program.Main", @" { // Code size 63 (0x3f) .maxstack 3 .locals init (Program.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0 Program.<>c__DisplayClass0_1 V_1, //CS$<>8__locals1 Program.<>c__DisplayClass0_2 V_2) //CS$<>8__locals2 IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: stfld ""int Program.<>c__DisplayClass0_0.q"" IL_0008: ldloc.0 IL_0009: ldfld ""int Program.<>c__DisplayClass0_0.q"" IL_000e: ldc.i4.0 IL_000f: ble.s IL_003e IL_0011: ldloca.s V_1 IL_0013: ldc.i4.2 IL_0014: stfld ""int Program.<>c__DisplayClass0_1.w"" IL_0019: ldloc.1 IL_001a: ldfld ""int Program.<>c__DisplayClass0_1.w"" IL_001f: ldc.i4.0 IL_0020: ble.s IL_003e IL_0022: ldloca.s V_2 IL_0024: ldc.i4.3 IL_0025: stfld ""int Program.<>c__DisplayClass0_2.e"" IL_002a: ldloc.2 IL_002b: ldfld ""int Program.<>c__DisplayClass0_2.e"" IL_0030: ldc.i4.0 IL_0031: ble.s IL_003e IL_0033: ldloca.s V_0 IL_0035: ldloca.s V_1 IL_0037: ldloca.s V_2 IL_0039: call ""void Program.<Main>g__Print|0_0(ref Program.<>c__DisplayClass0_0, ref Program.<>c__DisplayClass0_1, ref Program.<>c__DisplayClass0_2)"" IL_003e: ret } "); } [Fact] public void LocalFunctionAttribute() { var source = @" class A : System.Attribute { } class C { public void M() { [A] void local1() { } [return: A] void local2() { } void local3([A] int i) { } void local4<[A] T>() { } } } "; CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal( expected: new[] { "CompilerGeneratedAttribute", "A" }, actual: GetAttributeNames(attrs1)); var localFn2 = cClass.GetMethod("<M>g__local2|0_1"); var attrs2 = localFn2.GetReturnTypeAttributes(); Assert.Equal("A", attrs2.Single().AttributeClass.Name); var localFn3 = cClass.GetMethod("<M>g__local3|0_2"); var attrs3 = localFn3.GetParameters().Single().GetAttributes(); Assert.Equal("A", attrs3.Single().AttributeClass.Name); var localFn4 = cClass.GetMethod("<M>g__local4|0_3"); var attrs4 = localFn4.TypeParameters.Single().GetAttributes(); Assert.Equal("A", attrs4.Single().AttributeClass.Name); } } [Fact] public void LocalFunctionAttribute_Complex() { var source = @" class A1 : System.Attribute { } class A2 : System.Attribute { internal A2(int i, string s) { } } class A3 : System.Attribute { internal A3(params int[] values) { } } class C { public void M() { [A1, A2(1, ""hello"")] [A3(1, 2, 3, 4, 5)] void local1() { } } } "; CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs = localFn1.GetAttributes(); Assert.Equal( expected: new[] { "CompilerGeneratedAttribute", "A1", "A2", "A3" }, actual: GetAttributeNames(attrs)); Assert.Empty(attrs[0].ConstructorArguments); Assert.Empty(attrs[1].ConstructorArguments); Assert.Equal(new object[] { 1, "hello" }, attrs[2].ConstructorArguments.Select(a => a.Value)); var attr3Args = attrs[3].ConstructorArguments.Single().Values; Assert.Equal(new object[] { 1, 2, 3, 4, 5 }, attr3Args.Select(a => a.Value)); } } [Fact] public void LocalFunctionAttributeArgument() { var source = @" class A : System.Attribute { internal A(int i) { } } class C { public void M() { [A(42)] void local1() { } } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal( expected: new[] { "CompilerGeneratedAttribute", "A" }, actual: GetAttributeNames(attrs1)); var arg = attrs1[1].ConstructorArguments.Single(); Assert.Equal(42, arg.Value); } } [Fact] public void LocalFunction_EmitNullableAttribute() { var source = @" #nullable enable class C { public void M() { string? local1(string? s) => s; } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); AssertEx.Equal(new[] { "NullableContextAttribute", "CompilerGeneratedAttribute" }, attrs1.Select(a => a.AttributeClass.Name)); Assert.Empty(localFn1.GetReturnTypeAttributes()); Assert.Equal(NullableAnnotation.Annotated, localFn1.ReturnTypeWithAnnotations.NullableAnnotation); var param = localFn1.Parameters.Single(); Assert.Empty(param.GetAttributes()); Assert.Equal(NullableAnnotation.Annotated, param.TypeWithAnnotations.NullableAnnotation); } } [Fact] public void LocalFunctionDynamicAttribute() { var source = @" class C { public void M() { dynamic local1(dynamic d) => d; } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal("CompilerGeneratedAttribute", attrs1.Single().AttributeClass.Name); Assert.Equal("DynamicAttribute", localFn1.GetReturnTypeAttributes().Single().AttributeClass.Name); var param = localFn1.Parameters.Single(); Assert.Equal("DynamicAttribute", param.GetAttributes().Single().AttributeClass.Name); } } [Fact] public void LocalFunctionParamsArray_NoParamArrayAttribute() { var source = @" class C { void M() { #pragma warning disable 8321 void local1(params int[] arr) { } } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal("CompilerGeneratedAttribute", attrs1.Single().AttributeClass.Name); Assert.Empty(localFn1.GetReturnTypeAttributes()); var param = localFn1.Parameters.Single(); Assert.Empty(param.GetAttributes()); } } [Fact] public void LocalFunction_ConditionalAttributeDisallowed() { var source = @" using System.Diagnostics; class C { void M() { #pragma warning disable 8321 // Unreferenced local function [Conditional(""DEBUG"")] // 1 void local1() { } } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( // (9,10): error CS8764: Local function 'local1()' must be 'static' in order to use the Conditional attribute // [Conditional("DEBUG")] // 1 Diagnostic(ErrorCode.ERR_ConditionalOnLocalFunction, @"Conditional(""DEBUG"")").WithArguments("local1()").WithLocation(9, 10)); } [Fact] public void StaticLocalFunction_ConditionalAttribute_Errors() { var source = @" using System.Diagnostics; class C { void M() { #pragma warning disable 8321 // Unreferenced local function [Conditional(""hello world"")] // 1 static void local1() { } [Conditional(""DEBUG"")] // 2 static int local2() { return 42; } [Conditional(""DEBUG"")] // 3 static void local3(out string s) { s = ""hello""; } } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( // (9,22): error CS0633: The argument to the 'Conditional' attribute must be a valid identifier // [Conditional("hello world")] // 1 Diagnostic(ErrorCode.ERR_BadArgumentToAttribute, @"""hello world""").WithArguments("Conditional").WithLocation(9, 22), // (12,10): error CS0578: The Conditional attribute is not valid on 'local2()' because its return type is not void // [Conditional("DEBUG")] // 2 Diagnostic(ErrorCode.ERR_ConditionalMustReturnVoid, @"Conditional(""DEBUG"")").WithArguments("local2()").WithLocation(12, 10), // (18,10): error CS0685: Conditional member 'local3(out string)' cannot have an out parameter // [Conditional("DEBUG")] // 3 Diagnostic(ErrorCode.ERR_ConditionalWithOutParam, @"Conditional(""DEBUG"")").WithArguments("local3(out string)").WithLocation(18, 10)); } [Fact] public void StaticLocalFunction_ConditionalAttribute() { var source = @" using System.Diagnostics; using System; class C { static void Main() { local1(); [Conditional(""DEBUG"")] static void local1() { Console.Write(""hello""); } } } "; CompileAndVerify( source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9.WithPreprocessorSymbols("DEBUG"), symbolValidator: validate, expectedOutput: "hello"); CompileAndVerify( source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate, expectedOutput: ""); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<Main>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal(new[] { "CompilerGeneratedAttribute", "ConditionalAttribute" }, GetAttributeNames(attrs1)); } } [Fact] public void StaticLocalFunction_ConditionalAttribute_NoUnreferencedWarning() { var source = @" using System.Diagnostics; using System; class C { static void Main() { local1(); [Conditional(""DEBUG"")] static void local1() { Console.Write(""hello""); } } } "; CreateCompilation(source, parseOptions: TestOptions.Regular9).VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular9.WithPreprocessorSymbols("DEBUG")).VerifyDiagnostics(); } [Fact] public void StaticLocalFunction_IfDirective_Unreferenced() { var source = @" using System; class C { static void Main() { #if DEBUG local1(); #endif static void local1() { Console.Write(""hello""); } } } "; CreateCompilation(source, parseOptions: TestOptions.Regular9).VerifyDiagnostics( // (11,21): warning CS8321: The local function 'local1' is declared but never used // static void local1() // 1 Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local1").WithArguments("local1").WithLocation(11, 21)); CreateCompilation(source, parseOptions: TestOptions.Regular9.WithPreprocessorSymbols("DEBUG")).VerifyDiagnostics(); } [Fact] public void LocalFunction_AttributeMarkedConditional() { var source = @" using System.Diagnostics; using System; [Conditional(""DEBUG"")] class Attr : Attribute { } class C { void M() { #pragma warning disable 8321 [Attr] [return: Attr] T local1<[Attr] T>([Attr] T t) => t; } } "; CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9.WithPreprocessorSymbols("DEBUG"), symbolValidator: validate1); CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate2); static void validate1(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal(new[] { "CompilerGeneratedAttribute", "Attr" }, GetAttributeNames(attrs1)); Assert.Equal(new[] { "Attr" }, GetAttributeNames(localFn1.GetReturnTypeAttributes())); Assert.Equal(new[] { "Attr" }, GetAttributeNames(localFn1.TypeParameters.Single().GetAttributes())); Assert.Equal(new[] { "Attr" }, GetAttributeNames(localFn1.Parameters.Single().GetAttributes())); } static void validate2(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes(); Assert.Equal(new[] { "CompilerGeneratedAttribute" }, GetAttributeNames(attrs1)); Assert.Empty(localFn1.GetReturnTypeAttributes()); Assert.Empty(localFn1.TypeParameters.Single().GetAttributes()); Assert.Empty(localFn1.Parameters.Single().GetAttributes()); } } [ConditionalFact(typeof(DesktopOnly))] public void LocalFunctionAttribute_TypeIL() { var source = @" class A : System.Attribute { internal A(int i) { } } class C { public void M() { [A(42)] void local1() { } } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9); verifier.VerifyTypeIL("C", @" .class private auto ansi beforefieldinit C extends [mscorlib]System.Object { // Methods .method public hidebysig instance void M () cil managed { // Method begins at RVA 0x205a // Code size 3 (0x3) .maxstack 8 IL_0000: nop IL_0001: nop IL_0002: ret } // end of method C::M .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x205e // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method C::.ctor .method assembly hidebysig static void '<M>g__local1|0_0' () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void A::.ctor(int32) = ( 01 00 2a 00 00 00 00 00 ) // Method begins at RVA 0x2067 // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method C::'<M>g__local1|0_0' } // end of class C"); } [Fact] public void ExternLocalFunction() { var source = @" using System.Runtime.InteropServices; class C { public void M() { local1(); [DllImport(""something.dll"")] static extern void local1(); } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate, verify: Verification.Skipped); var comp = verifier.Compilation; var syntaxTree = comp.SyntaxTrees.Single(); var semanticModel = comp.GetSemanticModel(syntaxTree); var localFunction = semanticModel .GetDeclaredSymbol(syntaxTree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single()) .GetSymbol<LocalFunctionSymbol>(); Assert.Equal(new[] { "DllImportAttribute" }, GetAttributeNames(localFunction.GetAttributes())); validateLocalFunction(localFunction); void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes().As<CSharpAttributeData>(); Assert.Equal( expected: new[] { "CompilerGeneratedAttribute" }, actual: GetAttributeNames(attrs1)); validateLocalFunction(localFn1); } static void validateLocalFunction(MethodSymbol localFunction) { Assert.True(localFunction.IsExtern); var importData = localFunction.GetDllImportData(); Assert.NotNull(importData); Assert.Equal("something.dll", importData.ModuleName); Assert.Equal("local1", importData.EntryPointName); Assert.Equal(CharSet.None, importData.CharacterSet); Assert.False(importData.SetLastError); Assert.False(importData.ExactSpelling); Assert.Equal(MethodImplAttributes.PreserveSig, localFunction.ImplementationAttributes); Assert.Equal(CallingConvention.Winapi, importData.CallingConvention); Assert.Null(importData.BestFitMapping); Assert.Null(importData.ThrowOnUnmappableCharacter); } } [Fact] public void ExternLocalFunction_ComplexDllImport() { var source = @" using System.Runtime.InteropServices; class C { public void M() { local1(); [DllImport( ""something.dll"", EntryPoint = ""a"", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true, PreserveSig = false, CallingConvention = CallingConvention.Cdecl, BestFitMapping = false, ThrowOnUnmappableChar = true)] static extern void local1(); } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate, verify: Verification.Skipped); var comp = verifier.Compilation; var syntaxTree = comp.SyntaxTrees.Single(); var semanticModel = comp.GetSemanticModel(syntaxTree); var localFunction = semanticModel .GetDeclaredSymbol(syntaxTree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single()) .GetSymbol<LocalFunctionSymbol>(); Assert.Equal(new[] { "DllImportAttribute" }, GetAttributeNames(localFunction.GetAttributes())); validateLocalFunction(localFunction); void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); var attrs1 = localFn1.GetAttributes().As<CSharpAttributeData>(); Assert.Equal(new[] { "CompilerGeneratedAttribute" }, GetAttributeNames(attrs1)); validateLocalFunction(localFn1); } static void validateLocalFunction(MethodSymbol localFunction) { Assert.True(localFunction.IsExtern); var importData = localFunction.GetDllImportData(); Assert.NotNull(importData); Assert.Equal("something.dll", importData.ModuleName); Assert.Equal("a", importData.EntryPointName); Assert.Equal(CharSet.Ansi, importData.CharacterSet); Assert.True(importData.SetLastError); Assert.True(importData.ExactSpelling); Assert.Equal(MethodImplAttributes.IL, localFunction.ImplementationAttributes); Assert.Equal(CallingConvention.Cdecl, importData.CallingConvention); Assert.False(importData.BestFitMapping); Assert.True(importData.ThrowOnUnmappableCharacter); } } [Fact] public void LocalFunction_MethodImpl() { var source = @" using System.Runtime.CompilerServices; class C { void M() { #pragma warning disable 8321 // Unreferenced local function [MethodImpl(MethodImplOptions.ForwardRef)] static void forwardRef() { System.Console.WriteLine(0); } [MethodImpl(MethodImplOptions.NoInlining)] static void noInlining() { System.Console.WriteLine(1); } [MethodImpl(MethodImplOptions.NoOptimization)] static void noOptimization() { System.Console.WriteLine(2); } [MethodImpl(MethodImplOptions.Synchronized)] static void synchronized() { System.Console.WriteLine(3); } [MethodImpl(MethodImplOptions.InternalCall)] extern static void internalCallStatic(); } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, assemblyValidator: validateAssembly, verify: Verification.Skipped); var comp = verifier.Compilation; var syntaxTree = comp.SyntaxTrees.Single(); var semanticModel = comp.GetSemanticModel(syntaxTree); var localFunctions = syntaxTree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().ToList(); checkImplAttributes(localFunctions[0], MethodImplAttributes.ForwardRef); checkImplAttributes(localFunctions[1], MethodImplAttributes.NoInlining); checkImplAttributes(localFunctions[2], MethodImplAttributes.NoOptimization); checkImplAttributes(localFunctions[3], MethodImplAttributes.Synchronized); checkImplAttributes(localFunctions[4], MethodImplAttributes.InternalCall); void checkImplAttributes(LocalFunctionStatementSyntax localFunctionStatement, MethodImplAttributes expectedFlags) { var localFunction = semanticModel.GetDeclaredSymbol(localFunctionStatement).GetSymbol<LocalFunctionSymbol>(); Assert.Equal(expectedFlags, localFunction.ImplementationAttributes); } void validateAssembly(PEAssembly assembly) { var peReader = assembly.GetMetadataReader(); foreach (var methodHandle in peReader.MethodDefinitions) { var methodDef = peReader.GetMethodDefinition(methodHandle); var actualFlags = methodDef.ImplAttributes; var methodName = peReader.GetString(methodDef.Name); var expectedFlags = methodName switch { "<M>g__forwardRef|0_0" => MethodImplAttributes.ForwardRef, "<M>g__noInlining|0_1" => MethodImplAttributes.NoInlining, "<M>g__noOptimization|0_2" => MethodImplAttributes.NoOptimization, "<M>g__synchronized|0_3" => MethodImplAttributes.Synchronized, "<M>g__internalCallStatic|0_4" => MethodImplAttributes.InternalCall, ".ctor" => MethodImplAttributes.IL, "M" => MethodImplAttributes.IL, _ => throw TestExceptionUtilities.UnexpectedValue(methodName) }; Assert.Equal(expectedFlags, actualFlags); } } } [Fact] public void LocalFunction_SpecialName() { var source = @" using System.Runtime.CompilerServices; class C { void M() { #pragma warning disable 8321 // Unreferenced local function [SpecialName] void local1() { } } } "; var verifier = CompileAndVerify( source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular9, symbolValidator: validate); static void validate(ModuleSymbol module) { var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var localFn1 = cClass.GetMethod("<M>g__local1|0_0"); Assert.True(localFn1.HasSpecialName); } } [Fact] [WorkItem(57325, "https://github.com/dotnet/roslyn/issues/57325")] public void InOutAttributes() { var source = @" using System.Runtime.InteropServices; class State { public bool B; } class Program { static void M([In] [Out] State state) { local(state); static void local([In] [Out] State state) { } } } "; var verifier = CompileAndVerify( source, targetFramework: TargetFramework.StandardAndCSharp, symbolValidator: validateMetadata, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); var methodParam = ((CSharpCompilation)verifier.Compilation).GetMember<MethodSymbol>("Program.M").Parameters[0]; Assert.True(methodParam.IsMetadataIn); Assert.True(methodParam.IsMetadataOut); var localFunctionParam = verifier.FindLocalFunction("local").Parameters[0].GetSymbol<ParameterSymbol>(); Assert.True(localFunctionParam.IsMetadataIn); Assert.True(localFunctionParam.IsMetadataOut); void validateMetadata(ModuleSymbol module) { var methodParam = module.GlobalNamespace.GetMember<MethodSymbol>("Program.M").Parameters[0]; Assert.True(methodParam.IsMetadataIn); Assert.True(methodParam.IsMetadataOut); var localFunctionParam = module.GlobalNamespace.GetMember<MethodSymbol>("Program.<M>g__local|0_0").Parameters[0]; Assert.True(localFunctionParam.IsMetadataIn); Assert.True(localFunctionParam.IsMetadataOut); } } [Theory] [InlineData("[In] ")] [InlineData("[Attr] ")] [InlineData("[In] [Attr] ")] [InlineData("")] [WorkItem(57325, "https://github.com/dotnet/roslyn/issues/57325")] public void IsMetadataIn_UsingModifierInSource(string attributes) { var source = $$""" using System.Runtime.InteropServices; using System; class Attr : Attribute { } class State { public bool B; } class Program { static void M({{attributes}}in State state) { local(in state); static void local({{attributes}}in State state) { } } } """; var verifier = CompileAndVerify( source, targetFramework: TargetFramework.StandardAndCSharp, symbolValidator: validateMetadata, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); var methodParam = ((CSharpCompilation)verifier.Compilation).GetMember<MethodSymbol>("Program.M").Parameters[0]; Assert.True(methodParam.IsMetadataIn); Assert.False(methodParam.IsMetadataOut); var localFunctionParam = verifier.FindLocalFunction("local").Parameters[0].GetSymbol<ParameterSymbol>(); Assert.True(localFunctionParam.IsMetadataIn); Assert.False(localFunctionParam.IsMetadataOut); void validateMetadata(ModuleSymbol module) { var methodParam = module.GlobalNamespace.GetMember<MethodSymbol>("Program.M").Parameters[0]; Assert.True(methodParam.IsMetadataIn); Assert.False(methodParam.IsMetadataOut); var localFunctionParam = module.GlobalNamespace.GetMember<MethodSymbol>("Program.<M>g__local|0_0").Parameters[0]; Assert.True(localFunctionParam.IsMetadataIn); Assert.False(localFunctionParam.IsMetadataOut); } } [Theory] [InlineData("[Out] ")] [InlineData("[Attr] ")] [InlineData("[Out] [Attr] ")] [InlineData("")] [WorkItem(57325, "https://github.com/dotnet/roslyn/issues/57325")] public void IsMetadataOut_UsingModifierInSource(string attributes) { var source = $$""" using System.Runtime.InteropServices; using System; class Attr : Attribute { } class State { public bool B; } class Program { static void M({{attributes}}out State state) { local(out state); static void local({{attributes}}out State state) { state = null!; } } } """; var verifier = CompileAndVerify( source, targetFramework: TargetFramework.StandardAndCSharp, symbolValidator: validateMetadata, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); var methodParam = ((CSharpCompilation)verifier.Compilation).GetMember<MethodSymbol>("Program.M").Parameters[0]; Assert.False(methodParam.IsMetadataIn); Assert.True(methodParam.IsMetadataOut); var localFunctionParam = verifier.FindLocalFunction("local").Parameters[0].GetSymbol<ParameterSymbol>(); Assert.False(localFunctionParam.IsMetadataIn); Assert.True(localFunctionParam.IsMetadataOut); void validateMetadata(ModuleSymbol module) { var methodParam = module.GlobalNamespace.GetMember<MethodSymbol>("Program.M").Parameters[0]; Assert.False(methodParam.IsMetadataIn); Assert.True(methodParam.IsMetadataOut); var localFunctionParam = module.GlobalNamespace.GetMember<MethodSymbol>("Program.<M>g__local|0_0").Parameters[0]; Assert.False(localFunctionParam.IsMetadataIn); Assert.True(localFunctionParam.IsMetadataOut); } } [Fact] [WorkItem(49599, "https://github.com/dotnet/roslyn/issues/49599")] public void MultipleLocalFunctionsUsingDynamic_01() { var source = @" public class Program { static void Main() { Local1<object>(); Local1<object>(); static void Local1<T>() { System.Console.Write(Local2(Local3())); static string Local2(dynamic n) => n.ToString(); } static int Local3() => (int)(dynamic)4; } } "; CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, expectedOutput: "44"); } [Fact] [WorkItem(49599, "https://github.com/dotnet/roslyn/issues/49599")] public void MultipleLocalFunctionsUsingDynamic_02() { var source = @" public class Program { static void Main() { Local1<object>(); Local1<object>(); static void Local1<T>() { System.Console.Write(Local2(Local3<object>())); static string Local2(dynamic n) => n.ToString(); } static int Local3<S>() => (int)(dynamic)4; } } "; CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, expectedOutput: "44"); } [Fact] [WorkItem(49599, "https://github.com/dotnet/roslyn/issues/49599")] public void MultipleLocalFunctionsUsingDynamic_03() { var source = @" public class Program { static void Main() { Local1<object>(); Local1<object>(); static void Local1<T>() { System.Console.Write(Local2<object>(Local3<object>())); static string Local2<U>(dynamic n) => n.ToString(); } static int Local3<S>() => (int)(dynamic)4; } } "; CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, expectedOutput: "44"); } [Fact] [WorkItem(49599, "https://github.com/dotnet/roslyn/issues/49599")] public void MultipleLocalFunctionsUsingDynamic_04() { var source = @" public class Program { static void Main() { Local1<object>(); Local1<object>(); static void Local1<T>() { System.Console.Write(Local1<object>(Local3<object>())); static string Local1<U>(dynamic n) => n.ToString(); } static int Local3<S>() => (int)(dynamic)4; } } "; CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, expectedOutput: "44"); } [Fact] [WorkItem(49599, "https://github.com/dotnet/roslyn/issues/49599")] public void MultipleLocalFunctionsUsingDynamic_05() { var source = @" public class Program { static void Main() { Local1<object>(); Local1<object>(); static void Local1<T>() { System.Console.Write(Local2<object>(Local3<object>())); static string Local2<U>(dynamic n) => n.ToString(); } static int Local2<S>() => (int)(dynamic)4; static int Local3<S>() => (int)(dynamic)4; } } "; CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, expectedOutput: "44"); } [Theory, CombinatorialData] public void PropagateAttributes_01(bool withPreserve) { // attributes on local function var src = $$""" public class C { public void M() { local(0); [return: A] [B] void local([C] int i) { } } } {{(withPreserve ? "[System.Runtime.CompilerServices.CompilerLoweringPreserve]" : "")}} public class AAttribute : System.Attribute { } {{(withPreserve ? "[System.Runtime.CompilerServices.CompilerLoweringPreserve]" : "")}} public class BAttribute : System.Attribute { } {{(withPreserve ? "[System.Runtime.CompilerServices.CompilerLoweringPreserve]" : "")}} public class CAttribute : System.Attribute { } """; var verifier = CompileAndVerify([src, CompilerLoweringPreserveAttributeDefinition]).VerifyDiagnostics(); verifier.VerifyTypeIL("C", """ .class public auto ansi beforefieldinit C extends [mscorlib]System.Object { // Methods .method public hidebysig instance void M () cil managed { // Method begins at RVA 0x2067 // Code size 7 (0x7) .maxstack 8 IL_0000: ldc.i4.0 IL_0001: call void C::'<M>g__local|0_0'(int32) IL_0006: ret } // end of method C::M .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x206f // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method C::.ctor .method assembly hidebysig static void '<M>g__local|0_0' ( int32 i ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void BAttribute::.ctor() = ( 01 00 00 00 ) .param [0] .custom instance void AAttribute::.ctor() = ( 01 00 00 00 ) .param [1] .custom instance void CAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2077 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method C::'<M>g__local|0_0' } // end of class C """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } internal CompilationVerifier VerifyOutput(string source, string output, CSharpCompilationOptions options, Verification verify = default) { var comp = CreateCompilationWithMscorlib461AndCSharp(source, options: options); return CompileAndVerify(comp, expectedOutput: output, verify: verify).VerifyDiagnostics(); // no diagnostics } internal CompilationVerifier VerifyOutput(string source, string output) { var comp = CreateCompilationWithMscorlib461AndCSharp(source, options: TestOptions.ReleaseExe); return CompileAndVerify(comp, expectedOutput: output).VerifyDiagnostics(); // no diagnostics } internal CompilationVerifier VerifyOutputInMain(string methodBody, string output, params string[] usings) { for (var i = 0; i < usings.Length; i++) { usings[i] = "using " + usings[i] + ";"; } var usingBlock = string.Join(Environment.NewLine, usings); var source = usingBlock + @" class Program { static void Main() { " + methodBody + @" } }"; return VerifyOutput(source, output); } } }