/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs
10 179 строк
337 KB
Jared Parsons
Move net451 test assets to net461 (#74766)
17 авг 2024, 19:21
Не верифицирован
17 авг 2024, 19:21
75b26a2
Код
Авторство
О чём код?
// 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.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { [CompilerTrait(CompilerFeature.Tuples)] public class CodeGenDeconstructTests : CSharpTestBase { private static readonly MetadataReference[] s_valueTupleRefs = new[] { SystemRuntimeFacadeRef, ValueTupleRef }; const string commonSource = @"public class Pair<T1, T2> { T1 item1; T2 item2; public Pair(T1 item1, T2 item2) { this.item1 = item1; this.item2 = item2; } public void Deconstruct(out T1 item1, out T2 item2) { System.Console.WriteLine($""Deconstructing {ToString()}""); item1 = this.item1; item2 = this.item2; } public override string ToString() { return $""({item1.ToString()}, {item2.ToString()})""; } } public static class Pair { public static Pair<T1, T2> Create<T1, T2>(T1 item1, T2 item2) { return new Pair<T1, T2>(item1, item2); } } public class Integer { public int state; public override string ToString() { return state.ToString(); } public Integer(int i) { state = i; } public static implicit operator LongInteger(Integer i) { System.Console.WriteLine($""Converting {i}""); return new LongInteger(i.state); } } public class LongInteger { long state; public LongInteger(long l) { state = l; } public override string ToString() { return state.ToString(); } }"; [Fact] public void SimpleAssign() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal(@"(x, y)", lhs.ToString()); Assert.Equal("(System.Int64 x, System.String y)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Equal("(System.Int64 x, System.String y)", model.GetTypeInfo(lhs).ConvertedType.ToTestDisplayString()); var right = tree.GetRoot().DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single(); Assert.Equal(@"new C()", right.ToString()); Assert.Equal("C", model.GetTypeInfo(right).Type.ToTestDisplayString()); Assert.Equal("C", model.GetTypeInfo(right).ConvertedType.ToTestDisplayString()); Assert.Equal(ConversionKind.Identity, model.GetConversion(right).Kind); }; var comp = CompileAndVerifyWithMscorlib40(source, expectedOutput: "1 hello", references: s_valueTupleRefs, sourceSymbolValidator: validator); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 43 (0x2b) .maxstack 3 .locals init (long V_0, //x string V_1, //y int V_2, string V_3) IL_0000: newobj ""C..ctor()"" IL_0005: ldloca.s V_2 IL_0007: ldloca.s V_3 IL_0009: callvirt ""void C.Deconstruct(out int, out string)"" IL_000e: ldloc.2 IL_000f: conv.i8 IL_0010: stloc.0 IL_0011: ldloc.3 IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call ""string long.ToString()"" IL_001a: ldstr "" "" IL_001f: ldloc.1 IL_0020: call ""string string.Concat(string, string, string)"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ret }"); } [Fact] public void ObsoleteDeconstructMethod() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); foreach (var (z1, z2) in new[] { new C() }) { } } [System.Obsolete] public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (9,18): warning CS0612: 'C.Deconstruct(out int, out string)' is obsolete // (x, y) = new C(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()").WithArguments("C.Deconstruct(out int, out string)").WithLocation(9, 18), // (10,34): warning CS0612: 'C.Deconstruct(out int, out string)' is obsolete // foreach (var (z1, z2) in new[] { new C() }) { } Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new[] { new C() }").WithArguments("C.Deconstruct(out int, out string)").WithLocation(10, 34) ); } [Fact] [WorkItem(13632, "https://github.com/dotnet/roslyn/issues/13632")] public void SimpleAssignWithoutConversion() { string source = @" class C { static void Main() { int x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 42 (0x2a) .maxstack 3 .locals init (int V_0, //x string V_1, //y int V_2, string V_3) IL_0000: newobj ""C..ctor()"" IL_0005: ldloca.s V_2 IL_0007: ldloca.s V_3 IL_0009: callvirt ""void C.Deconstruct(out int, out string)"" IL_000e: ldloc.2 IL_000f: stloc.0 IL_0010: ldloc.3 IL_0011: stloc.1 IL_0012: ldloca.s V_0 IL_0014: call ""string int.ToString()"" IL_0019: ldstr "" "" IL_001e: ldloc.1 IL_001f: call ""string string.Concat(string, string, string)"" IL_0024: call ""void System.Console.WriteLine(string)"" IL_0029: ret }"); } [Fact] public void DeconstructMethodAmbiguous() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } public void Deconstruct(out int a) { a = 2; } }"; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); var tree = comp.Compilation.SyntaxTrees.First(); var model = comp.Compilation.GetSemanticModel(tree); var deconstruction = (AssignmentExpressionSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.SimpleAssignmentExpression).AsNode(); Assert.Equal("(x, y) = new C()", deconstruction.ToString()); var deconstructionInfo = model.GetDeconstructionInfo(deconstruction); var firstDeconstructMethod = ((CSharpCompilation)comp.Compilation).GetTypeByMetadataName("C").GetMembers(WellKnownMemberNames.DeconstructMethodName) .OfType<SourceOrdinaryMethodSymbol>().Where(m => m.ParameterCount == 2).Single(); Assert.Equal(firstDeconstructMethod.GetPublicSymbol(), deconstructionInfo.Method); Assert.Equal("void C.Deconstruct(out System.Int32 a, out System.String b)", deconstructionInfo.Method.ToTestDisplayString()); Assert.Null(deconstructionInfo.Conversion); var nested = deconstructionInfo.Nested; Assert.Equal(2, nested.Length); Assert.Null(nested[0].Method); Assert.Equal(ConversionKind.ImplicitNumeric, nested[0].Conversion.Value.Kind); Assert.Empty(nested[0].Nested); Assert.Null(nested[1].Method); Assert.Equal(ConversionKind.Identity, nested[1].Conversion.Value.Kind); Assert.Empty(nested[1].Nested); var assignment = (AssignmentExpressionSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.SimpleAssignmentExpression, occurrence: 2).AsNode(); Assert.Equal("a = 1", assignment.ToString()); var defaultInfo = model.GetDeconstructionInfo(assignment); Assert.Null(defaultInfo.Method); Assert.Empty(defaultInfo.Nested); Assert.Equal(ConversionKind.UnsetConversionKind, defaultInfo.Conversion.Value.Kind); } [Fact] [WorkItem(27520, "https://github.com/dotnet/roslyn/issues/27520")] public void GetDeconstructionInfoOnIncompleteCode() { string source = @" class C { static void M(string s) { foreach (char in s) { } } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,18): error CS1525: Invalid expression term 'char' // foreach (char in s) { } Diagnostic(ErrorCode.ERR_InvalidExprTerm, "char").WithArguments("char").WithLocation(6, 18), // (6,23): error CS0230: Type and identifier are both required in a foreach statement // foreach (char in s) { } Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 23) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var foreachDeconstruction = (ForEachVariableStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.ForEachVariableStatement).AsNode(); Assert.Equal(@"foreach (char in s) { }", foreachDeconstruction.ToString()); var deconstructionInfo = model.GetDeconstructionInfo(foreachDeconstruction); Assert.Equal(Conversion.UnsetConversion, deconstructionInfo.Conversion); Assert.Null(deconstructionInfo.Method); Assert.Empty(deconstructionInfo.Nested); } [Fact] [WorkItem(15634, "https://github.com/dotnet/roslyn/issues/15634")] public void DeconstructMustReturnVoid() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); } public int Deconstruct(out int a, out string b) { a = 1; b = ""hello""; return 42; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,18): error CS8129: No Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // (x, y) = new C(); Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 18) ); } [Fact] public void VerifyExecutionOrder_Deconstruct() { string source = @" using System; class C { int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } C getDeconstructReceiver() { Console.WriteLine(""getDeconstructReceiver""); return this; } static void Main() { C c = new C(); (c.getHolderForX().x, c.getHolderForY().y) = c.getDeconstructReceiver(); } public void Deconstruct(out D1 x, out D2 y) { x = new D1(); y = new D2(); Console.WriteLine(""Deconstruct""); } } class D1 { public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public static implicit operator int(D2 d) { Console.WriteLine(""Conversion2""); return 2; } } "; string expected = @"getHolderforX getHolderforY getDeconstructReceiver Deconstruct Conversion1 Conversion2 setX setY "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyExecutionOrder_Deconstruct_Conditional() { string source = @" using System; class C { int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } C getDeconstructReceiver() { Console.WriteLine(""getDeconstructReceiver""); return this; } static void Main() { C c = new C(); bool b = true; (c.getHolderForX().x, c.getHolderForY().y) = b ? c.getDeconstructReceiver() : default; } public void Deconstruct(out D1 x, out D2 y) { x = new D1(); y = new D2(); Console.WriteLine(""Deconstruct""); } } class D1 { public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public static implicit operator int(D2 d) { Console.WriteLine(""Conversion2""); return 2; } } "; string expected = @"getHolderforX getHolderforY getDeconstructReceiver Deconstruct Conversion1 Conversion2 setX setY "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyExecutionOrder_TupleLiteral() { string source = @" using System; class C { int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } static void Main() { C c = new C(); (c.getHolderForX().x, c.getHolderForY().y) = (new D1(), new D2()); } } class D1 { public D1() { Console.WriteLine(""Constructor1""); } public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public D2() { Console.WriteLine(""Constructor2""); } public static implicit operator int(D2 d) { Console.WriteLine(""Conversion2""); return 2; } } "; string expected = @"getHolderforX getHolderforY Constructor1 Conversion1 Constructor2 Conversion2 setX setY "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyExecutionOrder_TupleLiteral_Conditional() { string source = @" using System; class C { int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } static void Main() { C c = new C(); bool b = true; (c.getHolderForX().x, c.getHolderForY().y) = b ? (new D1(), new D2()) : default; } } class D1 { public D1() { Console.WriteLine(""Constructor1""); } public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public D2() { Console.WriteLine(""Constructor2""); } public static implicit operator int(D2 d) { Console.WriteLine(""Conversion2""); return 2; } } "; string expected = @"getHolderforX getHolderforY Constructor1 Constructor2 Conversion1 Conversion2 setX setY "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyExecutionOrder_TupleLiteralAndDeconstruction() { string source = @" using System; class C { int w { set { Console.WriteLine($""setW""); } } int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } int z { set { Console.WriteLine($""setZ""); } } C getHolderForW() { Console.WriteLine(""getHolderforW""); return this; } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } C getHolderForZ() { Console.WriteLine(""getHolderforZ""); return this; } static void Main() { C c = new C(); (c.getHolderForW().w, (c.getHolderForY().y, c.getHolderForZ().z), c.getHolderForX().x) = (new D1(), new D2(), new D3()); } } class D1 { public D1() { Console.WriteLine(""Constructor1""); } public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public D2() { Console.WriteLine(""Constructor2""); } public void Deconstruct(out int x, out int y) { x = 2; y = 3; Console.WriteLine(""deconstruct""); } } class D3 { public D3() { Console.WriteLine(""Constructor3""); } public static implicit operator int(D3 d) { Console.WriteLine(""Conversion3""); return 3; } } "; string expected = @"getHolderforW getHolderforY getHolderforZ getHolderforX Constructor1 Conversion1 Constructor2 Constructor3 Conversion3 deconstruct setW setY setZ setX "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyExecutionOrder_TupleLiteralAndDeconstruction_Conditional() { string source = @" using System; class C { int w { set { Console.WriteLine($""setW""); } } int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } int z { set { Console.WriteLine($""setZ""); } } C getHolderForW() { Console.WriteLine(""getHolderforW""); return this; } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } C getHolderForZ() { Console.WriteLine(""getHolderforZ""); return this; } static void Main() { C c = new C(); bool b = false; (c.getHolderForW().w, (c.getHolderForY().y, c.getHolderForZ().z), c.getHolderForX().x) = b ? default : (new D1(), new D2(), new D3()); } } class D1 { public D1() { Console.WriteLine(""Constructor1""); } public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public D2() { Console.WriteLine(""Constructor2""); } public void Deconstruct(out int x, out int y) { x = 2; y = 3; Console.WriteLine(""deconstruct""); } } class D3 { public D3() { Console.WriteLine(""Constructor3""); } public static implicit operator int(D3 d) { Console.WriteLine(""Conversion3""); return 3; } } "; string expected = @"getHolderforW getHolderforY getHolderforZ getHolderforX Constructor1 Constructor2 Constructor3 deconstruct Conversion1 Conversion3 setW setY setZ setX "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void DifferentVariableKinds() { string source = @" class C { int[] ArrayIndexer = new int[1]; string property; string Property { set { property = value; } } string AutoProperty { get; set; } static void Main() { C c = new C(); (c.ArrayIndexer[0], c.Property, c.AutoProperty) = new C(); System.Console.WriteLine(c.ArrayIndexer[0] + "" "" + c.property + "" "" + c.AutoProperty); } public void Deconstruct(out int a, out string b, out string c) { a = 1; b = ""hello""; c = ""world""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello world"); comp.VerifyDiagnostics(); } [Fact] public void Dynamic() { string source = @" class C { dynamic Dynamic1; dynamic Dynamic2; static void Main() { C c = new C(); (c.Dynamic1, c.Dynamic2) = c; System.Console.WriteLine(c.Dynamic1 + "" "" + c.Dynamic2); } public void Deconstruct(out int a, out dynamic b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void DeconstructInterfaceOnStruct() { string source = @" interface IDeconstructable { void Deconstruct(out int a, out string b); } struct C : IDeconstructable { string state; static void Main() { int x; string y; IDeconstructable c = new C() { state = ""initial"" }; System.Console.Write(c); (x, y) = c; System.Console.WriteLine("" "" + c + "" "" + x + "" "" + y); } void IDeconstructable.Deconstruct(out int a, out string b) { a = 1; b = ""hello""; state = ""modified""; } public override string ToString() { return state; } } "; var comp = CompileAndVerify(source, expectedOutput: "initial modified 1 hello", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void DeconstructMethodHasParams2() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b, params int[] c) // not a Deconstruct operator { a = 1; b = ""ignored""; } public void Deconstruct(out int a, out string b) { a = 2; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "2 hello"); comp.VerifyDiagnostics(); } [Fact] public void OutParamsDisallowed() { string source = @" class C { public void Deconstruct(out int a, out string b, out params int[] c) { a = 1; b = ""ignored""; c = new[] { 2, 2 }; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,58): error CS8328: The parameter modifier 'params' cannot be used with 'out' // public void Deconstruct(out int a, out string b, out params int[] c) Diagnostic(ErrorCode.ERR_BadParameterModifiers, "params").WithArguments("params", "out").WithLocation(4, 58)); } [Fact] public void DeconstructMethodHasArglist2() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } public void Deconstruct(out int a, out string b, __arglist) // not a Deconstruct operator { a = 2; b = ""ignored""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void DifferentStaticVariableKinds() { string source = @" class C { static int[] ArrayIndexer = new int[1]; static string property; static string Property { set { property = value; } } static string AutoProperty { get; set; } static void Main() { (C.ArrayIndexer[0], C.Property, C.AutoProperty) = new C(); System.Console.WriteLine(C.ArrayIndexer[0] + "" "" + C.property + "" "" + C.AutoProperty); } public void Deconstruct(out int a, out string b, out string c) { a = 1; b = ""hello""; c = ""world""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello world"); comp.VerifyDiagnostics(); } [Fact] public void DifferentVariableRefKinds() { string source = @" class C { static void Main() { long a = 1; int b; C.M(ref a, out b); System.Console.WriteLine(a + "" "" + b); } static void M(ref long a, out int b) { (a, b) = new C(); } public void Deconstruct(out int x, out byte y) { x = 2; y = (byte)3; } } "; var comp = CompileAndVerify(source, expectedOutput: "2 3"); comp.VerifyDiagnostics(); } [Fact] [CompilerTrait(CompilerFeature.RefLocalsReturns)] public void RefReturningMethod() { string source = @" class C { static int i = 0; static void Main() { (M(), M()) = new C(); System.Console.WriteLine($""Final i is {i}""); } static ref int M() { System.Console.WriteLine($""M (previous i is {i})""); return ref i; } void Deconstruct(out int x, out int y) { System.Console.WriteLine(""Deconstruct""); x = 42; y = 43; } } "; var expected = @"M (previous i is 0) M (previous i is 0) Deconstruct Final i is 43 "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact, CompilerTrait(CompilerFeature.RefLocalsReturns)] public void RefReturningProperty() { string source = @" class C { static int i = 0; static void Main() { (P, P) = new C(); System.Console.WriteLine($""Final i is {i}""); } static ref int P { get { System.Console.WriteLine($""P (previous i is {i})""); return ref i; } } void Deconstruct(out int x, out int y) { System.Console.WriteLine(""Deconstruct""); x = 42; y = 43; } } "; var expected = @"P (previous i is 0) P (previous i is 0) Deconstruct Final i is 43 "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] [CompilerTrait(CompilerFeature.RefLocalsReturns)] public void RefReturningMethodFlow() { string source = @" struct C { static C i; static C P { get { System.Console.WriteLine(""getP""); return i; } set { System.Console.WriteLine(""setP""); i = value; } } static void Main() { (M(), M()) = P; } static ref C M() { System.Console.WriteLine($""M (previous i is {i})""); return ref i; } void Deconstruct(out int x, out int y) { System.Console.WriteLine(""Deconstruct""); x = 42; y = 43; } public static implicit operator C(int x) { System.Console.WriteLine(""conversion""); return new C(); } } "; var expected = @"M (previous i is C) M (previous i is C) getP Deconstruct conversion conversion"; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void Indexers() { string source = @" class C { static SomeArray array; static void Main() { int y; (Goo()[Bar()], y) = new C(); System.Console.WriteLine($""Final array values[2] {array.values[2]}""); } static SomeArray Goo() { System.Console.WriteLine($""Goo""); array = new SomeArray(); return array; } static int Bar() { System.Console.WriteLine($""Bar""); return 2; } void Deconstruct(out int x, out int y) { System.Console.WriteLine(""Deconstruct""); x = 101; y = 102; } } class SomeArray { public int[] values; public SomeArray() { values = new [] { 42, 43, 44 }; } public int this[int index] { get { System.Console.WriteLine($""indexGet (with value {values[index]})""); return values[index]; } set { System.Console.WriteLine($""indexSet (with value {value})""); values[index] = value; } } } "; var expected = @"Goo Bar Deconstruct indexSet (with value 101) Final array values[2] 101 "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void AssigningTuple() { string source = @" class C { static void Main() { long x; string y; int i = 1; (x, y) = (i, ""hello""); System.Console.WriteLine(x + "" "" + y); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); var tree = comp.Compilation.SyntaxTrees.First(); var model = comp.Compilation.GetSemanticModel(tree); var deconstruction = tree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>().Single(); var deconstructionInfo = model.GetDeconstructionInfo(deconstruction); Assert.Null(deconstructionInfo.Method); Assert.Null(deconstructionInfo.Conversion); var nested = deconstructionInfo.Nested; Assert.Equal(2, nested.Length); Assert.Null(nested[0].Method); Assert.Equal(ConversionKind.Identity, nested[0].Conversion.Value.Kind); Assert.Empty(nested[0].Nested); Assert.Null(nested[1].Method); Assert.Equal(ConversionKind.Identity, nested[1].Conversion.Value.Kind); Assert.Empty(nested[1].Nested); var tuple = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(1); Assert.Equal(@"(i, ""hello"")", tuple.ToString()); var tupleConversion = model.GetConversion(tuple); Assert.Equal(ConversionKind.ImplicitTupleLiteral, tupleConversion.Kind); Assert.Equal(ConversionKind.ImplicitNumeric, tupleConversion.UnderlyingConversions[0].Kind); } [Fact] public void AssigningTupleWithConversion() { string source = @" class C { static void Main() { long x; string y; (x, y) = M(); System.Console.WriteLine(x + "" "" + y); } static System.ValueTuple<int, string> M() { return (1, ""hello""); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); } [Fact] public void AssigningLongTuple() { string source = @" class C { static void Main() { long x; int y; (x, x, x, x, x, x, x, x, x, y) = (1, 1, 1, 1, 1, 1, 1, 1, 4, 2); System.Console.WriteLine(string.Concat(x, "" "", y)); } } "; var comp = CompileAndVerify(source, expectedOutput: "4 2"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 31 (0x1f) .maxstack 3 .locals init (int V_0) //y IL_0000: ldc.i4.4 IL_0001: conv.i8 IL_0002: ldc.i4.2 IL_0003: stloc.0 IL_0004: box ""long"" IL_0009: ldstr "" "" IL_000e: ldloc.0 IL_000f: box ""int"" IL_0014: call ""string string.Concat(object, object, object)"" IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: ret }"); } [Fact] public void AssigningLongTupleWithNames() { string source = @" class C { static void Main() { long x; int y; (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); System.Console.WriteLine(x + "" "" + y); } } "; var comp = CompileAndVerify(source, expectedOutput: "9 10"); comp.VerifyDiagnostics( // (9,43): warning CS8123: The tuple element name 'a' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "a: 1").WithArguments("a", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 43), // (9,49): warning CS8123: The tuple element name 'b' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "b: 2").WithArguments("b", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 49), // (9,55): warning CS8123: The tuple element name 'c' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "c: 3").WithArguments("c", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 55), // (9,61): warning CS8123: The tuple element name 'd' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "d: 4").WithArguments("d", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 61), // (9,67): warning CS8123: The tuple element name 'e' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "e: 5").WithArguments("e", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 67), // (9,73): warning CS8123: The tuple element name 'f' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "f: 6").WithArguments("f", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 73), // (9,79): warning CS8123: The tuple element name 'g' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "g: 7").WithArguments("g", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 79), // (9,85): warning CS8123: The tuple element name 'h' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "h: 8").WithArguments("h", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 85), // (9,91): warning CS8123: The tuple element name 'i' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "i: 9").WithArguments("i", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 91), // (9,97): warning CS8123: The tuple element name 'j' is ignored because a different name is specified by the target type '(long, long, long, long, long, long, long, long, long, int)'. // (x, x, x, x, x, x, x, x, x, y) = (a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10); Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "j: 10").WithArguments("j", "(long, long, long, long, long, long, long, long, long, int)").WithLocation(9, 97) ); } [Fact] public void AssigningLongTuple2() { string source = @" class C { static void Main() { long x; int y; (x, x, x, x, x, x, x, x, x, y) = (1, 1, 1, 1, 1, 1, 1, 1, 4, (byte)2); System.Console.WriteLine(x + "" "" + y); } } "; var comp = CompileAndVerify(source, expectedOutput: "4 2"); comp.VerifyDiagnostics(); } [Fact] public void AssigningTypelessTuple() { string source = @" class C { static void Main() { string x = ""goodbye""; string y; (x, y) = (null, ""hello""); System.Console.WriteLine($""{x}{y}""); } } "; var comp = CompileAndVerify(source, expectedOutput: "hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 19 (0x13) .maxstack 2 .locals init (string V_0) //y IL_0000: ldnull IL_0001: ldstr ""hello"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call ""string string.Concat(string, string)"" IL_000d: call ""void System.Console.WriteLine(string)"" IL_0012: ret } "); } [Fact] public void ValueTupleReturnIsNotEmittedIfUnused() { string source = @" class C { public static void Main() { int x, y; (x, y) = new C(); } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 15 (0xf) .maxstack 3 .locals init (int V_0, int V_1) IL_0000: newobj ""C..ctor()"" IL_0005: ldloca.s V_0 IL_0007: ldloca.s V_1 IL_0009: callvirt ""void C.Deconstruct(out int, out int)"" IL_000e: ret }"); } [Fact] public void ValueTupleReturnIsEmittedIfUsed() { string source = @" class C { public static void Main() { int x, y; var z = ((x, y) = new C()); z.ToString(); } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; Action<ModuleSymbol> validator = module => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var x = nodes.OfType<VariableDeclaratorSyntax>().ElementAt(2); Assert.Equal("(System.Int32 x, System.Int32 y) z", model.GetDeclaredSymbol(x).ToTestDisplayString()); }; var comp = CompileAndVerify(source, sourceSymbolValidator: validator); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 37 (0x25) .maxstack 3 .locals init (System.ValueTuple<int, int> V_0, //z int V_1, int V_2) IL_0000: newobj ""C..ctor()"" IL_0005: ldloca.s V_1 IL_0007: ldloca.s V_2 IL_0009: callvirt ""void C.Deconstruct(out int, out int)"" IL_000e: ldloc.1 IL_000f: ldloc.2 IL_0010: newobj ""System.ValueTuple<int, int>..ctor(int, int)"" IL_0015: stloc.0 IL_0016: ldloca.s V_0 IL_0018: constrained. ""System.ValueTuple<int, int>"" IL_001e: callvirt ""string object.ToString()"" IL_0023: pop IL_0024: ret }"); } [Fact] public void ValueTupleReturnIsEmittedIfUsed_WithCSharp7_1() { string source = @" class C { public static void Main() { int x, y; var z = ((x, y) = new C()); z.ToString(); } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; Action<ModuleSymbol> validator = module => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var x = nodes.OfType<VariableDeclaratorSyntax>().ElementAt(2); Assert.Equal("(System.Int32 x, System.Int32 y) z", model.GetDeclaredSymbol(x).ToTestDisplayString()); }; var comp = CompileAndVerify(source, sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); comp.VerifyDiagnostics(); } [Fact] [WorkItem(18629, "https://github.com/dotnet/roslyn/issues/18629")] public void ValueTupleNotRequiredIfReturnIsNotUsed() { string source = @" class C { public static void Main() { int x, y; (x, y) = new C(); System.Console.Write($""assignment: {x} {y}. ""); foreach (var (a, b) in new[] { new C() }) { System.Console.Write($""foreach: {a} {b}.""); } } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; var comp = CreateCompilationWithMscorlib40(source, parseOptions: TestOptions.Regular7, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "assignment: 1 2. foreach: 1 2."); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var xy = nodes.OfType<TupleExpressionSyntax>().Single(); Assert.Equal("(x, y)", xy.ToString()); var tuple1 = model.GetTypeInfo(xy).Type; Assert.Equal("(System.Int32 x, System.Int32 y)[missing]", tuple1.ToTestDisplayString()); var ab = nodes.OfType<DeclarationExpressionSyntax>().Single(); var tuple2 = model.GetTypeInfo(ab).Type; Assert.Equal("(System.Int32 a, System.Int32 b)[missing]", tuple2.ToTestDisplayString()); Assert.Equal("(System.Int32 a, System.Int32 b)[missing]", model.GetTypeInfo(ab).ConvertedType.ToTestDisplayString()); } [Fact] [WorkItem(18629, "https://github.com/dotnet/roslyn/issues/18629")] public void ValueTupleNotRequiredIfReturnIsNotUsed2() { string source = @" class C { public static void Main() { int x, y; for((x, y) = new C(1); ; (x, y) = new C(2)) { } } public C(int c) { } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; var comp = CreateCompilationWithMscorlib40(source, parseOptions: TestOptions.Regular7, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var tuple1 = nodes.OfType<TupleExpressionSyntax>().ElementAt(0); Assert.Equal("(x, y) = new C(1)", tuple1.Parent.ToString()); var tupleType1 = model.GetTypeInfo(tuple1).Type; Assert.Equal("(System.Int32 x, System.Int32 y)[missing]", tupleType1.ToTestDisplayString()); var tuple2 = nodes.OfType<TupleExpressionSyntax>().ElementAt(1); Assert.Equal("(x, y) = new C(2)", tuple2.Parent.ToString()); var tupleType2 = model.GetTypeInfo(tuple1).Type; Assert.Equal("(System.Int32 x, System.Int32 y)[missing]", tupleType2.ToTestDisplayString()); } [Fact] [WorkItem(18629, "https://github.com/dotnet/roslyn/issues/18629")] public void ValueTupleNotRequiredIfReturnIsNotUsed3() { string source = @" class C { public static void Main() { int x, y; (x, y) = new C(); } public C() { } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } namespace System { [Obsolete] public struct ValueTuple<T1, T2> { [Obsolete] public T1 Item1; [Obsolete] public T2 Item2; public ValueTuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; } } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var tuple = nodes.OfType<TupleExpressionSyntax>().ElementAt(0); Assert.Equal("(x, y) = new C()", tuple.Parent.ToString()); var tupleType = model.GetTypeInfo(tuple).Type; Assert.Equal("(System.Int32 x, System.Int32 y)", tupleType.ToTestDisplayString()); var underlying = ((INamedTypeSymbol)tupleType).TupleUnderlyingType; Assert.Equal("(System.Int32, System.Int32)", underlying.ToTestDisplayString()); } [Fact] [WorkItem(18629, "https://github.com/dotnet/roslyn/issues/18629")] public void ValueTupleRequiredWhenRightHandSideIsTuple() { string source = @" class C { public static void Main() { int x, y; (x, y) = (1, 2); } } "; var comp = CreateCompilationWithMscorlib40(source, parseOptions: TestOptions.Regular7); comp.VerifyDiagnostics( // (7,18): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(1, 2)").WithArguments("System.ValueTuple`2").WithLocation(7, 18) ); } [Fact] [WorkItem(18629, "https://github.com/dotnet/roslyn/issues/18629")] public void ValueTupleRequiredWhenRightHandSideIsTupleButNoReferenceEmitted() { string source = @" class C { public static void Main() { int x, y; (x, y) = (1, 2); } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); comp.VerifyDiagnostics(); Action<PEAssembly> assemblyValidator = assembly => { var reader = assembly.GetMetadataReader(); var names = reader.GetAssemblyRefNames().Select(name => reader.GetString(name)); Assert.Empty(names.Where(name => name.Contains("ValueTuple"))); }; CompileAndVerifyCommon(comp, assemblyValidator: assemblyValidator); } [Fact] public void ValueTupleReturnMissingMemberWithCSharp7() { string source = @" class C { public void M() { int x, y; var nested = ((x, y) = (1, 2)); System.Console.Write(nested.x); } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyDiagnostics( // (8,37): error CS8305: Tuple element name 'x' is inferred. Please use language version 7.1 or greater to access an element by its inferred name. // System.Console.Write(nested.x); Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "x").WithArguments("x", "7.1").WithLocation(8, 37) ); } [Fact] public void ValueTupleReturnWithInferredNamesWithCSharp7_1() { string source = @" class C { public void M() { int x, y, Item1, Rest; var a = ((x, y) = (1, 2)); var b = ((x, x) = (1, 2)); var c = ((_, x) = (1, 2)); var d = ((Item1, Rest) = (1, 2)); var nested = ((x, Item1, y, (_, x, x), (x, y)) = (1, 2, 3, (4, 5, 6), (7, 8))); (int, int) f = ((x, y) = (1, 2)); } } "; Action<ModuleSymbol> validator = module => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var declarations = nodes.OfType<VariableDeclaratorSyntax>(); Assert.Equal("(System.Int32 x, System.Int32 y) a", model.GetDeclaredSymbol(declarations.ElementAt(4)).ToTestDisplayString()); Assert.Equal("(System.Int32, System.Int32) b", model.GetDeclaredSymbol(declarations.ElementAt(5)).ToTestDisplayString()); Assert.Equal("(System.Int32, System.Int32 x) c", model.GetDeclaredSymbol(declarations.ElementAt(6)).ToTestDisplayString()); var x = (ILocalSymbol)model.GetDeclaredSymbol(declarations.ElementAt(7)); Assert.Equal("(System.Int32, System.Int32) d", x.ToTestDisplayString()); Assert.True(x.Type.GetSymbol().TupleElementNames.IsDefault); Assert.Equal("(System.Int32 x, System.Int32, System.Int32 y, (System.Int32, System.Int32, System.Int32), (System.Int32 x, System.Int32 y)) nested", model.GetDeclaredSymbol(declarations.ElementAt(8)).ToTestDisplayString()); }; var comp = CompileAndVerify(source, sourceSymbolValidator: validator, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyDiagnostics(); } [Fact] public void DeconstructionDeclarationCanOnlyBeParsedAsStatement() { string source = @" class C { public static void Main() { var z = ((var x, int y) = new C()); } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,19): error CS8185: A declaration is not allowed in this context. // var z = ((var x, int y) = new C()); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var x").WithLocation(6, 19) ); } [ConditionalFact(typeof(DesktopOnly))] public void Constraints_01() { string source = @" using System; class C { public void M() { (int x, var (err1, y)) = (0, new C()); // ok, no return value used (ArgIterator err2, var err3) = M2(); // ok, no return value foreach ((ArgIterator err4, var err5) in new[] { M2() }) // ok, no return value { } } public static (ArgIterator, ArgIterator) M2() { return (default(ArgIterator), default(ArgIterator)); } public void Deconstruct(out ArgIterator a, out int b) { a = default(ArgIterator); b = 2; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (19,29): error CS1601: Cannot make reference to variable of type 'ArgIterator' // public void Deconstruct(out ArgIterator a, out int b) Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out ArgIterator a").WithArguments("System.ArgIterator").WithLocation(19, 29), // (14,46): error CS0306: The type 'ArgIterator' may not be used as a type argument // public static (ArgIterator, ArgIterator) M2() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("System.ArgIterator").WithLocation(14, 46), // (14,46): error CS0306: The type 'ArgIterator' may not be used as a type argument // public static (ArgIterator, ArgIterator) M2() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("System.ArgIterator").WithLocation(14, 46), // (16,17): error CS0306: The type 'ArgIterator' may not be used as a type argument // return (default(ArgIterator), default(ArgIterator)); Diagnostic(ErrorCode.ERR_BadTypeArgument, "default(ArgIterator)").WithArguments("System.ArgIterator").WithLocation(16, 17), // (16,39): error CS0306: The type 'ArgIterator' may not be used as a type argument // return (default(ArgIterator), default(ArgIterator)); Diagnostic(ErrorCode.ERR_BadTypeArgument, "default(ArgIterator)").WithArguments("System.ArgIterator").WithLocation(16, 39) ); } [Fact] public void Constraints_02() { string source = @" unsafe class C { public void M() { (int x, var (err1, y)) = (0, new C()); // ok, no return value (var err2, var err3) = M2(); // ok, no return value foreach ((var err4, var err5) in new[] { M2() }) // ok, no return value { } } public static (int*, int*) M2() { return (default(int*), default(int*)); } public void Deconstruct(out int* a, out int b) { a = default(int*); b = 2; } } "; var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (13,32): error CS0306: The type 'int*' may not be used as a type argument // public static (int*, int*) M2() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("int*").WithLocation(13, 32), // (13,32): error CS0306: The type 'int*' may not be used as a type argument // public static (int*, int*) M2() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("int*").WithLocation(13, 32), // (15,17): error CS0306: The type 'int*' may not be used as a type argument // return (default(int*), default(int*)); Diagnostic(ErrorCode.ERR_BadTypeArgument, "default(int*)").WithArguments("int*").WithLocation(15, 17), // (15,32): error CS0306: The type 'int*' may not be used as a type argument // return (default(int*), default(int*)); Diagnostic(ErrorCode.ERR_BadTypeArgument, "default(int*)").WithArguments("int*").WithLocation(15, 32) ); } [Fact] public void Constraints_03() { string source = @" unsafe class C { public void M() { int ok; int* err1, err2; var t = ((ok, (err1, ok)) = (0, new C())); var t2 = ((err1, err2) = M2()); } public static (int*, int*) M2() { throw null; } public void Deconstruct(out int* a, out int b) { a = default(int*); b = 2; } } "; var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (12,32): error CS0306: The type 'int*' may not be used as a type argument // public static (int*, int*) M2() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("int*").WithLocation(12, 32), // (12,32): error CS0306: The type 'int*' may not be used as a type argument // public static (int*, int*) M2() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("int*").WithLocation(12, 32), // (8,24): error CS0306: The type 'int*' may not be used as a type argument // var t = ((ok, (err1, ok)) = (0, new C())); Diagnostic(ErrorCode.ERR_BadTypeArgument, "err1").WithArguments("int*").WithLocation(8, 24), // (9,20): error CS0306: The type 'int*' may not be used as a type argument // var t2 = ((err1, err2) = M2()); Diagnostic(ErrorCode.ERR_BadTypeArgument, "err1").WithArguments("int*").WithLocation(9, 20), // (9,26): error CS0306: The type 'int*' may not be used as a type argument // var t2 = ((err1, err2) = M2()); Diagnostic(ErrorCode.ERR_BadTypeArgument, "err2").WithArguments("int*").WithLocation(9, 26) ); } [Fact] public void DeconstructionWithTupleNamesCannotBeParsed() { string source = @" class C { public static void Main() { (Alice: var x, Bob: int y) = (1, 2); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,10): error CS8187: Tuple element names are not permitted on the left of a deconstruction. // (Alice: var x, Bob: int y) = (1, 2); Diagnostic(ErrorCode.ERR_TupleElementNamesInDeconstruction, "Alice:").WithLocation(6, 10), // (6,24): error CS8187: Tuple element names are not permitted on the left of a deconstruction. // (Alice: var x, Bob: int y) = (1, 2); Diagnostic(ErrorCode.ERR_TupleElementNamesInDeconstruction, "Bob:").WithLocation(6, 24) ); } [Fact] public void ValueTupleReturnIsEmittedIfUsedInLambda() { string source = @" class C { static void F(System.Action a) { } static void F<T>(System.Func<T> f) { System.Console.Write(f().ToString()); } static void Main() { int x, y; F(() => (x, y) = (1, 2)); } } "; var comp = CompileAndVerify(source, expectedOutput: "(1, 2)"); comp.VerifyDiagnostics(); } [Fact] public void AssigningIntoProperties() { string source = @" class C { static int field; static long x { set { System.Console.WriteLine($""setX {value}""); } } static string y { get; set; } static ref int z { get { return ref @field; } } static void Main() { (x, y, z) = new C(); System.Console.WriteLine(y); System.Console.WriteLine($""field: {field}""); } public void Deconstruct(out int a, out string b, out int c) { a = 1; b = ""hello""; c = 2; } } "; string expected = @"setX 1 hello field: 2 "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void AssigningTupleIntoProperties() { string source = @" class C { static int field; static long x { set { System.Console.WriteLine($""setX {value}""); } } static string y { get; set; } static ref int z { get { return ref @field; } } static void Main() { (x, y, z) = (1, ""hello"", 2); System.Console.WriteLine(y); System.Console.WriteLine($""field: {field}""); } } "; string expected = @"setX 1 hello field: 2 "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] [WorkItem(18554, "https://github.com/dotnet/roslyn/issues/18554")] public void AssigningIntoIndexers() { string source = @" using System; class C { int field; ref int this[int x, int y, int z, int opt = 1] { get { Console.WriteLine($""this.get""); return ref field; } } int this[int x, long y, int z, int opt = 1] { set { Console.WriteLine($""this.set({value})""); } } int M(int i) { Console.WriteLine($""M({i})""); return 0; } void Test() { (this[z: M(1), x: M(2), y: 10], this[z: M(3), x: M(4), y: 10L]) = this; Console.WriteLine($""field: {field}""); } static void Main() { new C().Test(); } void Deconstruct(out int a, out int b) { Console.WriteLine(nameof(Deconstruct)); a = 1; b = 2; } } "; var expectedOutput = @"M(1) M(2) this.get M(3) M(4) Deconstruct this.set(2) field: 1 "; var comp = CompileAndVerify(source, expectedOutput: expectedOutput); comp.VerifyDiagnostics(); } [Fact] [WorkItem(18554, "https://github.com/dotnet/roslyn/issues/18554")] public void AssigningTupleIntoIndexers() { string source = @" using System; class C { int field; ref int this[int x, int y, int z, int opt = 1] { get { Console.WriteLine($""this.get""); return ref field; } } int this[int x, long y, int z, int opt = 1] { set { Console.WriteLine($""this.set({value})""); } } int M(int i) { Console.WriteLine($""M({i})""); return 0; } void Test() { (this[z: M(1), x: M(2), y: 10], this[z: M(3), x: M(4), y: 10L]) = (1, 2); Console.WriteLine($""field: {field}""); } static void Main() { new C().Test(); } } "; var expectedOutput = @"M(1) M(2) this.get M(3) M(4) this.set(2) field: 1 "; var comp = CompileAndVerify(source, expectedOutput: expectedOutput); comp.VerifyDiagnostics(); } [Fact] public void AssigningIntoIndexerWithOptionalValueParameter() { var ilSource = @" .class public auto ansi beforefieldinit C extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( int32 i, [opt] int32 'value' ) cil managed { .param [2] = int32(1) .maxstack 8 IL_0000: ldstr ""this.set({0})"" IL_0005: ldarg.2 IL_0006: box[mscorlib]System.Int32 IL_000b: call string[mscorlib] System.String::Format(string, object) IL_0010: call void [mscorlib]System.Console::WriteLine(string) IL_0015: ret } // end of method C::set_Item .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method C::.ctor .property instance int32 Item( int32 i ) { .set instance void C::set_Item(int32, int32) } } // end of class C "; var source = @" class Program { static void Main() { var c = new C(); (c[1], c[2]) = (1, 2); } } "; string expectedOutput = @"this.set(1) this.set(2) "; var comp = CreateCompilationWithILAndMscorlib40(source, ilSource, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] public void Swap() { string source = @" class C { static int x = 2; static int y = 4; static void Main() { Swap(); System.Console.WriteLine(x + "" "" + y); } static void Swap() { (x, y) = (y, x); } } "; var comp = CompileAndVerify(source, expectedOutput: "4 2"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Swap", @" { // Code size 23 (0x17) .maxstack 2 .locals init (int V_0) IL_0000: ldsfld ""int C.y"" IL_0005: ldsfld ""int C.x"" IL_000a: stloc.0 IL_000b: stsfld ""int C.x"" IL_0010: ldloc.0 IL_0011: stsfld ""int C.y"" IL_0016: ret } "); } [Fact] public void CircularFlow() { string source = @" class C { static void Main() { (object i, object ii) x = (1, 2); object y; (x.ii, y) = x; System.Console.WriteLine(x + "" "" + y); } } "; var comp = CompileAndVerify(source, expectedOutput: "(1, 1) 2"); comp.VerifyDiagnostics(); } [Fact] [CompilerTrait(CompilerFeature.RefLocalsReturns)] public void CircularFlow2() { string source = @" class C { static void Main() { (object i, object ii) x = (1,2); object y; ref var a = ref x; (a.ii, y) = x; System.Console.WriteLine(x + "" "" + y); } } "; var comp = CompileAndVerify(source, expectedOutput: "(1, 1) 2", parseOptions: TestOptions.Regular.WithRefsFeature()); comp.VerifyDiagnostics(); } [Fact] public void DeconstructUsingBaseDeconstructMethod() { string source = @" class Base { public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } class C : Base { static void Main() { int x, y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int c) { c = 42; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2", parseOptions: TestOptions.Regular.WithRefsFeature()); comp.VerifyDiagnostics(); } [Fact] public void NestedDeconstructUsingSystemTupleExtensionMethod() { string source = @" using System; class C { static void Main() { int x; string y, z; (x, (y, z)) = Tuple.Create(1, Tuple.Create(""hello"", ""world"")); System.Console.WriteLine(x + "" "" + y + "" "" + z); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello world", parseOptions: TestOptions.Regular.WithRefsFeature()); comp.VerifyDiagnostics(); var tree = comp.Compilation.SyntaxTrees.First(); var model = comp.Compilation.GetSemanticModel(tree); var deconstruction = (AssignmentExpressionSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.SimpleAssignmentExpression).AsNode(); Assert.Equal(@"(x, (y, z)) = Tuple.Create(1, Tuple.Create(""hello"", ""world""))", deconstruction.ToString()); var deconstructionInfo = model.GetDeconstructionInfo(deconstruction); Assert.Equal("void System.TupleExtensions.Deconstruct<System.Int32, System.Tuple<System.String, System.String>>(" + "this System.Tuple<System.Int32, System.Tuple<System.String, System.String>> value, " + "out System.Int32 item1, out System.Tuple<System.String, System.String> item2)", deconstructionInfo.Method.ToTestDisplayString()); Assert.Null(deconstructionInfo.Conversion); var nested = deconstructionInfo.Nested; Assert.Equal(2, nested.Length); Assert.Null(nested[0].Method); Assert.Equal(ConversionKind.Identity, nested[0].Conversion.Value.Kind); Assert.Empty(nested[0].Nested); Assert.Equal("void System.TupleExtensions.Deconstruct<System.String, System.String>(" + "this System.Tuple<System.String, System.String> value, " + "out System.String item1, out System.String item2)", nested[1].Method.ToTestDisplayString()); Assert.Null(nested[1].Conversion); var nested2 = nested[1].Nested; Assert.Equal(2, nested.Length); Assert.Null(nested2[0].Method); Assert.Equal(ConversionKind.Identity, nested2[0].Conversion.Value.Kind); Assert.Empty(nested2[0].Nested); Assert.Null(nested2[1].Method); Assert.Equal(ConversionKind.Identity, nested2[1].Conversion.Value.Kind); Assert.Empty(nested2[1].Nested); } [Fact] public void DeconstructUsingValueTupleExtensionMethod() { string source = @" class C { static void Main() { int x; string y, z; (x, y, z) = (1, 2); } } public static class Extensions { public static void Deconstruct(this (int, int) self, out int x, out string y, out string z) { throw null; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,25): error CS0029: Cannot implicitly convert type 'int' to 'string' // (x, y, z) = (1, 2); Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "string").WithLocation(8, 25), // (8,9): error CS8132: Cannot deconstruct a tuple of '2' elements into '3' variables. // (x, y, z) = (1, 2); Diagnostic(ErrorCode.ERR_DeconstructWrongCardinality, "(x, y, z) = (1, 2)").WithArguments("2", "3").WithLocation(8, 9) ); } [Fact] public void OverrideDeconstruct() { string source = @" class Base { public virtual void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } class C : Base { static void Main() { int x; string y; (x, y) = new C(); } public override void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; System.Console.WriteLine(""override""); } } "; var comp = CompileAndVerify(source, expectedOutput: "override", parseOptions: TestOptions.Regular.WithRefsFeature()); comp.VerifyDiagnostics(); } [Fact] public void DeconstructRefTuple() { string template = @" using System; class C { static void Main() { int VARIABLES; // int x1, x2, ... (VARIABLES) = (TUPLE).ToTuple(); // (x1, x2, ...) = (1, 2, ...).ToTuple(); System.Console.WriteLine(OUTPUT); } } "; for (int i = 2; i <= 21; i++) { var tuple = String.Join(", ", Enumerable.Range(1, i).Select(n => n.ToString())); var variables = String.Join(", ", Enumerable.Range(1, i).Select(n => $"x{n}")); var output = String.Join(@" + "" "" + ", Enumerable.Range(1, i).Select(n => $"x{n}")); var expected = String.Join(" ", Enumerable.Range(1, i).Select(n => n)); var source = template.Replace("VARIABLES", variables).Replace("TUPLE", tuple).Replace("OUTPUT", output); var comp = CompileAndVerify(source, expectedOutput: expected, parseOptions: TestOptions.Regular.WithRefsFeature()); comp.VerifyDiagnostics(); } } [Fact] public void DeconstructExtensionMethod() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } } static class D { public static void Deconstruct(this C value, out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); } [Fact] public void DeconstructRefExtensionMethod() { // https://github.com/dotnet/csharplang/blob/main/meetings/2018/LDM-2018-01-24.md string source = @" struct C { static void Main() { long x; string y; var c = new C(); (x, y) = c; System.Console.WriteLine(x + "" "" + y); } } static class D { public static void Deconstruct(this ref C value, out int a, out string b) { a = 1; b = ""hello""; } } "; CreateCompilation(source).VerifyDiagnostics( // (10,9): error CS1510: A ref or out value must be an assignable variable // (x, y) = c; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(x, y) = c").WithLocation(10, 9) ); } [Fact] public void DeconstructInExtensionMethod() { string source = @" struct C { static void Main() { long x; string y; (x, y) = new C(); System.Console.WriteLine(x + "" "" + y); } } static class D { public static void Deconstruct(this in C value, out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); } [Fact] public void UnderspecifiedDeconstructGenericExtensionMethod() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); } } static class Extension { public static void Deconstruct<T>(this C value, out int a, out T b) { a = 2; b = default(T); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,18): error CS0411: The type arguments for method 'Extension.Deconstruct<T>(C, out int, out T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // (x, y) = new C(); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "new C()").WithArguments("Extension.Deconstruct<T>(C, out int, out T)").WithLocation(8, 18), // (8,18): error CS8129: No Deconstruct instance or extension method was found for type 'C', with 2 out parameters. // (x, y) = new C(); Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 18) ); } [Fact] public void UnspecifiedGenericMethodIsNotCandidate() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C(); } } static class Extension { public static void Deconstruct<T>(this C value, out int a, out T b) { a = 2; b = default(T); } public static void Deconstruct(this C value, out int a, out string b) { a = 2; b = ""hello""; System.Console.Write(""Deconstructed""); } }"; var comp = CompileAndVerify(source, expectedOutput: "Deconstructed"); comp.VerifyDiagnostics(); } [Fact] public void DeconstructGenericExtensionMethod() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C1<string>(); } } public class C1<T> { } static class Extension { public static void Deconstruct<T>(this C1<T> value, out int a, out T b) { a = 2; b = default(T); System.Console.WriteLine(""Deconstructed""); } } "; var comp = CompileAndVerify(source, expectedOutput: "Deconstructed"); comp.VerifyDiagnostics(); } [Fact] public void DeconstructGenericMethod() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C1(); } } class C1 { public void Deconstruct<T>(out int a, out T b) { a = 2; b = default(T); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (9,18): error CS0411: The type arguments for method 'C1.Deconstruct<T>(out int, out T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // (x, y) = new C1(); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "new C1()").WithArguments("C1.Deconstruct<T>(out int, out T)").WithLocation(9, 18), // (9,18): error CS8129: No Deconstruct instance or extension method was found for type 'C1', with 2 out parameters and a void return type. // (x, y) = new C1(); Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C1()").WithArguments("C1", "2").WithLocation(9, 18) ); } [Fact] public void AmbiguousDeconstructGenericMethod() { string source = @" class C { static void Main() { long x; string y; (x, y) = new C1(); System.Console.Write($""{x} {y}""); } } class C1 { public void Deconstruct<T>(out int a, out T b) { a = 2; b = default(T); } public void Deconstruct(out int a, out string b) { a = 2; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "2 hello"); comp.VerifyDiagnostics(); } [Fact] public void NestedTupleAssignment() { string source = @" class C { static void Main() { long x; string y, z; (x, (y, z)) = ((int)1, (""a"", ""b"")); System.Console.WriteLine(x + "" "" + y + "" "" + z); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal(@"(x, (y, z))", lhs.ToString()); Assert.Equal("(System.Int64 x, (System.String y, System.String z))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Equal("(System.Int64 x, (System.String y, System.String z))", model.GetTypeInfo(lhs).ConvertedType.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 a b", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void NestedTypelessTupleAssignment() { string source = @" class C { static void Main() { string x, y, z; (x, (y, z)) = (null, (null, null)); System.Console.WriteLine(""nothing"" + x + y + z); } } "; var comp = CompileAndVerify(source, expectedOutput: "nothing"); comp.VerifyDiagnostics(); } [Fact] public void NestedDeconstructAssignment() { string source = @" class C { static void Main() { int x; string y, z; (x, (y, z)) = new D1(); System.Console.WriteLine(x + "" "" + y + "" "" + z); } } class D1 { public void Deconstruct(out int item1, out D2 item2) { item1 = 1; item2 = new D2(); } } class D2 { public void Deconstruct(out string item1, out string item2) { item1 = ""a""; item2 = ""b""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 a b"); comp.VerifyDiagnostics(); } [Fact] public void NestedMixedAssignment1() { string source = @" class C { static void Main() { int x, y, z; (x, (y, z)) = (1, new D1()); System.Console.WriteLine(x + "" "" + y + "" "" + z); } } class D1 { public void Deconstruct(out int item1, out int item2) { item1 = 2; item2 = 3; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2 3"); comp.VerifyDiagnostics(); } [Fact] public void NestedMixedAssignment2() { string source = @" class C { static void Main() { int x; string y, z; (x, (y, z)) = new D1(); System.Console.WriteLine(x + "" "" + y + "" "" + z); } } class D1 { public void Deconstruct(out int item1, out (string, string) item2) { item1 = 1; item2 = (""a"", ""b""); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 a b"); comp.VerifyDiagnostics(); } [Fact] public void VerifyNestedExecutionOrder() { string source = @" using System; class C { int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } int z { set { Console.WriteLine($""setZ""); } } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } C getHolderForZ() { Console.WriteLine(""getHolderforZ""); return this; } C getDeconstructReceiver() { Console.WriteLine(""getDeconstructReceiver""); return this; } static void Main() { C c = new C(); (c.getHolderForX().x, (c.getHolderForY().y, c.getHolderForZ().z)) = c.getDeconstructReceiver(); } public void Deconstruct(out D1 x, out C1 t) { x = new D1(); t = new C1(); Console.WriteLine(""Deconstruct1""); } } class C1 { public void Deconstruct(out D2 y, out D3 z) { y = new D2(); z = new D3(); Console.WriteLine(""Deconstruct2""); } } class D1 { public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public static implicit operator int(D2 d) { Console.WriteLine(""Conversion2""); return 2; } } class D3 { public static implicit operator int(D3 d) { Console.WriteLine(""Conversion3""); return 3; } } "; string expected = @"getHolderforX getHolderforY getHolderforZ getDeconstructReceiver Deconstruct1 Deconstruct2 Conversion1 Conversion2 Conversion3 setX setY setZ "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyNestedExecutionOrder_Conditional() { string source = @" using System; class C { int x { set { Console.WriteLine($""setX""); } } int y { set { Console.WriteLine($""setY""); } } int z { set { Console.WriteLine($""setZ""); } } C getHolderForX() { Console.WriteLine(""getHolderforX""); return this; } C getHolderForY() { Console.WriteLine(""getHolderforY""); return this; } C getHolderForZ() { Console.WriteLine(""getHolderforZ""); return this; } C getDeconstructReceiver() { Console.WriteLine(""getDeconstructReceiver""); return this; } static void Main() { C c = new C(); bool b = false; (c.getHolderForX().x, (c.getHolderForY().y, c.getHolderForZ().z)) = b ? default : c.getDeconstructReceiver(); } public void Deconstruct(out D1 x, out C1 t) { x = new D1(); t = new C1(); Console.WriteLine(""Deconstruct1""); } } class C1 { public void Deconstruct(out D2 y, out D3 z) { y = new D2(); z = new D3(); Console.WriteLine(""Deconstruct2""); } } class D1 { public static implicit operator int(D1 d) { Console.WriteLine(""Conversion1""); return 1; } } class D2 { public static implicit operator int(D2 d) { Console.WriteLine(""Conversion2""); return 2; } } class D3 { public static implicit operator int(D3 d) { Console.WriteLine(""Conversion3""); return 3; } } "; string expected = @"getHolderforX getHolderforY getHolderforZ getDeconstructReceiver Deconstruct1 Deconstruct2 Conversion1 Conversion2 Conversion3 setX setY setZ "; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void VerifyNestedExecutionOrder2() { string source = @" using System; class C { static LongInteger x1 { set { Console.WriteLine($""setX1 {value}""); } } static LongInteger x2 { set { Console.WriteLine($""setX2 {value}""); } } static LongInteger x3 { set { Console.WriteLine($""setX3 {value}""); } } static LongInteger x4 { set { Console.WriteLine($""setX4 {value}""); } } static LongInteger x5 { set { Console.WriteLine($""setX5 {value}""); } } static LongInteger x6 { set { Console.WriteLine($""setX6 {value}""); } } static LongInteger x7 { set { Console.WriteLine($""setX7 {value}""); } } static void Main() { ((x1, (x2, x3)), ((x4, x5), (x6, x7))) = Pair.Create(Pair.Create(new Integer(1), Pair.Create(new Integer(2), new Integer(3))), Pair.Create(Pair.Create(new Integer(4), new Integer(5)), Pair.Create(new Integer(6), new Integer(7)))); } } " + commonSource; string expected = @"Deconstructing ((1, (2, 3)), ((4, 5), (6, 7))) Deconstructing (1, (2, 3)) Deconstructing (2, 3) Deconstructing ((4, 5), (6, 7)) Deconstructing (4, 5) Deconstructing (6, 7) Converting 1 Converting 2 Converting 3 Converting 4 Converting 5 Converting 6 Converting 7 setX1 1 setX2 2 setX3 3 setX4 4 setX5 5 setX6 6 setX7 7"; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void MixOfAssignments() { string source = @" class C { static void Main() { long x; string y; C a, b, c; c = new C(); (x, y) = a = b = c; System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); } [Fact] [WorkItem(12400, "https://github.com/dotnet/roslyn/issues/12400")] public void AssignWithPostfixOperator() { string source = @" class C { int state = 1; static void Main() { long x; string y; C c = new C(); (x, y) = c++; System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = state; b = ""hello""; } public static C operator ++(C c1) { return new C() { state = 2 }; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); } [Fact] [WorkItem(12400, "https://github.com/dotnet/roslyn/issues/12400")] public void AssignWithPrefixOperator() { string source = @" class C { int state = 1; static void Main() { long x; string y; C c = new C(); (x, y) = ++c; System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b) { a = state; b = ""hello""; } public static C operator ++(C c1) { return new C() { state = 2 }; } } "; var comp = CompileAndVerify(source, expectedOutput: "2 hello"); comp.VerifyDiagnostics(); } [Fact] [WorkItem(13631, "https://github.com/dotnet/roslyn/issues/13631")] public void DeconstructionDeclaration() { string source = @" class C { static void Main() { var (x1, x2) = (1, ""hello""); System.Console.WriteLine(x1 + "" "" + x2); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 32 (0x20) .maxstack 3 .locals init (int V_0, //x1 string V_1) //x2 IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldstr ""hello"" IL_0007: stloc.1 IL_0008: ldloca.s V_0 IL_000a: call ""string int.ToString()"" IL_000f: ldstr "" "" IL_0014: ldloc.1 IL_0015: call ""string string.Concat(string, string, string)"" IL_001a: call ""void System.Console.WriteLine(string)"" IL_001f: ret }"); } [Fact] public void NestedVarDeconstructionDeclaration() { string source = @" class C { static void Main() { var (x1, (x2, x3)) = (1, (2, ""hello"")); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().First(); Assert.Equal(@"var (x1, (x2, x3))", lhs.ToString()); Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhs).Symbol); var lhsNested = tree.GetRoot().DescendantNodes().OfType<ParenthesizedVariableDesignationSyntax>().ElementAt(1); Assert.Equal(@"(x2, x3)", lhsNested.ToString()); Assert.Null(model.GetTypeInfo(lhsNested).Type); Assert.Null(model.GetSymbolInfo(lhsNested).Symbol); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionLocal(model, x3, x3Ref); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 hello", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void NestedVarDeconstructionDeclaration_WithCSharp7_1() { string source = @" class C { static void Main() { (int x1, var (x2, (x3, x4)), var x5) = (1, (2, (3, ""hello"")), 5); System.Console.WriteLine($""{x1} {x2} {x3} {x4} {x5}""); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal(@"(int x1, var (x2, (x3, x4)), var x5)", lhs.ToString()); Assert.Equal("(System.Int32 x1, (System.Int32 x2, (System.Int32 x3, System.String x4)), System.Int32 x5)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhs).Symbol); var x234 = tree.GetRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ElementAt(1); Assert.Equal(@"var (x2, (x3, x4))", x234.ToString()); Assert.Equal("(System.Int32 x2, (System.Int32 x3, System.String x4))", model.GetTypeInfo(x234).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(x234).Symbol); var x34 = tree.GetRoot().DescendantNodes().OfType<ParenthesizedVariableDesignationSyntax>().ElementAt(1); Assert.Equal(@"(x3, x4)", x34.ToString()); Assert.Null(model.GetTypeInfo(x34).Type); Assert.Null(model.GetSymbolInfo(x34).Symbol); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionLocal(model, x3, x3Ref); var x4 = GetDeconstructionVariable(tree, "x4"); var x4Ref = GetReference(tree, "x4"); VerifyModelForDeconstructionLocal(model, x4, x4Ref); var x5 = GetDeconstructionVariable(tree, "x5"); var x5Ref = GetReference(tree, "x5"); VerifyModelForDeconstructionLocal(model, x5, x5Ref); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 3 hello 5", sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); comp.VerifyDiagnostics(); } [Fact] public void NestedVarDeconstructionAssignment_WithCSharp7_1() { string source = @" class C { static void Main() { int x1, x2, x3; (x1, (x2, x3)) = (1, (2, 3)); System.Console.WriteLine($""{x1} {x2} {x3}""); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x123 = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal(@"(x1, (x2, x3))", x123.ToString()); Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.Int32 x3))", model.GetTypeInfo(x123).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(x123).Symbol); var x23 = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(1); Assert.Equal(@"(x2, x3)", x23.ToString()); Assert.Equal("(System.Int32 x2, System.Int32 x3)", model.GetTypeInfo(x23).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(x23).Symbol); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 3", sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); comp.VerifyDiagnostics(); } [Fact] public void NestedVarDeconstructionDeclaration2() { string source = @" class C { static void Main() { (var x1, var (x2, x3)) = (1, (2, ""hello"")); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal("(var x1, var (x2, x3))", lhs.ToString()); Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).ConvertedType.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhs).Symbol); var lhsNested = tree.GetRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ElementAt(1); Assert.Equal("var (x2, x3)", lhsNested.ToString()); Assert.Equal("(System.Int32 x2, System.String x3)", model.GetTypeInfo(lhsNested).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 x2, System.String x3)", model.GetTypeInfo(lhsNested).ConvertedType.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhsNested).Symbol); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionLocal(model, x3, x3Ref); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 hello", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void NestedVarDeconstructionDeclarationWithCSharp7_1() { string source = @" class C { static void Main() { (var x1, byte _, var (x2, x3)) = (1, 2, (3, ""hello"")); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal("(var x1, byte _, var (x2, x3))", lhs.ToString()); Assert.Equal("(System.Int32 x1, System.Byte, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 x1, System.Byte, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).ConvertedType.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhs).Symbol); var lhsNested = tree.GetRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ElementAt(2); Assert.Equal("var (x2, x3)", lhsNested.ToString()); Assert.Equal("(System.Int32 x2, System.String x3)", model.GetTypeInfo(lhsNested).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhsNested).Symbol); }; var comp = CompileAndVerify(source, sourceSymbolValidator: validator, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyDiagnostics(); } [Fact] public void NestedDeconstructionDeclaration() { string source = @" class C { static void Main() { (int x1, (int x2, string x3)) = (1, (2, ""hello"")); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionLocal(model, x3, x3Ref); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 hello", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void VarMethodExists() { string source = @" class C { static void Main() { int x1 = 1; int x2 = 1; var (x1, x2); } static void var(int a, int b) { System.Console.WriteLine(""var""); } } "; var comp = CompileAndVerify(source, expectedOutput: "var"); comp.VerifyDiagnostics(); } [Fact] public void TypeMergingSuccess1() { string source = @" class C { static void Main() { (var (x1, x2), string x3) = ((1, 2), null); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } "; var comp = CompileAndVerify(source, expectedOutput: " 1 2"); comp.VerifyDiagnostics(); } [Fact] public void TypeMergingSuccess2() { string source = @" class C { static void Main() { (string x1, byte x2, var x3) = (null, 2, 3); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal(@"(string x1, byte x2, var x3)", lhs.ToString()); Assert.Equal("(System.String x1, System.Byte x2, System.Int32 x3)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); var literal = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(1); Assert.Equal(@"(null, 2, 3)", literal.ToString()); Assert.Null(model.GetTypeInfo(literal).Type); Assert.Equal("(System.String, System.Byte, System.Int32)", model.GetTypeInfo(literal).ConvertedType.ToTestDisplayString()); Assert.Equal(ConversionKind.ImplicitTupleLiteral, model.GetConversion(literal).Kind); }; var comp = CompileAndVerify(source, expectedOutput: " 2 3", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void TypeMergingSuccess3() { string source = @" class C { static void Main() { (string x1, var x2) = (null, (1, 2)); System.Console.WriteLine(x1 + "" "" + x2); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().First(); Assert.Equal(@"(string x1, var x2)", lhs.ToString()); Assert.Equal("(System.String x1, (System.Int32, System.Int32) x2)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); var literal = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(1); Assert.Equal(@"(null, (1, 2))", literal.ToString()); Assert.Null(model.GetTypeInfo(literal).Type); Assert.Equal("(System.String, (System.Int32, System.Int32))", model.GetTypeInfo(literal).ConvertedType.ToTestDisplayString()); Assert.Equal(ConversionKind.ImplicitTupleLiteral, model.GetConversion(literal).Kind); var nestedLiteral = literal.Arguments[1].Expression; Assert.Equal(@"(1, 2)", nestedLiteral.ToString()); Assert.Equal("(System.Int32, System.Int32)", model.GetTypeInfo(nestedLiteral).Type.ToTestDisplayString()); Assert.Equal("(System.Int32, System.Int32)", model.GetTypeInfo(nestedLiteral).ConvertedType.ToTestDisplayString()); Assert.Equal(ConversionKind.Identity, model.GetConversion(nestedLiteral).Kind); }; var comp = CompileAndVerify(source, expectedOutput: " (1, 2)", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void TypeMergingSuccess4() { string source = @" class C { static void Main() { ((string x1, byte x2, var x3), int x4) = (M(), 4); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3 + "" "" + x4); } static (string, byte, int) M() { return (null, 2, 3); } } "; var comp = CompileAndVerify(source, expectedOutput: " 2 3 4"); comp.VerifyDiagnostics(); } [Fact] public void VarVarDeclaration() { string source = @" class C { static void Main() { (var (x1, x2), var x3) = Pair.Create(Pair.Create(1, ""hello""), 2); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } " + commonSource; string expected = @"Deconstructing ((1, hello), 2) Deconstructing (1, hello) 1 hello 2"; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionLocal(model, x3, x3Ref); }; var comp = CompileAndVerify(source, expectedOutput: expected, parseOptions: TestOptions.Regular, sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } private static void VerifyModelForDeconstructionLocal(SemanticModel model, SingleVariableDesignationSyntax decl, params IdentifierNameSyntax[] references) { VerifyModelForDeconstruction(model, decl, LocalDeclarationKind.DeconstructionVariable, references); } private static void VerifyModelForLocal(SemanticModel model, SingleVariableDesignationSyntax decl, LocalDeclarationKind kind, params IdentifierNameSyntax[] references) { VerifyModelForDeconstruction(model, decl, kind, references); } private static void VerifyModelForDeconstructionForeach(SemanticModel model, SingleVariableDesignationSyntax decl, params IdentifierNameSyntax[] references) { VerifyModelForDeconstruction(model, decl, LocalDeclarationKind.ForEachIterationVariable, references); } private static void VerifyModelForDeconstruction(SemanticModel model, SingleVariableDesignationSyntax decl, LocalDeclarationKind kind, params IdentifierNameSyntax[] references) { var symbol = model.GetDeclaredSymbol(decl); Assert.Equal(decl.Identifier.ValueText, symbol.Name); Assert.Equal(kind, symbol.GetSymbol<LocalSymbol>().DeclarationKind); Assert.Same(symbol, model.GetDeclaredSymbol((SyntaxNode)decl)); Assert.Same(symbol, model.LookupSymbols(decl.SpanStart, name: decl.Identifier.ValueText).Single()); Assert.True(model.LookupNames(decl.SpanStart).Contains(decl.Identifier.ValueText)); var local = symbol.GetSymbol<SourceLocalSymbol>(); var typeSyntax = GetTypeSyntax(decl); if (local.IsVar && local.Type.IsErrorType()) { Assert.Null(model.GetSymbolInfo(typeSyntax).Symbol); } else { if (typeSyntax != null) { Assert.Equal(local.Type.GetPublicSymbol(), model.GetSymbolInfo(typeSyntax).Symbol); } } foreach (var reference in references) { Assert.Same(symbol, model.GetSymbolInfo(reference).Symbol); Assert.Same(symbol, model.LookupSymbols(reference.SpanStart, name: decl.Identifier.ValueText).Single()); Assert.True(model.LookupNames(reference.SpanStart).Contains(decl.Identifier.ValueText)); Assert.Equal(local.Type.GetPublicSymbol(), model.GetTypeInfo(reference).Type); } } private static void VerifyModelForDeconstructionField(SemanticModel model, SingleVariableDesignationSyntax decl, params IdentifierNameSyntax[] references) { var field = (IFieldSymbol)model.GetDeclaredSymbol(decl); Assert.Equal(decl.Identifier.ValueText, field.Name); Assert.Equal(SymbolKind.Field, field.Kind); Assert.Same(field, model.GetDeclaredSymbol((SyntaxNode)decl)); Assert.Same(field, model.LookupSymbols(decl.SpanStart, name: decl.Identifier.ValueText).Single()); Assert.Equal(Accessibility.Private, field.DeclaredAccessibility); Assert.True(model.LookupNames(decl.SpanStart).Contains(decl.Identifier.ValueText)); foreach (var reference in references) { Assert.Same(field, model.GetSymbolInfo(reference).Symbol); Assert.Same(field, model.LookupSymbols(reference.SpanStart, name: decl.Identifier.ValueText).Single()); Assert.True(model.LookupNames(reference.SpanStart).Contains(decl.Identifier.ValueText)); Assert.Equal(field.Type, model.GetTypeInfo(reference).Type); } } private static TypeSyntax GetTypeSyntax(SingleVariableDesignationSyntax decl) { return (decl.Parent as DeclarationExpressionSyntax)?.Type; } private static SingleVariableDesignationSyntax GetDeconstructionVariable(SyntaxTree tree, string name) { return tree.GetRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().Where(d => d.Identifier.ValueText == name).Single(); } private static IEnumerable<DiscardDesignationSyntax> GetDiscardDesignations(SyntaxTree tree) { return tree.GetRoot().DescendantNodes().OfType<DiscardDesignationSyntax>(); } private static IEnumerable<IdentifierNameSyntax> GetDiscardIdentifiers(SyntaxTree tree) { return tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(i => i.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken); } private static IdentifierNameSyntax GetReference(SyntaxTree tree, string name) { return GetReferences(tree, name).Single(); } private static IdentifierNameSyntax[] GetReferences(SyntaxTree tree, string name, int count) { var nameRef = GetReferences(tree, name).ToArray(); Assert.Equal(count, nameRef.Length); return nameRef; } private static IEnumerable<IdentifierNameSyntax> GetReferences(SyntaxTree tree, string name) { return tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(id => id.Identifier.ValueText == name); } [Fact] public void DeclarationWithActualVarType() { string source = @" class C { static void Main() { (var x1, int x2) = (new var(), 2); System.Console.WriteLine(x1 + "" "" + x2); } } class @var { public override string ToString() { return ""var""; } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); // extra checks on x1 var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("var", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); // extra checks on x2 var x2Type = GetTypeSyntax(x2); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x2Type).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(x2Type).Symbol.ToDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "var 2", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void DeclarationWithImplicitVarType() { string source = @" class C { static void Main() { (var x1, var x2) = (1, 2); var (x3, x4) = (3, 4); System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3 + "" "" + x4); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionLocal(model, x3, x3Ref); var x4 = GetDeconstructionVariable(tree, "x4"); var x4Ref = GetReference(tree, "x4"); VerifyModelForDeconstructionLocal(model, x4, x4Ref); // extra checks on x1 var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); Assert.Null(model.GetAliasInfo(x1Type)); var x34Var = (DeclarationExpressionSyntax)x3.Parent.Parent; Assert.Equal("var", x34Var.Type.ToString()); Assert.Equal("(System.Int32 x3, System.Int32 x4)", model.GetSymbolInfo(x34Var.Type).Symbol.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 3 4", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void DeclarationWithAliasedVarType() { string source = @" using @var = D; class C { static void Main() { (var x1, int x2) = (new var(), 2); System.Console.WriteLine(x1 + "" "" + x2); } } class D { public override string ToString() { return ""var""; } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); // extra checks on x1 var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("D", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); var x1Alias = model.GetAliasInfo(x1Type); Assert.Equal(SymbolKind.NamedType, x1Alias.Target.Kind); Assert.Equal("D", x1Alias.Target.ToDisplayString()); // extra checks on x2 var x2Type = GetTypeSyntax(x2); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x2Type).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(x2Type).Symbol.ToDisplayString()); Assert.Null(model.GetAliasInfo(x2Type)); }; var comp = CompileAndVerify(source, expectedOutput: "var 2", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void ForWithImplicitVarType() { string source = @" class C { static void Main() { for (var (x1, x2) = (1, 2); x1 < 2; (x1, x2) = (x1 + 1, x2 + 1)) { System.Console.WriteLine(x1 + "" "" + x2); } } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReferences(tree, "x1", 4); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReferences(tree, "x2", 3); VerifyModelForDeconstructionLocal(model, x2, x2Ref); // extra check on var var x12Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x12Var.Type.ToString()); Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetSymbolInfo(x12Var.Type).Symbol.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 2", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void ForWithVarDeconstructInitializersCanParse() { string source = @" using System; class C { static void Main() { int x3; for (var (x1, x2) = (1, 2), x3 = 3; true; ) { Console.WriteLine(x1); Console.WriteLine(x2); Console.WriteLine(x3); break; } } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); }; var comp = CompileAndVerify(source, expectedOutput: @"1 2 3", sourceSymbolValidator: validator); comp.VerifyDiagnostics( // this is permitted now, as it is just an assignment expression ); } [Fact] public void ForWithActualVarType() { string source = @" class C { static void Main() { for ((int x1, var x2) = (1, new var()); x1 < 2; x1++) { System.Console.WriteLine(x1 + "" "" + x2); } } } class @var { public override string ToString() { return ""var""; } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReferences(tree, "x1", 3); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); // extra checks on x1 var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); // extra checks on x2 var x2Type = GetTypeSyntax(x2); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x2Type).Symbol.Kind); Assert.Equal("var", model.GetSymbolInfo(x2Type).Symbol.ToDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 var", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void ForWithTypes() { string source = @" class C { static void Main() { for ((int x1, var x2) = (1, 2); x1 < 2; x1++) { System.Console.WriteLine(x1 + "" "" + x2); } } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReferences(tree, "x1", 3); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); // extra checks on x1 var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); // extra checks on x2 var x2Type = GetTypeSyntax(x2); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x2Type).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(x2Type).Symbol.ToDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 2", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void ForEachIEnumerableDeclarationWithImplicitVarType() { string source = @" using System.Collections.Generic; class C { static void Main() { foreach (var (x1, x2) in M()) { Print(x1, x2); } } static IEnumerable<(int, int)> M() { yield return (1, 2); } static void Print(object a, object b) { System.Console.WriteLine(a + "" "" + b); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionForeach(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionForeach(model, x2, x2Ref); // extra check on var var x12Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x12Var.Type.ToString()); Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetTypeInfo(x12Var).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetSymbolInfo(x12Var.Type).Symbol.ToTestDisplayString()); // verify deconstruction info var deconstructionForeach = tree.GetRoot().DescendantNodes().OfType<ForEachVariableStatementSyntax>().Single(); var deconstructionInfo = model.GetDeconstructionInfo(deconstructionForeach); Assert.Null(deconstructionInfo.Method); Assert.Null(deconstructionInfo.Conversion); var nested = deconstructionInfo.Nested; Assert.Equal(2, nested.Length); Assert.Null(nested[0].Method); Assert.Equal(ConversionKind.Identity, nested[0].Conversion.Value.Kind); Assert.Empty(nested[0].Nested); Assert.Null(nested[1].Method); Assert.Equal(ConversionKind.Identity, nested[1].Conversion.Value.Kind); Assert.Empty(nested[1].Nested); }; var comp = CompileAndVerify(source, expectedOutput: "1 2", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); var comp7_1 = CompileAndVerify(source, expectedOutput: "1 2", sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); comp7_1.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 70 (0x46) .maxstack 2 .locals init (System.Collections.Generic.IEnumerator<System.ValueTuple<int, int>> V_0, int V_1, //x1 int V_2) //x2 IL_0000: call ""System.Collections.Generic.IEnumerable<System.ValueTuple<int, int>> C.M()"" IL_0005: callvirt ""System.Collections.Generic.IEnumerator<System.ValueTuple<int, int>> System.Collections.Generic.IEnumerable<System.ValueTuple<int, int>>.GetEnumerator()"" IL_000a: stloc.0 .try { IL_000b: br.s IL_0031 IL_000d: ldloc.0 IL_000e: callvirt ""System.ValueTuple<int, int> System.Collections.Generic.IEnumerator<System.ValueTuple<int, int>>.Current.get"" IL_0013: dup IL_0014: ldfld ""int System.ValueTuple<int, int>.Item1"" IL_0019: stloc.1 IL_001a: ldfld ""int System.ValueTuple<int, int>.Item2"" IL_001f: stloc.2 IL_0020: ldloc.1 IL_0021: box ""int"" IL_0026: ldloc.2 IL_0027: box ""int"" IL_002c: call ""void C.Print(object, object)"" IL_0031: ldloc.0 IL_0032: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" IL_0037: brtrue.s IL_000d IL_0039: leave.s IL_0045 } finally { IL_003b: ldloc.0 IL_003c: brfalse.s IL_0044 IL_003e: ldloc.0 IL_003f: callvirt ""void System.IDisposable.Dispose()"" IL_0044: endfinally } IL_0045: ret }"); } [Fact] public void ForEachSZArrayDeclarationWithImplicitVarType() { string source = @" class C { static void Main() { foreach (var (x1, x2) in M()) { System.Console.Write(x1 + "" "" + x2 + "" - ""); } } static (int, int)[] M() { return new[] { (1, 2), (3, 4) }; } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); var symbol = model.GetDeclaredSymbol(x1); VerifyModelForDeconstructionForeach(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionForeach(model, x2, x2Ref); // extra check on var var x12Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x12Var.Type.ToString()); Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetSymbolInfo(x12Var.Type).Symbol.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 - 3 4 -", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 75 (0x4b) .maxstack 4 .locals init (System.ValueTuple<int, int>[] V_0, int V_1, int V_2, //x1 int V_3) //x2 IL_0000: call ""System.ValueTuple<int, int>[] C.M()"" IL_0005: stloc.0 IL_0006: ldc.i4.0 IL_0007: stloc.1 IL_0008: br.s IL_0044 IL_000a: ldloc.0 IL_000b: ldloc.1 IL_000c: ldelem ""System.ValueTuple<int, int>"" IL_0011: dup IL_0012: ldfld ""int System.ValueTuple<int, int>.Item1"" IL_0017: stloc.2 IL_0018: ldfld ""int System.ValueTuple<int, int>.Item2"" IL_001d: stloc.3 IL_001e: ldloca.s V_2 IL_0020: call ""string int.ToString()"" IL_0025: ldstr "" "" IL_002a: ldloca.s V_3 IL_002c: call ""string int.ToString()"" IL_0031: ldstr "" - "" IL_0036: call ""string string.Concat(string, string, string, string)"" IL_003b: call ""void System.Console.Write(string)"" IL_0040: ldloc.1 IL_0041: ldc.i4.1 IL_0042: add IL_0043: stloc.1 IL_0044: ldloc.1 IL_0045: ldloc.0 IL_0046: ldlen IL_0047: conv.i4 IL_0048: blt.s IL_000a IL_004a: ret }"); } [Fact] public void ForEachMDArrayDeclarationWithImplicitVarType() { string source = @" class C { static void Main() { foreach (var (x1, x2) in M()) { Print(x1, x2); } } static (int, int)[,] M() { return new (int, int)[2, 2] { { (1, 2), (3, 4) }, { (5, 6), (7, 8) } }; } static void Print(object a, object b) { System.Console.Write(a + "" "" + b + "" - ""); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionForeach(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionForeach(model, x2, x2Ref); // extra check on var var x12Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x12Var.Type.ToString()); Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetSymbolInfo(x12Var.Type).Symbol.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 - 3 4 - 5 6 - 7 8 -", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 106 (0x6a) .maxstack 3 .locals init (System.ValueTuple<int, int>[,] V_0, int V_1, int V_2, int V_3, int V_4, int V_5, //x1 int V_6) //x2 IL_0000: call ""System.ValueTuple<int, int>[,] C.M()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.0 IL_0008: callvirt ""int System.Array.GetUpperBound(int)"" IL_000d: stloc.1 IL_000e: ldloc.0 IL_000f: ldc.i4.1 IL_0010: callvirt ""int System.Array.GetUpperBound(int)"" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldc.i4.0 IL_0018: callvirt ""int System.Array.GetLowerBound(int)"" IL_001d: stloc.3 IL_001e: br.s IL_0065 IL_0020: ldloc.0 IL_0021: ldc.i4.1 IL_0022: callvirt ""int System.Array.GetLowerBound(int)"" IL_0027: stloc.s V_4 IL_0029: br.s IL_005c IL_002b: ldloc.0 IL_002c: ldloc.3 IL_002d: ldloc.s V_4 IL_002f: call ""(int, int)[*,*].Get"" IL_0034: dup IL_0035: ldfld ""int System.ValueTuple<int, int>.Item1"" IL_003a: stloc.s V_5 IL_003c: ldfld ""int System.ValueTuple<int, int>.Item2"" IL_0041: stloc.s V_6 IL_0043: ldloc.s V_5 IL_0045: box ""int"" IL_004a: ldloc.s V_6 IL_004c: box ""int"" IL_0051: call ""void C.Print(object, object)"" IL_0056: ldloc.s V_4 IL_0058: ldc.i4.1 IL_0059: add IL_005a: stloc.s V_4 IL_005c: ldloc.s V_4 IL_005e: ldloc.2 IL_005f: ble.s IL_002b IL_0061: ldloc.3 IL_0062: ldc.i4.1 IL_0063: add IL_0064: stloc.3 IL_0065: ldloc.3 IL_0066: ldloc.1 IL_0067: ble.s IL_0020 IL_0069: ret }"); } [Fact] public void ForEachStringDeclarationWithImplicitVarType() { string source = @" class C { static void Main() { foreach (var (x1, x2) in M()) { Print(x1, x2); } } static string M() { return ""123""; } static void Print(object a, object b) { System.Console.Write(a + "" "" + b + "" - ""); } } static class Extension { public static void Deconstruct(this char value, out int item1, out int item2) { item1 = item2 = System.Int32.Parse(value.ToString()); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionForeach(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionForeach(model, x2, x2Ref); // extra check on var var x12Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x12Var.Type.ToString()); Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetSymbolInfo(x12Var.Type).Symbol.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 1 - 2 2 - 3 3 - ", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 60 (0x3c) .maxstack 3 .locals init (string V_0, int V_1, int V_2, //x2 int V_3, int V_4) IL_0000: call ""string C.M()"" IL_0005: stloc.0 IL_0006: ldc.i4.0 IL_0007: stloc.1 IL_0008: br.s IL_0032 IL_000a: ldloc.0 IL_000b: ldloc.1 IL_000c: callvirt ""char string.this[int].get"" IL_0011: ldloca.s V_3 IL_0013: ldloca.s V_4 IL_0015: call ""void Extension.Deconstruct(char, out int, out int)"" IL_001a: ldloc.3 IL_001b: ldloc.s V_4 IL_001d: stloc.2 IL_001e: box ""int"" IL_0023: ldloc.2 IL_0024: box ""int"" IL_0029: call ""void C.Print(object, object)"" IL_002e: ldloc.1 IL_002f: ldc.i4.1 IL_0030: add IL_0031: stloc.1 IL_0032: ldloc.1 IL_0033: ldloc.0 IL_0034: callvirt ""int string.Length.get"" IL_0039: blt.s IL_000a IL_003b: ret }"); } [Fact] [WorkItem(22495, "https://github.com/dotnet/roslyn/issues/22495")] public void ForEachCollectionSymbol() { string source = @" using System.Collections.Generic; class Deconstructable { void M(IEnumerable<Deconstructable> x) { foreach (var (y1, y2) in x) { } } void Deconstruct(out int i, out int j) { i = 0; j = 0; } } "; var compilation = CreateCompilation(source); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<ForEachVariableStatementSyntax>().Single().Expression; Assert.Equal("x", collection.ToString()); var symbol = model.GetSymbolInfo(collection).Symbol; Assert.Equal(SymbolKind.Parameter, symbol.Kind); Assert.Equal("x", symbol.Name); Assert.Equal("System.Collections.Generic.IEnumerable<Deconstructable> x", symbol.ToTestDisplayString()); } [Fact] public void ForEachIEnumerableDeclarationWithNesting() { string source = @" using System.Collections.Generic; class C { static void Main() { foreach ((int x1, var (x2, x3), (int x4, int x5)) in M()) { System.Console.Write(x1 + "" "" + x2 + "" "" + x3 + "" "" + x4 + "" "" + x5 + "" - ""); } } static IEnumerable<(int, (int, int), (int, int))> M() { yield return (1, (2, 3), (4, 5)); yield return (6, (7, 8), (9, 10)); } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionForeach(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionForeach(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionForeach(model, x3, x3Ref); var x4 = GetDeconstructionVariable(tree, "x4"); var x4Ref = GetReference(tree, "x4"); VerifyModelForDeconstructionForeach(model, x4, x4Ref); var x5 = GetDeconstructionVariable(tree, "x5"); var x5Ref = GetReference(tree, "x5"); VerifyModelForDeconstructionForeach(model, x5, x5Ref); // extra check on var var x23Var = (DeclarationExpressionSyntax)x2.Parent.Parent; Assert.Equal("var", x23Var.Type.ToString()); Assert.Equal("(System.Int32 x2, System.Int32 x3)", model.GetSymbolInfo(x23Var.Type).Symbol.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 2 3 4 5 - 6 7 8 9 10 -", sourceSymbolValidator: validator); comp.VerifyDiagnostics(); } [Fact] public void ForEachSZArrayDeclarationWithNesting() { string source = @" class C { static void Main() { foreach ((int x1, var (x2, x3), (int x4, int x5)) in M()) { System.Console.Write(x1 + "" "" + x2 + "" "" + x3 + "" "" + x4 + "" "" + x5 + "" - ""); } } static (int, (int, int), (int, int))[] M() { return new[] { (1, (2, 3), (4, 5)), (6, (7, 8), (9, 10)) }; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2 3 4 5 - 6 7 8 9 10 -"); comp.VerifyDiagnostics(); } [Fact] public void ForEachMDArrayDeclarationWithNesting() { string source = @" class C { static void Main() { foreach ((int x1, var (x2, x3), (int x4, int x5)) in M()) { System.Console.Write(x1 + "" "" + x2 + "" "" + x3 + "" "" + x4 + "" "" + x5 + "" - ""); } } static (int, (int, int), (int, int))[,] M() { return new(int, (int, int), (int, int))[1, 2] { { (1, (2, 3), (4, 5)), (6, (7, 8), (9, 10)) } }; } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2 3 4 5 - 6 7 8 9 10 -"); comp.VerifyDiagnostics(); } [Fact] public void ForEachStringDeclarationWithNesting() { string source = @" class C { static void Main() { foreach ((int x1, var (x2, x3)) in M()) { System.Console.Write(x1 + "" "" + x2 + "" "" + x3 + "" - ""); } } static string M() { return ""12""; } } static class Extension { public static void Deconstruct(this char value, out int item1, out (int, int) item2) { item1 = System.Int32.Parse(value.ToString()); item2 = (item1, item1); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 1 1 - 2 2 2 - "); comp.VerifyDiagnostics(); } [Fact] public void DeconstructExtensionOnInterface() { string source = @" public interface Interface { } class C : Interface { static void Main() { var (x, y) = new C(); System.Console.Write($""{x} {y}""); } } static class Extension { public static void Deconstruct(this Interface value, out int item1, out string item2) { item1 = 42; item2 = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "42 hello"); comp.VerifyDiagnostics(); } [Fact] public void ForEachIEnumerableDeclarationWithDeconstruct() { string source = @" using System.Collections.Generic; class C { static void Main() { foreach ((long x1, var (x2, x3)) in M()) { Print(x1, x2, x3); } } static IEnumerable<Pair<int, Pair<int, int>>> M() { yield return Pair.Create(1, Pair.Create(2, 3)); yield return Pair.Create(4, Pair.Create(5, 6)); } static void Print(object a, object b, object c) { System.Console.WriteLine(a + "" "" + b + "" "" + c); } } " + commonSource; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionForeach(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionForeach(model, x2, x2Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Ref = GetReference(tree, "x3"); VerifyModelForDeconstructionForeach(model, x3, x3Ref); // extra check on var var x23Var = (DeclarationExpressionSyntax)x2.Parent.Parent; Assert.Equal("var", x23Var.Type.ToString()); Assert.Equal("(System.Int32 x2, System.Int32 x3)", model.GetSymbolInfo(x23Var.Type).Symbol.ToTestDisplayString()); }; string expected = @"Deconstructing (1, (2, 3)) Deconstructing (2, 3) 1 2 3 Deconstructing (4, (5, 6)) Deconstructing (5, 6) 4 5 6"; var comp = CompileAndVerify(source, expectedOutput: expected, sourceSymbolValidator: validator); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 90 (0x5a) .maxstack 3 .locals init (System.Collections.Generic.IEnumerator<Pair<int, Pair<int, int>>> V_0, int V_1, //x2 int V_2, //x3 int V_3, Pair<int, int> V_4, int V_5, int V_6) IL_0000: call ""System.Collections.Generic.IEnumerable<Pair<int, Pair<int, int>>> C.M()"" IL_0005: callvirt ""System.Collections.Generic.IEnumerator<Pair<int, Pair<int, int>>> System.Collections.Generic.IEnumerable<Pair<int, Pair<int, int>>>.GetEnumerator()"" IL_000a: stloc.0 .try { IL_000b: br.s IL_0045 IL_000d: ldloc.0 IL_000e: callvirt ""Pair<int, Pair<int, int>> System.Collections.Generic.IEnumerator<Pair<int, Pair<int, int>>>.Current.get"" IL_0013: ldloca.s V_3 IL_0015: ldloca.s V_4 IL_0017: callvirt ""void Pair<int, Pair<int, int>>.Deconstruct(out int, out Pair<int, int>)"" IL_001c: ldloc.s V_4 IL_001e: ldloca.s V_5 IL_0020: ldloca.s V_6 IL_0022: callvirt ""void Pair<int, int>.Deconstruct(out int, out int)"" IL_0027: ldloc.3 IL_0028: conv.i8 IL_0029: ldloc.s V_5 IL_002b: stloc.1 IL_002c: ldloc.s V_6 IL_002e: stloc.2 IL_002f: box ""long"" IL_0034: ldloc.1 IL_0035: box ""int"" IL_003a: ldloc.2 IL_003b: box ""int"" IL_0040: call ""void C.Print(object, object, object)"" IL_0045: ldloc.0 IL_0046: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" IL_004b: brtrue.s IL_000d IL_004d: leave.s IL_0059 } finally { IL_004f: ldloc.0 IL_0050: brfalse.s IL_0058 IL_0052: ldloc.0 IL_0053: callvirt ""void System.IDisposable.Dispose()"" IL_0058: endfinally } IL_0059: ret } "); } [Fact] public void ForEachSZArrayDeclarationWithDeconstruct() { string source = @" class C { static void Main() { foreach ((int x1, var (x2, x3)) in M()) { System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } static Pair<int, Pair<int, int>>[] M() { return new[] { Pair.Create(1, Pair.Create(2, 3)), Pair.Create(4, Pair.Create(5, 6)) }; } } " + commonSource; string expected = @"Deconstructing (1, (2, 3)) Deconstructing (2, 3) 1 2 3 Deconstructing (4, (5, 6)) Deconstructing (5, 6) 4 5 6"; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void ForEachMDArrayDeclarationWithDeconstruct() { string source = @" class C { static void Main() { foreach ((int x1, var (x2, x3)) in M()) { System.Console.WriteLine(x1 + "" "" + x2 + "" "" + x3); } } static Pair<int, Pair<int, int>>[,] M() { return new Pair<int, Pair<int, int>> [1, 2] { { Pair.Create(1, Pair.Create(2, 3)), Pair.Create(4, Pair.Create(5, 6)) } }; } } " + commonSource; string expected = @"Deconstructing (1, (2, 3)) Deconstructing (2, 3) 1 2 3 Deconstructing (4, (5, 6)) Deconstructing (5, 6) 4 5 6"; var comp = CompileAndVerify(source, expectedOutput: expected); comp.VerifyDiagnostics(); } [Fact] public void ForEachWithExpressionBody() { string source = @" class C { static void Main() { foreach (var (x1, x2) in new[] { (1, 2), (3, 4) }) System.Console.Write(x1 + "" "" + x2 + "" - ""); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2 - 3 4 -"); comp.VerifyDiagnostics(); } [Fact] public void ForEachCreatesNewVariables() { string source = @" class C { static void Main() { var lambdas = new System.Action[2]; int index = 0; foreach (var (x1, x2) in M()) { lambdas[index] = () => { System.Console.Write(x1 + "" ""); }; index++; } lambdas[0](); lambdas[1](); } static (int, int)[] M() { return new[] { (0, 0), (10, 10) }; } } "; var comp = CompileAndVerify(source, expectedOutput: "0 10 "); comp.VerifyDiagnostics(); } [Fact] public void TupleDeconstructionIntoDynamicArrayIndexer() { string source = @" class C { static void Main() { dynamic x = new string[] { """", """" }; M(x); System.Console.WriteLine($""{x[0]} {x[1]}""); } static void M(dynamic x) { (x[0], x[1]) = (""hello"", ""world""); } } "; var comp = CompileAndVerify(source, expectedOutput: "hello world", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void IntTupleDeconstructionIntoDynamicArrayIndexer() { string source = @" class C { static void Main() { dynamic x = new int[] { 1, 2 }; M(x); System.Console.WriteLine($""{x[0]} {x[1]}""); } static void M(dynamic x) { (x[0], x[1]) = (3, 4); } } "; var comp = CompileAndVerify(source, expectedOutput: "3 4", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void DeconstructionIntoDynamicArrayIndexer() { string source = @" class C { static void Main() { dynamic x = new string[] { """", """" }; M(x); System.Console.WriteLine($""{x[0]} {x[1]}""); } static void M(dynamic x) { (x[0], x[1]) = new C(); } public void Deconstruct(out string a, out string b) { a = ""hello""; b = ""world""; } } "; var comp = CompileAndVerify(source, expectedOutput: "hello world", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void TupleDeconstructionIntoDynamicArray() { string source = @" class C { static void Main() { dynamic[] x = new string[] { """", """" }; M(x); System.Console.WriteLine($""{x[0]} {x[1]}""); } static void M(dynamic[] x) { (x[0], x[1]) = (""hello"", ""world""); } } "; var comp = CompileAndVerify(source, expectedOutput: "hello world", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void DeconstructionIntoDynamicArray() { string source = @" class C { static void Main() { dynamic[] x = new string[] { """", """" }; M(x); System.Console.WriteLine($""{x[0]} {x[1]}""); } static void M(dynamic[] x) { (x[0], x[1]) = new C(); } public void Deconstruct(out string a, out string b) { a = ""hello""; b = ""world""; } } "; var comp = CompileAndVerify(source, expectedOutput: "hello world", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void DeconstructionIntoDynamicMember() { string source = @" class C { static void Main() { dynamic x = System.ValueTuple.Create(1, 2); (x.Item1, x.Item2) = new C(); System.Console.WriteLine($""{x.Item1} {x.Item2}""); } public void Deconstruct(out int a, out int b) { a = 3; b = 4; } } "; var comp = CompileAndVerify(source, expectedOutput: "3 4", references: new[] { CSharpRef }); comp.VerifyDiagnostics(); } [Fact] public void FieldAndLocalWithSameName() { string source = @" class C { public int x = 3; static void Main() { new C().M(); } void M() { var (x, y) = (1, 2); System.Console.Write($""{x} {y} {this.x}""); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2 3"); comp.VerifyDiagnostics(); } [Fact] public void NoGlobalDeconstructionUnlessScript() { string source = @" class C { var (x, y) = (1, 2); } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (4,11): error CS1001: Identifier expected // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(4, 11), // (4,14): error CS1001: Identifier expected // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 14), // (4,16): error CS1002: ; expected // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_SemicolonExpected, "=").WithLocation(4, 16), // (4,16): error CS1519: Invalid token '=' in class, record, struct, or interface member declaration // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=").WithArguments("=").WithLocation(4, 16), // (4,19): error CS1031: Type expected // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_TypeExpected, "1").WithLocation(4, 19), // (4,19): error CS8124: Tuple must contain at least two elements. // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_TupleTooFewElements, "1").WithLocation(4, 19), // (4,19): error CS1026: ) expected // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_CloseParenExpected, "1").WithLocation(4, 19), // (4,19): error CS1519: Invalid token '1' in class, record, struct, or interface member declaration // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "1").WithArguments("1").WithLocation(4, 19), // (4,5): error CS1520: Method must have a return type // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_MemberNeedsType, "var").WithLocation(4, 5), // (4,5): error CS0501: 'C.C(x, y)' must declare a body because it is not marked abstract, extern, or partial // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "var").WithArguments("C.C(x, y)").WithLocation(4, 5), // (4,10): error CS0246: The type or namespace name 'x' could not be found (are you missing a using directive or an assembly reference?) // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "x").WithArguments("x").WithLocation(4, 10), // (4,13): error CS0246: The type or namespace name 'y' could not be found (are you missing a using directive or an assembly reference?) // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "y").WithArguments("y").WithLocation(4, 13) ); var nodes = comp.SyntaxTrees[0].GetCompilationUnitRoot().DescendantNodesAndSelf(); Assert.False(nodes.Any(n => n.Kind() == SyntaxKind.SimpleAssignmentExpression)); } [Fact] public void SimpleDeconstructionInScript() { var source = @" using @alias = System.Int32; (string x, alias y) = (""hello"", 42); System.Console.Write($""{x} {y}""); "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x = GetDeconstructionVariable(tree, "x"); var xSymbol = model.GetDeclaredSymbol(x); var xRef = GetReference(tree, "x"); Assert.Equal("System.String Script.x", xSymbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x, xRef); var y = GetDeconstructionVariable(tree, "y"); var ySymbol = model.GetDeclaredSymbol(y); var yRef = GetReference(tree, "y"); Assert.Equal("System.Int32 Script.y", ySymbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, y, yRef); // extra checks on x var xType = GetTypeSyntax(x); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(xType).Symbol.Kind); Assert.Equal("string", model.GetSymbolInfo(xType).Symbol.ToDisplayString()); Assert.Null(model.GetAliasInfo(xType)); // extra checks on y var yType = GetTypeSyntax(y); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(yType).Symbol.Kind); Assert.Equal("int", model.GetSymbolInfo(yType).Symbol.ToDisplayString()); Assert.Equal("alias=System.Int32", model.GetAliasInfo(yType).ToTestDisplayString()); }; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "hello 42", sourceSymbolValidator: validator); verifier.VerifyIL("<<Initialize>>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 129 (0x81) .maxstack 3 .locals init (int V_0, object V_1, System.Exception V_2) IL_0000: ldarg.0 IL_0001: ldfld ""int <<Initialize>>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldarg.0 IL_0008: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_000d: ldstr ""hello"" IL_0012: stfld ""string x"" IL_0017: ldarg.0 IL_0018: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_001d: ldc.i4.s 42 IL_001f: stfld ""int y"" IL_0024: ldstr ""{0} {1}"" IL_0029: ldarg.0 IL_002a: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_002f: ldfld ""string x"" IL_0034: ldarg.0 IL_0035: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_003a: ldfld ""int y"" IL_003f: box ""int"" IL_0044: call ""string string.Format(string, object, object)"" IL_0049: call ""void System.Console.Write(string)"" IL_004e: nop IL_004f: ldnull IL_0050: stloc.1 IL_0051: leave.s IL_006b } catch System.Exception { IL_0053: stloc.2 IL_0054: ldarg.0 IL_0055: ldc.i4.s -2 IL_0057: stfld ""int <<Initialize>>d__0.<>1__state"" IL_005c: ldarg.0 IL_005d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object> <<Initialize>>d__0.<>t__builder"" IL_0062: ldloc.2 IL_0063: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object>.SetException(System.Exception)"" IL_0068: nop IL_0069: leave.s IL_0080 } IL_006b: ldarg.0 IL_006c: ldc.i4.s -2 IL_006e: stfld ""int <<Initialize>>d__0.<>1__state"" IL_0073: ldarg.0 IL_0074: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object> <<Initialize>>d__0.<>t__builder"" IL_0079: ldloc.1 IL_007a: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object>.SetResult(object)"" IL_007f: nop IL_0080: ret }"); } [Fact] public void GlobalDeconstructionOutsideScript() { var source = @" (string x, int y) = (""hello"", 42); System.Console.Write(x); System.Console.Write(y); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Regular9, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var nodes = comp.SyntaxTrees[0].GetCompilationUnitRoot().DescendantNodesAndSelf(); Assert.True(nodes.Any(n => n.Kind() == SyntaxKind.SimpleAssignmentExpression)); CompileAndVerify(comp, expectedOutput: "hello42"); } [Fact] public void NestedDeconstructionInScript() { var source = @" (string x, (int y, int z)) = (""hello"", (42, 43)); System.Console.Write($""{x} {y} {z}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "hello 42 43"); } [Fact] public void VarDeconstructionInScript() { var source = @" (var x, var y) = (""hello"", 42); System.Console.Write($""{x} {y}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "hello 42"); } [Fact] public void NestedVarDeconstructionInScript() { var source = @" (var x1, var (x2, x3)) = (""hello"", (42, 43)); System.Console.Write($""{x1} {x2} {x3}""); "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("System.String Script.x1", x1Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Symbol = model.GetDeclaredSymbol(x2); var x2Ref = GetReference(tree, "x2"); Assert.Equal("System.Int32 Script.x2", x2Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x2, x2Ref); // extra checks on x1's var var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("string", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); Assert.Null(model.GetAliasInfo(x1Type)); // extra check on x2 and x3's var var x23Var = (DeclarationExpressionSyntax)x2.Parent.Parent; Assert.Equal("var", x23Var.Type.ToString()); Assert.Equal("(System.Int32 x2, System.Int32 x3)", model.GetSymbolInfo(x23Var.Type).Symbol.ToTestDisplayString()); }; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "hello 42 43", sourceSymbolValidator: validator); } [Fact] public void EvaluationOrderForDeconstructionInScript() { var source = @" (int, int) M(out int x) { x = 1; return (2, 3); } var (x2, x3) = M(out var x1); System.Console.Write($""{x1} {x2} {x3}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "1 2 3"); verifier.VerifyIL("<<Initialize>>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 178 (0xb2) .maxstack 4 .locals init (int V_0, object V_1, System.ValueTuple<int, int> V_2, System.Exception V_3) IL_0000: ldarg.0 IL_0001: ldfld ""int <<Initialize>>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldarg.0 IL_0008: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_000d: ldarg.0 IL_000e: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_0013: ldflda ""int x1"" IL_0018: call ""System.ValueTuple<int, int> M(out int)"" IL_001d: stloc.2 IL_001e: ldarg.0 IL_001f: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_0024: ldloc.2 IL_0025: ldfld ""int System.ValueTuple<int, int>.Item1"" IL_002a: stfld ""int x2"" IL_002f: ldarg.0 IL_0030: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_0035: ldloc.2 IL_0036: ldfld ""int System.ValueTuple<int, int>.Item2"" IL_003b: stfld ""int x3"" IL_0040: ldstr ""{0} {1} {2}"" IL_0045: ldarg.0 IL_0046: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_004b: ldfld ""int x1"" IL_0050: box ""int"" IL_0055: ldarg.0 IL_0056: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_005b: ldfld ""int x2"" IL_0060: box ""int"" IL_0065: ldarg.0 IL_0066: ldfld ""Script <<Initialize>>d__0.<>4__this"" IL_006b: ldfld ""int x3"" IL_0070: box ""int"" IL_0075: call ""string string.Format(string, object, object, object)"" IL_007a: call ""void System.Console.Write(string)"" IL_007f: nop IL_0080: ldnull IL_0081: stloc.1 IL_0082: leave.s IL_009c } catch System.Exception { IL_0084: stloc.3 IL_0085: ldarg.0 IL_0086: ldc.i4.s -2 IL_0088: stfld ""int <<Initialize>>d__0.<>1__state"" IL_008d: ldarg.0 IL_008e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object> <<Initialize>>d__0.<>t__builder"" IL_0093: ldloc.3 IL_0094: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object>.SetException(System.Exception)"" IL_0099: nop IL_009a: leave.s IL_00b1 } IL_009c: ldarg.0 IL_009d: ldc.i4.s -2 IL_009f: stfld ""int <<Initialize>>d__0.<>1__state"" IL_00a4: ldarg.0 IL_00a5: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object> <<Initialize>>d__0.<>t__builder"" IL_00aa: ldloc.1 IL_00ab: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<object>.SetResult(object)"" IL_00b0: nop IL_00b1: ret } "); } [Fact] public void DeconstructionForEachInScript() { var source = @" foreach ((string x1, var (x2, x3)) in new[] { (""hello"", (42, ""world"")) }) { System.Console.Write($""{x1} {x2} {x3}""); } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); Assert.Equal("System.String x1", x1Symbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Local, x1Symbol.Kind); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Symbol = model.GetDeclaredSymbol(x2); Assert.Equal("System.Int32 x2", x2Symbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Local, x2Symbol.Kind); }; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "hello 42 world", sourceSymbolValidator: validator); } [Fact] public void DeconstructionInForLoopInScript() { var source = @" for ((string x1, var (x2, x3)) = (""hello"", (42, ""world"")); ; ) { System.Console.Write($""{x1} {x2} {x3}""); break; } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); Assert.Equal("System.String x1", x1Symbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Local, x1Symbol.Kind); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Symbol = model.GetDeclaredSymbol(x2); Assert.Equal("System.Int32 x2", x2Symbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Local, x2Symbol.Kind); }; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "hello 42 world", sourceSymbolValidator: validator); } [Fact] public void DeconstructionInCSharp6Script() { var source = @" var (x, y) = (1, 2); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script.WithLanguageVersion(LanguageVersion.CSharp6), options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (2,5): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(x, y)").WithArguments("tuples", "7.0").WithLocation(2, 5), // (2,14): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater. // var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(1, 2)").WithArguments("tuples", "7.0").WithLocation(2, 14) ); } [Fact] public void InvalidDeconstructionInScript() { var source = @" int (x, y) = (1, 2); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (2,5): error CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // int (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "(x, y)").WithLocation(2, 5) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x = GetDeconstructionVariable(tree, "x"); var xSymbol = model.GetDeclaredSymbol(x); Assert.Equal("System.Int32 Script.x", xSymbol.ToTestDisplayString()); var xType = ((IFieldSymbol)xSymbol).Type; Assert.False(xType.IsErrorType()); Assert.Equal("System.Int32", xType.ToTestDisplayString()); var y = GetDeconstructionVariable(tree, "y"); var ySymbol = model.GetDeclaredSymbol(y); Assert.Equal("System.Int32 Script.y", ySymbol.ToTestDisplayString()); var yType = ((IFieldSymbol)ySymbol).Type; Assert.False(yType.IsErrorType()); Assert.Equal("System.Int32", yType.ToTestDisplayString()); } [Fact] public void InvalidDeconstructionInScript_2() { var source = @" (int (x, y), int z) = ((1, 2), 3); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (2,6): error CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // (int (x, y), int z) = ((1, 2), 3); Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "(x, y)").WithLocation(2, 6) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x = GetDeconstructionVariable(tree, "x"); var xSymbol = model.GetDeclaredSymbol(x); Assert.Equal("System.Int32 Script.x", xSymbol.ToTestDisplayString()); var xType = ((IFieldSymbol)xSymbol).Type; Assert.False(xType.IsErrorType()); Assert.Equal("System.Int32", xType.ToTestDisplayString()); var y = GetDeconstructionVariable(tree, "y"); var ySymbol = model.GetDeclaredSymbol(y); Assert.Equal("System.Int32 Script.y", ySymbol.ToTestDisplayString()); var yType = ((IFieldSymbol)ySymbol).Type; Assert.False(yType.IsErrorType()); Assert.Equal("System.Int32", yType.ToTestDisplayString()); } [Fact] public void NameConflictInDeconstructionInScript() { var source = @" int x1; var (x1, x2) = (1, 2); System.Console.Write(x1); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (3,6): error CS0102: The type 'Script' already contains a definition for 'x1' // var (x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "x1").WithArguments("Script", "x1").WithLocation(3, 6), // (4,22): error CS0229: Ambiguity between 'x1' and 'x1' // System.Console.Write(x1); Diagnostic(ErrorCode.ERR_AmbigMember, "x1").WithArguments("x1", "x1").WithLocation(4, 22) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var firstX1 = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().Where(d => d.Identifier.ValueText == "x1").Single(); var firstX1Symbol = model.GetDeclaredSymbol(firstX1); Assert.Equal("System.Int32 Script.x1", firstX1Symbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, firstX1Symbol.Kind); var secondX1 = GetDeconstructionVariable(tree, "x1"); var secondX1Symbol = model.GetDeclaredSymbol(secondX1); Assert.Equal("System.Int32 Script.x1", secondX1Symbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, secondX1Symbol.Kind); Assert.NotEqual(firstX1Symbol, secondX1Symbol); } [Fact] public void NameConflictInDeconstructionInScript2() { var source = @" var (x, y) = (1, 2); var (z, y) = (1, 2); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (3,9): error CS0102: The type 'Script' already contains a definition for 'y' // var (z, y) = (1, 2); Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "y").WithArguments("Script", "y").WithLocation(3, 9) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var firstY = tree.GetRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().Where(d => d.Identifier.ValueText == "y").First(); var firstYSymbol = model.GetDeclaredSymbol(firstY); Assert.Equal("System.Int32 Script.y", firstYSymbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, firstYSymbol.Kind); var secondY = tree.GetRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().Where(d => d.Identifier.ValueText == "y").ElementAt(1); var secondYSymbol = model.GetDeclaredSymbol(secondY); Assert.Equal("System.Int32 Script.y", secondYSymbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, secondYSymbol.Kind); Assert.NotEqual(firstYSymbol, secondYSymbol); } [Fact] public void NameConflictInDeconstructionInScript3() { var source = @" var (x, (y, x)) = (1, (2, ""hello"")); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (2,13): error CS0102: The type 'Script' already contains a definition for 'x' // var (x, (y, x)) = (1, (2, "hello")); Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "x").WithArguments("Script", "x").WithLocation(2, 13) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var firstX = tree.GetRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().Where(d => d.Identifier.ValueText == "x").First(); var firstXSymbol = model.GetDeclaredSymbol(firstX); Assert.Equal("System.Int32 Script.x", firstXSymbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, firstXSymbol.Kind); var secondX = tree.GetRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().Where(d => d.Identifier.ValueText == "x").ElementAt(1); var secondXSymbol = model.GetDeclaredSymbol(secondX); Assert.Equal("System.String Script.x", secondXSymbol.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, secondXSymbol.Kind); Assert.NotEqual(firstXSymbol, secondXSymbol); } [Fact] public void UnassignedUsedInDeconstructionInScript() { var source = @" System.Console.Write(x); var (x, y) = (1, 2); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x = GetDeconstructionVariable(tree, "x"); var xSymbol = model.GetDeclaredSymbol(x); var xRef = GetReference(tree, "x"); Assert.Equal("System.Int32 Script.x", xSymbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x, xRef); var xType = ((IFieldSymbol)xSymbol).Type; Assert.False(xType.IsErrorType()); Assert.Equal("System.Int32", xType.ToTestDisplayString()); } [Fact] public void FailedInferenceInDeconstructionInScript() { var source = @" var (x, y) = (1, null); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.GetDeclarationDiagnostics().Verify( // (2,6): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x'. // var (x, y) = (1, null); Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x").WithArguments("x").WithLocation(2, 6), // (2,9): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'. // var (x, y) = (1, null); Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(2, 9) ); comp.VerifyDiagnostics( // (2,6): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x'. // var (x, y) = (1, null); Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x").WithArguments("x").WithLocation(2, 6), // (2,9): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'. // var (x, y) = (1, null); Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(2, 9) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x = GetDeconstructionVariable(tree, "x"); var xSymbol = model.GetDeclaredSymbol(x); Assert.Equal("var Script.x", xSymbol.ToTestDisplayString()); var xType = xSymbol.GetSymbol<FieldSymbol>().TypeWithAnnotations; Assert.True(xType.Type.IsErrorType()); Assert.Equal("var", xType.ToTestDisplayString()); var xTypeISymbol = xType.Type.GetPublicSymbol(); Assert.Equal(SymbolKind.ErrorType, xTypeISymbol.Kind); var y = GetDeconstructionVariable(tree, "y"); var ySymbol = model.GetDeclaredSymbol(y); Assert.Equal("var Script.y", ySymbol.ToTestDisplayString()); var yType = ((IFieldSymbol)ySymbol).Type; Assert.True(yType.IsErrorType()); Assert.Equal("var", yType.ToTestDisplayString()); } [Fact] public void FailedCircularInferenceInDeconstructionInScript() { var source = @" var (x1, x2) = (x2, x1); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.GetDeclarationDiagnostics().Verify( // (2,10): error CS7019: Type of 'x2' cannot be inferred since its initializer directly or indirectly refers to the definition. // var (x1, x2) = (x2, x1); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "x2").WithArguments("x2").WithLocation(2, 10), // (2,6): error CS7019: Type of 'x1' cannot be inferred since its initializer directly or indirectly refers to the definition. // var (x1, x2) = (x2, x1); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "x1").WithArguments("x1").WithLocation(2, 6) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("var Script.x1", x1Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x1, x1Ref); var x1Type = ((IFieldSymbol)x1Symbol).Type; Assert.True(x1Type.IsErrorType()); Assert.Equal("var", x1Type.Name); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Symbol = model.GetDeclaredSymbol(x2); var x2Ref = GetReference(tree, "x2"); Assert.Equal("var Script.x2", x2Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x2, x2Ref); var x2Type = ((IFieldSymbol)x2Symbol).Type; Assert.True(x2Type.IsErrorType()); Assert.Equal("var", x2Type.Name); } [Fact] public void FailedCircularInferenceInDeconstructionInScript2() { var source = @" var (x1, x2) = (y1, y2); var (y1, y2) = (x1, x2); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.GetDeclarationDiagnostics().Verify( // (3,6): error CS7019: Type of 'y1' cannot be inferred since its initializer directly or indirectly refers to the definition. // var (y1, y2) = (x1, x2); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "y1").WithArguments("y1").WithLocation(3, 6), // (2,6): error CS7019: Type of 'x1' cannot be inferred since its initializer directly or indirectly refers to the definition. // var (x1, x2) = (y1, y2); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "x1").WithArguments("x1").WithLocation(2, 6), // (2,10): error CS7019: Type of 'x2' cannot be inferred since its initializer directly or indirectly refers to the definition. // var (x1, x2) = (y1, y2); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "x2").WithArguments("x2").WithLocation(2, 10) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("var Script.x1", x1Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x1, x1Ref); var x1Type = ((IFieldSymbol)x1Symbol).Type; Assert.True(x1Type.IsErrorType()); Assert.Equal("var", x1Type.Name); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Symbol = model.GetDeclaredSymbol(x2); var x2Ref = GetReference(tree, "x2"); Assert.Equal("var Script.x2", x2Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x2, x2Ref); var x2Type = ((IFieldSymbol)x2Symbol).Type; Assert.True(x2Type.IsErrorType()); Assert.Equal("var", x2Type.Name); } [Fact] public void VarAliasInVarDeconstructionInScript() { var source = @" using @var = System.Byte; var (x1, (x2, x3)) = (1, (2, 3)); System.Console.Write($""{x1} {x2} {x3}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (3,5): error CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // var (x1, (x2, x3)) = (1, (2, 3)); Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "(x1, (x2, x3))").WithLocation(3, 5) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("System.Byte Script.x1", x1Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x1, x1Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Symbol = model.GetDeclaredSymbol(x3); var x3Ref = GetReference(tree, "x3"); Assert.Equal("System.Byte Script.x3", x3Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x3, x3Ref); // extra check on var var x123Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x123Var.Type.ToString()); Assert.Equal("System.Byte", model.GetTypeInfo(x123Var.Type).Type.ToTestDisplayString()); Assert.Equal("System.Byte", model.GetSymbolInfo(x123Var.Type).Symbol.ToTestDisplayString()); } [Fact] public void VarTypeInVarDeconstructionInScript() { var source = @" class @var { public static implicit operator var(int i) { return null; } } var (x1, (x2, x3)) = (1, (2, 3)); System.Console.Write($""{x1} {x2} {x3}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics( // (6,5): error CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // var (x1, (x2, x3)) = (1, (2, 3)); Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "(x1, (x2, x3))").WithLocation(6, 5) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("Script.var Script.x1", x1Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x1, x1Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Symbol = model.GetDeclaredSymbol(x3); var x3Ref = GetReference(tree, "x3"); Assert.Equal("Script.var Script.x3", x3Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x3, x3Ref); // extra check on var var x123Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x123Var.Type.ToString()); Assert.Equal("Script.var", model.GetTypeInfo(x123Var.Type).Type.ToTestDisplayString()); Assert.Equal("Script.var", model.GetSymbolInfo(x123Var.Type).Symbol.ToTestDisplayString()); } [Fact] public void VarAliasInTypedDeconstructionInScript() { var source = @" using @var = System.Byte; (var x1, (var x2, var x3)) = (1, (2, 3)); System.Console.Write($""{x1} {x2} {x3}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2 3"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("System.Byte Script.x1", x1Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x1, x1Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Symbol = model.GetDeclaredSymbol(x3); var x3Ref = GetReference(tree, "x3"); Assert.Equal("System.Byte Script.x3", x3Symbol.ToTestDisplayString()); VerifyModelForDeconstructionField(model, x3, x3Ref); // extra checks on x1's var var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("byte", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); var x1Alias = model.GetAliasInfo(x1Type); Assert.Equal(SymbolKind.NamedType, x1Alias.Target.Kind); Assert.Equal("byte", x1Alias.Target.ToDisplayString()); // extra checks on x3's var var x3Type = GetTypeSyntax(x3); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x3Type).Symbol.Kind); Assert.Equal("byte", model.GetSymbolInfo(x3Type).Symbol.ToDisplayString()); var x3Alias = model.GetAliasInfo(x3Type); Assert.Equal(SymbolKind.NamedType, x3Alias.Target.Kind); Assert.Equal("byte", x3Alias.Target.ToDisplayString()); } [Fact] public void VarTypeInTypedDeconstructionInScript() { var source = @" class @var { public static implicit operator var(int i) { return new var(); } public override string ToString() { return ""var""; } } (var x1, (var x2, var x3)) = (1, (2, 3)); System.Console.Write($""{x1} {x2} {x3}""); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "var var var"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Symbol = model.GetDeclaredSymbol(x1); var x1Ref = GetReference(tree, "x1"); Assert.Equal("Script.var Script.x1", x1Symbol.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(x1).Symbol); VerifyModelForDeconstructionField(model, x1, x1Ref); var x3 = GetDeconstructionVariable(tree, "x3"); var x3Symbol = model.GetDeclaredSymbol(x3); var x3Ref = GetReference(tree, "x3"); Assert.Equal("Script.var Script.x3", x3Symbol.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(x3).Symbol); VerifyModelForDeconstructionField(model, x3, x3Ref); // extra checks on x1's var var x1Type = GetTypeSyntax(x1); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x1Type).Symbol.Kind); Assert.Equal("var", model.GetSymbolInfo(x1Type).Symbol.ToDisplayString()); Assert.Null(model.GetAliasInfo(x1Type)); // extra checks on x3's var var x3Type = GetTypeSyntax(x3); Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(x3Type).Symbol.Kind); Assert.Equal("var", model.GetSymbolInfo(x3Type).Symbol.ToDisplayString()); Assert.Null(model.GetAliasInfo(x3Type)); } [Fact] public void SimpleDiscardWithConversion() { var source = @" class C { static void Main() { (int _, var x) = (new C(1), 1); (var _, var y) = (new C(2), 2); var (_, z) = (new C(3), 3); System.Console.Write($""Output {x} {y} {z}.""); } int _i; public C(int i) { _i = i; } public static implicit operator int(C c) { System.Console.Write($""Converted {c._i}. ""); return 0; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Converted 1. Output 1 2 3."); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard1 = GetDiscardDesignations(tree).First(); Assert.Null(model.GetDeclaredSymbol(discard1)); Assert.Null(model.GetSymbolInfo(discard1).Symbol); var declaration1 = (DeclarationExpressionSyntax)discard1.Parent; Assert.Equal("int _", declaration1.ToString()); Assert.Equal("System.Int32", model.GetTypeInfo(declaration1).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(declaration1).Symbol); var discard2 = GetDiscardDesignations(tree).ElementAt(1); Assert.Null(model.GetDeclaredSymbol(discard2)); Assert.Null(model.GetSymbolInfo(discard2).Symbol); var declaration2 = (DeclarationExpressionSyntax)discard2.Parent; Assert.Equal("var _", declaration2.ToString()); Assert.Equal("C", model.GetTypeInfo(declaration2).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(declaration2).Symbol); var discard3 = GetDiscardDesignations(tree).ElementAt(2); var declaration3 = (DeclarationExpressionSyntax)discard3.Parent.Parent; Assert.Equal("var (_, z)", declaration3.ToString()); Assert.Equal("(C, System.Int32 z)", model.GetTypeInfo(declaration3).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(declaration3).Symbol); } [Fact] public void CannotDeconstructIntoDiscardOfWrongType() { var source = @" class C { static void Main() { (int _, string _) = (""hello"", 42); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (6,30): error CS0029: Cannot implicitly convert type 'string' to 'int' // (int _, string _) = ("hello", 42); Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""hello""").WithArguments("string", "int").WithLocation(6, 30), // (6,39): error CS0029: Cannot implicitly convert type 'int' to 'string' // (int _, string _) = ("hello", 42); Diagnostic(ErrorCode.ERR_NoImplicitConv, "42").WithArguments("int", "string").WithLocation(6, 39) ); } [Fact] public void DiscardFromDeconstructMethod() { var source = @" class C { static void Main() { (var _, string y) = new C(); System.Console.Write(y); } void Deconstruct(out int x, out string y) { x = 42; y = ""hello""; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "hello"); } [Fact] public void ShortDiscardInDeclaration() { var source = @" class C { static void Main() { (_, var x) = (1, 2); System.Console.Write(x); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "2"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard = GetDiscardIdentifiers(tree).First(); var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard).Symbol; Assert.Equal("int _", symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal("System.Int32", model.GetTypeInfo(discard).Type.ToTestDisplayString()); var isymbol = (ISymbol)symbol; Assert.Equal(SymbolKind.Discard, isymbol.Kind); } [Fact, WorkItem(25829, "https://github.com/dotnet/roslyn/issues/25829")] public void SameTypeDiscardsAreEqual01() { var source = @" class C { static void Main() { (_, _) = (1, 2); _ = 3; M(out _); } static void M(out int x) => x = 1; } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discards = GetDiscardIdentifiers(tree).ToArray(); Assert.Equal(4, discards.Length); var symbol0 = (IDiscardSymbol)model.GetSymbolInfo(discards[0]).Symbol; Assert.Equal(symbol0, symbol0); var set = new HashSet<ISymbol>(); foreach (var discard in discards) { var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard).Symbol; set.Add(symbol); Assert.Equal(SymbolKind.Discard, symbol.Kind); Assert.Equal("int _", symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal(symbol0, symbol); Assert.Equal(symbol, symbol); Assert.Equal(symbol.GetHashCode(), symbol0.GetHashCode()); // Test to show that reference-unequal discards are equal by type. IDiscardSymbol symbolClone = new DiscardSymbol(TypeWithAnnotations.Create(symbol.Type.GetSymbol())).GetPublicSymbol(); Assert.NotSame(symbol, symbolClone); Assert.Equal(SymbolKind.Discard, symbolClone.Kind); Assert.Equal("int _", symbolClone.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal(symbol.Type, symbolClone.Type); Assert.Equal(symbol0, symbolClone); Assert.Equal(symbol, symbolClone); Assert.Same(symbol.Type, symbolClone.Type); // original symbol for System.Int32 has identity. Assert.Equal(symbol.GetHashCode(), symbolClone.GetHashCode()); } Assert.Equal(1, set.Count); } [Fact, WorkItem(25829, "https://github.com/dotnet/roslyn/issues/25829")] public void SameTypeDiscardsAreEqual02() { var source = @"using System.Collections.Generic; class C { static void Main() { (_, _) = (new List<int>(), new List<int>()); _ = new List<int>(); M(out _); } static void M(out List<int> x) => x = new List<int>(); } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discards = GetDiscardIdentifiers(tree).ToArray(); Assert.Equal(4, discards.Length); var symbol0 = (IDiscardSymbol)model.GetSymbolInfo(discards[0]).Symbol; Assert.Equal(symbol0, symbol0); var set = new HashSet<ISymbol>(); foreach (var discard in discards) { var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard).Symbol; set.Add(symbol); Assert.Equal(SymbolKind.Discard, symbol.Kind); Assert.Equal("List<int> _", symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal(symbol0, symbol); Assert.Equal(symbol0.Type, symbol.Type); Assert.Equal(symbol, symbol); Assert.Equal(symbol.GetHashCode(), symbol0.GetHashCode()); if (discard != discards[0]) { // Although it is not part of the compiler's contract, at the moment distinct constructions are distinct Assert.NotSame(symbol.Type, symbol0.Type); Assert.NotSame(symbol, symbol0); } } Assert.Equal(1, set.Count); } [Fact, WorkItem(25829, "https://github.com/dotnet/roslyn/issues/25829")] public void DifferentTypeDiscardsAreNotEqual() { var source = @" class C { static void Main() { (_, _) = (1.0, 2); _ = 3; M(out _); } static void M(out int x) => x = 1; } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discards = GetDiscardIdentifiers(tree).ToArray(); Assert.Equal(4, discards.Length); var symbol0 = (IDiscardSymbol)model.GetSymbolInfo(discards[0]).Symbol; var set = new HashSet<ISymbol>(); foreach (var discard in discards) { var symbol = (IDiscardSymbol)model.GetSymbolInfo(discard).Symbol; Assert.Equal(SymbolKind.Discard, symbol.Kind); set.Add(symbol); if (discard == discards[0]) { Assert.Equal("double _", symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal(symbol0, symbol); Assert.Equal(symbol0.GetHashCode(), symbol.GetHashCode()); } else { Assert.Equal("int _", symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.NotEqual(symbol0, symbol); } } Assert.Equal(2, set.Count); } [Fact] public void EscapedUnderscoreInDeclaration() { var source = @" class C { static void Main() { (@_, var x) = (1, 2); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( // (6,10): error CS0103: The name '_' does not exist in the current context // (@_, var x) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "@_").WithArguments("_").WithLocation(6, 10) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); Assert.Empty(GetDiscardIdentifiers(tree)); } [Fact] public void EscapedUnderscoreInDeclarationCSharp9() { var source = @" class C { static void Main() { (@_, var x) = (1, 2); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( // (6,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (@_, var x) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(@_, var x) = (1, 2)").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(6, 9), // (6,10): error CS0103: The name '_' does not exist in the current context // (@_, var x) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "@_").WithArguments("_").WithLocation(6, 10) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); Assert.Empty(GetDiscardIdentifiers(tree)); } [Fact] public void UnderscoreLocalInDeconstructDeclaration() { var source = @" class C { static void Main() { int _; (_, var x) = (1, 2); System.Console.Write(_); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1") .VerifyIL("C.Main", @" { // Code size 13 (0xd) .maxstack 1 .locals init (int V_0, //_ int V_1) //x IL_0000: nop IL_0001: ldc.i4.1 IL_0002: stloc.0 IL_0003: ldc.i4.2 IL_0004: stloc.1 IL_0005: ldloc.0 IL_0006: call ""void System.Console.Write(int)"" IL_000b: nop IL_000c: ret }"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard = GetDiscardIdentifiers(tree).First(); Assert.Equal("(_, var x)", discard.Parent.Parent.ToString()); var symbol = (ILocalSymbol)model.GetSymbolInfo(discard).Symbol; Assert.Equal("int _", symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal("System.Int32", model.GetTypeInfo(discard).Type.ToTestDisplayString()); } [Fact] public void ShortDiscardInDeconstructAssignment() { var source = @" class C { static void Main() { int x; (_, _, x) = (1, 2, 3); System.Console.Write(x); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "3"); } [Fact] public void DiscardInDeconstructAssignment() { var source = @" class C { static void Main() { int x; (_, x) = (1L, 2); System.Console.Write(x); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "2"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard1 = GetDiscardIdentifiers(tree).First(); Assert.Null(model.GetDeclaredSymbol(discard1)); Assert.Equal("long _", model.GetSymbolInfo(discard1).Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); var tuple1 = (TupleExpressionSyntax)discard1.Parent.Parent; Assert.Equal("(_, x)", tuple1.ToString()); Assert.Equal("(System.Int64, System.Int32 x)", model.GetTypeInfo(tuple1).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(tuple1).Symbol); } [Fact] public void DiscardInDeconstructDeclaration() { var source = @" class C { static void Main() { var (_, x) = (1, 2); System.Console.Write(x); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "2"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard1 = GetDiscardDesignations(tree).First(); Assert.Null(model.GetDeclaredSymbol(discard1)); var tuple1 = (DeclarationExpressionSyntax)discard1.Parent.Parent; Assert.Equal("var (_, x)", tuple1.ToString()); Assert.Equal("(System.Int32, System.Int32 x)", model.GetTypeInfo(tuple1).Type.ToTestDisplayString()); } [Fact] public void UnderscoreLocalInDeconstructAssignment() { var source = @" class C { static void Main() { int x, _; (_, x) = (1, 2); System.Console.Write($""{_} {x}""); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard = GetDiscardIdentifiers(tree).First(); Assert.Equal("(_, x)", discard.Parent.Parent.ToString()); var symbol = (ILocalSymbol)model.GetSymbolInfo(discard).Symbol; Assert.Equal("System.Int32", symbol.Type.ToTestDisplayString()); } [Fact] public void DiscardInForeach() { var source = @" class C { static void Main() { foreach (var (_, x) in new[] { (1, ""hello"") }) { System.Console.Write(""1 ""); } foreach ((_, (var y, int z)) in new[] { (1, (""hello"", 2)) }) { System.Console.Write(""2""); } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); DiscardDesignationSyntax discard1 = GetDiscardDesignations(tree).First(); Assert.Null(model.GetDeclaredSymbol(discard1)); Assert.Null(model.GetTypeInfo(discard1).Type); var declaration1 = (DeclarationExpressionSyntax)discard1.Parent.Parent; Assert.Equal("var (_, x)", declaration1.ToString()); Assert.Null(model.GetTypeInfo(discard1).Type); Assert.Equal("(System.Int32, System.String x)", model.GetTypeInfo(declaration1).Type.ToTestDisplayString()); IdentifierNameSyntax discard2 = GetDiscardIdentifiers(tree).First(); Assert.Equal("(_, (var y, int z))", discard2.Parent.Parent.ToString()); Assert.Equal("int _", model.GetSymbolInfo(discard2).Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); Assert.Equal("System.Int32", model.GetTypeInfo(discard2).Type.ToTestDisplayString()); var yz = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(2); Assert.Equal("(var y, int z)", yz.ToString()); Assert.Equal("(System.String y, System.Int32 z)", model.GetTypeInfo(yz).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(yz).Symbol); var y = tree.GetRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ElementAt(1); Assert.Equal("var y", y.ToString()); Assert.Equal("System.String", model.GetTypeInfo(y).Type.ToTestDisplayString()); Assert.Equal("System.String y", model.GetSymbolInfo(y).Symbol.ToTestDisplayString()); } [Fact] public void TwoDiscardsInForeach() { var source = @" class C { static void Main() { foreach ((_, _) in new[] { (1, ""hello"") }) { System.Console.Write(""2""); } } } "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var refs = GetReferences(tree, "_"); Assert.Equal(2, refs.Count()); model.GetTypeInfo(refs.ElementAt(0)); // Assert.Equal("int", model.GetTypeInfo(refs.ElementAt(0)).Type.ToDisplayString()); model.GetTypeInfo(refs.ElementAt(1)); // Assert.Equal("string", model.GetTypeInfo(refs.ElementAt(1)).Type.ToDisplayString()); var tuple = (TupleExpressionSyntax)refs.ElementAt(0).Parent.Parent; Assert.Equal("(_, _)", tuple.ToString()); Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(tuple).Type.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: @"2", sourceSymbolValidator: validator); comp.VerifyDiagnostics( // this is permitted now, as it is just an assignment expression ); } [Fact] public void UnderscoreLocalDisallowedInForEach() { var source = @" class C { static void Main() { { foreach ((var x, _) in new[] { (1, ""hello"") }) { System.Console.Write(""2 ""); } } { int _; foreach ((var y, _) in new[] { (1, ""hello"") }) { System.Console.Write(""4""); } // error } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (11,30): error CS0029: Cannot implicitly convert type 'string' to 'int' // foreach ((var y, _) in new[] { (1, "hello") }) { System.Console.Write("4"); } // error Diagnostic(ErrorCode.ERR_NoImplicitConv, "_").WithArguments("string", "int").WithLocation(11, 30), // (11,22): error CS8186: A foreach loop must declare its iteration variables. // foreach ((var y, _) in new[] { (1, "hello") }) { System.Console.Write("4"); } // error Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(var y, _)").WithLocation(11, 22), // (10,17): warning CS0168: The variable '_' is declared but never used // int _; Diagnostic(ErrorCode.WRN_UnreferencedVar, "_").WithArguments("_").WithLocation(10, 17) ); } [Fact] public void TwoDiscardsInDeconstructAssignment() { var source = @" class C { static void Main() { (_, _) = (new C(), new C()); } public C() { System.Console.Write(""C""); } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "CC"); } [Fact] public void VerifyDiscardIL() { var source = @" class C { C() { System.Console.Write(""ctor""); } static int Main() { var (x, _, _) = (1, new C(), 2); return x; } } "; var comp = CompileAndVerify(source, expectedOutput: "ctor"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main()", @" { // Code size 8 (0x8) .maxstack 1 IL_0000: newobj ""C..ctor()"" IL_0005: pop IL_0006: ldc.i4.1 IL_0007: ret }"); } [Fact] public void SingleDiscardInAssignment() { var source = @" class C { static void Main() { _ = M(); } public static int M() { System.Console.Write(""M""); return 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "M"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard1 = GetDiscardIdentifiers(tree).First(); Assert.Null(model.GetDeclaredSymbol(discard1)); Assert.Equal("System.Int32", model.GetTypeInfo(discard1).Type.ToTestDisplayString()); } [Fact] public void SingleDiscardInAssignmentInCSharp6() { var source = @" class C { static void Error() { _ = 1; } static void Ok() { int _; _ = 1; System.Console.Write(_); } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular6); comp.VerifyDiagnostics( // (6,9): error CS8059: Feature 'discards' is not available in C# 6. Please use language version 7.0 or greater. // _ = 1; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "_").WithArguments("discards", "7.0").WithLocation(6, 9) ); } [Fact] public void VariousDiscardsInCSharp6() { var source = @" class C { static void M1(out int x) { (_, var _, int _) = (1, 2, 3); var (_, _) = (1, 2); bool b = 3 is int _; switch (3) { case int _: break; } M1(out var _); M1(out int _); M1(out _); x = 2; } static void M2() { const int _ = 3; switch (3) { case _: // not a discard break; } } } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular6); comp.VerifyDiagnostics( // (6,9): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // (_, var _, int _) = (1, 2, 3); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(_, var _, int _)").WithArguments("tuples", "7.0").WithLocation(6, 9), // (6,10): error CS8059: Feature 'discards' is not available in C# 6. Please use language version 7.0 or greater. // (_, var _, int _) = (1, 2, 3); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "_").WithArguments("discards", "7.0").WithLocation(6, 10), // (6,29): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // (_, var _, int _) = (1, 2, 3); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(1, 2, 3)").WithArguments("tuples", "7.0").WithLocation(6, 29), // (7,13): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // var (_, _) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(_, _)").WithArguments("tuples", "7.0").WithLocation(7, 13), // (7,22): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // var (_, _) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(1, 2)").WithArguments("tuples", "7.0").WithLocation(7, 22), // (8,20): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater. // bool b = 3 is int _; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "is").WithArguments("pattern matching", "7.0").WithLocation(8, 20), // (11,13): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater. // case int _: Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "case").WithArguments("pattern matching", "7.0").WithLocation(11, 13), // (14,12): error CS8059: Feature 'out variable declaration' is not available in C# 6. Please use language version 7.0 or greater. // M1(out var _); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "out").WithArguments("out variable declaration", "7.0").WithLocation(14, 12), // (15,12): error CS8059: Feature 'out variable declaration' is not available in C# 6. Please use language version 7.0 or greater. // M1(out int _); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "out").WithArguments("out variable declaration", "7.0").WithLocation(15, 12), // (16,16): error CS8059: Feature 'discards' is not available in C# 6. Please use language version 7.0 or greater. // M1(out _); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "_").WithArguments("discards", "7.0").WithLocation(16, 16), // (24,18): warning CS8512: The name '_' refers to the constant, not the discard pattern. Use 'var _' to discard the value, or '@_' to refer to a constant by that name. // case _: // not a discard Diagnostic(ErrorCode.WRN_CaseConstantNamedUnderscore, "_").WithLocation(24, 18)); } [Fact] public void SingleDiscardInAsyncAssignment() { var source = @" class C { async void M() { System.Threading.Tasks.Task.Delay(new System.TimeSpan(0)); // warning _ = System.Threading.Tasks.Task.Delay(new System.TimeSpan(0)); // fire-and-forget await System.Threading.Tasks.Task.Delay(new System.TimeSpan(0)); } } "; var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugDll); comp.VerifyDiagnostics( // (6,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // System.Threading.Tasks.Task.Delay(new System.TimeSpan(0)); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "System.Threading.Tasks.Task.Delay(new System.TimeSpan(0))").WithLocation(6, 9) ); } [Fact] public void SingleDiscardInUntypedAssignment() { var source = @" class C { static void Main() { _ = null; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (6,9): error CS8183: Cannot infer the type of implicitly-typed discard. // _ = null; Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(6, 9) ); } [Fact] public void UnderscoreLocalInAssignment() { var source = @" class C { static void Main() { int _; _ = M(); System.Console.Write(_); } public static int M() { return 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1"); } [Fact] public void DeclareAndUseLocalInDeconstruction() { var source = @" class C { static void Main() { (var x, x) = (1, 2); (y, var y) = (1, 2); } } "; var compCSharp9 = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); compCSharp9.VerifyDiagnostics( // (6,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (var x, x) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(var x, x) = (1, 2)").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(6, 9), // (6,17): error CS0841: Cannot use local variable 'x' before it is declared // (var x, x) = (1, 2); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x").WithArguments("x").WithLocation(6, 17), // (7,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (y, var y) = (1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(y, var y) = (1, 2)").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(7, 9), // (7,10): error CS0841: Cannot use local variable 'y' before it is declared // (y, var y) = (1, 2); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "y").WithArguments("y").WithLocation(7, 10) ); var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( // (6,17): error CS0841: Cannot use local variable 'x' before it is declared // (var x, x) = (1, 2); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x").WithArguments("x").WithLocation(6, 17), // (7,10): error CS0841: Cannot use local variable 'y' before it is declared // (y, var y) = (1, 2); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "y").WithArguments("y").WithLocation(7, 10) ); } [Fact] public void OutVarAndUsageInDeconstructAssignment() { var source = @" class C { static void Main() { (M(out var x).P, x) = (1, x); System.Console.Write(x); } static C M(out int i) { i = 42; return new C(); } int P { set { System.Console.Write($""Written {value}. ""); } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (6,26): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (M(out var x).P, x) = (1, x); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "x").WithLocation(6, 26) ); CompileAndVerify(comp, expectedOutput: "Written 1. 42"); } [Fact] public void OutDiscardInDeconstructAssignment() { var source = @" class C { static void Main() { int _; (M(out var _).P, _) = (1, 2); System.Console.Write(_); } static C M(out int i) { i = 42; return new C(); } int P { set { System.Console.Write($""Written {value}. ""); } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Written 1. 2"); } [Fact] public void OutDiscardInDeconstructTarget() { var source = @" class C { static void Main() { (x, _) = (M(out var x), 2); System.Console.Write(x); } static int M(out int i) { i = 42; return 3; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (6,10): error CS0841: Cannot use local variable 'x' before it is declared // (x, _) = (M(out var x), 2); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x").WithArguments("x").WithLocation(6, 10) ); } [Fact] public void SimpleDiscardDeconstructInScript() { var source = @" using @alias = System.Int32; (string _, alias _) = (""hello"", 42); "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var discard1 = GetDiscardDesignations(tree).First(); var declaration1 = (DeclarationExpressionSyntax)discard1.Parent; Assert.Equal("string _", declaration1.ToString()); Assert.Null(model.GetDeclaredSymbol(declaration1)); Assert.Null(model.GetDeclaredSymbol(discard1)); var discard2 = GetDiscardDesignations(tree).ElementAt(1); var declaration2 = (DeclarationExpressionSyntax)discard2.Parent; Assert.Equal("alias _", declaration2.ToString()); Assert.Null(model.GetDeclaredSymbol(declaration2)); Assert.Null(model.GetDeclaredSymbol(discard2)); var tuple = (TupleExpressionSyntax)declaration1.Parent.Parent; Assert.Equal("(string _, alias _)", tuple.ToString()); Assert.Equal("(System.String, System.Int32)", model.GetTypeInfo(tuple).Type.ToTestDisplayString()); }; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); CompileAndVerify(comp, sourceSymbolValidator: validator); } [Fact] public void SimpleDiscardDeconstructInScript2() { var source = @" public class C { public C() { System.Console.Write(""ctor""); } public void Deconstruct(out string x, out string y) { x = y = null; } } (string _, string _) = new C(); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "ctor"); } [Fact] public void SingleDiscardInAssignmentInScript() { var source = @" int M() { System.Console.Write(""M""); return 1; } _ = M(); "; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "M"); } [Fact] public void NestedVarDiscardDeconstructionInScript() { var source = @" (var _, var (_, x3)) = (""hello"", (42, 43)); System.Console.Write($""{x3}""); "; Action<ModuleSymbol> validator = (ModuleSymbol module) => { var sourceModule = (SourceModuleSymbol)module; var compilation = sourceModule.DeclaringCompilation; var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var discard2 = GetDiscardDesignations(tree).ElementAt(1); var nestedDeclaration = (DeclarationExpressionSyntax)discard2.Parent.Parent; Assert.Equal("var (_, x3)", nestedDeclaration.ToString()); Assert.Null(model.GetDeclaredSymbol(nestedDeclaration)); Assert.Null(model.GetDeclaredSymbol(discard2)); Assert.Equal("(System.Int32, System.Int32 x3)", model.GetTypeInfo(nestedDeclaration).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(nestedDeclaration).Symbol); var tuple = (TupleExpressionSyntax)discard2.Parent.Parent.Parent.Parent; Assert.Equal("(var _, var (_, x3))", tuple.ToString()); Assert.Equal("(System.String, (System.Int32, System.Int32 x3))", model.GetTypeInfo(tuple).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(tuple).Symbol); }; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Script, options: TestOptions.DebugExe, references: s_valueTupleRefs); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "43", sourceSymbolValidator: validator); } [Fact] public void VariousDiscardsInForeach() { var source = @" class C { static void Main() { foreach ((var _, int _, _, var (_, _), int x) in new[] { (1L, 2, 3, (""hello"", 5), 6) }) { System.Console.Write(x); } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "6"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var discard1 = GetDiscardDesignations(tree).First(); Assert.Null(model.GetDeclaredSymbol(discard1)); Assert.True(model.GetSymbolInfo(discard1).IsEmpty); Assert.Null(model.GetTypeInfo(discard1).Type); var declaration1 = (DeclarationExpressionSyntax)discard1.Parent; Assert.Equal("var _", declaration1.ToString()); Assert.Equal("System.Int64", model.GetTypeInfo(declaration1).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(declaration1).Symbol); var discard2 = GetDiscardDesignations(tree).ElementAt(1); Assert.Null(model.GetDeclaredSymbol(discard2)); Assert.True(model.GetSymbolInfo(discard2).IsEmpty); Assert.Null(model.GetTypeInfo(discard2).Type); var declaration2 = (DeclarationExpressionSyntax)discard2.Parent; Assert.Equal("int _", declaration2.ToString()); Assert.Equal("System.Int32", model.GetTypeInfo(declaration2).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(declaration2).Symbol); var discard3 = GetDiscardIdentifiers(tree).First(); Assert.Equal("_", discard3.Parent.ToString()); Assert.Null(model.GetDeclaredSymbol(discard3)); Assert.Equal("System.Int32", model.GetTypeInfo(discard3).Type.ToTestDisplayString()); Assert.Equal("int _", model.GetSymbolInfo(discard3).Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); var discard3Symbol = (IDiscardSymbol)model.GetSymbolInfo(discard3).Symbol; Assert.Equal("System.Int32", discard3Symbol.Type.ToTestDisplayString()); Assert.Equal("System.Int32", model.GetTypeInfo(discard3).Type.ToTestDisplayString()); var discard4 = GetDiscardDesignations(tree).ElementAt(2); Assert.Null(model.GetDeclaredSymbol(discard4)); Assert.True(model.GetSymbolInfo(discard4).IsEmpty); Assert.Null(model.GetTypeInfo(discard4).Type); var nestedDeclaration = (DeclarationExpressionSyntax)discard4.Parent.Parent; Assert.Equal("var (_, _)", nestedDeclaration.ToString()); Assert.Equal("(System.String, System.Int32)", model.GetTypeInfo(nestedDeclaration).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(nestedDeclaration).Symbol); } [Fact] public void UnderscoreInCSharp6Foreach() { var source = @" class C { static void Main() { foreach (var _ in M()) { System.Console.Write(_); } } static System.Collections.Generic.IEnumerable<int> M() { System.Console.Write(""M ""); yield return 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "M 1"); } [Fact] public void ShortDiscardDisallowedInForeach() { var source = @" class C { static void Main() { foreach (_ in M()) { } } static System.Collections.Generic.IEnumerable<int> M() { yield return 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (6,18): error CS8186: A foreach loop must declare its iteration variables. // foreach (_ in M()) Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "_").WithLocation(6, 18) ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); var discard = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().First(); var symbol = (DiscardSymbol)model.GetSymbolInfo(discard).Symbol.GetSymbol(); Assert.True(symbol.TypeWithAnnotations.Type.IsErrorType()); } [Fact] public void ExistingUnderscoreLocalInLegacyForeach() { var source = @" class C { static void Main() { int _; foreach (var _ in new[] { 1 }) { } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (7,22): error CS0136: A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // foreach (var _ in new[] { 1 }) Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "_").WithArguments("_").WithLocation(7, 22), // (6,13): warning CS0168: The variable '_' is declared but never used // int _; Diagnostic(ErrorCode.WRN_UnreferencedVar, "_").WithArguments("_").WithLocation(6, 13) ); } [Fact] public void MixedDeconstruction_01() { string source = @" class Program { static void Main(string[] args) { var t = (1, 2); var x = (int x1, int x2) = t; System.Console.WriteLine(x1); System.Console.WriteLine(x2); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics( // (7,18): error CS8185: A declaration is not allowed in this context. // var x = (int x1, int x2) = t; Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(7, 18) ); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2, x2Ref); } [Fact] public void MixedDeconstruction_02() { string source = @" class Program { static void Main(string[] args) { var t = (1, 2); int z; (int x1, z) = t; System.Console.WriteLine(x1); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "1") .VerifyIL("Program.Main", @" { // Code size 32 (0x20) .maxstack 3 .locals init (System.ValueTuple<int, int> V_0, //t int V_1, //z int V_2) //x1 IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: ldc.i4.1 IL_0004: ldc.i4.2 IL_0005: call ""System.ValueTuple<int, int>..ctor(int, int)"" IL_000a: ldloc.0 IL_000b: dup IL_000c: ldfld ""int System.ValueTuple<int, int>.Item1"" IL_0011: stloc.2 IL_0012: ldfld ""int System.ValueTuple<int, int>.Item2"" IL_0017: stloc.1 IL_0018: ldloc.2 IL_0019: call ""void System.Console.WriteLine(int)"" IL_001e: nop IL_001f: ret }"); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); } [Fact] public void MixedDeconstruction_03() { string source = @" class Program { static void Main(string[] args) { var t = (1, 2); int z; for ((int x1, z) = t; ; ) { System.Console.WriteLine(x1); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "1") .VerifyIL("Program.Main", @" { // Code size 39 (0x27) .maxstack 3 .locals init (System.ValueTuple<int, int> V_0, //t int V_1, //z int V_2) //x1 IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: ldc.i4.1 IL_0004: ldc.i4.2 IL_0005: call ""System.ValueTuple<int, int>..ctor(int, int)"" IL_000a: ldloc.0 IL_000b: dup IL_000c: ldfld ""int System.ValueTuple<int, int>.Item1"" IL_0011: stloc.2 IL_0012: ldfld ""int System.ValueTuple<int, int>.Item2"" IL_0017: stloc.1 IL_0018: br.s IL_0024 IL_001a: nop IL_001b: ldloc.2 IL_001c: call ""void System.Console.WriteLine(int)"" IL_0021: nop IL_0022: br.s IL_0026 IL_0024: br.s IL_001a IL_0026: ret }"); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); var symbolInfo = model.GetSymbolInfo(x1Ref); Assert.Equal(symbolInfo.Symbol, model.GetDeclaredSymbol(x1)); Assert.Equal(SpecialType.System_Int32, symbolInfo.Symbol.GetTypeOrReturnType().SpecialType); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(1); Assert.Equal(@"(int x1, z)", lhs.ToString()); Assert.Equal("(System.Int32 x1, System.Int32 z)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 x1, System.Int32 z)", model.GetTypeInfo(lhs).ConvertedType.ToTestDisplayString()); } [Fact] public void MixedDeconstruction_03CSharp9() { string source = @" class Program { static void Main(string[] args) { var t = (1, 2); int z; for ((int x1, z) = t; ; ) { System.Console.WriteLine(x1); } } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); compilation.VerifyDiagnostics( // (8,14): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // for ((int x1, z) = t; ; ) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(int x1, z) = t").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(8, 14)); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1, x1Ref); } [Fact] public void MixedDeconstruction_04() { string source = @" class Program { static void Main(string[] args) { var t = (1, 2); for (; ; (int x1, int x2) = t) { System.Console.WriteLine(x1); System.Console.WriteLine(x2); } } }"; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (7,19): error CS8185: A declaration is not allowed in this context. // for (; ; (int x1, int x2) = t) Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(7, 19), // (9,38): error CS0103: The name 'x1' does not exist in the current context // System.Console.WriteLine(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(9, 38), // (10,38): error CS0103: The name 'x2' does not exist in the current context // System.Console.WriteLine(x2); Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(10, 38) ); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); VerifyModelForDeconstructionLocal(model, x1); var symbolInfo = model.GetSymbolInfo(x1Ref); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); VerifyModelForDeconstructionLocal(model, x2); symbolInfo = model.GetSymbolInfo(x2Ref); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); } [Fact] public void MixedDeconstruction_05() { string source = @" class Program { static void Main(string[] args) { foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) { System.Console.WriteLine(x1); System.Console.WriteLine(x2); } } static int _M; static ref int M(out int x) { x = 2; return ref _M; } }"; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (6,34): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "args is var x2").WithLocation(6, 34), // (6,34): error CS0029: Cannot implicitly convert type 'int' to 'bool' // foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) Diagnostic(ErrorCode.ERR_NoImplicitConv, "args is var x2").WithArguments("int", "bool").WithLocation(6, 34), // (6,18): error CS8186: A foreach loop must declare its iteration variables. // foreach ((M(out var x1), args is var x2, _) in new[] { (1, 2, 3) }) Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(M(out var x1), args is var x2, _)").WithLocation(6, 18) ); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); Assert.Equal("int", model.GetTypeInfo(x1Ref).Type.ToDisplayString()); model = compilation.GetSemanticModel(tree); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); Assert.Equal("string[]", model.GetTypeInfo(x2Ref).Type.ToDisplayString()); VerifyModelForLocal(model, x1, LocalDeclarationKind.OutVariable, x1Ref); VerifyModelForLocal(model, x2, LocalDeclarationKind.PatternVariable, x2Ref); } [Fact] public void ForeachIntoExpression() { string source = @" class Program { static void Main(string[] args) { foreach (M(out var x1) in new[] { 1, 2, 3 }) { System.Console.WriteLine(x1); } } static int _M; static ref int M(out int x) { x = 2; return ref _M; } }"; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (6,32): error CS0230: Type and identifier are both required in a foreach statement // foreach (M(out var x1) in new[] { 1, 2, 3 }) Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 32) ); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); Assert.Equal("int", model.GetTypeInfo(x1Ref).Type.ToDisplayString()); VerifyModelForLocal(model, x1, LocalDeclarationKind.OutVariable, x1Ref); } [Fact] public void MixedDeconstruction_06() { string source = @" class Program { static void Main(string[] args) { foreach (M1(M2(out var x1, args is var x2), x1, x2) in new[] {1, 2, 3}) { System.Console.WriteLine(x1); System.Console.WriteLine(x2); } } static int _M; static ref int M1(int m2, int x, string[] y) { return ref _M; } static int M2(out int x, bool b) => x = 2; }"; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (6,61): error CS0230: Type and identifier are both required in a foreach statement // foreach (M1(M2(out var x1, args is var x2), x1, x2) in new[] {1, 2, 3}) Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(6, 61) ); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReferences(tree, "x1"); Assert.Equal("int", model.GetTypeInfo(x1Ref.First()).Type.ToDisplayString()); model = compilation.GetSemanticModel(tree); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReferences(tree, "x2"); Assert.Equal("string[]", model.GetTypeInfo(x2Ref.First()).Type.ToDisplayString()); VerifyModelForLocal(model, x1, LocalDeclarationKind.OutVariable, x1Ref.ToArray()); VerifyModelForLocal(model, x2, LocalDeclarationKind.PatternVariable, x2Ref.ToArray()); } [Fact] public void MixedDeconstruction_07() { string source = @" class Program { static void Main(string[] args) { var t = (1, ("""", true)); string y; for ((int x, (y, var z)) = t; ; ) { System.Console.Write(x); System.Console.Write(z); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); CompileAndVerify(compilation, expectedOutput: "1True") .VerifyIL("Program.Main", @" { // Code size 73 (0x49) .maxstack 4 .locals init (System.ValueTuple<int, System.ValueTuple<string, bool>> V_0, //t string V_1, //y int V_2, //x bool V_3, //z System.ValueTuple<string, bool> V_4) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: ldc.i4.1 IL_0004: ldstr """" IL_0009: ldc.i4.1 IL_000a: newobj ""System.ValueTuple<string, bool>..ctor(string, bool)"" IL_000f: call ""System.ValueTuple<int, System.ValueTuple<string, bool>>..ctor(int, System.ValueTuple<string, bool>)"" IL_0014: ldloc.0 IL_0015: dup IL_0016: ldfld ""System.ValueTuple<string, bool> System.ValueTuple<int, System.ValueTuple<string, bool>>.Item2"" IL_001b: stloc.s V_4 IL_001d: ldfld ""int System.ValueTuple<int, System.ValueTuple<string, bool>>.Item1"" IL_0022: stloc.2 IL_0023: ldloc.s V_4 IL_0025: ldfld ""string System.ValueTuple<string, bool>.Item1"" IL_002a: stloc.1 IL_002b: ldloc.s V_4 IL_002d: ldfld ""bool System.ValueTuple<string, bool>.Item2"" IL_0032: stloc.3 IL_0033: br.s IL_0046 IL_0035: nop IL_0036: ldloc.2 IL_0037: call ""void System.Console.Write(int)"" IL_003c: nop IL_003d: ldloc.3 IL_003e: call ""void System.Console.Write(bool)"" IL_0043: nop IL_0044: br.s IL_0048 IL_0046: br.s IL_0035 IL_0048: ret }"); compilation.VerifyDiagnostics(); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x = GetDeconstructionVariable(tree, "x"); var xRef = GetReference(tree, "x"); VerifyModelForDeconstructionLocal(model, x, xRef); var xSymbolInfo = model.GetSymbolInfo(xRef); Assert.Equal(xSymbolInfo.Symbol, model.GetDeclaredSymbol(x)); Assert.Equal(SpecialType.System_Int32, xSymbolInfo.Symbol.GetTypeOrReturnType().SpecialType); var z = GetDeconstructionVariable(tree, "z"); var zRef = GetReference(tree, "z"); VerifyModelForDeconstructionLocal(model, z, zRef); var zSymbolInfo = model.GetSymbolInfo(zRef); Assert.Equal(zSymbolInfo.Symbol, model.GetDeclaredSymbol(z)); Assert.Equal(SpecialType.System_Boolean, zSymbolInfo.Symbol.GetTypeOrReturnType().SpecialType); var lhs = tree.GetRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(2); Assert.Equal(@"(int x, (y, var z))", lhs.ToString()); Assert.Equal("(System.Int32 x, (System.String y, System.Boolean z))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Equal("(System.Int32 x, (System.String y, System.Boolean z))", model.GetTypeInfo(lhs).ConvertedType.ToTestDisplayString()); } [Fact] public void IncompleteDeclarationIsSeenAsTupleLiteral() { string source = @" class C { static void Main() { (int x1, string x2); System.Console.WriteLine(x1); System.Console.WriteLine(x2); } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (6,10): error CS8185: A declaration is not allowed in this context. // (int x1, string x2); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(6, 10), // (6,18): error CS8185: A declaration is not allowed in this context. // (int x1, string x2); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "string x2").WithLocation(6, 18), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (int x1, string x2); Diagnostic(ErrorCode.ERR_IllegalStatement, "(int x1, string x2)").WithLocation(6, 9), // (6,10): error CS0165: Use of unassigned local variable 'x1' // (int x1, string x2); Diagnostic(ErrorCode.ERR_UseDefViolation, "int x1").WithArguments("x1").WithLocation(6, 10), // (6,18): error CS0165: Use of unassigned local variable 'x2' // (int x1, string x2); Diagnostic(ErrorCode.ERR_UseDefViolation, "string x2").WithArguments("x2").WithLocation(6, 18) ); var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree); var x1 = GetDeconstructionVariable(tree, "x1"); var x1Ref = GetReference(tree, "x1"); Assert.Equal("int", model.GetTypeInfo(x1Ref).Type.ToDisplayString()); var x2 = GetDeconstructionVariable(tree, "x2"); var x2Ref = GetReference(tree, "x2"); Assert.Equal("string", model.GetTypeInfo(x2Ref).Type.ToDisplayString()); VerifyModelForDeconstruction(model, x1, LocalDeclarationKind.DeclarationExpressionVariable, x1Ref); VerifyModelForDeconstruction(model, x2, LocalDeclarationKind.DeclarationExpressionVariable, x2Ref); } [Fact] [WorkItem(15893, "https://github.com/dotnet/roslyn/issues/15893")] public void DeconstructionOfOnlyOneElement() { string source = @" class C { static void Main() { var (p2) = (1, 2); } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (6,16): error CS1003: Syntax error, ',' expected // var (p2) = (1, 2); Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",").WithLocation(6, 16), // (6,16): error CS1001: Identifier expected // var (p2) = (1, 2); Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(6, 16) ); } [Fact] [WorkItem(14876, "https://github.com/dotnet/roslyn/issues/14876")] public void TupleTypeInDeconstruction() { string source = @" class C { static void Main() { (int x, (string, long) y) = M(); System.Console.Write($""{x} {y}""); } static (int, (string, long)) M() { return (5, (""Goo"", 34983490)); } } "; var comp = CompileAndVerify(source, expectedOutput: "5 (Goo, 34983490)"); comp.VerifyDiagnostics(); } [Fact] [WorkItem(12468, "https://github.com/dotnet/roslyn/issues/12468")] public void RefReturningVarInvocation() { string source = @" class C { static int i; static void Main() { int x = 0, y = 0; (var(x, y)) = 42; // parsed as invocation System.Console.Write(i); } static ref int var(int a, int b) { return ref i; } } "; var comp = CompileAndVerify(source, expectedOutput: "42", verify: Verification.Passes); comp.VerifyDiagnostics(); } [Fact] public void InvokeVarForLvalueInParens() { var source = @" class Program { public static void Main() { (var(x, y)) = 10; System.Console.WriteLine(z); } static int x = 1, y = 2, z = 3; static ref int var(int x, int y) { return ref z; } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); // PEVerify fails with ref return https://github.com/dotnet/roslyn/issues/12285 CompileAndVerify(compilation, expectedOutput: "10", verify: Verification.Fails); } [Fact] [WorkItem(16106, "https://github.com/dotnet/roslyn/issues/16106")] public void DefAssignmentsStruct001() { string source = @" using System.Collections.Generic; public class MyClass { public static void Main() { ((int, int), string)[] arr = new((int, int), string)[1]; Test5(arr); } public static void Test4(IEnumerable<(KeyValuePair<int, int>, string)> en) { foreach ((KeyValuePair<int, int> kv, string s) in en) { var a = kv.Key; // false error CS0170: Use of possibly unassigned field } } public static void Test5(IEnumerable<((int, int), string)> en) { foreach (((int, int k) t, string s) in en) { var a = t.k; // false error CS0170: Use of possibly unassigned field System.Console.WriteLine(a); } } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "0"); } [Fact] [WorkItem(16106, "https://github.com/dotnet/roslyn/issues/16106")] public void DefAssignmentsStruct002() { string source = @" public class MyClass { public static void Main() { var data = new int[10]; var arr = new int[2]; foreach (arr[out int size] in data) {} } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (9,36): error CS0230: Type and identifier are both required in a foreach statement // foreach (arr[out int size] in data) {} Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(9, 36) ); } [Fact] [WorkItem(16106, "https://github.com/dotnet/roslyn/issues/16106")] public void DefAssignmentsStruct003() { string source = @" public class MyClass { public static void Main() { var data = new (int, int)[10]; var arr = new int[2]; foreach ((arr[out int size], int b) in data) {} } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (9,27): error CS1615: Argument 1 may not be passed with the 'out' keyword // foreach ((arr[out int size], int b) in data) {} Diagnostic(ErrorCode.ERR_BadArgExtraRef, "int size").WithArguments("1", "out").WithLocation(9, 27), // (9,18): error CS8186: A foreach loop must declare its iteration variables. // foreach ((arr[out int size], int b) in data) {} Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(arr[out int size], int b)").WithLocation(9, 18) ); } [Fact] [WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")] public void Events_01() { string source = @" class C { static event System.Action E; static void Main() { (E, _) = (null, 1); System.Console.WriteLine(E == null); (E, _) = (Handler, 1); E(); } static void Handler() { System.Console.WriteLine(""Handler""); } } "; var comp = CompileAndVerify(source, expectedOutput: @"True Handler"); comp.VerifyDiagnostics(); } [Fact] [WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")] public void Events_02() { string source = @" struct S { event System.Action E; class C { static void Main() { var s = new S(); (s.E, _) = (null, 1); System.Console.WriteLine(s.E == null); (s.E, _) = (Handler, 1); s.E(); } static void Handler() { System.Console.WriteLine(""Handler""); } } } "; var comp = CompileAndVerify(source, expectedOutput: @"True Handler"); comp.VerifyDiagnostics(); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.WinRTNeedsWindowsDesktop)] [WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")] public void Events_03() { string source1 = @" public interface EventInterface { event System.Action E; } "; var comp1 = CreateEmptyCompilation(source1, WinRtRefs, TestOptions.ReleaseWinMD, TestOptions.Regular); string source2 = @" class C : EventInterface { public event System.Action E; static void Main() { var c = new C(); c.Test(); } void Test() { (E, _) = (null, 1); System.Console.WriteLine(E == null); (E, _) = (Handler, 1); E(); } static void Handler() { System.Console.WriteLine(""Handler""); } } "; var comp2 = CompileAndVerify(source2, targetFramework: TargetFramework.Empty, expectedOutput: @"True Handler", references: WinRtRefs.Concat(new[] { ValueTupleRef, comp1.ToMetadataReference() })); comp2.VerifyDiagnostics(); Assert.True(comp2.Compilation.GetMember<IEventSymbol>("C.E").IsWindowsRuntimeEvent); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.WinRTNeedsWindowsDesktop)] [WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")] public void Events_04() { string source1 = @" public interface EventInterface { event System.Action E; } "; var comp1 = CreateEmptyCompilation(source1, WinRtRefs, TestOptions.ReleaseWinMD, TestOptions.Regular); string source2 = @" struct S : EventInterface { public event System.Action E; class C { S s = new S(); static void Main() { var c = new C(); (GetC(c).s.E, _) = (null, GetInt(1)); System.Console.WriteLine(c.s.E == null); (GetC(c).s.E, _) = (Handler, GetInt(2)); c.s.E(); } static int GetInt(int i) { System.Console.WriteLine(i); return i; } static C GetC(C c) { System.Console.WriteLine(""GetC""); return c; } static void Handler() { System.Console.WriteLine(""Handler""); } } } "; var comp2 = CompileAndVerify(source2, targetFramework: TargetFramework.Empty, expectedOutput: @"GetC 1 True GetC 2 Handler", references: WinRtRefs.Concat(new[] { ValueTupleRef, comp1.ToMetadataReference() })); comp2.VerifyDiagnostics(); Assert.True(comp2.Compilation.GetMember<IEventSymbol>("S.E").IsWindowsRuntimeEvent); } [Fact] [WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")] public void Events_05() { string source = @" class C { public static event System.Action E; } class Program { static void Main() { (C.E, _) = (null, 1); } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (11,12): error CS0070: The event 'C.E' can only appear on the left hand side of += or -= (except when used from within the type 'C') // (C.E, _) = (null, 1); Diagnostic(ErrorCode.ERR_BadEventUsage, "E").WithArguments("C.E", "C").WithLocation(11, 12) ); } [Fact] [WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")] public void Events_06() { string source = @" class C { static event System.Action E { add {} remove {} } static void Main() { (E, _) = (null, 1); } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (12,10): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= // (E, _) = (null, 1); Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(12, 10) ); } [Fact] public void SimpleAssignInConstructor() { string source = @" public class C { public long x; public string y; public C(int a, string b) => (x, y) = (a, b); public static void Main() { var c = new C(1, ""hello""); System.Console.WriteLine(c.x + "" "" + c.y); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C..ctor(int, string)", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (long V_0, string V_1) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.1 IL_0007: conv.i8 IL_0008: stloc.0 IL_0009: ldarg.2 IL_000a: stloc.1 IL_000b: ldarg.0 IL_000c: ldloc.0 IL_000d: stfld ""long C.x"" IL_0012: ldarg.0 IL_0013: ldloc.1 IL_0014: stfld ""string C.y"" IL_0019: ret }"); } [Fact] public void DeconstructAssignInConstructor() { string source = @" public class C { public long x; public string y; public C(C oldC) => (x, y) = oldC; public C() { } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } public static void Main() { var oldC = new C() { x = 1, y = ""hello"" }; var newC = new C(oldC); System.Console.WriteLine(newC.x + "" "" + newC.y); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C..ctor(C)", @" { // Code size 34 (0x22) .maxstack 3 .locals init (int V_0, string V_1, long V_2) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.1 IL_0007: ldloca.s V_0 IL_0009: ldloca.s V_1 IL_000b: callvirt ""void C.Deconstruct(out int, out string)"" IL_0010: ldloc.0 IL_0011: conv.i8 IL_0012: stloc.2 IL_0013: ldarg.0 IL_0014: ldloc.2 IL_0015: stfld ""long C.x"" IL_001a: ldarg.0 IL_001b: ldloc.1 IL_001c: stfld ""string C.y"" IL_0021: ret }"); } [Fact] public void AssignInConstructorWithProperties() { string source = @" public class C { public long X { get; set; } public string Y { get; } private int z; public ref int Z { get { return ref z; } } public C(int a, string b, ref int c) => (X, Y, Z) = (a, b, c); public static void Main() { int number = 2; var c = new C(1, ""hello"", ref number); System.Console.WriteLine($""{c.X} {c.Y} {c.Z}""); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 hello 2"); comp.VerifyDiagnostics(); comp.VerifyIL("C..ctor(int, string, ref int)", @" { // Code size 37 (0x25) .maxstack 3 .locals init (long V_0, string V_1, int V_2) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.0 IL_0007: call ""ref int C.Z.get"" IL_000c: ldarg.1 IL_000d: conv.i8 IL_000e: stloc.0 IL_000f: ldarg.2 IL_0010: stloc.1 IL_0011: ldarg.3 IL_0012: ldind.i4 IL_0013: stloc.2 IL_0014: ldarg.0 IL_0015: ldloc.0 IL_0016: call ""void C.X.set"" IL_001b: ldarg.0 IL_001c: ldloc.1 IL_001d: stfld ""string C.<Y>k__BackingField"" IL_0022: ldloc.2 IL_0023: stind.i4 IL_0024: ret }"); } [Fact] [WorkItem(38702, "https://github.com/dotnet/roslyn/issues/38702")] public void AssignInConstructorWithProperties2() { string source = @" public class Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) => (X, Y) = (x, y); public static void Main() { var p = new Point(1, 2); System.Console.WriteLine(p.X + "" "" + p.Y); } } "; var comp = CompileAndVerify(source, expectedOutput: "1 2"); comp.VerifyDiagnostics(); comp.VerifyIL("Point..ctor(int, int)", @" { // Code size 25 (0x19) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldarg.2 IL_0009: stloc.1 IL_000a: ldarg.0 IL_000b: ldloc.0 IL_000c: call ""void Point.X.set"" IL_0011: ldarg.0 IL_0012: ldloc.1 IL_0013: call ""void Point.Y.set"" IL_0018: ret }"); } [Fact] public void VerifyDeconstructionInAsync() { var source = @" using System.Threading.Tasks; class C { static void Main() { System.Console.Write(C.M().Result); } static async Task<int> M() { await Task.Delay(0); var (x, y) = (1, 2); return x + y; } } "; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "3"); } [Fact] public void DeconstructionWarnsForSelfAssignment() { var source = @" class C { object x = 1; static object y = 2; void M() { ((x, x), this.x, C.y) = ((x, (1, 2)), x, y); } } "; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugDll); comp.VerifyDiagnostics( // (8,11): warning CS1717: Assignment made to same variable; did you mean to assign something else? // ((x, x), this.x, C.y) = ((x, (1, 2)), x, y); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "x").WithLocation(8, 11), // (8,18): warning CS1717: Assignment made to same variable; did you mean to assign something else? // ((x, x), this.x, C.y) = ((x, (1, 2)), x, y); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "this.x").WithLocation(8, 18), // (8,26): warning CS1717: Assignment made to same variable; did you mean to assign something else? // ((x, x), this.x, C.y) = ((x, (1, 2)), x, y); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "C.y").WithLocation(8, 26) ); } [Fact] public void DeconstructionWarnsForSelfAssignment2() { var source = @" class C { object x = 1; static object y = 2; void M() { object z = 3; (x, (y, z)) = (x, (y, z)); } } "; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugDll); comp.VerifyDiagnostics( // (9,10): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (x, (y, z)) = (x, (y, z)); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "x"), // (9,14): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (x, (y, z)) = (x, (y, z)); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "y").WithLocation(9, 14), // (9,17): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (x, (y, z)) = (x, (y, z)); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "z").WithLocation(9, 17) ); } [Fact] public void DeconstructionWarnsForSelfAssignment_WithUserDefinedConversionOnElement() { var source = @" class C { object x = 1; static C y = null; void M() { (x, y) = (x, (C)(D)y); } public static implicit operator C(D d) => null; } class D { public static implicit operator D(C c) => null; } "; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugDll); comp.VerifyDiagnostics( // (8,10): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (x, y) = (x, (C)(D)y); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "x").WithLocation(8, 10) ); } [Fact] public void DeconstructionWarnsForSelfAssignment_WithNestedConversions() { var source = @" class C { object x = 1; int y = 2; byte b = 3; void M() { // The conversions on the right-hand-side: // - a deconstruction conversion // - an implicit tuple literal conversion on the entire right-hand-side // - another implicit tuple literal conversion on the nested tuple // - a conversion on element `b` (_, (x, y)) = (1, (x, b)); } } "; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugDll); comp.VerifyDiagnostics( // (14,14): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (_, (x, y)) = (1, (x, b)); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "x").WithLocation(14, 14) ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructionWarnsForSelfAssignment_WithExplicitTupleConversion() { var source = @" class C { int y = 2; byte b = 3; void M() { // The conversions on the right-hand-side: // - a deconstruction conversion on the entire right-hand-side // - an identity conversion as its operand // - an explicit tuple literal conversion as its operand (y, _) = ((int, int))(y, b); } } "; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugDll); comp.VerifyDiagnostics( ); var tree = comp.SyntaxTrees.Single(); var node = tree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().Single(); Assert.Equal("((int, int))(y, b)", node.ToString()); comp.VerifyOperationTree(node, expectedOperationTree: @" IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Int32)) (Syntax: '((int, int))(y, b)') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 y, System.Int32 b)) (Syntax: '(y, b)') NaturalType: (System.Int32 y, System.Byte b) Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IFieldReferenceOperation: System.Int32 C.y (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'y') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'y') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'b') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IFieldReferenceOperation: System.Byte C.b (OperationKind.FieldReference, Type: System.Byte) (Syntax: 'b') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'b') "); } [Fact] public void DeconstructionWarnsForSelfAssignment_WithDeconstruct() { var source = @" class C { object x = 1; static object y = 2; void M() { object z = 3; (x, (y, z)) = (x, y); } } static class Extensions { public static void Deconstruct(this object input, out object output1, out object output2) { output1 = input; output2 = input; } }"; var comp = CreateCompilationWithMscorlib461(source, references: s_valueTupleRefs, options: TestOptions.DebugDll); comp.VerifyDiagnostics( // (9,10): warning CS1717: Assignment made to same variable; did you mean to assign something else? // (x, (y, z)) = (x, y); Diagnostic(ErrorCode.WRN_AssignmentToSelf, "x").WithLocation(9, 10) ); } [Fact] public void TestDeconstructOnErrorType() { var source = @" class C { Error M() { int x, y; (x, y) = M(); throw null; } }"; var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugDll); // no ValueTuple reference comp.VerifyDiagnostics( // (4,5): error CS0246: The type or namespace name 'Error' could not be found (are you missing a using directive or an assembly reference?) // Error M() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Error").WithArguments("Error").WithLocation(4, 5) ); } [Fact] public void TestDeconstructOnErrorTypeFromImageReference() { var missing_cs = "public class Missing { }"; var missing = CreateCompilationWithMscorlib461(missing_cs, options: TestOptions.DebugDll, assemblyName: "missing"); var lib_cs = "public class C { public Missing M() { throw null; } }"; var lib = CreateCompilationWithMscorlib461(lib_cs, references: new[] { missing.EmitToImageReference() }, options: TestOptions.DebugDll); var source = @" class D { void M() { int x, y; (x, y) = new C().M(); throw null; } }"; var comp = CreateCompilationWithMscorlib461(source, references: new[] { lib.EmitToImageReference() }, options: TestOptions.DebugDll); // no ValueTuple reference comp.VerifyDiagnostics( // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // (x, y) = new C().M(); Diagnostic(ErrorCode.ERR_NoTypeDef, "new C().M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18) ); } [Fact] public void TestDeconstructOnErrorTypeFromCompilationReference() { var missing_cs = "public class Missing { }"; var missing = CreateCompilationWithMscorlib461(missing_cs, options: TestOptions.DebugDll, assemblyName: "missing"); var lib_cs = "public class C { public Missing M() { throw null; } }"; var lib = CreateCompilationWithMscorlib461(lib_cs, references: new[] { missing.ToMetadataReference() }, options: TestOptions.DebugDll); var source = @" class D { void M() { int x, y; (x, y) = new C().M(); throw null; } }"; var comp = CreateCompilationWithMscorlib461(source, references: new[] { lib.ToMetadataReference() }, options: TestOptions.DebugDll); // no ValueTuple reference comp.VerifyDiagnostics( // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // (x, y) = new C().M(); Diagnostic(ErrorCode.ERR_NoTypeDef, "new C().M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18) ); } [Fact, WorkItem(17756, "https://github.com/dotnet/roslyn/issues/17756")] public void TestDiscardedAssignmentNotLvalue() { var source = @" class Program { struct S1 { public int field; public int Increment() => field++; } static void Main() { S1 v = default(S1); v.Increment(); (_ = v).Increment(); System.Console.WriteLine(v.field); } } "; string expectedOutput = @"1"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void TupleCastInDeconstruction() { var source = @" class C { static void Main() { var t = (1, 2); var (a, b) = ((byte, byte))t; System.Console.Write($""{a} {b}""); } }"; CompileAndVerify(source, expectedOutput: @"1 2"); } [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void TupleCastInDeconstruction2() { var source = @" class C { static void Main() { var t = (new C(), new D()); var (a, _) = ((byte, byte))t; System.Console.Write($""{a}""); } public static explicit operator byte(C c) { System.Console.Write(""Convert ""); return 1; } } class D { public static explicit operator byte(D c) { System.Console.Write(""Convert2 ""); return 2; } }"; CompileAndVerify(source, expectedOutput: @"Convert Convert2 1"); } [CompilerTrait(CompilerFeature.IOperation)] [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void TupleCastInDeconstruction3() { var source = @" class C { static int A { set { System.Console.Write(""A ""); } } static int B { set { System.Console.Write(""B""); } } static void Main() { (A, B) = ((byte, byte))(new C(), new D()); } public static explicit operator byte(C c) { System.Console.Write(""Convert ""); return 1; } public C() { System.Console.Write(""C ""); } } class D { public static explicit operator byte(D c) { System.Console.Write(""Convert2 ""); return 2; } public D() { System.Console.Write(""D ""); } }"; var compilation = CompileAndVerify(source, expectedOutput: @"C Convert D Convert2 A B").Compilation; var tree = compilation.SyntaxTrees.Single(); var node = tree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().Single(); Assert.Equal("((byte, byte))(new C(), new D())", node.ToString()); compilation.VerifyOperationTree(node, expectedOperationTree: @" IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Byte, System.Byte)) (Syntax: '((byte, byt ... ), new D())') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Byte, System.Byte)) (Syntax: '(new C(), new D())') NaturalType: (C, D) Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Byte C.op_Explicit(C c)) (OperationKind.Conversion, Type: System.Byte, IsImplicit) (Syntax: 'new C()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Byte C.op_Explicit(C c)) Operand: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Byte D.op_Explicit(D c)) (OperationKind.Conversion, Type: System.Byte, IsImplicit) (Syntax: 'new D()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Byte D.op_Explicit(D c)) Operand: IObjectCreationOperation (Constructor: D..ctor()) (OperationKind.ObjectCreation, Type: D) (Syntax: 'new D()') Arguments(0) Initializer: null "); } [CompilerTrait(CompilerFeature.IOperation)] [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void TupleCastInDeconstruction4() { var source = @" class C { static void Main() { var (a, _) = ((short, short))((int, int))(1L, 2L); System.Console.Write($""{a}""); } }"; var compilation = CompileAndVerify(source, expectedOutput: @"1").Compilation; var tree = compilation.SyntaxTrees.Single(); var node = tree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().ElementAt(1); Assert.Equal("((int, int))(1L, 2L)", node.ToString()); compilation.VerifyOperationTree(node, expectedOperationTree: @" IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Int32)) (Syntax: '((int, int))(1L, 2L)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1L, 2L)') NaturalType: (System.Int64, System.Int64) Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '1L') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int64, Constant: 1) (Syntax: '1L') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '2L') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int64, Constant: 2) (Syntax: '2L') "); Assert.Equal("((short, short))((int, int))(1L, 2L)", node.Parent.ToString()); compilation.VerifyOperationTree(node.Parent, expectedOperationTree: @" IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int16, System.Int16)) (Syntax: '((short, sh ... t))(1L, 2L)') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Int32)) (Syntax: '((int, int))(1L, 2L)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1L, 2L)') NaturalType: (System.Int64, System.Int64) Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '1L') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int64, Constant: 1) (Syntax: '1L') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '2L') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int64, Constant: 2) (Syntax: '2L') "); } [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void UserDefinedCastInDeconstruction() { var source = @" class C { static void Main() { var c = new C(); var (a, b) = ((byte, byte))c; System.Console.Write($""{a} {b}""); } public static explicit operator (byte, byte)(C c) { return (3, 4); } }"; CompileAndVerify(source, expectedOutput: @"3 4"); } [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void DeconstructionLoweredToNothing() { var source = @" class C { static void M() { for (var(_, _) = (1, 2); ; (_, _) = (3, 4)) { } } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp); verifier.VerifyIL("C.M", @" { // Code size 2 (0x2) .maxstack 0 IL_0000: br.s IL_0000 }"); } [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void DeconstructionLoweredToNothing2() { var source = @" class C { static void M() { (_, _) = (1, 2); } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp); verifier.VerifyIL("C.M", @" { // Code size 1 (0x1) .maxstack 0 IL_0000: ret }"); } [Fact, WorkItem(19398, "https://github.com/dotnet/roslyn/issues/19398")] public void DeconstructionLoweredToNothing3() { var source = @" class C { static void Main() { foreach (var(_, _) in new[] { (1, 2) }) { System.Console.Write(""once""); } } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "once"); } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] [Fact] public void InferredName() { var source = @"class C { static void Main() { int x = 0, y = 1; var t = (x, y); var (a, b) = t; } }"; // C# 7.0 var comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(); // C# 7.1 comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(); } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] [Fact] public void InferredName_ConditionalOperator() { var source = @"class C { static void M(int a, int b, bool c) { (var x, var y) = c ? (a, default(object)) : (b, null); (x, y) = c ? (a, default(string)) : (b, default(object)); } }"; // C# 7.0 var comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(); // C# 7.1 comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(); } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] [Fact] public void InferredName_ImplicitArray() { var source = @"class C { static void M(int x) { int y; object z; (y, z) = (new [] { (x, default(object)), (2, 3) })[0]; } }"; // C# 7.0 var comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(); // C# 7.1 comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(); } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] [Fact(Skip = "https://github.com/dotnet/roslyn/issues/32006")] public void InferredName_Lambda() { // See https://github.com/dotnet/roslyn/issues/32006 // need to relax assertion in GetImplicitTupleLiteralConversion var source = @"class C { static T F<T>(System.Func<object, bool, T> f) { return f(null, false); } static void M() { var (x, y) = F((a, b) => { if (b) return (default(object), a); return (null, null); }); } }"; // C# 7.0 var comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(); // C# 7.1 comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(); } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] [Fact] public void InferredName_ConditionalOperator_LongTuple() { var source = @"class C { static void M(object a, object b, bool c) { var (_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) = c ? (1, 2, 3, 4, 5, 6, 7, a, b, 10) : (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } }"; // C# 7.0 var comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(); // C# 7.1 comp = CreateCompilation( source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(); } [WorkItem(21028, "https://github.com/dotnet/roslyn/issues/21028")] [Fact] public void InferredName_ConditionalOperator_UseSite() { var source = @"class C { static void M(int a, int b, bool c) { var (x, y) = c ? ((object)1, a) : (b, 2); } } namespace System { struct ValueTuple<T1, T2> { public T1 Item1; private T2 Item2; public ValueTuple(T1 item1, T2 item2) => throw null; } }"; var expected = new[] { // (12,19): warning CS0649: Field '(T1, T2).Item1' is never assigned to, and will always have its default value // public T1 Item1; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Item1").WithArguments("(T1, T2).Item1", "").WithLocation(12, 19), // (13,20): warning CS0169: The field '(T1, T2).Item2' is never used // private T2 Item2; Diagnostic(ErrorCode.WRN_UnreferencedField, "Item2").WithArguments("(T1, T2).Item2").WithLocation(13, 20) }; // C# 7.0 var comp = CreateCompilation( source, assemblyName: "39f5d0e8-2935-4207-a74d-517a8e55af08", parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(expected); // C# 7.1 comp = CreateCompilation( source, assemblyName: "39f5d0e8-2935-4207-a74d-517a8e55af08", parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(expected); } [Fact] public void InferredName_ConditionalOperator_UseSite_AccessingWithinConstructor() { var source = @"class C { static void M(int a, int b, bool c) { var (x, y) = c ? ((object)1, a) : (b, 2); } } namespace System { struct ValueTuple<T1, T2> { public T1 Item1; private T2 Item2; public ValueTuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; } } }"; var expected = new[] { // (13,20): warning CS0169: The field '(T1, T2).Item2' is never used // private T2 Item2; Diagnostic(ErrorCode.WRN_UnreferencedField, "Item2").WithArguments("(T1, T2).Item2").WithLocation(13, 20), // (14,16): error CS0171: Field '(T1, T2).Item2' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the field. // public ValueTuple(T1 item1, T2 item2) Diagnostic(ErrorCode.ERR_UnassignedThisUnsupportedVersion, "ValueTuple").WithArguments("(T1, T2).Item2", "11.0").WithLocation(14, 16), // (14,16): error CS0171: Field '(T1, T2).Item2' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the field. // public ValueTuple(T1 item1, T2 item2) Diagnostic(ErrorCode.ERR_UnassignedThisUnsupportedVersion, "ValueTuple").WithArguments("(T1, T2).Item2", "11.0").WithLocation(14, 16), // (17,13): error CS0229: Ambiguity between '(T1, T2).Item2' and '(T1, T2).Item2' // Item2 = item2; Diagnostic(ErrorCode.ERR_AmbigMember, "Item2").WithArguments("(T1, T2).Item2", "(T1, T2).Item2").WithLocation(17, 13) }; // C# 7.0 var comp = CreateCompilation( source, assemblyName: "39f5d0e8-2935-4207-a74d-517a8e55af08", parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyEmitDiagnostics(expected); // C# 7.1 comp = CreateCompilation( source, assemblyName: "39f5d0e8-2935-4207-a74d-517a8e55af08", parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); comp.VerifyEmitDiagnostics(expected); } [Fact] public void TestGetDeconstructionInfoOnIncompleteCode() { string source = @" class C { void M() { var (y1, y2) =} void Deconstruct(out int x1, out int x2) { x1 = 1; x2 = 2; } } "; var comp = CreateCompilation(source); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>().First(); Assert.Equal("var (y1, y2) =", node.ToString()); var info = model.GetDeconstructionInfo(node); Assert.Null(info.Method); Assert.Empty(info.Nested); } [Fact] public void TestDeconstructStructThis() { string source = @" public struct S { int I; public static void Main() { S s = new S(); s.M(); } public void M() { this.I = 42; var (x, (y, z)) = (this, this /* mutating deconstruction */); System.Console.Write($""{x.I} {y} {z}""); } void Deconstruct(out int x1, out int x2) { x1 = I++; x2 = I++; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "42 42 43"); } [Fact] public void TestDeconstructClassThis() { string source = @" public class C { int I; public static void Main() { C c = new C(); c.M(); } public void M() { this.I = 42; var (x, (y, z)) = (this, this /* mutating deconstruction */); System.Console.Write($""{x.I} {y} {z}""); } void Deconstruct(out int x1, out int x2) { x1 = I++; x2 = I++; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "44 42 43"); } [Fact] public void AssigningConditional_OutParams() { string source = @" using System; class C { static void Main() { Test(true, false); Test(false, true); Test(false, false); } static void Test(bool b1, bool b2) { M(out int x, out int y, b1, b2); Console.Write(x); Console.Write(y); } static void M(out int x, out int y, bool b1, bool b2) { (x, y) = b1 ? (10, 20) : b2 ? (30, 40) : (50, 60); } } "; var comp = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "102030405060"); comp.VerifyDiagnostics(); comp.VerifyIL("C.M", @" { // Code size 33 (0x21) .maxstack 2 IL_0000: ldarg.2 IL_0001: brtrue.s IL_0018 IL_0003: ldarg.3 IL_0004: brtrue.s IL_000f IL_0006: ldarg.0 IL_0007: ldc.i4.s 50 IL_0009: stind.i4 IL_000a: ldarg.1 IL_000b: ldc.i4.s 60 IL_000d: stind.i4 IL_000e: ret IL_000f: ldarg.0 IL_0010: ldc.i4.s 30 IL_0012: stind.i4 IL_0013: ldarg.1 IL_0014: ldc.i4.s 40 IL_0016: stind.i4 IL_0017: ret IL_0018: ldarg.0 IL_0019: ldc.i4.s 10 IL_001b: stind.i4 IL_001c: ldarg.1 IL_001d: ldc.i4.s 20 IL_001f: stind.i4 IL_0020: ret }"); } [Fact] public void AssigningConditional_VarDeconstruction() { string source = @" using System; class C { static void Main() { M(true, false); M(false, true); M(false, false); } static void M(bool b1, bool b2) { var (x, y) = b1 ? (10, 20) : b2 ? (30, 40) : (50, 60); Console.Write(x); Console.Write(y); } } "; var comp = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "102030405060"); comp.VerifyDiagnostics(); comp.VerifyIL("C.M", @" { // Code size 41 (0x29) .maxstack 1 .locals init (int V_0, //x int V_1) //y IL_0000: ldarg.0 IL_0001: brtrue.s IL_0016 IL_0003: ldarg.1 IL_0004: brtrue.s IL_000e IL_0006: ldc.i4.s 50 IL_0008: stloc.0 IL_0009: ldc.i4.s 60 IL_000b: stloc.1 IL_000c: br.s IL_001c IL_000e: ldc.i4.s 30 IL_0010: stloc.0 IL_0011: ldc.i4.s 40 IL_0013: stloc.1 IL_0014: br.s IL_001c IL_0016: ldc.i4.s 10 IL_0018: stloc.0 IL_0019: ldc.i4.s 20 IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: call ""void System.Console.Write(int)"" IL_0022: ldloc.1 IL_0023: call ""void System.Console.Write(int)"" IL_0028: ret }"); } [Fact] public void AssigningConditional_MixedDeconstruction() { string source = @" using System; class C { static void Main() { M(true, false); M(false, true); M(false, false); } static void M(bool b1, bool b2) { (var x, long y) = b1 ? (10, 20) : b2 ? (30, 40) : (50, 60); Console.Write(x); Console.Write(y); } } "; var comp = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "102030405060"); comp.VerifyDiagnostics(); comp.VerifyIL("C.M", @" { // Code size 50 (0x32) .maxstack 1 .locals init (int V_0, //x long V_1, //y long V_2) IL_0000: ldarg.0 IL_0001: brtrue.s IL_001c IL_0003: ldarg.1 IL_0004: brtrue.s IL_0011 IL_0006: ldc.i4.s 60 IL_0008: conv.i8 IL_0009: stloc.2 IL_000a: ldc.i4.s 50 IL_000c: stloc.0 IL_000d: ldloc.2 IL_000e: stloc.1 IL_000f: br.s IL_0025 IL_0011: ldc.i4.s 40 IL_0013: conv.i8 IL_0014: stloc.2 IL_0015: ldc.i4.s 30 IL_0017: stloc.0 IL_0018: ldloc.2 IL_0019: stloc.1 IL_001a: br.s IL_0025 IL_001c: ldc.i4.s 20 IL_001e: conv.i8 IL_001f: stloc.2 IL_0020: ldc.i4.s 10 IL_0022: stloc.0 IL_0023: ldloc.2 IL_0024: stloc.1 IL_0025: ldloc.0 IL_0026: call ""void System.Console.Write(int)"" IL_002b: ldloc.1 IL_002c: call ""void System.Console.Write(long)"" IL_0031: ret }"); } [Fact] public void AssigningConditional_SideEffects() { string source = @" using System; class C { static void Main() { M(true, false); M(false, true); M(false, false); SideEffect(true); SideEffect(false); } static int left; static int right; static ref int SideEffect(bool isLeft) { Console.WriteLine($""{(isLeft ? ""left"" : ""right"")}: {(isLeft ? left : right)}""); return ref isLeft ? ref left : ref right; } static void M(bool b1, bool b2) { (SideEffect(isLeft: true), SideEffect(isLeft: false)) = b1 ? (10, 20) : b2 ? (30, 40) : (50, 60); } } "; var expected = @"left: 0 right: 0 left: 10 right: 20 left: 30 right: 40 left: 50 right: 60"; var comp = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expected); comp.VerifyDiagnostics(); comp.VerifyIL("C.M", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (int& V_0, int& V_1) IL_0000: ldc.i4.1 IL_0001: call ""ref int C.SideEffect(bool)"" IL_0006: stloc.0 IL_0007: ldc.i4.0 IL_0008: call ""ref int C.SideEffect(bool)"" IL_000d: stloc.1 IL_000e: ldarg.0 IL_000f: brtrue.s IL_0026 IL_0011: ldarg.1 IL_0012: brtrue.s IL_001d IL_0014: ldloc.0 IL_0015: ldc.i4.s 50 IL_0017: stind.i4 IL_0018: ldloc.1 IL_0019: ldc.i4.s 60 IL_001b: stind.i4 IL_001c: ret IL_001d: ldloc.0 IL_001e: ldc.i4.s 30 IL_0020: stind.i4 IL_0021: ldloc.1 IL_0022: ldc.i4.s 40 IL_0024: stind.i4 IL_0025: ret IL_0026: ldloc.0 IL_0027: ldc.i4.s 10 IL_0029: stind.i4 IL_002a: ldloc.1 IL_002b: ldc.i4.s 20 IL_002d: stind.i4 IL_002e: ret }"); } [Fact] public void AssigningConditional_SideEffects_RHS() { string source = @" using System; class C { static void Main() { M(true, false); M(false, true); M(false, false); } static T Echo<T>(T v, int i) { Console.WriteLine(i + "": "" + v); return v; } static void M(bool b1, bool b2) { var (x, y) = Echo(b1, 1) ? Echo((10, 20), 2) : Echo(b2, 3) ? Echo((30, 40), 4) : Echo((50, 60), 5); Console.WriteLine(""x: "" + x); Console.WriteLine(""y: "" + y); Console.WriteLine(); } } "; var expectedOutput = @"1: True 2: (10, 20) x: 10 y: 20 1: False 3: True 4: (30, 40) x: 30 y: 40 1: False 3: False 5: (50, 60) x: 50 y: 60 "; var comp = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput); comp.VerifyDiagnostics(); } [Fact] public void AssigningConditional_UnusedDeconstruction() { string source = @" class C { static void M(bool b1, bool b2) { (_, _) = b1 ? (10, 20) : b2 ? (30, 40) : (50, 60); } } "; var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("C.M", @" { // Code size 6 (0x6) .maxstack 1 IL_0000: ldarg.0 IL_0001: brtrue.s IL_0005 IL_0003: ldarg.1 IL_0004: pop IL_0005: ret }"); } [Fact, WorkItem(46562, "https://github.com/dotnet/roslyn/issues/46562")] public void CompoundAssignment() { string source = @" class C { void M() { decimal x = 0; (var y, _) += 0.00m; (int z, _) += z; (var t, _) += (1, 2); } } "; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,17): warning CS0219: The variable 'x' is assigned but its value is never used // decimal x = 0; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(6, 17), // (7,10): error CS8185: A declaration is not allowed in this context. // (var y, _) += 0.00m; Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var y").WithLocation(7, 10), // (7,17): error CS0103: The name '_' does not exist in the current context // (var y, _) += 0.00m; Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(7, 17), // (8,10): error CS8185: A declaration is not allowed in this context. // (int z, _) += z; Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int z").WithLocation(8, 10), // (8,10): error CS0165: Use of unassigned local variable 'z' // (int z, _) += z; Diagnostic(ErrorCode.ERR_UseDefViolation, "int z").WithArguments("z").WithLocation(8, 10), // (8,17): error CS0103: The name '_' does not exist in the current context // (int z, _) += z; Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(8, 17), // (9,10): error CS8185: A declaration is not allowed in this context. // (var t, _) += (1, 2); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var t").WithLocation(9, 10), // (9,17): error CS0103: The name '_' does not exist in the current context // (var t, _) += (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(9, 17) ); } [Fact, WorkItem(50654, "https://github.com/dotnet/roslyn/issues/50654")] public void Repro50654() { string source = @" class C { static void Main() { (int, (int, (int, int), (int, int)))[] vals = new[] { (1, (2, (3, 4), (5, 6))), (11, (12, (13, 14), (15, 16))) }; foreach (var (a, (b, (c, d), (e, f))) in vals) { System.Console.Write($""{a + b + c + d + e + f} ""); } foreach ((int a, (int b, (int c, int d), (int e, int f))) in vals) { System.Console.Write($""{a + b + c + d + e + f} ""); } } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "21 81 21 81"); } [Fact] public void MixDeclarationAndAssignmentPermutationsOf2() { string source = @" class C { static void Main() { int x1 = 0; (x1, string y1) = new C(); System.Console.WriteLine(x1 + "" "" + y1); int x2; (x2, var y2) = new C(); System.Console.WriteLine(x2 + "" "" + y2); string y3 = """"; (int x3, y3) = new C(); System.Console.WriteLine(x3 + "" "" + y3); string y4; (var x4, y4) = new C(); System.Console.WriteLine(x4 + "" "" + y4); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: @"1 hello 1 hello 1 hello 1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 188 (0xbc) .maxstack 3 .locals init (int V_0, //x1 string V_1, //y1 int V_2, //x2 string V_3, //y2 string V_4, //y3 int V_5, //x3 string V_6, //y4 int V_7, //x4 int V_8, string V_9) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: newobj ""C..ctor()"" IL_0007: ldloca.s V_8 IL_0009: ldloca.s V_9 IL_000b: callvirt ""void C.Deconstruct(out int, out string)"" IL_0010: ldloc.s V_8 IL_0012: stloc.0 IL_0013: ldloc.s V_9 IL_0015: stloc.1 IL_0016: ldloca.s V_0 IL_0018: call ""string int.ToString()"" IL_001d: ldstr "" "" IL_0022: ldloc.1 IL_0023: call ""string string.Concat(string, string, string)"" IL_0028: call ""void System.Console.WriteLine(string)"" IL_002d: newobj ""C..ctor()"" IL_0032: ldloca.s V_8 IL_0034: ldloca.s V_9 IL_0036: callvirt ""void C.Deconstruct(out int, out string)"" IL_003b: ldloc.s V_8 IL_003d: stloc.2 IL_003e: ldloc.s V_9 IL_0040: stloc.3 IL_0041: ldloca.s V_2 IL_0043: call ""string int.ToString()"" IL_0048: ldstr "" "" IL_004d: ldloc.3 IL_004e: call ""string string.Concat(string, string, string)"" IL_0053: call ""void System.Console.WriteLine(string)"" IL_0058: ldstr """" IL_005d: stloc.s V_4 IL_005f: newobj ""C..ctor()"" IL_0064: ldloca.s V_8 IL_0066: ldloca.s V_9 IL_0068: callvirt ""void C.Deconstruct(out int, out string)"" IL_006d: ldloc.s V_8 IL_006f: stloc.s V_5 IL_0071: ldloc.s V_9 IL_0073: stloc.s V_4 IL_0075: ldloca.s V_5 IL_0077: call ""string int.ToString()"" IL_007c: ldstr "" "" IL_0081: ldloc.s V_4 IL_0083: call ""string string.Concat(string, string, string)"" IL_0088: call ""void System.Console.WriteLine(string)"" IL_008d: newobj ""C..ctor()"" IL_0092: ldloca.s V_8 IL_0094: ldloca.s V_9 IL_0096: callvirt ""void C.Deconstruct(out int, out string)"" IL_009b: ldloc.s V_8 IL_009d: stloc.s V_7 IL_009f: ldloc.s V_9 IL_00a1: stloc.s V_6 IL_00a3: ldloca.s V_7 IL_00a5: call ""string int.ToString()"" IL_00aa: ldstr "" "" IL_00af: ldloc.s V_6 IL_00b1: call ""string string.Concat(string, string, string)"" IL_00b6: call ""void System.Console.WriteLine(string)"" IL_00bb: ret }"); } [Fact] public void MixDeclarationAndAssignmentPermutationsOf3() { string source = @" class C { static void Main() { int x1; string y1; (x1, y1, var z1) = new C(); System.Console.WriteLine(x1 + "" "" + y1 + "" "" + z1); int x2; bool z2; (x2, var y2, z2) = new C(); System.Console.WriteLine(x2 + "" "" + y2 + "" "" + z2); string y3; bool z3; (var x3, y3, z3) = new C(); System.Console.WriteLine(x3 + "" "" + y3 + "" "" + z3); bool z4; (var x4, var y4, z4) = new C(); System.Console.WriteLine(x4 + "" "" + y4 + "" "" + z4); string y5; (var x5, y5, var z5) = new C(); System.Console.WriteLine(x5 + "" "" + y5 + "" "" + z5); int x6; (x6, var y6, var z6) = new C(); System.Console.WriteLine(x6 + "" "" + y6 + "" "" + z6); } public void Deconstruct(out int a, out string b, out bool c) { a = 1; b = ""hello""; c = true; } } "; var comp = CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: @"1 hello True 1 hello True 1 hello True 1 hello True 1 hello True 1 hello True"); comp.VerifyDiagnostics(); } [Fact] public void DontAllowMixedDeclarationAndAssignmentInExpressionContext() { string source = @" class C { static void Main() { int x1 = 0; var z1 = (x1, string y1) = new C(); string y2 = """"; var z2 = (int x2, y2) = new C(); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; CreateCompilation(source).VerifyDiagnostics( // (7,23): error CS8185: A declaration is not allowed in this context. // var z1 = (x1, string y1) = new C(); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "string y1").WithLocation(7, 23), // (9,19): error CS8185: A declaration is not allowed in this context. // var z2 = (int x2, y2) = new C(); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x2").WithLocation(9, 19)); } [Fact] public void DontAllowMixedDeclarationAndAssignmentInForeachDeclarationVariable() { string source = @" class C { static void Main() { int x1; foreach((x1, string y1) in new C[0]); string y2; foreach((int x2, y2) in new C[0]); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; CreateCompilation(source).VerifyDiagnostics( // (6,13): warning CS0168: The variable 'x1' is declared but never used // int x1; Diagnostic(ErrorCode.WRN_UnreferencedVar, "x1").WithArguments("x1").WithLocation(6, 13), // (7,17): error CS8186: A foreach loop must declare its iteration variables. // foreach((x1, string y1) in new C[0]); Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(x1, string y1)").WithLocation(7, 17), // (8,16): warning CS0168: The variable 'y2' is declared but never used // string y2; Diagnostic(ErrorCode.WRN_UnreferencedVar, "y2").WithArguments("y2").WithLocation(8, 16), // (9,17): error CS8186: A foreach loop must declare its iteration variables. // foreach((int x2, y2) in new C[0]); Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(int x2, y2)").WithLocation(9, 17)); } [Fact] public void DuplicateDeclarationOfVariableDeclaredInMixedDeclarationAndAssignment() { string source = @" class C { static void Main() { int x1; string y1; (x1, string y1) = new C(); string y2; (int x2, y2) = new C(); int x2; } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( // (7,16): warning CS0168: The variable 'y1' is declared but never used // string y1; Diagnostic(ErrorCode.WRN_UnreferencedVar, "y1").WithArguments("y1").WithLocation(7, 16), // (8,21): error CS0128: A local variable or function named 'y1' is already defined in this scope // (x1, string y1) = new C(); Diagnostic(ErrorCode.ERR_LocalDuplicate, "y1").WithArguments("y1").WithLocation(8, 21), // (11,13): error CS0128: A local variable or function named 'x2' is already defined in this scope // int x2; Diagnostic(ErrorCode.ERR_LocalDuplicate, "x2").WithArguments("x2").WithLocation(11, 13), // (11,13): warning CS0168: The variable 'x2' is declared but never used // int x2; Diagnostic(ErrorCode.WRN_UnreferencedVar, "x2").WithArguments("x2").WithLocation(11, 13)); } [Fact] public void AssignmentToUndeclaredVariableInMixedDeclarationAndAssignment() { string source = @" class C { static void Main() { (x1, string y1) = new C(); (int x2, y2) = new C(); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( // (6,10): error CS0103: The name 'x1' does not exist in the current context // (x1, string y1) = new C(); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 10), // (7,18): error CS0103: The name 'y2' does not exist in the current context // (int x2, y2) = new C(); Diagnostic(ErrorCode.ERR_NameNotInContext, "y2").WithArguments("y2").WithLocation(7, 18)); } [Fact] public void MixedDeclarationAndAssignmentInForInitialization() { string source = @" class C { static void Main() { int x1; for((x1, string y1) = new C(); x1 < 2; x1++) System.Console.WriteLine(x1 + "" "" + y1); string y2; for((int x2, y2) = new C(); x2 < 2; x2++) System.Console.WriteLine(x2 + "" "" + y2); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: @"1 hello 1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 109 (0x6d) .maxstack 3 .locals init (int V_0, //x1 string V_1, //y2 string V_2, //y1 int V_3, string V_4, int V_5) //x2 IL_0000: newobj ""C..ctor()"" IL_0005: ldloca.s V_3 IL_0007: ldloca.s V_4 IL_0009: callvirt ""void C.Deconstruct(out int, out string)"" IL_000e: ldloc.3 IL_000f: stloc.0 IL_0010: ldloc.s V_4 IL_0012: stloc.2 IL_0013: br.s IL_0030 IL_0015: ldloca.s V_0 IL_0017: call ""string int.ToString()"" IL_001c: ldstr "" "" IL_0021: ldloc.2 IL_0022: call ""string string.Concat(string, string, string)"" IL_0027: call ""void System.Console.WriteLine(string)"" IL_002c: ldloc.0 IL_002d: ldc.i4.1 IL_002e: add IL_002f: stloc.0 IL_0030: ldloc.0 IL_0031: ldc.i4.2 IL_0032: blt.s IL_0015 IL_0034: newobj ""C..ctor()"" IL_0039: ldloca.s V_3 IL_003b: ldloca.s V_4 IL_003d: callvirt ""void C.Deconstruct(out int, out string)"" IL_0042: ldloc.3 IL_0043: stloc.s V_5 IL_0045: ldloc.s V_4 IL_0047: stloc.1 IL_0048: br.s IL_0067 IL_004a: ldloca.s V_5 IL_004c: call ""string int.ToString()"" IL_0051: ldstr "" "" IL_0056: ldloc.1 IL_0057: call ""string string.Concat(string, string, string)"" IL_005c: call ""void System.Console.WriteLine(string)"" IL_0061: ldloc.s V_5 IL_0063: ldc.i4.1 IL_0064: add IL_0065: stloc.s V_5 IL_0067: ldloc.s V_5 IL_0069: ldc.i4.2 IL_006a: blt.s IL_004a IL_006c: ret }"); } [Fact] public void MixDeclarationAndAssignmentInTupleDeconstructPermutationsOf2() { string source = @" class C { static void Main() { int x1 = 0; (x1, string y1) = (1, ""hello""); System.Console.WriteLine(x1 + "" "" + y1); int x2; (x2, var y2) = (1, ""hello""); System.Console.WriteLine(x2 + "" "" + y2); string y3 = """"; (int x3, y3) = (1, ""hello""); System.Console.WriteLine(x3 + "" "" + y3); string y4; (var x4, y4) = (1, ""hello""); System.Console.WriteLine(x4 + "" "" + y4); } } "; var comp = CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: @"1 hello 1 hello 1 hello 1 hello"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 140 (0x8c) .maxstack 3 .locals init (int V_0, //x1 string V_1, //y1 int V_2, //x2 string V_3, //y2 string V_4, //y3 int V_5, //x3 string V_6, //y4 int V_7) //x4 IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldc.i4.1 IL_0003: stloc.0 IL_0004: ldstr ""hello"" IL_0009: stloc.1 IL_000a: ldloca.s V_0 IL_000c: call ""string int.ToString()"" IL_0011: ldstr "" "" IL_0016: ldloc.1 IL_0017: call ""string string.Concat(string, string, string)"" IL_001c: call ""void System.Console.WriteLine(string)"" IL_0021: ldc.i4.1 IL_0022: stloc.2 IL_0023: ldstr ""hello"" IL_0028: stloc.3 IL_0029: ldloca.s V_2 IL_002b: call ""string int.ToString()"" IL_0030: ldstr "" "" IL_0035: ldloc.3 IL_0036: call ""string string.Concat(string, string, string)"" IL_003b: call ""void System.Console.WriteLine(string)"" IL_0040: ldstr """" IL_0045: stloc.s V_4 IL_0047: ldc.i4.1 IL_0048: stloc.s V_5 IL_004a: ldstr ""hello"" IL_004f: stloc.s V_4 IL_0051: ldloca.s V_5 IL_0053: call ""string int.ToString()"" IL_0058: ldstr "" "" IL_005d: ldloc.s V_4 IL_005f: call ""string string.Concat(string, string, string)"" IL_0064: call ""void System.Console.WriteLine(string)"" IL_0069: ldc.i4.1 IL_006a: stloc.s V_7 IL_006c: ldstr ""hello"" IL_0071: stloc.s V_6 IL_0073: ldloca.s V_7 IL_0075: call ""string int.ToString()"" IL_007a: ldstr "" "" IL_007f: ldloc.s V_6 IL_0081: call ""string string.Concat(string, string, string)"" IL_0086: call ""void System.Console.WriteLine(string)"" IL_008b: ret }"); } [Fact] public void MixedDeclarationAndAssignmentCSharpNine() { string source = @" class Program { static void Main() { int x1; (x1, string y1) = new A(); string y2; (int x2, y2) = new A(); bool z3; (int x3, (string y3, z3)) = new B(); int x4; (x4, var (y4, z4)) = new B(); } } class A { public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } class B { public void Deconstruct(out int a, out (string b, bool c) tuple) { a = 1; tuple = (""hello"", true); } } "; CreateCompilation(source, parseOptions: TestOptions.Regular9).VerifyDiagnostics( // (7,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (x1, string y1) = new A(); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(x1, string y1) = new A()").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(7, 9), // (9,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (int x2, y2) = new A(); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(int x2, y2) = new A()").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(9, 9), // (11,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (int x3, (string y3, z3)) = new B(); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(int x3, (string y3, z3)) = new B()").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(11, 9), // (13,9): error CS8773: Feature 'Mixed declarations and expressions in deconstruction' is not available in C# 9.0. Please use language version 10.0 or greater. // (x4, var (y4, z4)) = new B(); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(x4, var (y4, z4)) = new B()").WithArguments("Mixed declarations and expressions in deconstruction", "10.0").WithLocation(13, 9)); } [Fact] public void NestedMixedDeclarationAndAssignmentPermutations() { string source = @" class C { static void Main() { int x1; string y1; (x1, (y1, var z1)) = new C(); System.Console.WriteLine(x1 + "" "" + y1 + "" "" + z1); int x2; bool z2; (x2, (var y2, z2)) = new C(); System.Console.WriteLine(x2 + "" "" + y2 + "" "" + z2); string y3; bool z3; (var x3, (y3, z3)) = new C(); System.Console.WriteLine(x3 + "" "" + y3 + "" "" + z3); bool z4; (var x4, (var y4, z4)) = new C(); System.Console.WriteLine(x4 + "" "" + y4 + "" "" + z4); string y5; (var x5, (y5, var z5)) = new C(); System.Console.WriteLine(x5 + "" "" + y5 + "" "" + z5); int x6; (x6, (var y6, var z6)) = new C(); System.Console.WriteLine(x6 + "" "" + y6 + "" "" + z6); int x7; (x7, var (y7, z7)) = new C(); System.Console.WriteLine(x7 + "" "" + y7 + "" "" + z7); } public void Deconstruct(out int a, out (string a, bool b) b) { a = 1; b = (""hello"", true); } } "; var comp = CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: @"1 hello True 1 hello True 1 hello True 1 hello True 1 hello True 1 hello True 1 hello True"); comp.VerifyDiagnostics(); } [Fact] public void MixedDeclarationAndAssignmentUseBeforeDeclaration() { string source = @" class Program { static void Main() { (x1, string y1) = new A(); int x1; (int x2, y2) = new A(); string y2; (int x3, (string y3, z3)) = new B(); bool z3; (x4, var (y4, z4)) = new B(); int x4; } } class A { public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } class B { public void Deconstruct(out int a, out (string b, bool c) tuple) { a = 1; tuple = (""hello"", true); } } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview) .VerifyDiagnostics( // (6,10): error CS0841: Cannot use local variable 'x1' before it is declared // (x1, string y1) = new A(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x1", isSuppressed: false).WithArguments("x1").WithLocation(6, 10), // (8,18): error CS0841: Cannot use local variable 'y2' before it is declared // (int x2, y2) = new A(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "y2", isSuppressed: false).WithArguments("y2").WithLocation(8, 18), // (10,30): error CS0841: Cannot use local variable 'z3' before it is declared // (int x3, (string y3, z3)) = new B(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "z3", isSuppressed: false).WithArguments("z3").WithLocation(10, 30), // (12,10): error CS0841: Cannot use local variable 'x4' before it is declared // (x4, var (y4, z4)) = new B(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4", isSuppressed: false).WithArguments("x4").WithLocation(12, 10)); } [Fact] public void MixedDeclarationAndAssignmentUseDeclaredVariableInAssignment() { string source = @" class Program { static void Main() { (var x1, x1) = new A(); (x2, var x2) = new A(); (var x3, (var y3, x3)) = new B(); (x4, (var y4, var x4)) = new B(); } } class A { public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } class B { public void Deconstruct(out int a, out (string b, bool c) tuple) { a = 1; tuple = (""hello"", true); } } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview) .VerifyDiagnostics( // (6,18): error CS0841: Cannot use local variable 'x1' before it is declared // (var x1, x1) = new A(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x1").WithArguments("x1").WithLocation(6, 18), // (7,10): error CS0841: Cannot use local variable 'x2' before it is declared // (x2, var x2) = new A(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x2").WithArguments("x2").WithLocation(7, 10), // (8,27): error CS0841: Cannot use local variable 'x3' before it is declared // (var x3, (var y3, x3)) = new B(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x3").WithArguments("x3").WithLocation(8, 27), // (9,10): error CS0841: Cannot use local variable 'x4' before it is declared // (x4, (var y4, var x4)) = new B(); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(9, 10)); } } }