|
// 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(57325, "https://github.com/dotnet/roslyn/issues/57325")]
public void BaseParameterWithDifferentRefKind()
{
var source = $$"""
using System;
class Attr : Attribute { }
public class State
{
public bool B;
}
static class Program
{
static void M()
{
local(new State());
static void local([Attr] in State state)
{
}
}
}
""";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
var localFunctionSyntax = tree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single();
var localFunction = model.GetDeclaredSymbol(localFunctionSyntax).GetSymbol<LocalFunctionSymbol>();
var param = localFunction.Parameters[0];
Assert.True(param.IsMetadataIn);
Assert.False(param.IsMetadataOut);
// Test a scenario where the baseParameterAttributes has a different RefKind than the synthesized parameter.
// We expect the RefKind of the base parameter to be ignored here.
var synthesizedParam = SynthesizedParameterSymbol.Create(localFunction, param.TypeWithAnnotations, ordinal: 0, RefKind.Out, param.Name, baseParameterForAttributes: (SourceComplexParameterSymbolBase)param);
Assert.False(synthesizedParam.IsMetadataIn);
Assert.True(synthesizedParam.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");
}
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);
}
}
}
|