File: CodeGen\CodeGenDeconstructTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit\Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit.UnitTests)
// 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));
        }
    }
}