/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs
6 637 строк
315 KB
Julien Couvreur
Fix `GetDeconstructionInfo` on converted deconstruction assignment (#82324)
14 фев 2026, 12:44
Не верифицирован
14 фев 2026, 12:44
d3884fa
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Linq; using 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.Semantics { [CompilerTrait(CompilerFeature.Tuples)] public class DeconstructionTests : CompilingTestBase { 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(); } }"; [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructMethodMissing() { string source = @" class C { static void Main() { long x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1061: 'C' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "new C()").WithArguments("C", "Deconstruct").WithLocation(8, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructWrongParams() { string source = @" class C { static void Main() { long x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public void Deconstruct(out int a) // too few arguments { a = 1; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1501: No overload for method 'Deconstruct' takes 2 arguments // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgCount, "new C()").WithArguments("Deconstruct", "2").WithLocation(8, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructWrongParams2() { string source = @" class C { static void Main() { long x, y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public void Deconstruct(out int a, out int b, out int c) // too many arguments { a = b = c = 1; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.Int64 y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.Int64 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS7036: There is no argument given that corresponds to the required parameter 'c' of 'C.Deconstruct(out int, out int, out int)' // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "new C()").WithArguments("c", "C.Deconstruct(out int, out int, out int)").WithLocation(7, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void AssignmentWithLeftHandSideErrors() { string source = @" class C { static void Main() { long x = 1; string y = ""hello""; /*<bind>*/(x.f, y.g) = new C()/*</bind>*/; } public void Deconstruct() { } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x.f, y.g) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (? f, ? g), IsInvalid) (Syntax: '(x.f, y.g)') NaturalType: (? f, ? g) Elements(2): IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'x.f') Children(1): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'y.g') Children(1): ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1061: 'long' does not contain a definition for 'f' and no extension method 'f' accepting a first argument of type 'long' could be found (are you missing a using directive or an assembly reference?) // /*<bind>*/(x.f, y.g) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "f").WithArguments("long", "f").WithLocation(8, 22), // CS1061: 'string' does not contain a definition for 'g' and no extension method 'g' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // /*<bind>*/(x.f, y.g) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "g").WithArguments("string", "g").WithLocation(8, 27), // CS1501: No overload for method 'Deconstruct' takes 2 arguments // /*<bind>*/(x.f, y.g) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgCount, "new C()").WithArguments("Deconstruct", "2").WithLocation(8, 32), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x.f, y.g) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 32) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructWithInParam() { string source = @" class C { static void Main() { int x; int y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public void Deconstruct(out int x, int y) { x = 1; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y), IsInvalid) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1615: Argument 2 may not be passed with the 'out' keyword // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgExtraRef, "(x, y) = new C()").WithArguments("2", "out").WithLocation(8, 19), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructWithRefParam() { string source = @" class C { static void Main() { int x; int y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public void Deconstruct(ref int x, out int y) { x = 1; y = 2; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y), IsInvalid) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1620: Argument 1 must be passed with the 'ref' keyword // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgRef, "(x, y) = new C()").WithArguments("1", "ref").WithLocation(8, 19), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(8, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructManually() { string source = @" struct C { static void Main() { long x; string y; C c = new C(); c.Deconstruct(out x, out y); // error /*<bind>*/(x, y) = c/*</bind>*/; } void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y) = c') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1503: Argument 1: cannot convert from 'out long' to 'out int' // c.Deconstruct(out x, out y); // error Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "out long", "out int").WithLocation(10, 27) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructMethodHasOptionalParam() { string source = @" class C { static void Main() { long x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b, int c = 42) // not a Deconstruct operator { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(9, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void BadDeconstructShadowsBaseDeconstruct() { string source = @" class D { public void Deconstruct(out int a, out string b) { a = 2; b = ""world""; } } class C : D { static void Main() { long x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b, int c = 42) // not a Deconstruct operator { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(13, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructMethodHasParams() { string source = @" class C { static void Main() { long x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out string b, params int[] c) // not a Deconstruct operator { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(9, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructMethodHasArglist() { string source = @" class C { static void Main() { long x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public void Deconstruct(out int a, out string b, __arglist) // not a Deconstruct operator { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS7036: There is no argument given that corresponds to the required parameter '__arglist' of 'C.Deconstruct(out int, out string, __arglist)' // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "new C()").WithArguments("__arglist", "C.Deconstruct(out int, out string, __arglist)").WithLocation(9, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(9, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructDelegate() { string source = @" delegate void D1(out int x, out int y); class C { public D1 Deconstruct; // not a Deconstruct operator static void Main() { int x, y; /*<bind>*/(x, y) = new C() { Deconstruct = DeconstructMethod }/*</bind>*/; } public static void DeconstructMethod(out int a, out int b) { a = 1; b = 2; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = ne ... uctMethod }') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C() { D ... uctMethod }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C, IsInvalid) (Syntax: '{ Deconstru ... uctMethod }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: D1, IsInvalid) (Syntax: 'Deconstruct ... tructMethod') Left: IFieldReferenceOperation: D1 C.Deconstruct (OperationKind.FieldReference, Type: D1, IsInvalid) (Syntax: 'Deconstruct') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'Deconstruct') Right: IDelegateCreationOperation (OperationKind.DelegateCreation, Type: D1, IsInvalid, IsImplicit) (Syntax: 'DeconstructMethod') Target: IMethodReferenceOperation: void C.DeconstructMethod(out System.Int32 a, out System.Int32 b) (Static) (OperationKind.MethodReference, Type: null, IsInvalid) (Syntax: 'DeconstructMethod') Instance Receiver: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C() { Deconstruct = DeconstructMethod }/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C() { Deconstruct = DeconstructMethod }").WithArguments("C", "2").WithLocation(11, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructDelegate2() { string source = @" delegate void D1(out int x, out int y); class C { public D1 Deconstruct; static void Main() { int x, y; /*<bind>*/(x, y) = new C() { Deconstruct = DeconstructMethod }/*</bind>*/; } public static void DeconstructMethod(out int a, out int b) { a = 1; b = 2; } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x, System.Int32 y), IsInvalid) (Syntax: '(x, y) = ne ... uctMethod }') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C() { D ... uctMethod }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C, IsInvalid) (Syntax: '{ Deconstru ... uctMethod }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: 'Deconstruct ... tructMethod') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid, IsImplicit) (Syntax: 'Deconstruct') Children(1): IOperation: (OperationKind.None, Type: null, IsInvalid) (Syntax: 'Deconstruct') Children(1): IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'C') Right: IOperation: (OperationKind.None, Type: null) (Syntax: 'DeconstructMethod') Children(1): IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'DeconstructMethod') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0102: The type 'C' already contains a definition for 'Deconstruct' // public void Deconstruct(out int a, out int b) { a = 1; b = 2; } Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "Deconstruct").WithArguments("C", "Deconstruct").WithLocation(16, 17), // CS1913: Member 'Deconstruct' cannot be initialized. It is not a field or property. // /*<bind>*/(x, y) = new C() { Deconstruct = DeconstructMethod }/*</bind>*/; Diagnostic(ErrorCode.ERR_MemberCannotBeInitialized, "Deconstruct").WithArguments("Deconstruct").WithLocation(11, 38), // CS0649: Field 'C.Deconstruct' is never assigned to, and will always have its default value null // public D1 Deconstruct; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Deconstruct").WithArguments("C.Deconstruct", "null").WithLocation(6, 15) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructEvent() { string source = @" delegate void D1(out int x, out int y); class C { public event D1 Deconstruct; // not a Deconstruct operator static void Main() { long x; int y; C c = new C(); c.Deconstruct += DeconstructMethod; /*<bind>*/(x, y) = c/*</bind>*/; } public static void DeconstructMethod(out int a, out int b) { a = 1; b = 2; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = c') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsInvalid) (Syntax: 'c') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = c/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "c").WithArguments("C", "2").WithLocation(14, 28), // CS0067: The event 'C.Deconstruct' is never used // public event D1 Deconstruct; // not a Deconstruct operator Diagnostic(ErrorCode.WRN_UnreferencedEvent, "Deconstruct").WithArguments("C.Deconstruct").WithLocation(6, 21) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ConversionErrors() { string source = @" class C { static void Main() { byte x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public void Deconstruct(out int a, out int b) { a = b = 1; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Byte x, System.String y), IsInvalid) (Syntax: '(x, y)') NaturalType: (System.Byte x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Byte, IsInvalid) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String, IsInvalid) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0266: Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?) // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("int", "byte").WithLocation(8, 20), // CS0029: Cannot implicitly convert type 'int' to 'string' // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("int", "string").WithLocation(8, 23) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void ExpressionType() { string source = @" class C { static void Main() { int x, y; var type = ((x, y) = new C()).GetType(); System.Console.Write(type.ToString()); } public void Deconstruct(out int a, out int b) { a = b = 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "System.ValueTuple`2[System.Int32,System.Int32]"); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ExpressionType_IOperation() { string source = @" class C { static void Main() { int x, y; var type = (/*<bind>*/(x, y) = new C()/*</bind>*/).GetType(); System.Console.Write(type.ToString()); } public void Deconstruct(out int a, out int b) { a = b = 1; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void LambdaStillNotValidStatement() { string source = @" class C { static void Main() { (a) => a; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (a) => a; Diagnostic(ErrorCode.ERR_IllegalStatement, "(a) => a").WithLocation(6, 9) ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void LambdaWithBodyStillNotValidStatement() { string source = @" class C { static void Main() { /*<bind>*/(a, b) => { }/*</bind>*/; } } "; string expectedOperationTree = @" IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null, IsInvalid) (Syntax: '(a, b) => { }') IBlockOperation (0 statements) (OperationKind.Block, Type: null, IsInvalid) (Syntax: '{ }') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // /*<bind>*/(a, b) => { }/*</bind>*/; Diagnostic(ErrorCode.ERR_IllegalStatement, "(a, b) => { }").WithLocation(6, 19) }; VerifyOperationTreeAndDiagnosticsForTest<ParenthesizedLambdaExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void CastButNotCast() { // int and string must be types, so (int, string) must be type and ((int, string)) a cast, but then .String() cannot follow a cast... string source = @" class C { static void Main() { /*<bind>*/((int, string)).ToString()/*</bind>*/; } } "; string expectedOperationTree = @" IInvocationOperation (virtual System.String (System.Int32, System.String).ToString()) (OperationKind.Invocation, Type: System.String, IsInvalid) (Syntax: '((int, stri ... .ToString()') Instance Receiver: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.String), IsInvalid) (Syntax: '(int, string)') NaturalType: (System.Int32, System.String) Elements(2): IOperation: (OperationKind.None, Type: System.Int32, IsInvalid) (Syntax: 'int') IOperation: (OperationKind.None, Type: System.String, IsInvalid) (Syntax: 'string') Arguments(0) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1525: Invalid expression term 'int' // /*<bind>*/((int, string)).ToString()/*</bind>*/; Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 21), // CS1525: Invalid expression term 'string' // /*<bind>*/((int, string)).ToString()/*</bind>*/; Diagnostic(ErrorCode.ERR_InvalidExprTerm, "string").WithArguments("string").WithLocation(6, 26) }; VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact, CompilerTrait(CompilerFeature.RefLocalsReturns)] [WorkItem(12283, "https://github.com/dotnet/roslyn/issues/12283")] public void RefReturningMethod2() { string source = @" class C { static int i; static void Main() { (M(), M()) = new C(); System.Console.Write(i); } static ref int M() { System.Console.Write(""M ""); return ref i; } void Deconstruct(out int i, out int j) { i = 42; j = 43; } } "; var comp = CompileAndVerify(source, expectedOutput: "M M 43"); comp.VerifyDiagnostics( ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact, CompilerTrait(CompilerFeature.RefLocalsReturns)] [WorkItem(12283, "https://github.com/dotnet/roslyn/issues/12283")] public void RefReturningMethod2_IOperation() { string source = @" class C { static int i; static void Main() { /*<bind>*/(M(), M()) = new C()/*</bind>*/; System.Console.Write(i); } static ref int M() { System.Console.Write(""M ""); return ref i; } void Deconstruct(out int i, out int j) { i = 42; j = 43; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32, System.Int32)) (Syntax: '(M(), M()) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(M(), M())') NaturalType: (System.Int32, System.Int32) Elements(2): IInvocationOperation (ref System.Int32 C.M()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'M()') Instance Receiver: null Arguments(0) IInvocationOperation (ref System.Int32 C.M()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'M()') Instance Receiver: null Arguments(0) Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void UninitializedRight() { string source = @" class C { static void Main() { int x; /*<bind>*/(x, x) = x/*</bind>*/; } } static class D { public static void Deconstruct(this int input, out int output, out int output2) { output = input; output2 = input; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(x, x) = x') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(x, x)') NaturalType: (System.Int32, System.Int32) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0165: Use of unassigned local variable 'x' // /*<bind>*/(x, x) = x/*</bind>*/; Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NullRight() { string source = @" class C { static void Main() { int x; /*<bind>*/(x, x) = null/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: '(x, x) = null') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(x, x)') NaturalType: (System.Int32, System.Int32) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. // /*<bind>*/(x, x) = null/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "null").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ErrorRight() { string source = @" class C { static void Main() { int x; /*<bind>*/(x, x) = undeclared/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: '(x, x) = undeclared') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(x, x)') NaturalType: (System.Int32, System.Int32) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'undeclared') Children(0) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0103: The name 'undeclared' does not exist in the current context // /*<bind>*/(x, x) = undeclared/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "undeclared").WithArguments("undeclared").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void VoidRight() { string source = @" class C { static void Main() { int x; /*<bind>*/(x, x) = M()/*</bind>*/; } static void M() { } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, x) = M()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(x, x)') NaturalType: (System.Int32, System.Int32) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: IInvocationOperation (void C.M()) (OperationKind.Invocation, Type: System.Void, IsInvalid) (Syntax: 'M()') Instance Receiver: null Arguments(0) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1061: 'void' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'void' could be found (are you missing a using directive or an assembly reference?) // /*<bind>*/(x, x) = M()/*</bind>*/; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M()").WithArguments("void", "Deconstruct").WithLocation(7, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'void', with 2 out parameters and a void return type. // /*<bind>*/(x, x) = M()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "M()").WithArguments("void", "2").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void AssigningTupleWithNoConversion() { string source = @" class C { static void Main() { byte x; string y; /*<bind>*/(x, y) = (1, 2)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Byte x, System.String y), IsInvalid) (Syntax: '(x, y) = (1, 2)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Byte x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Byte x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Byte) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Byte, System.String), IsInvalid, IsImplicit) (Syntax: '(1, 2)') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0029: Cannot implicitly convert type 'int' to 'string' // /*<bind>*/(x, y) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "string").WithLocation(9, 32) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NotAssignable() { string source = @" class C { static void Main() { /*<bind>*/(1, P) = (1, 2)/*</bind>*/; } static int P { get { return 1; } } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32, System.Int32 P), IsInvalid) (Syntax: '(1, P) = (1, 2)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32 P), IsInvalid) (Syntax: '(1, P)') NaturalType: (System.Int32, System.Int32 P) Elements(2): IInvalidOperation (OperationKind.Invalid, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: '1') Children(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') IInvalidOperation (OperationKind.Invalid, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: 'P') Children(1): IPropertyReferenceOperation: System.Int32 C.P { get; } (Static) (OperationKind.PropertyReference, Type: System.Int32, IsInvalid) (Syntax: 'P') Instance Receiver: null Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0131: The left-hand side of an assignment must be a variable, property or indexer // /*<bind>*/(1, P) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "1").WithLocation(6, 20), // CS0200: Property or indexer 'C.P' cannot be assigned to -- it is read only // /*<bind>*/(1, P) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "P").WithArguments("C.P").WithLocation(6, 23) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void TupleWithUseSiteError() { string source = @" namespace System { public struct ValueTuple<T1, T2> { public T1 Item1; public ValueTuple(T1 item1, T2 item2) { this.Item1 = item1; } } } class C { static void Main() { int x; int y; (x, y) = (1, 2); System.Console.WriteLine($""{x} {y}""); } } "; var comp = CreateCompilation(source, assemblyName: "comp", options: TestOptions.DebugExe); comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2"); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TupleWithUseSiteError_IOperation() { string source = @" namespace System { struct ValueTuple<T1, T2> { public T1 Item1; public ValueTuple(T1 item1, T2 item2) { this.Item1 = item1; } } } class C { static void Main() { int x; int y; /*<bind>*/(x, y) = (1, 2)/*</bind>*/; System.Console.WriteLine($""{x} {y}""); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y) = (1, 2)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void AssignUsingAmbiguousDeconstruction() { string source = @" class Base { public void Deconstruct(out int a, out int b) { a = 1; b = 2; } public void Deconstruct(out long a, out long b) { a = 1; b = 2; } } class C : Base { static void Main() { int x, y; /*<bind>*/(x, y) = new C()/*</bind>*/; System.Console.WriteLine(x + "" "" + y); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // file.cs(12,28): error CS0121: The call is ambiguous between the following methods or properties: 'Base.Deconstruct(out int, out int)' and 'Base.Deconstruct(out long, out long)' // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "new C()").WithArguments("Base.Deconstruct(out int, out int)", "Base.Deconstruct(out long, out long)").WithLocation(12, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructIsDynamicField() { string source = @" class C { static void Main() { int x, y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public dynamic Deconstruct = null; } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructIsField() { string source = @" class C { static void Main() { int x, y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public object Deconstruct = null; } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.Int32 y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1955: Non-invocable member 'C.Deconstruct' cannot be used like a method. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_NonInvocableMemberCalled, "new C()").WithArguments("C.Deconstruct").WithLocation(7, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(7, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void CannotDeconstructRefTuple22() { string template = @" using System; class C { static void Main() { int VARIABLES; // int x1, x2, ... (VARIABLES) = CreateLongRef(1, 2, 3, 4, 5, 6, 7, CreateLongRef(8, 9, 10, 11, 12, 13, 14, Tuple.Create(15, 16, 17, 18, 19, 20, 21, 22))); } public static Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> CreateLongRef<T1, T2, T3, T4, T5, T6, T7, TRest>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) => new Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>(item1, item2, item3, item4, item5, item6, item7, rest); } "; var tuple = String.Join(", ", Enumerable.Range(1, 22).Select(n => n.ToString())); var variables = String.Join(", ", Enumerable.Range(1, 22).Select(n => $"x{n}")); var source = template.Replace("VARIABLES", variables).Replace("TUPLE", tuple); var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,113): error CS1501: No overload for method 'Deconstruct' takes 22 arguments // (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22) = CreateLongRef(1, 2, 3, 4, 5, 6, 7, CreateLongRef(8, 9, 10, 11, 12, 13, 14, Tuple.Create(15, 16, 17, 18, 19, 20, 21, 22))); Diagnostic(ErrorCode.ERR_BadArgCount, "CreateLongRef(1, 2, 3, 4, 5, 6, 7, CreateLongRef(8, 9, 10, 11, 12, 13, 14, Tuple.Create(15, 16, 17, 18, 19, 20, 21, 22)))").WithArguments("Deconstruct", "22").WithLocation(8, 113), // (8,113): error CS8129: No Deconstruct instance or extension method was found for type 'Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int, int, int, Tuple<int>>>>', with 22 out parameters. // (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22) = CreateLongRef(1, 2, 3, 4, 5, 6, 7, CreateLongRef(8, 9, 10, 11, 12, 13, 14, Tuple.Create(15, 16, 17, 18, 19, 20, 21, 22))); Diagnostic(ErrorCode.ERR_MissingDeconstruct, "CreateLongRef(1, 2, 3, 4, 5, 6, 7, CreateLongRef(8, 9, 10, 11, 12, 13, 14, Tuple.Create(15, 16, 17, 18, 19, 20, 21, 22)))").WithArguments("System.Tuple<int, int, int, int, int, int, int, System.Tuple<int, int, int, int, int, int, int, System.Tuple<int, int, int, int, int, int, int, System.Tuple<int>>>>", "22").WithLocation(8, 113) ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructUsingDynamicMethod() { string source = @" class C { static void Main() { int x; string y; dynamic c = new C(); /*<bind>*/(x, y) = c/*</bind>*/; } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = c') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: dynamic, IsInvalid) (Syntax: 'c') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8133: Cannot deconstruct dynamic objects. // /*<bind>*/(x, y) = c/*</bind>*/; Diagnostic(ErrorCode.ERR_CannotDeconstructDynamic, "c").WithLocation(10, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructMethodInaccessible() { string source = @" class C { static void Main() { int x; string y; /*<bind>*/(x, y) = new C1()/*</bind>*/; } } class C1 { protected void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C1()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C1..ctor()) (OperationKind.ObjectCreation, Type: C1, IsInvalid) (Syntax: 'new C1()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0122: 'C1.Deconstruct(out int, out string)' is inaccessible due to its protection level // /*<bind>*/(x, y) = new C1()/*</bind>*/; Diagnostic(ErrorCode.ERR_BadAccess, "new C1()").WithArguments("C1.Deconstruct(out int, out string)").WithLocation(9, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C1', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C1()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C1()").WithArguments("C1", "2").WithLocation(9, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void DeconstructHasUseSiteError() { string libMissingSource = @"public class Missing { }"; string libSource = @" public class C { public void Deconstruct(out Missing a, out Missing b) { a = new Missing(); b = new Missing(); } } "; string source = @" class C1 { static void Main() { object x, y; (x, y) = new C(); } } "; var libMissingComp = CreateCompilation(new string[] { libMissingSource }, assemblyName: "libMissingComp").VerifyDiagnostics(); var libMissingRef = libMissingComp.EmitToImageReference(); var libComp = CreateCompilation(new string[] { libSource }, references: new[] { libMissingRef }, parseOptions: TestOptions.Regular).VerifyDiagnostics(); var libRef = libComp.EmitToImageReference(); var comp = CreateCompilation(new string[] { source }, references: new[] { libRef }); 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 'libMissingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // (x, y) = new C(); Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()").WithArguments("Missing", "libMissingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18), // (7,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(7, 18) ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void StaticDeconstruct() { string source = @" class C { static void Main() { int x; string y; /*<bind>*/(x, y) = new C()/*</bind>*/; } public static void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int32 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0176: Member 'C.Deconstruct(out int, out string)' cannot be accessed with an instance reference; qualify it with a type name instead // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_ObjectProhibited, "new C()").WithArguments("C.Deconstruct(out int, out string)").WithLocation(9, 28), // CS8129: No suitable Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type. // /*<bind>*/(x, y) = new C()/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(9, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void AssignmentTypeIsValueTuple() { string source = @" class C { public static void Main() { long x; string y; var z1 = ((x, y) = new C()).ToString(); var z2 = ((x, y) = new C()); var z3 = (x, y) = new C(); System.Console.Write($""{z1} {z2.ToString()} {z3.ToString()}""); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "(1, hello) (1, hello) (1, hello)"); comp.VerifyDiagnostics(); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void AssignmentTypeIsValueTuple_IOperation() { string source = @" class C { public static void Main() { long x; string y; var z1 = ((x, y) = new C()).ToString(); var z2 = (/*<bind>*/(x, y) = new C()/*</bind>*/); var z3 = (x, y) = new C(); System.Console.Write($""{z1} {z2.ToString()} {z3.ToString()}""); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x, System.String y)) (Syntax: '(x, y)') NaturalType: (System.Int64 x, System.String y) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.String) (Syntax: 'y') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void NestedAssignmentTypeIsValueTuple() { string source = @" class C { public static void Main() { long x1; string x2; int x3; var y = ((x1, x2), x3) = (new C(), 3); System.Console.Write($""{y.ToString()}""); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; var comp = CompileAndVerify(source, expectedOutput: "((1, hello), 3)"); comp.VerifyDiagnostics(); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NestedAssignmentTypeIsValueTuple_IOperation() { string source = @" class C { public static void Main() { long x1; string x2; int x3; var y = /*<bind>*/((x1, x2), x3) = (new C(), 3)/*</bind>*/; System.Console.Write($""{y.ToString()}""); } public void Deconstruct(out int a, out string b) { a = 1; b = ""hello""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ((System.Int64 x1, System.String x2), System.Int32 x3)) (Syntax: '((x1, x2), ... new C(), 3)') Left: ITupleOperation (OperationKind.Tuple, Type: ((System.Int64 x1, System.String x2), System.Int32 x3)) (Syntax: '((x1, x2), x3)') NaturalType: ((System.Int64 x1, System.String x2), System.Int32 x3) Elements(2): ITupleOperation (OperationKind.Tuple, Type: (System.Int64 x1, System.String x2)) (Syntax: '(x1, x2)') NaturalType: (System.Int64 x1, System.String x2) Elements(2): ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x1') ILocalReferenceOperation: x2 (OperationKind.LocalReference, Type: System.String) (Syntax: 'x2') ILocalReferenceOperation: x3 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x3') Right: ITupleOperation (OperationKind.Tuple, Type: (C, System.Int32)) (Syntax: '(new C(), 3)') NaturalType: (C, System.Int32) Elements(2): IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void AssignmentReturnsLongValueTuple() { string source = @" class C { public static void Main() { long x; var y = (x, x, x, x, x, x, x, x, x) = new C(); System.Console.Write($""{y.ToString()}""); } public void Deconstruct(out int x1, out int x2, out int x3, out int x4, out int x5, out int x6, out int x7, out int x8, out int x9) { x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = 1; x9 = 9; } } "; var comp = CompileAndVerify(source, expectedOutput: "(1, 1, 1, 1, 1, 1, 1, 1, 9)"); comp.VerifyDiagnostics(); var tree = comp.Compilation.SyntaxTrees.First(); var model = comp.Compilation.GetSemanticModel(tree, ignoreAccessibility: false); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var y = nodes.OfType<VariableDeclaratorSyntax>().Skip(1).First(); Assert.Equal("y = (x, x, x, x, x, x, x, x, x) = new C()", y.ToFullString()); Assert.Equal("(System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64) y", model.GetDeclaredSymbol(y).ToTestDisplayString()); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void AssignmentReturnsLongValueTuple_IOperation() { string source = @" class C { public static void Main() { long x; var /*<bind>*/y = (x, x, x, x, x, x, x, x, x) = new C()/*</bind>*/; System.Console.Write($""{y.ToString()}""); } public void Deconstruct(out int x1, out int x2, out int x3, out int x4, out int x5, out int x6, out int x7, out int x8, out int x9) { x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = 1; x9 = 9; } } "; string expectedOperationTree = @" IVariableDeclaratorOperation (Symbol: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64) y) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'y = (x, x, ... ) = new C()') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= (x, x, x, ... ) = new C()') IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64)) (Syntax: '(x, x, x, x ... ) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64)) (Syntax: '(x, x, x, x ... x, x, x, x)') NaturalType: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64) Elements(9): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<VariableDeclaratorSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void DeconstructWithoutValueTupleLibrary() { string source = @" class C { public static void Main() { long x; var y = (x, x) = new C(); System.Console.Write(y.ToString()); } public void Deconstruct(out int x1, out int x2) { x1 = x2 = 1; } } "; var comp = CreateCompilationWithMscorlib40(source); comp.VerifyDiagnostics( // (7,17): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // var y = (x, x) = new C(); Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(x, x)").WithArguments("System.ValueTuple`2").WithLocation(7, 17) ); } [Fact] public void ChainedAssignment() { string source = @" class C { public static void Main() { long x1, x2; var y = (x1, x1) = (x2, x2) = new C(); System.Console.Write($""{y.ToString()} {x1} {x2}""); } public void Deconstruct(out int a, out int b) { a = b = 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "(1, 1) 1 1"); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ChainedAssignment_IOperation() { string source = @" class C { public static void Main() { long x1, x2; var y = /*<bind>*/(x1, x1) = (x2, x2) = new C()/*</bind>*/; System.Console.Write($""{y.ToString()} {x1} {x2}""); } public void Deconstruct(out int a, out int b) { a = b = 1; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int64, System.Int64)) (Syntax: '(x1, x1) = ... ) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64, System.Int64)) (Syntax: '(x1, x1)') NaturalType: (System.Int64, System.Int64) Elements(2): ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x1') ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x1') Right: IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int64, System.Int64)) (Syntax: '(x2, x2) = new C()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int64, System.Int64)) (Syntax: '(x2, x2)') NaturalType: (System.Int64, System.Int64) Elements(2): ILocalReferenceOperation: x2 (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x2') ILocalReferenceOperation: x2 (OperationKind.LocalReference, Type: System.Int64) (Syntax: 'x2') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) Initializer: null "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NestedTypelessTupleAssignment2() { string source = @" class C { static void Main() { int x, y, z; // int cannot be null /*<bind>*/(x, (y, z)) = (null, (null, null))/*</bind>*/; System.Console.WriteLine(""nothing"" + x + y + z); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x, (System.Int32 y, System.Int32 z)), IsInvalid) (Syntax: '(x, (y, z)) ... ull, null))') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, (System.Int32 y, System.Int32 z))) (Syntax: '(x, (y, z))') NaturalType: (System.Int32 x, (System.Int32 y, System.Int32 z)) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') ITupleOperation (OperationKind.Tuple, Type: (System.Int32 y, System.Int32 z)) (Syntax: '(y, z)') NaturalType: (System.Int32 y, System.Int32 z) Elements(2): ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') ILocalReferenceOperation: z (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'z') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, (System.Int32, System.Int32)), IsInvalid, IsImplicit) (Syntax: '(null, (null, null))') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: null, IsInvalid) (Syntax: '(null, (null, null))') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') ITupleOperation (OperationKind.Tuple, Type: null, IsInvalid) (Syntax: '(null, null)') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0037: Cannot convert null to 'int' because it is a non-nullable value type // /*<bind>*/(x, (y, z)) = (null, (null, null))/*</bind>*/; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(8, 34), // CS0037: Cannot convert null to 'int' because it is a non-nullable value type // /*<bind>*/(x, (y, z)) = (null, (null, null))/*</bind>*/; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(8, 41), // CS0037: Cannot convert null to 'int' because it is a non-nullable value type // /*<bind>*/(x, (y, z)) = (null, (null, null))/*</bind>*/; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(8, 47) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TupleWithWrongCardinality() { string source = @" class C { static void Main() { int x, y, z; /*<bind>*/(x, y, z) = MakePair()/*</bind>*/; } public static (int, int) MakePair() { return (42, 42); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, y, z) = MakePair()') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32 y, System.Int32 z), IsInvalid) (Syntax: '(x, y, z)') NaturalType: (System.Int32 x, System.Int32 y, System.Int32 z) Elements(3): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'y') ILocalReferenceOperation: z (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'z') Right: IInvocationOperation ((System.Int32, System.Int32) C.MakePair()) (OperationKind.Invocation, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: 'MakePair()') Instance Receiver: null Arguments(0) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8132: Cannot deconstruct a tuple of '2' elements into '3' variables. // /*<bind>*/(x, y, z) = MakePair()/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructWrongCardinality, "(x, y, z) = MakePair()").WithArguments("2", "3").WithLocation(8, 19) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NestedTupleWithWrongCardinality() { string source = @" class C { static void Main() { int x, y, z, w; /*<bind>*/(x, (y, z, w)) = Pair.Create(42, (43, 44))/*</bind>*/; } } " + commonSource; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(x, (y, z, ... , (43, 44))') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, (System.Int32 y, System.Int32 z, System.Int32 w)), IsInvalid) (Syntax: '(x, (y, z, w))') NaturalType: (System.Int32 x, (System.Int32 y, System.Int32 z, System.Int32 w)) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ITupleOperation (OperationKind.Tuple, Type: (System.Int32 y, System.Int32 z, System.Int32 w), IsInvalid) (Syntax: '(y, z, w)') NaturalType: (System.Int32 y, System.Int32 z, System.Int32 w) Elements(3): ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'y') ILocalReferenceOperation: z (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'z') ILocalReferenceOperation: w (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'w') Right: IInvocationOperation (Pair<System.Int32, (System.Int32, System.Int32)> Pair.Create<System.Int32, (System.Int32, System.Int32)>(System.Int32 item1, (System.Int32, System.Int32) item2)) (OperationKind.Invocation, Type: Pair<System.Int32, (System.Int32, System.Int32)>, IsInvalid) (Syntax: 'Pair.Create ... , (43, 44))') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item1) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: '42') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42, IsInvalid) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item2) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: '(43, 44)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(43, 44)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 43, IsInvalid) (Syntax: '43') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 44, IsInvalid) (Syntax: '44') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8132: Cannot deconstruct a tuple of '2' elements into '3' variables. // /*<bind>*/(x, (y, z, w)) = Pair.Create(42, (43, 44))/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructWrongCardinality, "(x, (y, z, w)) = Pair.Create(42, (43, 44))").WithArguments("2", "3").WithLocation(8, 19) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeconstructionTooFewElements() { string source = @" class C { static void Main() { for (/*<bind>*/(var(x, y)) = Pair.Create(1, 2)/*</bind>*/; ;) { } } } " + commonSource; string expectedOperationTree = @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: '(var(x, y)) ... reate(1, 2)') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var(x, y)') Children(3): IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var') Children(0) IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'x') Children(0) IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'y') Children(0) Right: IInvocationOperation (Pair<System.Int32, System.Int32> Pair.Create<System.Int32, System.Int32>(System.Int32 item1, System.Int32 item2)) (OperationKind.Invocation, Type: Pair<System.Int32, System.Int32>) (Syntax: 'Pair.Create(1, 2)') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item1) (OperationKind.Argument, Type: null) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item2) (OperationKind.Argument, Type: null) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0103: The name 'var' does not exist in the current context // for (/*<bind>*/(var(x, y)) = Pair.Create(1, 2)/*</bind>*/; ;) { } Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(6, 25), // CS0103: The name 'x' does not exist in the current context // for (/*<bind>*/(var(x, y)) = Pair.Create(1, 2)/*</bind>*/; ;) { } Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(6, 29), // CS0103: The name 'y' does not exist in the current context // for (/*<bind>*/(var(x, y)) = Pair.Create(1, 2)/*</bind>*/; ;) { } Diagnostic(ErrorCode.ERR_NameNotInContext, "y").WithArguments("y").WithLocation(6, 32) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void DeconstructionDeclarationInCSharp6() { string source = @" class C { static void Main() { var (x1, x2) = Pair.Create(1, 2); (int x3, int x4) = Pair.Create(1, 2); foreach ((int x5, var (x6, x7)) in new[] { Pair.Create(1, Pair.Create(2, 3)) }) { } for ((int x8, var (x9, x10)) = Pair.Create(1, Pair.Create(2, 3)); ; ) { } } } " + commonSource; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular6); comp.VerifyDiagnostics( // (6,13): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // var (x1, x2) = Pair.Create(1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(x1, x2)").WithArguments("tuples", "7.0").WithLocation(6, 13), // (7,9): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // (int x3, int x4) = Pair.Create(1, 2); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x3, int x4)").WithArguments("tuples", "7.0").WithLocation(7, 9), // (8,18): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // foreach ((int x5, var (x6, x7)) in new[] { Pair.Create(1, Pair.Create(2, 3)) }) { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x5, var (x6, x7))").WithArguments("tuples", "7.0").WithLocation(8, 18), // (9,14): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // for ((int x8, var (x9, x10)) = Pair.Create(1, Pair.Create(2, 3)); ; ) { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x8, var (x9, x10))").WithArguments("tuples", "7.0").WithLocation(9, 14) ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclareLocalTwice() { string source = @" class C { static void Main() { /*<bind>*/var (x1, x1) = (1, 2)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: 'var (x1, x1) = (1, 2)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: 'var (x1, x1)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(x1, x1)') NaturalType: (System.Int32, System.Int32) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x1') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0128: A local variable or function named 'x1' is already defined in this scope // /*<bind>*/var (x1, x1) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_LocalDuplicate, "x1").WithArguments("x1").WithLocation(6, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclareLocalTwice2() { string source = @" class C { static void Main() { string x1 = null; /*<bind>*/var (x1, x2) = (1, 2)/*</bind>*/; System.Console.WriteLine(x1); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x1, System.Int32 x2), IsInvalid) (Syntax: 'var (x1, x2) = (1, 2)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (System.Int32 x1, System.Int32 x2), IsInvalid) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2), IsInvalid) (Syntax: '(x1, x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0128: A local variable or function named 'x1' is already defined in this scope // /*<bind>*/var (x1, x2) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_LocalDuplicate, "x1").WithArguments("x1").WithLocation(7, 24) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void VarMethodMissing() { string source = @" class C { static void Main() { int x1 = 1; int x2 = 1; /*<bind>*/var(x1, x2)/*</bind>*/; } } "; string expectedOperationTree = @" IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var(x1, x2)') Children(3): IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var') Children(0) ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') ILocalReferenceOperation: x2 (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0103: The name 'var' does not exist in the current context // /*<bind>*/var(x1, x2)/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(8, 19) }; VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void UseBeforeDeclared() { string source = @" class C { static void Main() { /*<bind>*/(int x1, int x2) = M(x1)/*</bind>*/; } static (int, int) M(int a) { return (1, 2); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x1, System.Int32 x2), IsInvalid) (Syntax: '(int x1, int x2) = M(x1)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2)) (Syntax: '(int x1, int x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: IInvocationOperation ((System.Int32, System.Int32) C.M(System.Int32 a)) (OperationKind.Invocation, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: 'M(x1)') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0165: Use of unassigned local variable 'x1' // /*<bind>*/(int x1, int x2) = M(x1)/*</bind>*/; Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(6, 40) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclareWithVoidType() { string source = @" class C { static void Main() { /*<bind>*/(int x1, int x2) = M(x1)/*</bind>*/; } static void M(int a) { } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(int x1, int x2) = M(x1)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2)) (Syntax: '(int x1, int x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: IInvocationOperation (void C.M(System.Int32 a)) (OperationKind.Invocation, Type: System.Void, IsInvalid) (Syntax: 'M(x1)') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1061: 'void' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'void' could be found (are you missing a using directive or an assembly reference?) // /*<bind>*/(int x1, int x2) = M(x1)/*</bind>*/; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M(x1)").WithArguments("void", "Deconstruct").WithLocation(6, 38), // CS8129: No suitable Deconstruct instance or extension method was found for type 'void', with 2 out parameters and a void return type. // /*<bind>*/(int x1, int x2) = M(x1)/*</bind>*/; Diagnostic(ErrorCode.ERR_MissingDeconstruct, "M(x1)").WithArguments("void", "2").WithLocation(6, 38), // CS0165: Use of unassigned local variable 'x1' // /*<bind>*/(int x1, int x2) = M(x1)/*</bind>*/; Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(6, 40) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void UseBeforeDeclared2() { string source = @" class C { static void Main() { System.Console.WriteLine(x1); /*<bind>*/(int x1, int x2) = (1, 2)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x1, System.Int32 x2)) (Syntax: '(int x1, in ... 2) = (1, 2)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2)) (Syntax: '(int x1, int x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0841: Cannot use local variable 'x1' before it is declared // System.Console.WriteLine(x1); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x1").WithArguments("x1").WithLocation(6, 34) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NullAssignmentInDeclaration() { string source = @" class C { static void Main() { /*<bind>*/(int x1, int x2) = null/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: '(int x1, int x2) = null') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2)) (Syntax: '(int x1, int x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. // /*<bind>*/(int x1, int x2) = null/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "null").WithLocation(6, 38) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void NullAssignmentInVarDeclaration() { string source = @" class C { static void Main() { /*<bind>*/var (x1, x2) = null/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: 'var (x1, x2) = null') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, var x2), IsInvalid) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, var x2), IsInvalid) (Syntax: '(x1, x2)') NaturalType: (var x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') Right: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. // /*<bind>*/var (x1, x2) = null/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "null").WithLocation(6, 34), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x1'. // /*<bind>*/var (x1, x2) = null/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x1").WithArguments("x1").WithLocation(6, 24), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. // /*<bind>*/var (x1, x2) = null/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(6, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TypelessDeclaration() { string source = @" class C { static void Main() { /*<bind>*/var (x1, x2) = (1, null)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: 'var (x1, x2) = (1, null)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, var x2), IsInvalid) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, var x2), IsInvalid) (Syntax: '(x1, x2)') NaturalType: (var x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') Right: ITupleOperation (OperationKind.Tuple, Type: null) (Syntax: '(1, null)') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x1'. // /*<bind>*/var (x1, x2) = (1, null)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x1").WithArguments("x1").WithLocation(6, 24), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. // /*<bind>*/var (x1, x2) = (1, null)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(6, 28) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TypeMergingWithMultipleAmbiguousVars() { string source = @" class C { static void Main() { /*<bind>*/(string x1, (byte x2, var x3), var x4) = (null, (2, null), null)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: '(string x1, ... ull), null)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.String x1, (System.Byte x2, var x3), var x4), IsInvalid) (Syntax: '(string x1, ... 3), var x4)') NaturalType: (System.String x1, (System.Byte x2, var x3), var x4) Elements(3): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String) (Syntax: 'string x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String) (Syntax: 'x1') ITupleOperation (OperationKind.Tuple, Type: (System.Byte x2, var x3), IsInvalid) (Syntax: '(byte x2, var x3)') NaturalType: (System.Byte x2, var x3) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Byte) (Syntax: 'byte x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Byte) (Syntax: 'x2') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: var, IsInvalid) (Syntax: 'var x3') ILocalReferenceOperation: x3 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x3') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: var, IsInvalid) (Syntax: 'var x4') ILocalReferenceOperation: x4 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x4') Right: ITupleOperation (OperationKind.Tuple, Type: null) (Syntax: '(null, (2, null), null)') NaturalType: null Elements(3): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') ITupleOperation (OperationKind.Tuple, Type: null) (Syntax: '(2, null)') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x3'. // /*<bind>*/(string x1, (byte x2, var x3), var x4) = (null, (2, null), null)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x3").WithArguments("x3").WithLocation(6, 45), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x4'. // /*<bind>*/(string x1, (byte x2, var x3), var x4) = (null, (2, null), null)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x4").WithArguments("x4").WithLocation(6, 54) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TypeMergingWithTooManyLeftNestings() { string source = @" class C { static void Main() { /*<bind>*/((string x1, byte x2, var x3), int x4) = (null, 4)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: '((string x1 ... = (null, 4)') Left: ITupleOperation (OperationKind.Tuple, Type: ((System.String x1, System.Byte x2, var x3), System.Int32 x4), IsInvalid) (Syntax: '((string x1 ... 3), int x4)') NaturalType: ((System.String x1, System.Byte x2, var x3), System.Int32 x4) Elements(2): ITupleOperation (OperationKind.Tuple, Type: (System.String x1, System.Byte x2, var x3), IsInvalid) (Syntax: '(string x1, ... x2, var x3)') NaturalType: (System.String x1, System.Byte x2, var x3) Elements(3): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String) (Syntax: 'string x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Byte) (Syntax: 'byte x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Byte) (Syntax: 'x2') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: var, IsInvalid) (Syntax: 'var x3') ILocalReferenceOperation: x3 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x3') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x4') ILocalReferenceOperation: x4 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x4') Right: ITupleOperation (OperationKind.Tuple, Type: null, IsInvalid) (Syntax: '(null, 4)') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 4) (Syntax: '4') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. // /*<bind>*/((string x1, byte x2, var x3), int x4) = (null, 4)/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "null").WithLocation(6, 61), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x3'. // /*<bind>*/((string x1, byte x2, var x3), int x4) = (null, 4)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x3").WithArguments("x3").WithLocation(6, 45) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TypeMergingWithTooManyRightNestings() { string source = @" class C { static void Main() { /*<bind>*/(string x1, var x2) = (null, (null, 2))/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: System.Void, IsInvalid) (Syntax: '(string x1, ... (null, 2))') Left: ITupleOperation (OperationKind.Tuple, Type: (System.String x1, var x2), IsInvalid) (Syntax: '(string x1, var x2)') NaturalType: (System.String x1, var x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String) (Syntax: 'string x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: var, IsInvalid) (Syntax: 'var x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') Right: ITupleOperation (OperationKind.Tuple, Type: null) (Syntax: '(null, (null, 2))') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') ITupleOperation (OperationKind.Tuple, Type: null) (Syntax: '(null, 2)') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. // /*<bind>*/(string x1, var x2) = (null, (null, 2))/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(6, 35) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TypeMergingWithTooManyLeftVariables() { string source = @" class C { static void Main() { /*<bind>*/(string x1, var x2, int x3) = (null, ""hello"")/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(string x1, ... l, ""hello"")') Left: ITupleOperation (OperationKind.Tuple, Type: (System.String x1, System.String x2, System.Int32 x3), IsInvalid) (Syntax: '(string x1, ... x2, int x3)') NaturalType: (System.String x1, System.String x2, System.Int32 x3) Elements(3): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String, IsInvalid) (Syntax: 'string x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String, IsInvalid) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String, IsInvalid) (Syntax: 'var x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String, IsInvalid) (Syntax: 'x2') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32, IsInvalid) (Syntax: 'int x3') ILocalReferenceOperation: x3 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x3') Right: ITupleOperation (OperationKind.Tuple, Type: (System.String, System.String), IsInvalid) (Syntax: '(null, ""hello"")') NaturalType: null Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, Constant: null, IsInvalid, IsImplicit) (Syntax: 'null') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""hello"", IsInvalid) (Syntax: '""hello""') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8132: Cannot deconstruct a tuple of '2' elements into '3' variables. // /*<bind>*/(string x1, var x2, int x3) = (null, "hello")/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructWrongCardinality, @"(string x1, var x2, int x3) = (null, ""hello"")").WithArguments("2", "3").WithLocation(6, 19) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TypeMergingWithTooManyRightElements() { string source = @" class C { static void Main() { /*<bind>*/(string x1, var y1) = (null, ""hello"", 3)/*</bind>*/; (string x2, var y2) = (null, ""hello"", null); } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(string x1, ... ""hello"", 3)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.String x1, System.String y1), IsInvalid) (Syntax: '(string x1, var y1)') NaturalType: (System.String x1, System.String y1) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String, IsInvalid) (Syntax: 'string x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String, IsInvalid) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.String, IsInvalid) (Syntax: 'var y1') ILocalReferenceOperation: y1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.String, IsInvalid) (Syntax: 'y1') Right: ITupleOperation (OperationKind.Tuple, Type: (System.String, System.String, System.Int32), IsInvalid) (Syntax: '(null, ""hello"", 3)') NaturalType: null Elements(3): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, Constant: null, IsInvalid, IsImplicit) (Syntax: 'null') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""hello"", IsInvalid) (Syntax: '""hello""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3, IsInvalid) (Syntax: '3') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8132: Cannot deconstruct a tuple of '3' elements into '2' variables. // /*<bind>*/(string x1, var y1) = (null, "hello", 3)/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructWrongCardinality, @"(string x1, var y1) = (null, ""hello"", 3)").WithArguments("3", "2").WithLocation(6, 19), // CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. // (string x2, var y2) = (null, "hello", null); Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "null").WithLocation(7, 47), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y2'. // (string x2, var y2) = (null, "hello", null); Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y2").WithArguments("y2").WithLocation(7, 25) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclarationVarFormWithActualVarType() { string source = @" class C { static void Main() { /*<bind>*/var (x1, x2) = (1, 2)/*</bind>*/; } } class @var { } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (var x1, var x2), IsInvalid) (Syntax: 'var (x1, x2) = (1, 2)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, var x2), IsInvalid) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, var x2), IsInvalid) (Syntax: '(x1, x2)') NaturalType: (var x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (var, var), IsInvalid, IsImplicit) (Syntax: '(1, 2)') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // /*<bind>*/var (x1, x2) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "(x1, x2)").WithLocation(6, 23), // CS0029: Cannot implicitly convert type 'int' to 'var' // /*<bind>*/var (x1, x2) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "var").WithLocation(6, 35), // CS0029: Cannot implicitly convert type 'int' to 'var' // /*<bind>*/var (x1, x2) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "var").WithLocation(6, 38) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclarationVarFormWithAliasedVarType() { string source = @" using @var = D; class C { static void Main() { /*<bind>*/var (x3, x4) = (3, 4)/*</bind>*/; } } class D { public override string ToString() { return ""var""; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (D x3, D x4), IsInvalid) (Syntax: 'var (x3, x4) = (3, 4)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (D x3, D x4), IsInvalid) (Syntax: 'var (x3, x4)') ITupleOperation (OperationKind.Tuple, Type: (D x3, D x4), IsInvalid) (Syntax: '(x3, x4)') NaturalType: (D x3, D x4) Elements(2): ILocalReferenceOperation: x3 (IsDeclaration: True) (OperationKind.LocalReference, Type: D, IsInvalid) (Syntax: 'x3') ILocalReferenceOperation: x4 (IsDeclaration: True) (OperationKind.LocalReference, Type: D, IsInvalid) (Syntax: 'x4') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (D, D), IsInvalid, IsImplicit) (Syntax: '(3, 4)') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(3, 4)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3, IsInvalid) (Syntax: '3') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 4, IsInvalid) (Syntax: '4') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8136: Deconstruction 'var (...)' form disallows a specific type for 'var'. // /*<bind>*/var (x3, x4) = (3, 4)/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "(x3, x4)").WithLocation(7, 23), // CS0029: Cannot implicitly convert type 'int' to 'D' // /*<bind>*/var (x3, x4) = (3, 4)/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "D").WithLocation(7, 35), // CS0029: Cannot implicitly convert type 'int' to 'D' // /*<bind>*/var (x3, x4) = (3, 4)/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "4").WithArguments("int", "D").WithLocation(7, 38) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclarationWithWrongCardinality() { string source = @" class C { static void Main() { /*<bind>*/(var (x1, x2), var x3) = (1, 2, 3)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: '(var (x1, x ... = (1, 2, 3)') Left: ITupleOperation (OperationKind.Tuple, Type: ((var x1, var x2), System.Int32 x3), IsInvalid) (Syntax: '(var (x1, x2), var x3)') NaturalType: ((var x1, var x2), System.Int32 x3) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, var x2), IsInvalid) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, var x2), IsInvalid) (Syntax: '(x1, x2)') NaturalType: (var x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32, IsInvalid) (Syntax: 'var x3') ILocalReferenceOperation: x3 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x3') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32, System.Int32), IsInvalid) (Syntax: '(1, 2, 3)') NaturalType: (System.Int32, System.Int32, System.Int32) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3, IsInvalid) (Syntax: '3') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8132: Cannot deconstruct a tuple of '3' elements into '2' variables. // /*<bind>*/(var (x1, x2), var x3) = (1, 2, 3)/*</bind>*/; Diagnostic(ErrorCode.ERR_DeconstructWrongCardinality, "(var (x1, x2), var x3) = (1, 2, 3)").WithArguments("3", "2").WithLocation(6, 19), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x1'. // /*<bind>*/(var (x1, x2), var x3) = (1, 2, 3)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x1").WithArguments("x1").WithLocation(6, 25), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. // /*<bind>*/(var (x1, x2), var x3) = (1, 2, 3)/*</bind>*/; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(6, 29) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclarationWithCircularity1() { string source = @" class C { static void Main() { /*<bind>*/var (x1, x2) = (1, x1)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x1, var x2), IsInvalid) (Syntax: 'var (x1, x2) = (1, x1)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (System.Int32 x1, var x2)) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, var x2)) (Syntax: '(x1, x2)') NaturalType: (System.Int32 x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var) (Syntax: 'x2') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, var), IsInvalid, IsImplicit) (Syntax: '(1, x1)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, var x1), IsInvalid) (Syntax: '(1, x1)') NaturalType: (System.Int32, var x1) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0841: Cannot use local variable 'x1' before it is declared // /*<bind>*/var (x1, x2) = (1, x1)/*</bind>*/; Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x1").WithArguments("x1").WithLocation(6, 38) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclarationWithCircularity2() { string source = @" class C { static void Main() { /*<bind>*/var (x1, x2) = (x2, 2)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (var x1, System.Int32 x2), IsInvalid) (Syntax: 'var (x1, x2) = (x2, 2)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, System.Int32 x2)) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, System.Int32 x2)) (Syntax: '(x1, x2)') NaturalType: (var x1, System.Int32 x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (var, System.Int32), IsInvalid, IsImplicit) (Syntax: '(x2, 2)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (var x2, System.Int32), IsInvalid) (Syntax: '(x2, 2)') NaturalType: (var x2, System.Int32) Elements(2): ILocalReferenceOperation: x2 (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0841: Cannot use local variable 'x2' before it is declared // /*<bind>*/var (x1, x2) = (x2, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x2").WithArguments("x2").WithLocation(6, 35) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact, CompilerTrait(CompilerFeature.RefLocalsReturns)] [WorkItem(12283, "https://github.com/dotnet/roslyn/issues/12283")] public void RefReturningVarInvocation() { string source = @" class C { static int i; static void Main() { int x = 0, y = 0; /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction System.Console.WriteLine(i); } static ref int var(int a, int b) { return ref i; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: ?, IsInvalid) (Syntax: 'var (x, y) = 42') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x, var y), IsInvalid) (Syntax: 'var (x, y)') ITupleOperation (OperationKind.Tuple, Type: (var x, var y), IsInvalid) (Syntax: '(x, y)') NaturalType: (var x, var y) Elements(2): ILocalReferenceOperation: x (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x') ILocalReferenceOperation: y (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'y') Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42, IsInvalid) (Syntax: '42') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0128: A local variable or function named 'x' is already defined in this scope // /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction Diagnostic(ErrorCode.ERR_LocalDuplicate, "x").WithArguments("x").WithLocation(9, 24), // CS0128: A local variable or function named 'y' is already defined in this scope // /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction Diagnostic(ErrorCode.ERR_LocalDuplicate, "y").WithArguments("y").WithLocation(9, 27), // CS1061: 'int' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) // /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "42").WithArguments("int", "Deconstruct").WithLocation(9, 32), // CS8129: No suitable Deconstruct instance or extension method was found for type 'int', with 2 out parameters and a void return type. // /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "2").WithLocation(9, 32), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x'. // /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x").WithArguments("x").WithLocation(9, 24), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'. // /*<bind>*/var (x, y) = 42/*</bind>*/; // parsed as deconstruction Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(9, 27), // CS0219: The variable 'x' is assigned but its value is never used // int x = 0, y = 0; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(8, 13), // CS0219: The variable 'y' is assigned but its value is never used // int x = 0, y = 0; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y").WithArguments("y").WithLocation(8, 20) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/12468"), CompilerTrait(CompilerFeature.RefLocalsReturns)] [WorkItem(12468, "https://github.com/dotnet/roslyn/issues/12468")] public void RefReturningVarInvocation2() { string source = @" class C { static int i = 0; static void Main() { int x = 0, y = 0; @var(x, y) = 42; // parsed as invocation System.Console.Write(i + "" ""); (var(x, y)) = 43; // parsed as invocation System.Console.Write(i + "" ""); (var(x, y) = 44); // parsed as invocation System.Console.Write(i); } static ref int var(int a, int b) { return ref i; } } "; // The correct expectation is for the code to compile and execute //var comp = CompileAndVerify(source, expectedOutput: "42 43 44"); var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (11,9): error CS8134: Deconstruction must contain at least two variables. // (var(x, y)) = 43; // parsed as invocation Diagnostic(ErrorCode.ERR_DeconstructTooFewElements, "(var(x, y)) = 43").WithLocation(11, 9), // (13,20): error CS1026: ) expected // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_CloseParenExpected, "=").WithLocation(13, 20), // (13,24): error CS1002: ; expected // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(13, 24), // (13,24): error CS1513: } expected // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(13, 24), // (9,14): error CS0128: A local variable named 'x' is already defined in this scope // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_LocalDuplicate, "x").WithArguments("x").WithLocation(9, 14), // (9,9): error CS0246: The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?) // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "@var").WithArguments("var").WithLocation(9, 9), // (9,14): error CS8136: Deconstruction `var (...)` form disallows a specific type for 'var'. // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "x").WithLocation(9, 14), // (9,17): error CS0128: A local variable named 'y' is already defined in this scope // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_LocalDuplicate, "y").WithArguments("y").WithLocation(9, 17), // (9,9): error CS0246: The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?) // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "@var").WithArguments("var").WithLocation(9, 9), // (9,17): error CS8136: Deconstruction `var (...)` form disallows a specific type for 'var'. // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_DeconstructionVarFormDisallowsSpecificType, "y").WithLocation(9, 17), // (9,22): error CS1061: 'int' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "42").WithArguments("int", "Deconstruct").WithLocation(9, 22), // (9,22): error CS8129: No Deconstruct instance or extension method was found for type 'int', with 2 out parameters. // @var(x, y) = 42; // parsed as invocation Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "2").WithLocation(9, 22), // (11,14): error CS0128: A local variable named 'x' is already defined in this scope // (var(x, y)) = 43; // parsed as invocation Diagnostic(ErrorCode.ERR_LocalDuplicate, "x").WithArguments("x").WithLocation(11, 14), // (11,17): error CS0128: A local variable named 'y' is already defined in this scope // (var(x, y)) = 43; // parsed as invocation Diagnostic(ErrorCode.ERR_LocalDuplicate, "y").WithArguments("y").WithLocation(11, 17), // (11,23): error CS1061: 'int' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) // (var(x, y)) = 43; // parsed as invocation Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "43").WithArguments("int", "Deconstruct").WithLocation(11, 23), // (11,23): error CS8129: No Deconstruct instance or extension method was found for type 'int', with 1 out parameters. // (var(x, y)) = 43; // parsed as invocation Diagnostic(ErrorCode.ERR_MissingDeconstruct, "43").WithArguments("int", "1").WithLocation(11, 23), // (13,14): error CS0128: A local variable named 'x' is already defined in this scope // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_LocalDuplicate, "x").WithArguments("x").WithLocation(13, 14), // (13,17): error CS0128: A local variable named 'y' is already defined in this scope // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_LocalDuplicate, "y").WithArguments("y").WithLocation(13, 17), // (13,22): error CS1061: 'int' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "44").WithArguments("int", "Deconstruct").WithLocation(13, 22), // (13,22): error CS8129: No Deconstruct instance or extension method was found for type 'int', with 1 out parameters. // (var(x, y) = 44); // parsed as invocation Diagnostic(ErrorCode.ERR_MissingDeconstruct, "44").WithArguments("int", "1").WithLocation(13, 22), // (8,13): warning CS0219: The variable 'x' is assigned but its value is never used // int x = 0, y = 0; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(8, 13), // (8,20): warning CS0219: The variable 'y' is assigned but its value is never used // int x = 0, y = 0; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y").WithArguments("y").WithLocation(8, 20) ); } [Fact, CompilerTrait(CompilerFeature.RefLocalsReturns)] [WorkItem(12283, "https://github.com/dotnet/roslyn/issues/12283")] public void RefReturningInvocation() { string source = @" class C { static int i; static void Main() { int x = 0, y = 0; M(x, y) = 42; System.Console.WriteLine(i); } static ref int M(int a, int b) { return ref i; } } "; var comp = CompileAndVerify(source, expectedOutput: "42"); comp.VerifyDiagnostics(); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DeclarationWithTypeInsideVarForm() { string source = @" class C { static void Main() { var(int x1, x2) = (1, 2); var(var x3, x4) = (1, 2); /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; } } "; string expectedOperationTree = @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: 'var(x5, var ... (1, (2, 3))') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var(x5, var(x6, x7))') Children(3): IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var') Children(0) IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'x5') Children(0) IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var(x6, x7)') Children(3): IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'var') Children(0) IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'x6') Children(0) IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'x7') Children(0) Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, (System.Int32, System.Int32))) (Syntax: '(1, (2, 3))') NaturalType: (System.Int32, (System.Int32, System.Int32)) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(2, 3)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1525: Invalid expression term 'int' // var(int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 13), // CS1003: Syntax error, ',' expected // var(int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_SyntaxError, "x1").WithArguments(",").WithLocation(6, 17), // CS1003: Syntax error, ',' expected // var(var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_SyntaxError, "x3").WithArguments(",").WithLocation(7, 17), // CS8199: The syntax 'var (...)' as an lvalue is reserved. // var(int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(int x1, x2)").WithLocation(6, 9), // CS0103: The name 'var' does not exist in the current context // var(int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(6, 9), // CS0103: The name 'x1' does not exist in the current context // var(int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 17), // CS0103: The name 'x2' does not exist in the current context // var(int x1, x2) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(6, 21), // CS8199: The syntax 'var (...)' as an lvalue is reserved. // var(var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(var x3, x4)").WithLocation(7, 9), // CS0103: The name 'var' does not exist in the current context // var(var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(7, 9), // CS0103: The name 'var' does not exist in the current context // var(var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(7, 13), // CS0103: The name 'x3' does not exist in the current context // var(var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "x3").WithArguments("x3").WithLocation(7, 17), // CS0103: The name 'x4' does not exist in the current context // var(var x3, x4) = (1, 2); Diagnostic(ErrorCode.ERR_NameNotInContext, "x4").WithArguments("x4").WithLocation(7, 21), // CS8199: The syntax 'var (...)' as an lvalue is reserved. // /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var(x5, var(x6, x7))").WithLocation(8, 19), // CS0103: The name 'var' does not exist in the current context // /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(8, 19), // CS0103: The name 'x5' does not exist in the current context // /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "x5").WithArguments("x5").WithLocation(8, 23), // CS0103: The name 'var' does not exist in the current context // /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(8, 27), // CS0103: The name 'x6' does not exist in the current context // /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "x6").WithArguments("x6").WithLocation(8, 31), // CS0103: The name 'x7' does not exist in the current context // /*<bind>*/var(x5, var(x6, x7)) = (1, (2, 3))/*</bind>*/; Diagnostic(ErrorCode.ERR_NameNotInContext, "x7").WithArguments("x7").WithLocation(8, 35) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ForWithCircularity1() { string source = @" class C { static void Main() { for (/*<bind>*/var (x1, x2) = (1, x1)/*</bind>*/; ;) { } } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x1, var x2), IsInvalid) (Syntax: 'var (x1, x2) = (1, x1)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (System.Int32 x1, var x2)) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, var x2)) (Syntax: '(x1, x2)') NaturalType: (System.Int32 x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var) (Syntax: 'x2') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, var), IsInvalid, IsImplicit) (Syntax: '(1, x1)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, var x1), IsInvalid) (Syntax: '(1, x1)') NaturalType: (System.Int32, var x1) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILocalReferenceOperation: x1 (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0841: Cannot use local variable 'x1' before it is declared // for (/*<bind>*/var (x1, x2) = (1, x1)/*</bind>*/; ;) { } Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x1").WithArguments("x1").WithLocation(6, 43) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ForWithCircularity2() { string source = @" class C { static void Main() { for (/*<bind>*/var (x1, x2) = (x2, 2)/*</bind>*/; ;) { } } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (var x1, System.Int32 x2), IsInvalid) (Syntax: 'var (x1, x2) = (x2, 2)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, System.Int32 x2)) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, System.Int32 x2)) (Syntax: '(x1, x2)') NaturalType: (var x1, System.Int32 x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (var, System.Int32), IsInvalid, IsImplicit) (Syntax: '(x2, 2)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (var x2, System.Int32), IsInvalid) (Syntax: '(x2, 2)') NaturalType: (var x2, System.Int32) Elements(2): ILocalReferenceOperation: x2 (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0841: Cannot use local variable 'x2' before it is declared // for (/*<bind>*/var (x1, x2) = (x2, 2)/*</bind>*/; ;) { } Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x2").WithArguments("x2").WithLocation(6, 40) }; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ForEachNameConflict() { string source = @" class C { static void Main() { int x1 = 1; /*<bind>*/foreach ((int x1, int x2) in M()) { }/*</bind>*/ System.Console.Write(x1); } static (int, int)[] M() { return new[] { (1, 2) }; } } "; string expectedOperationTree = @" IForEachLoopOperation (LoopKind.ForEach, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'foreach ((i ... in M()) { }') Locals: Local_1: System.Int32 x1 Local_2: System.Int32 x2 LoopControlVariable: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2), IsInvalid) (Syntax: '(int x1, int x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32, IsInvalid) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Collection: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.IEnumerable, IsImplicit) (Syntax: 'M()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) Operand: IInvocationOperation ((System.Int32, System.Int32)[] C.M()) (OperationKind.Invocation, Type: (System.Int32, System.Int32)[]) (Syntax: 'M()') Instance Receiver: null Arguments(0) Body: IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ }') NextVariables(0) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0136: A local or parameter named 'x1' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // /*<bind>*/foreach ((int x1, int x2) in M()) { }/*</bind>*/ Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x1").WithArguments("x1").WithLocation(7, 33) }; VerifyOperationTreeAndDiagnosticsForTest<ForEachVariableStatementSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ForEachNameConflict2() { string source = @" class C { static void Main() { /*<bind>*/foreach ((int x1, int x2) in M(out int x1)) { }/*</bind>*/ } static (int, int)[] M(out int a) { a = 1; return new[] { (1, 2) }; } } "; string expectedOperationTree = @" IForEachLoopOperation (LoopKind.ForEach, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'foreach ((i ... nt x1)) { }') Locals: Local_1: System.Int32 x1 Local_2: System.Int32 x2 LoopControlVariable: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, System.Int32 x2), IsInvalid) (Syntax: '(int x1, int x2)') NaturalType: (System.Int32 x1, System.Int32 x2) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32, IsInvalid) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x2') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') Collection: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.IEnumerable, IsImplicit) (Syntax: 'M(out int x1)') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) Operand: IInvocationOperation ((System.Int32, System.Int32)[] C.M(out System.Int32 a)) (OperationKind.Invocation, Type: (System.Int32, System.Int32)[]) (Syntax: 'M(out int x1)') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'out int x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Body: IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ }') NextVariables(0) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0136: A local or parameter named 'x1' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // /*<bind>*/foreach ((int x1, int x2) in M(out int x1)) { }/*</bind>*/ Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x1").WithArguments("x1").WithLocation(6, 33) }; VerifyOperationTreeAndDiagnosticsForTest<ForEachVariableStatementSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void ForEachNameConflict3() { string source = @" class C { static void Main() { foreach ((int x1, int x2) in M()) { int x1 = 1; System.Console.Write(x1); } } static (int, int)[] M() { return new[] { (1, 2) }; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,17): error CS0136: A local or parameter named 'x1' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // int x1 = 1; Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x1").WithArguments("x1").WithLocation(8, 17) ); } [Fact] public void ForEachUseBeforeDeclared() { string source = @" class C { static void Main() { foreach ((int x1, int x2) in M(x1)) { } } static (int, int)[] M(int a) { return new[] { (1, 2) }; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,40): error CS0103: The name 'x1' does not exist in the current context // foreach ((int x1, int x2) in M(x1)) Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 40) ); } [Fact] public void ForEachUseOutsideScope() { string source = @" class C { static void Main() { foreach ((int x1, int x2) in M()) { } System.Console.Write(x1); } static (int, int)[] M() { return new[] { (1, 2) }; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,30): error CS0103: The name 'x1' does not exist in the current context // System.Console.Write(x1); Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(7, 30) ); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ForEachNoIEnumerable() { string source = @" class C { static void Main() { foreach (/*<bind>*/var (x1, x2)/*</bind>*/ in 1) { System.Console.WriteLine(x1 + "" "" + x2); } } } "; string expectedOperationTree = @" IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (var x1, var x2), IsInvalid) (Syntax: 'var (x1, x2)') ITupleOperation (OperationKind.Tuple, Type: (var x1, var x2), IsInvalid) (Syntax: '(x1, x2)') NaturalType: (var x1, var x2) Elements(2): ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x1') ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsInvalid) (Syntax: 'x2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1579: foreach statement cannot operate on variables of type 'int' because 'int' does not contain a public definition for 'GetEnumerator' // foreach (/*<bind>*/var (x1, x2)/*</bind>*/ in 1) Diagnostic(ErrorCode.ERR_ForEachMissingMember, "1").WithArguments("int", "GetEnumerator").WithLocation(6, 55), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x1'. // foreach (/*<bind>*/var (x1, x2)/*</bind>*/ in 1) Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x1").WithArguments("x1").WithLocation(6, 33), // CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. // foreach (/*<bind>*/var (x1, x2)/*</bind>*/ in 1) Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(6, 37) }; VerifyOperationTreeAndDiagnosticsForTest<DeclarationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ForEachIterationVariablesAreReadonly() { string source = @" class C { static void Main() { foreach (/*<bind>*/(int x1, var (x2, x3))/*</bind>*/ in new[] { (1, (1, 1)) }) { x1 = 1; x2 = 2; x3 = 3; } } } "; string expectedOperationTree = @" ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x1, (System.Int32 x2, System.Int32 x3))) (Syntax: '(int x1, var (x2, x3))') NaturalType: (System.Int32 x1, (System.Int32 x2, System.Int32 x3)) Elements(2): IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: System.Int32) (Syntax: 'int x1') ILocalReferenceOperation: x1 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x1') IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (System.Int32 x2, System.Int32 x3)) (Syntax: 'var (x2, x3)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x2, System.Int32 x3)) (Syntax: '(x2, x3)') NaturalType: (System.Int32 x2, System.Int32 x3) Elements(2): ILocalReferenceOperation: x2 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x2') ILocalReferenceOperation: x3 (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x3') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1656: Cannot assign to 'x1' because it is a 'foreach iteration variable' // x1 = 1; Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "x1").WithArguments("x1", "foreach iteration variable").WithLocation(8, 13), // CS1656: Cannot assign to 'x2' because it is a 'foreach iteration variable' // x2 = 2; Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "x2").WithArguments("x2", "foreach iteration variable").WithLocation(9, 13), // CS1656: Cannot assign to 'x3' because it is a 'foreach iteration variable' // x3 = 3; Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "x3").WithArguments("x3", "foreach iteration variable").WithLocation(10, 13) }; VerifyOperationTreeAndDiagnosticsForTest<TupleExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] public void ForEachScoping() { string source = @" class C { static void Main() { foreach (var (x1, x2) in M(x1)) { } } static (int, int) M(int i) { return (1, 2); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,36): error CS0103: The name 'x1' does not exist in the current context // foreach (var (x1, x2) in M(x1)) { } Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 36), // (6,34): error CS1579: foreach statement cannot operate on variables of type '(int, int)' because '(int, int)' does not contain a public instance or extension definition for 'GetEnumerator' // foreach (var (x1, x2) in M(x1)) { } Diagnostic(ErrorCode.ERR_ForEachMissingMember, "M(x1)").WithArguments("(int, int)", "GetEnumerator").WithLocation(6, 34), // (6,23): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x1'. // foreach (var (x1, x2) in M(x1)) { } Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x1").WithArguments("x1").WithLocation(6, 23), // (6,27): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. // foreach (var (x1, x2) in M(x1)) { } Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(6, 27) ); } [Fact] public void AssignmentDataFlow() { string source = @" class C { static void Main() { int x, y; (x, y) = new C(); // x and y are assigned here, so no complaints on usage of un-initialized locals on the line below System.Console.WriteLine(x + "" "" + y); } public void Deconstruct(out int a, out int b) { a = 1; b = 2; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void GetTypeInfoForTupleLiteral() { var source = @" class C { static void Main() { var x1 = (1, 2); var (x2, x3) = (1, 2); System.Console.Write($""{x1} {x2} {x3}""); } } "; 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 literal1 = nodes.OfType<TupleExpressionSyntax>().First(); Assert.Equal("(int, int)", model.GetTypeInfo(literal1).Type.ToDisplayString()); var literal2 = nodes.OfType<TupleExpressionSyntax>().Skip(1).First(); Assert.Equal("(int, int)", model.GetTypeInfo(literal2).Type.ToDisplayString()); }; var verifier = CompileAndVerify(source, sourceSymbolValidator: validator); verifier.VerifyDiagnostics(); } [Fact] public void DeclarationWithCircularity3() { string source = @" class C { static void Main() { var (x1, x2) = (M(out x2), M(out x1)); } static T M<T>(out T x) { x = default(T); return x; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,31): error CS0841: Cannot use local variable 'x2' before it is declared // var (x1, x2) = (M(out x2), M(out x1)); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x2").WithArguments("x2").WithLocation(6, 31), // (6,42): error CS0841: Cannot use local variable 'x1' before it is declared // var (x1, x2) = (M(out x2), M(out x1)); Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x1").WithArguments("x1").WithLocation(6, 42) ); } [Fact, WorkItem(13081, "https://github.com/dotnet/roslyn/issues/13081")] public void GettingDiagnosticsWhenValueTupleIsMissing() { var source = @" class C1 { static void Test(int arg1, (byte, byte) arg2) { foreach ((int, int) e in new (int, int)[10]) { } } } "; var comp = CreateCompilationWithMscorlib40(source); comp.VerifyDiagnostics( // (4,32): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // static void Test(int arg1, (byte, byte) arg2) Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(byte, byte)").WithArguments("System.ValueTuple`2").WithLocation(4, 32), // (6,38): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // foreach ((int, int) e in new (int, int)[10]) Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(int, int)").WithArguments("System.ValueTuple`2").WithLocation(6, 38), // (6,18): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported // foreach ((int, int) e in new (int, int)[10]) Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(int, int)").WithArguments("System.ValueTuple`2").WithLocation(6, 18) ); // no crash } [Fact] public void DeconstructionMayBeEmbedded() { var source = @" class C1 { void M() { if (true) var (x, y) = (1, 2); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // this is no longer considered a declaration statement, // but rather is an assignment expression. So no error. ); } [Fact] public void AssignmentExpressionCanBeUsedInEmbeddedStatement() { var source = @" class C1 { void M() { int x, y; if (true) (x, y) = (1, 2); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); } [Fact] public void DeconstructObsoleteWarning() { var source = @" class C { void M() { (int y1, int y2) = new C(); } [System.Obsolete()] void Deconstruct(out int x1, out int x2) { x1 = 1; x2 = 2; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,27): warning CS0612: 'C.Deconstruct(out int, out int)' is obsolete // (int y1, int y2) = new C(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()").WithArguments("C.Deconstruct(out int, out int)").WithLocation(6, 27) ); } [Fact] public void DeconstructObsoleteError() { var source = @" class C { void M() { (int y1, int y2) = new C(); } [System.Obsolete(""Deprecated"", error: true)] void Deconstruct(out int x1, out int x2) { x1 = 1; x2 = 2; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,27): error CS0619: 'C.Deconstruct(out int, out int)' is obsolete: 'Deprecated' // (int y1, int y2) = new C(); Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new C()").WithArguments("C.Deconstruct(out int, out int)", "Deprecated").WithLocation(6, 27) ); } [Fact] public void DeconstructionLocalsDeclaredNotUsed() { // Check that there are no *use sites* within this code for local variables. // They are not declared. So they should not be returned // by SemanticModel.GetSymbolInfo. Similarly, check that all designation syntax // forms declare deconstruction locals. string source = @" class Program { static void Main() { var (x1, y1) = (1, 2); (var x2, var y2) = (1, 2); } static void M((int, int) t) { var (x3, y3) = t; (var x4, var y4) = t; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); foreach (var node in nodes) { var si = model.GetSymbolInfo(node); var symbol = si.Symbol; if ((object)symbol != null) { if (node is DeclarationExpressionSyntax) { Assert.Equal(SymbolKind.Local, symbol.Kind); Assert.Equal(LocalDeclarationKind.DeconstructionVariable, symbol.GetSymbol<LocalSymbol>().DeclarationKind); } else { Assert.NotEqual(SymbolKind.Local, symbol.Kind); } } symbol = model.GetDeclaredSymbol(node); if ((object)symbol != null) { if (node is SingleVariableDesignationSyntax) { Assert.Equal(SymbolKind.Local, symbol.Kind); Assert.Equal(LocalDeclarationKind.DeconstructionVariable, symbol.GetSymbol<LocalSymbol>().DeclarationKind); } else { Assert.NotEqual(SymbolKind.Local, symbol.Kind); } } } } [CompilerTrait(CompilerFeature.IOperation)] [Fact, WorkItem(14287, "https://github.com/dotnet/roslyn/issues/14287")] public void TupleDeconstructionStatementWithTypesCannotBeConst() { string source = @" class C { static void Main() { /*<bind>*/const (int x, int y) = (1, 2);/*</bind>*/ } } "; string expectedOperationTree = @" IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'const (int ... ) = (1, 2);') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: '(int x, int y) = (1, 2)') Declarators: IVariableDeclaratorOperation (Symbol: (System.Int32 x, System.Int32 y) ) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: '= (1, 2)') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= (1, 2)') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32 x, System.Int32 y), IsImplicit) (Syntax: '(1, 2)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1001: Identifier expected // const /*<bind>*/(int x, int y) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_IdentifierExpected, "=").WithLocation(6, 40), // CS0283: The type '(int x, int y)' cannot be declared const // const /*<bind>*/(int x, int y) = (1, 2)/*</bind>*/; Diagnostic(ErrorCode.ERR_BadConstType, "(int x, int y)").WithArguments("(int x, int y)").WithLocation(6, 25) }; VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact, WorkItem(14287, "https://github.com/dotnet/roslyn/issues/14287")] public void TupleDeconstructionStatementWithoutTypesCannotBeConst() { string source = @" class C { static void Main() { const var (x, y) = (1, 2); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,9): error CS0106: The modifier 'const' is not valid for this item // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_BadMemberFlag, "const").WithArguments("const").WithLocation(6, 9), // (6,19): error CS1001: Identifier expected // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(6, 19), // (6,21): error CS1001: Identifier expected // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(6, 21), // (6,24): error CS1001: Identifier expected // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(6, 24), // (6,26): error CS1002: ; expected // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_SemicolonExpected, "=").WithLocation(6, 26), // (6,26): error CS1525: Invalid expression term '=' // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(6, 26), // (6,19): error CS8112: '(x, y)' is a local function and must therefore always have a body. // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_LocalFunctionMissingBody, "").WithArguments("(x, y)").WithLocation(6, 19), // (6,20): error CS0246: The type or namespace name 'x' could not be found (are you missing a using directive or an assembly reference?) // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "x").WithArguments("x").WithLocation(6, 20), // (6,23): error CS0246: The type or namespace name 'y' could not be found (are you missing a using directive or an assembly reference?) // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "y").WithArguments("y").WithLocation(6, 23), // (6,15): error CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code // const var (x, y) = (1, 2); Diagnostic(ErrorCode.ERR_TypeVarNotFound, "var").WithLocation(6, 15) ); } [Fact, WorkItem(15934, "https://github.com/dotnet/roslyn/issues/15934")] public void PointerTypeInDeconstruction() { string source = @" unsafe class C { static void Main(C c) { (int* x1, int y1) = c; (var* x2, int y2) = c; (int*[] x3, int y3) = c; (var*[] x4, int y4) = c; } public void Deconstruct(out dynamic x, out dynamic y) { x = y = null; } } "; var comp = CreateCompilationWithMscorlib40AndSystemCore(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview); // The precise diagnostics here are not important, and may be sensitive to parser // adjustments. This is a test that we don't crash. The errors here are likely to // change as we adjust the parser and semantic analysis of error cases. comp.VerifyDiagnostics( // (6,10): error CS1525: Invalid expression term 'int' // (int* x1, int y1) = c; Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 10), // (6,15): error CS0103: The name 'x1' does not exist in the current context // (int* x1, int y1) = c; Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 15), // (6,19): error CS0266: Cannot implicitly convert type 'dynamic' to 'int'. An explicit conversion exists (are you missing a cast?) // (int* x1, int y1) = c; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int y1").WithArguments("dynamic", "int").WithLocation(6, 19), // (7,10): error CS0103: The name 'var' does not exist in the current context // (var* x2, int y2) = c; Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(7, 10), // (7,15): error CS0103: The name 'x2' does not exist in the current context // (var* x2, int y2) = c; Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(7, 15), // (7,19): error CS0266: Cannot implicitly convert type 'dynamic' to 'int'. An explicit conversion exists (are you missing a cast?) // (var* x2, int y2) = c; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int y2").WithArguments("dynamic", "int").WithLocation(7, 19), // (8,10): error CS0266: Cannot implicitly convert type 'dynamic' to 'int*[]'. An explicit conversion exists (are you missing a cast?) // (int*[] x3, int y3) = c; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int*[] x3").WithArguments("dynamic", "int*[]").WithLocation(8, 10), // (8,21): error CS0266: Cannot implicitly convert type 'dynamic' to 'int'. An explicit conversion exists (are you missing a cast?) // (int*[] x3, int y3) = c; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int y3").WithArguments("dynamic", "int").WithLocation(8, 21), // (9,10): error CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code // (var*[] x4, int y4) = c; Diagnostic(ErrorCode.ERR_TypeVarNotFound, "var").WithLocation(9, 10), // (9,21): error CS0266: Cannot implicitly convert type 'dynamic' to 'int'. An explicit conversion exists (are you missing a cast?) // (var*[] x4, int y4) = c; Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int y4").WithArguments("dynamic", "int").WithLocation(9, 21) ); } [Fact] public void DeclarationInsideNameof() { string source = @" class Program { static void Main() { string s = nameof((int x1, var x2) = (1, 2)).ToString(); string s1 = x1, s2 = x2; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,28): error CS8185: A declaration is not allowed in this context. // string s = nameof((int x1, var x2) = (1, 2)).ToString(); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x1").WithLocation(6, 28), // (6,27): error CS8081: Expression does not have a name. // string s = nameof((int x1, var x2) = (1, 2)).ToString(); Diagnostic(ErrorCode.ERR_ExpressionHasNoName, "(int x1, var x2) = (1, 2)").WithLocation(6, 27), // (7,21): error CS0029: Cannot implicitly convert type 'int' to 'string' // string s1 = x1, s2 = x2; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x1").WithArguments("int", "string").WithLocation(7, 21), // (7,30): error CS0029: Cannot implicitly convert type 'int' to 'string' // string s1 = x1, s2 = x2; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x2").WithArguments("int", "string").WithLocation(7, 30), // (7,21): error CS0165: Use of unassigned local variable 'x1' // string s1 = x1, s2 = x2; Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(7, 21), // (7,30): error CS0165: Use of unassigned local variable 'x2' // string s1 = x1, s2 = x2; Diagnostic(ErrorCode.ERR_UseDefViolation, "x2").WithArguments("x2").WithLocation(7, 30) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(2, designations.Count()); var refs = tree.GetCompilationUnitRoot().DescendantNodes().OfType<IdentifierNameSyntax>(); var x1 = model.GetDeclaredSymbol(designations[0]); Assert.Equal("x1", x1.Name); Assert.Equal("System.Int32", ((ILocalSymbol)x1).Type.ToTestDisplayString()); Assert.Same(x1, model.GetSymbolInfo(refs.Where(r => r.Identifier.ValueText == "x1").Single()).Symbol); var x2 = model.GetDeclaredSymbol(designations[1]); Assert.Equal("x2", x2.Name); Assert.Equal("System.Int32", ((ILocalSymbol)x2).Type.ToTestDisplayString()); Assert.Same(x2, model.GetSymbolInfo(refs.Where(r => r.Identifier.ValueText == "x2").Single()).Symbol); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_01() { string source1 = @" class C { static void Main() { (var (a,b), var c, int d); } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (6,10): error CS8185: A declaration is not allowed in this context. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (a,b)"), // (6,21): error CS8185: A declaration is not allowed in this context. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var c"), // (6,28): error CS8185: A declaration is not allowed in this context. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int d").WithLocation(6, 28), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var (a,b), var c, int d)").WithLocation(6, 9), // (6,28): error CS0165: Use of unassigned local variable 'd' // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_UseDefViolation, "int d").WithArguments("d").WithLocation(6, 28) ); StandAlone_01_VerifySemanticModel(comp1, LocalDeclarationKind.DeclarationExpressionVariable); string source2 = @" class C { static void Main() { (var (a,b), var c, int d) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_01_VerifySemanticModel(comp2, LocalDeclarationKind.DeconstructionVariable); } private static void StandAlone_01_VerifySemanticModel(CSharpCompilation comp, LocalDeclarationKind localDeclarationKind) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(4, designations.Count()); var a = model.GetDeclaredSymbol(designations[0]); Assert.Equal("var a", a.ToTestDisplayString()); Assert.Equal(localDeclarationKind, a.GetSymbol<LocalSymbol>().DeclarationKind); var b = model.GetDeclaredSymbol(designations[1]); Assert.Equal("var b", b.ToTestDisplayString()); Assert.Equal(localDeclarationKind, b.GetSymbol<LocalSymbol>().DeclarationKind); var c = model.GetDeclaredSymbol(designations[2]); Assert.Equal("var c", c.ToTestDisplayString()); Assert.Equal(localDeclarationKind, c.GetSymbol<LocalSymbol>().DeclarationKind); var d = model.GetDeclaredSymbol(designations[3]); Assert.Equal("System.Int32 d", d.ToTestDisplayString()); Assert.Equal(localDeclarationKind, d.GetSymbol<LocalSymbol>().DeclarationKind); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(3, declarations.Count()); Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("(var a, var b)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.Equal(TypeKind.Struct, typeInfo.Type.TypeKind); Assert.Equal(TypeKind.Error, ((INamedTypeSymbol)typeInfo.Type).TypeArguments[0].TypeKind); Assert.Equal(TypeKind.Error, ((INamedTypeSymbol)typeInfo.Type).TypeArguments[1].TypeKind); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("(var a, var b)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("var c", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("var c", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[1].Type)); Assert.Equal("int d", declarations[2].ToString()); typeInfo = model.GetTypeInfo(declarations[2]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2]).IsIdentity); Assert.Equal("System.Int32 d", model.GetSymbolInfo(declarations[2]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[2].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[2].Type)); var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single(); typeInfo = model.GetTypeInfo(tuple); Assert.Equal("((var a, var b), var c, System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_02() { string source1 = @" (var (a,b), var c, int d); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); comp1.VerifyDiagnostics( // (2,7): error CS7019: Type of 'a' cannot be inferred since its initializer directly or indirectly refers to the definition. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "a").WithArguments("a"), // (2,9): error CS7019: Type of 'b' cannot be inferred since its initializer directly or indirectly refers to the definition. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "b").WithArguments("b"), // (2,17): error CS7019: Type of 'c' cannot be inferred since its initializer directly or indirectly refers to the definition. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "c").WithArguments("c"), // (2,2): error CS8185: A declaration is not allowed in this context. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (a,b)"), // (2,13): error CS8185: A declaration is not allowed in this context. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var c"), // (2,20): error CS8185: A declaration is not allowed in this context. // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int d").WithLocation(2, 20), // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var (a,b), var c, int d); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var (a,b), var c, int d)").WithLocation(2, 1) ); StandAlone_02_VerifySemanticModel(comp1); string source2 = @" (var (a,b), var c, int d) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_02_VerifySemanticModel(comp2); } private static void StandAlone_02_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(4, designations.Count()); var a = model.GetDeclaredSymbol(designations[0]); Assert.Equal("var Script.a", a.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, a.Kind); var b = model.GetDeclaredSymbol(designations[1]); Assert.Equal("var Script.b", b.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, b.Kind); var c = model.GetDeclaredSymbol(designations[2]); Assert.Equal("var Script.c", c.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, c.Kind); var d = model.GetDeclaredSymbol(designations[3]); Assert.Equal("System.Int32 Script.d", d.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, d.Kind); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(3, declarations.Count()); Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("(var a, var b)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("(var a, var b)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("var c", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("var Script.c", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[1].Type)); Assert.Equal("int d", declarations[2].ToString()); typeInfo = model.GetTypeInfo(declarations[2]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2]).IsIdentity); Assert.Equal("System.Int32 Script.d", model.GetSymbolInfo(declarations[2]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[2].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[2].Type)); var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single(); typeInfo = model.GetTypeInfo(tuple); Assert.Equal("((var a, var b), var c, System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_03() { string source1 = @" class C { static void Main() { (var (_, _), var _, int _); } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (6,10): error CS8185: A declaration is not allowed in this context. // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (_, _)"), // (6,22): error CS8185: A declaration is not allowed in this context. // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var _"), // (6,29): error CS8185: A declaration is not allowed in this context. // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int _").WithLocation(6, 29), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var (_, _), var _, int _)").WithLocation(6, 9) ); StandAlone_03_VerifySemanticModel(comp1); string source2 = @" class C { static void Main() { (var (_, _), var _, int _) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_03_VerifySemanticModel(comp2); } private static void StandAlone_03_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); int count = 0; foreach (var designation in tree.GetCompilationUnitRoot().DescendantNodes().OfType<DiscardDesignationSyntax>()) { Assert.Null(model.GetDeclaredSymbol(designation)); count++; } Assert.Equal(4, count); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(3, declarations.Count()); Assert.Equal("var (_, _)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("(var, var)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.Equal(TypeKind.Struct, typeInfo.Type.TypeKind); Assert.Equal(TypeKind.Error, ((INamedTypeSymbol)typeInfo.Type).TypeArguments[0].TypeKind); Assert.Equal(TypeKind.Error, ((INamedTypeSymbol)typeInfo.Type).TypeArguments[1].TypeKind); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("(var, var)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("var _", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[1].Type)); Assert.Equal("int _", declarations[2].ToString()); typeInfo = model.GetTypeInfo(declarations[2]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2]).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[2].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[2].Type)); var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single(); typeInfo = model.GetTypeInfo(tuple); Assert.Equal("((var, var), var, System.Int32)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_04() { string source1 = @" (var (_, _), var _, int _); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); comp1.VerifyDiagnostics( // (2,2): error CS8185: A declaration is not allowed in this context. // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (_, _)"), // (2,14): error CS8185: A declaration is not allowed in this context. // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var _"), // (2,21): error CS8185: A declaration is not allowed in this context. // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int _").WithLocation(2, 21), // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var (_, _), var _, int _); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var (_, _), var _, int _)").WithLocation(2, 1) ); StandAlone_03_VerifySemanticModel(comp1); string source2 = @" (var (_, _), var _, int _) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_03_VerifySemanticModel(comp2); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_05() { string source1 = @" using var = System.Int32; class C { static void Main() { (var (a,b), var c); } } "; var comp1 = CreateCompilation(source1); StandAlone_05_VerifySemanticModel(comp1); string source2 = @" using var = System.Int32; class C { static void Main() { (var (a,b), var c) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_05_VerifySemanticModel(comp2); } private static void StandAlone_05_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(2, declarations.Count()); Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(System.Int32 a, System.Int32 b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Equal("var=System.Int32", model.GetAliasInfo(declarations[0].Type).ToTestDisplayString()); Assert.Equal("var c", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("System.Int32 c", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Equal("var=System.Int32", model.GetAliasInfo(declarations[1].Type).ToTestDisplayString()); } [Fact] [WorkItem(23651, "https://github.com/dotnet/roslyn/issues/23651")] public void StandAlone_05_WithDuplicateNames() { string source1 = @" using var = System.Int32; class C { static void Main() { (var (a, a), var c); } } "; var comp1 = CreateCompilation(source1); var tree = comp1.SyntaxTrees.Single(); var model = comp1.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var aa = nodes.OfType<DeclarationExpressionSyntax>().ElementAt(0); Assert.Equal("var (a, a)", aa.ToString()); var aaType = model.GetTypeInfo(aa).Type.GetSymbol(); Assert.True(aaType.TupleElementNames.IsDefault); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_06() { string source1 = @" using var = System.Int32; (var (a,b), var c); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); StandAlone_06_VerifySemanticModel(comp1); string source2 = @" using var = System.Int32; (var (a,b), var c) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_06_VerifySemanticModel(comp2); } private static void StandAlone_06_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(2, declarations.Count()); Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(System.Int32 a, System.Int32 b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Equal("var=System.Int32", model.GetAliasInfo(declarations[0].Type).ToTestDisplayString()); Assert.Equal("var c", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("System.Int32 Script.c", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Equal("var=System.Int32", model.GetAliasInfo(declarations[1].Type).ToTestDisplayString()); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_07() { string source1 = @" using var = System.Int32; class C { static void Main() { (var (_, _), var _); } } "; var comp1 = CreateCompilation(source1); StandAlone_07_VerifySemanticModel(comp1); string source2 = @" using var = System.Int32; class C { static void Main() { (var (_, _), var _) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_07_VerifySemanticModel(comp2); } private static void StandAlone_07_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(2, declarations.Count()); Assert.Equal("var (_, _)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(System.Int32, System.Int32)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Equal("var=System.Int32", model.GetAliasInfo(declarations[0].Type).ToTestDisplayString()); Assert.Equal("var _", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Equal("var=System.Int32", model.GetAliasInfo(declarations[1].Type).ToTestDisplayString()); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_08() { string source1 = @" using var = System.Int32; (var (_, _), var _); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); StandAlone_07_VerifySemanticModel(comp1); string source2 = @" using var = System.Int32; (var (_, _), var _) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_07_VerifySemanticModel(comp2); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_09() { string source1 = @" using al = System.Int32; class C { static void Main() { (al (a,b), al c); } } "; var comp1 = CreateCompilation(source1); StandAlone_09_VerifySemanticModel(comp1); string source2 = @" using al = System.Int32; class C { static void Main() { (al (a,b), al c) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_09_VerifySemanticModel(comp2); } private static void StandAlone_09_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var declaration = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().Single(); Assert.Equal("al c", declaration.ToString()); var typeInfo = model.GetTypeInfo(declaration); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declaration).IsIdentity); Assert.Equal("System.Int32 c", model.GetSymbolInfo(declaration).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declaration.Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declaration.Type).IsIdentity); var symbolInfo = model.GetSymbolInfo(declaration.Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Equal("al=System.Int32", model.GetAliasInfo(declaration.Type).ToTestDisplayString()); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_10() { string source1 = @" using al = System.Int32; (al (a,b), al c); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); StandAlone_10_VerifySemanticModel(comp1); string source2 = @" using al = System.Int32; (al (a,b), al c) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_10_VerifySemanticModel(comp2); } private static void StandAlone_10_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var declaration = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().Single(); Assert.Equal("al c", declaration.ToString()); var typeInfo = model.GetTypeInfo(declaration); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declaration).IsIdentity); Assert.Equal("System.Int32 Script.c", model.GetSymbolInfo(declaration).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declaration.Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declaration.Type).IsIdentity); var symbolInfo = model.GetSymbolInfo(declaration.Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Equal("al=System.Int32", model.GetAliasInfo(declaration.Type).ToTestDisplayString()); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_11() { string source1 = @" using al = System.Int32; class C { static void Main() { (al (_, _), al _); } } "; var comp1 = CreateCompilation(source1); StandAlone_11_VerifySemanticModel(comp1); string source2 = @" using al = System.Int32; class C { static void Main() { (al (_, _), al _) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_11_VerifySemanticModel(comp2); } private static void StandAlone_11_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var declaration = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().Single(); Assert.Equal("al _", declaration.ToString()); var typeInfo = model.GetTypeInfo(declaration); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declaration).IsIdentity); var symbolInfo = model.GetSymbolInfo(declaration); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declaration.Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declaration.Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declaration.Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Equal("al=System.Int32", model.GetAliasInfo(declaration.Type).ToTestDisplayString()); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_12() { string source1 = @" using al = System.Int32; (al (_, _), al _); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); StandAlone_11_VerifySemanticModel(comp1); string source2 = @" using al = System.Int32; (al (_, _), al _) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_11_VerifySemanticModel(comp2); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_13() { string source1 = @" class C { static void Main() { var (a, b); var (c, d) } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (7,19): error CS1002: ; expected // var (c, d) Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(7, 19), // (6,9): error CS0103: The name 'var' does not exist in the current context // var (a, b); Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(6, 9), // (6,14): error CS0103: The name 'a' does not exist in the current context // var (a, b); Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 14), // (6,17): error CS0103: The name 'b' does not exist in the current context // var (a, b); Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(6, 17), // (7,9): error CS0103: The name 'var' does not exist in the current context // var (c, d) Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(7, 9), // (7,14): error CS0103: The name 'c' does not exist in the current context // var (c, d) Diagnostic(ErrorCode.ERR_NameNotInContext, "c").WithArguments("c").WithLocation(7, 14), // (7,17): error CS0103: The name 'd' does not exist in the current context // var (c, d) Diagnostic(ErrorCode.ERR_NameNotInContext, "d").WithArguments("d").WithLocation(7, 17) ); var tree = comp1.SyntaxTrees.First(); Assert.False(tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().Any()); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_14() { string source1 = @" class C { static void Main() { ((var (a,b), var c), int d); } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (6,11): error CS8185: A declaration is not allowed in this context. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (a,b)").WithLocation(6, 11), // (6,22): error CS8185: A declaration is not allowed in this context. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var c").WithLocation(6, 22), // (6,30): error CS8185: A declaration is not allowed in this context. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int d").WithLocation(6, 30), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_IllegalStatement, "((var (a,b), var c), int d)").WithLocation(6, 9), // (6,30): error CS0165: Use of unassigned local variable 'd' // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_UseDefViolation, "int d").WithArguments("d").WithLocation(6, 30) ); StandAlone_14_VerifySemanticModel(comp1, LocalDeclarationKind.DeclarationExpressionVariable); string source2 = @" class C { static void Main() { ((var (a,b), var c), int d) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_14_VerifySemanticModel(comp2, LocalDeclarationKind.DeconstructionVariable); } private static void StandAlone_14_VerifySemanticModel(CSharpCompilation comp, LocalDeclarationKind localDeclarationKind) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(4, designations.Count()); var a = model.GetDeclaredSymbol(designations[0]); Assert.Equal("var a", a.ToTestDisplayString()); Assert.Equal(localDeclarationKind, a.GetSymbol<LocalSymbol>().DeclarationKind); var b = model.GetDeclaredSymbol(designations[1]); Assert.Equal("var b", b.ToTestDisplayString()); Assert.Equal(localDeclarationKind, b.GetSymbol<LocalSymbol>().DeclarationKind); var c = model.GetDeclaredSymbol(designations[2]); Assert.Equal("var c", c.ToTestDisplayString()); Assert.Equal(localDeclarationKind, c.GetSymbol<LocalSymbol>().DeclarationKind); var d = model.GetDeclaredSymbol(designations[3]); Assert.Equal("System.Int32 d", d.ToTestDisplayString()); Assert.Equal(localDeclarationKind, d.GetSymbol<LocalSymbol>().DeclarationKind); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(3, declarations.Count()); Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("(var a, var b)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("(var a, var b)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("var c", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("var c", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[1].Type)); Assert.Equal("int d", declarations[2].ToString()); typeInfo = model.GetTypeInfo(declarations[2]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2]).IsIdentity); Assert.Equal("System.Int32 d", model.GetSymbolInfo(declarations[2]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[2].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[2].Type)); var tuples = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ToArray(); Assert.Equal(2, tuples.Length); Assert.Equal("((var (a,b), var c), int d)", tuples[0].ToString()); typeInfo = model.GetTypeInfo(tuples[0]); Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[0]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Equal("(var (a,b), var c)", tuples[1].ToString()); typeInfo = model.GetTypeInfo(tuples[1]); Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_15() { string source1 = @" ((var (a,b), var c), int d); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); comp1.VerifyDiagnostics( // (2,8): error CS7019: Type of 'a' cannot be inferred since its initializer directly or indirectly refers to the definition. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "a").WithArguments("a").WithLocation(2, 8), // (2,10): error CS7019: Type of 'b' cannot be inferred since its initializer directly or indirectly refers to the definition. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "b").WithArguments("b").WithLocation(2, 10), // (2,18): error CS7019: Type of 'c' cannot be inferred since its initializer directly or indirectly refers to the definition. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "c").WithArguments("c").WithLocation(2, 18), // (2,3): error CS8185: A declaration is not allowed in this context. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (a,b)").WithLocation(2, 3), // (2,14): error CS8185: A declaration is not allowed in this context. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var c").WithLocation(2, 14), // (2,22): error CS8185: A declaration is not allowed in this context. // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int d").WithLocation(2, 22), // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // ((var (a,b), var c), int d); Diagnostic(ErrorCode.ERR_IllegalStatement, "((var (a,b), var c), int d)").WithLocation(2, 1) ); StandAlone_15_VerifySemanticModel(comp1); string source2 = @" ((var (a,b), var c), int d) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_15_VerifySemanticModel(comp2); } private static void StandAlone_15_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(4, designations.Count()); var a = model.GetDeclaredSymbol(designations[0]); Assert.Equal("var Script.a", a.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, a.Kind); var b = model.GetDeclaredSymbol(designations[1]); Assert.Equal("var Script.b", b.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, b.Kind); var c = model.GetDeclaredSymbol(designations[2]); Assert.Equal("var Script.c", c.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, c.Kind); var d = model.GetDeclaredSymbol(designations[3]); Assert.Equal("System.Int32 Script.d", d.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, d.Kind); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(3, declarations.Count()); Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("(var a, var b)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("(var a, var b)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("var c", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("var Script.c", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[1].Type)); Assert.Equal("int d", declarations[2].ToString()); typeInfo = model.GetTypeInfo(declarations[2]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2]).IsIdentity); Assert.Equal("System.Int32 Script.d", model.GetSymbolInfo(declarations[2]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[2].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[2].Type)); var tuples = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ToArray(); Assert.Equal(2, tuples.Length); Assert.Equal("((var (a,b), var c), int d)", tuples[0].ToString()); typeInfo = model.GetTypeInfo(tuples[0]); Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[0]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Equal("(var (a,b), var c)", tuples[1].ToString()); typeInfo = model.GetTypeInfo(tuples[1]); Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_16() { string source1 = @" class C { static void Main() { ((var (_, _), var _), int _); } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (6,11): error CS8185: A declaration is not allowed in this context. // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (_, _)").WithLocation(6, 11), // (6,23): error CS8185: A declaration is not allowed in this context. // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var _").WithLocation(6, 23), // (6,31): error CS8185: A declaration is not allowed in this context. // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int _").WithLocation(6, 31), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_IllegalStatement, "((var (_, _), var _), int _)").WithLocation(6, 9) ); StandAlone_16_VerifySemanticModel(comp1); string source2 = @" class C { static void Main() { ((var (_, _), var _), int _) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_16_VerifySemanticModel(comp2); } private static void StandAlone_16_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); int count = 0; foreach (var designation in tree.GetCompilationUnitRoot().DescendantNodes().OfType<DiscardDesignationSyntax>()) { Assert.Null(model.GetDeclaredSymbol(designation)); count++; } Assert.Equal(4, count); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(3, declarations.Count()); Assert.Equal("var (_, _)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("(var, var)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("(var, var)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("var _", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("var", typeInfo.Type.ToTestDisplayString()); Assert.Equal(SymbolKind.ErrorType, typeInfo.Type.Kind); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[1].Type)); Assert.Equal("int _", declarations[2].ToString()); typeInfo = model.GetTypeInfo(declarations[2]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2]).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[2].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[2].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[2].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[2].Type)); var tuples = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ToArray(); Assert.Equal(2, tuples.Length); Assert.Equal("((var (_, _), var _), int _)", tuples[0].ToString()); typeInfo = model.GetTypeInfo(tuples[0]); Assert.Equal("(((var, var), var), System.Int32)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[0]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Equal("(var (_, _), var _)", tuples[1].ToString()); typeInfo = model.GetTypeInfo(tuples[1]); Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_17() { string source1 = @" ((var (_, _), var _), int _); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); comp1.VerifyDiagnostics( // (2,3): error CS8185: A declaration is not allowed in this context. // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var (_, _)").WithLocation(2, 3), // (2,15): error CS8185: A declaration is not allowed in this context. // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var _").WithLocation(2, 15), // (2,23): error CS8185: A declaration is not allowed in this context. // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int _").WithLocation(2, 23), // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // ((var (_, _), var _), int _); Diagnostic(ErrorCode.ERR_IllegalStatement, "((var (_, _), var _), int _)").WithLocation(2, 1) ); StandAlone_16_VerifySemanticModel(comp1); string source2 = @" ((var (_, _), var _), int _) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_16_VerifySemanticModel(comp2); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_18() { string source1 = @" class C { static void Main() { (var ((a,b), c), int d); } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (6,10): error CS8185: A declaration is not allowed in this context. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var ((a,b), c)").WithLocation(6, 10), // (6,26): error CS8185: A declaration is not allowed in this context. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int d").WithLocation(6, 26), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var ((a,b), c), int d)").WithLocation(6, 9), // (6,26): error CS0165: Use of unassigned local variable 'd' // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_UseDefViolation, "int d").WithArguments("d").WithLocation(6, 26) ); StandAlone_18_VerifySemanticModel(comp1, LocalDeclarationKind.DeclarationExpressionVariable); string source2 = @" class C { static void Main() { (var ((a,b), c), int d) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_18_VerifySemanticModel(comp2, LocalDeclarationKind.DeconstructionVariable); } private static void StandAlone_18_VerifySemanticModel(CSharpCompilation comp, LocalDeclarationKind localDeclarationKind) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(4, designations.Count()); var a = model.GetDeclaredSymbol(designations[0]); Assert.Equal("var a", a.ToTestDisplayString()); Assert.Equal(localDeclarationKind, a.GetSymbol<LocalSymbol>().DeclarationKind); var b = model.GetDeclaredSymbol(designations[1]); Assert.Equal("var b", b.ToTestDisplayString()); Assert.Equal(localDeclarationKind, b.GetSymbol<LocalSymbol>().DeclarationKind); var c = model.GetDeclaredSymbol(designations[2]); Assert.Equal("var c", c.ToTestDisplayString()); Assert.Equal(localDeclarationKind, c.GetSymbol<LocalSymbol>().DeclarationKind); var d = model.GetDeclaredSymbol(designations[3]); Assert.Equal("System.Int32 d", d.ToTestDisplayString()); Assert.Equal(localDeclarationKind, d.GetSymbol<LocalSymbol>().DeclarationKind); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(2, declarations.Count()); Assert.Equal("var ((a,b), c)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("((var a, var b), var c)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("((var a, var b), var c)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("int d", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("System.Int32 d", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[1].Type)); var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single(); typeInfo = model.GetTypeInfo(tuple); Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_19() { string source1 = @" (var ((a,b), c), int d); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); comp1.VerifyDiagnostics( // (2,8): error CS7019: Type of 'a' cannot be inferred since its initializer directly or indirectly refers to the definition. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "a").WithArguments("a").WithLocation(2, 8), // (2,10): error CS7019: Type of 'b' cannot be inferred since its initializer directly or indirectly refers to the definition. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "b").WithArguments("b").WithLocation(2, 10), // (2,14): error CS7019: Type of 'c' cannot be inferred since its initializer directly or indirectly refers to the definition. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_RecursivelyTypedVariable, "c").WithArguments("c").WithLocation(2, 14), // (2,2): error CS8185: A declaration is not allowed in this context. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var ((a,b), c)").WithLocation(2, 2), // (2,18): error CS8185: A declaration is not allowed in this context. // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int d").WithLocation(2, 18), // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var ((a,b), c), int d); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var ((a,b), c), int d)").WithLocation(2, 1) ); StandAlone_19_VerifySemanticModel(comp1); string source2 = @" (var ((a,b), c), int d) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_19_VerifySemanticModel(comp2); } private static void StandAlone_19_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var designations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SingleVariableDesignationSyntax>().ToArray(); Assert.Equal(4, designations.Count()); var a = model.GetDeclaredSymbol(designations[0]); Assert.Equal("var Script.a", a.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, a.Kind); var b = model.GetDeclaredSymbol(designations[1]); Assert.Equal("var Script.b", b.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, b.Kind); var c = model.GetDeclaredSymbol(designations[2]); Assert.Equal("var Script.c", c.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, c.Kind); var d = model.GetDeclaredSymbol(designations[3]); Assert.Equal("System.Int32 Script.d", d.ToTestDisplayString()); Assert.Equal(SymbolKind.Field, d.Kind); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(2, declarations.Count()); Assert.Equal("var ((a,b), c)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("((var a, var b), var c)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("((var a, var b), var c)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("int d", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); Assert.Equal("System.Int32 Script.d", model.GetSymbolInfo(declarations[1]).Symbol.ToTestDisplayString()); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[1].Type)); var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single(); typeInfo = model.GetTypeInfo(tuple); Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_20() { string source1 = @" class C { static void Main() { (var ((_, _), _), int _); } } "; var comp1 = CreateCompilation(source1); comp1.VerifyDiagnostics( // (6,10): error CS8185: A declaration is not allowed in this context. // (var ((_, _), _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var ((_, _), _)").WithLocation(6, 10), // (6,27): error CS8185: A declaration is not allowed in this context. // (var ((_, _), _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int _").WithLocation(6, 27), // (6,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var ((_, _), _), int _); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var ((_, _), _), int _)").WithLocation(6, 9) ); StandAlone_20_VerifySemanticModel(comp1); string source2 = @" class C { static void Main() { (var ((_, _), _), int _) = D; } } "; var comp2 = CreateCompilation(source2); StandAlone_20_VerifySemanticModel(comp2); } private static void StandAlone_20_VerifySemanticModel(CSharpCompilation comp) { var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); int count = 0; foreach (var designation in tree.GetCompilationUnitRoot().DescendantNodes().OfType<DiscardDesignationSyntax>()) { Assert.Null(model.GetDeclaredSymbol(designation)); count++; } Assert.Equal(4, count); var declarations = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DeclarationExpressionSyntax>().ToArray(); Assert.Equal(2, declarations.Count()); Assert.Equal("var ((_, _), _)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[0].Type); Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); Assert.Equal("((var, var), var)", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(declarations[0].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[0].Type); Assert.Equal("((var, var), var)", symbolInfo.Symbol.ToTestDisplayString()); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); Assert.Null(model.GetAliasInfo(declarations[0].Type)); Assert.Equal("int _", declarations[1].ToString()); typeInfo = model.GetTypeInfo(declarations[1]); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1]); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); typeInfo = model.GetTypeInfo(declarations[1].Type); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[1].Type).IsIdentity); symbolInfo = model.GetSymbolInfo(declarations[1].Type); Assert.Equal("System.Int32", symbolInfo.Symbol.ToTestDisplayString()); Assert.Null(model.GetAliasInfo(declarations[1].Type)); var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single(); typeInfo = model.GetTypeInfo(tuple); Assert.Equal("(((var, var), var), System.Int32)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); Assert.Null(symbolInfo.Symbol); Assert.Empty(symbolInfo.CandidateSymbols); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } [Fact, WorkItem(17572, "https://github.com/dotnet/roslyn/issues/17572")] public void StandAlone_21() { string source1 = @" (var ((_, _), _), int _); "; var comp1 = CreateCompilation(source1, parseOptions: TestOptions.Script); comp1.VerifyDiagnostics( // (2,2): error CS8185: A declaration is not allowed in this context. // (var ((_, _), _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "var ((_, _), _)").WithLocation(2, 2), // (2,19): error CS8185: A declaration is not allowed in this context. // (var ((_, _), _), int _); Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int _").WithLocation(2, 19), // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // (var ((_, _), _), int _); Diagnostic(ErrorCode.ERR_IllegalStatement, "(var ((_, _), _), int _)").WithLocation(2, 1) ); StandAlone_20_VerifySemanticModel(comp1); string source2 = @" (var ((_, _), _), int _) = D; "; var comp2 = CreateCompilation(source2, parseOptions: TestOptions.Script); StandAlone_20_VerifySemanticModel(comp2); } [Fact, WorkItem(17921, "https://github.com/dotnet/roslyn/issues/17921")] public void DiscardVoid_01() { var source = @"class C { static void Main() { (_, _) = (1, Main()); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,22): error CS8210: A tuple may not contain a value of type 'void'. // (_, _) = (1, Main()); Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 22) ); var main = comp.GetMember<MethodSymbol>("C.Main"); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainCall = tree.GetRoot().DescendantNodes().OfType<ExpressionSyntax>().Where(n => n.ToString() == "Main()").Single(); var type = model.GetTypeInfo(mainCall); Assert.Equal(SpecialType.System_Void, type.Type.SpecialType); Assert.Equal(SpecialType.System_Void, type.ConvertedType.SpecialType); Assert.Equal(ConversionKind.Identity, model.GetConversion(mainCall).Kind); var symbols = model.GetSymbolInfo(mainCall); Assert.Equal(symbols.Symbol, main.GetPublicSymbol()); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); // the ArgumentSyntax above a tuple element doesn't support GetTypeInfo or GetSymbolInfo. var argument = (ArgumentSyntax)mainCall.Parent; type = model.GetTypeInfo(argument); Assert.Null(type.Type); Assert.Null(type.ConvertedType); symbols = model.GetSymbolInfo(argument); Assert.Null(symbols.Symbol); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); } [Fact, WorkItem(17921, "https://github.com/dotnet/roslyn/issues/17921")] public void DeconstructVoid_01() { var source = @"class C { static void Main() { (int x, void y) = (1, Main()); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,17): error CS1547: Keyword 'void' cannot be used in this context // (int x, void y) = (1, Main()); Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(5, 17), // (5,31): error CS8210: A tuple may not contain a value of type 'void'. // (int x, void y) = (1, Main()); Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 31) ); var main = comp.GetMember<MethodSymbol>("C.Main"); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainCall = tree.GetRoot().DescendantNodes().OfType<ExpressionSyntax>().Where(n => n.ToString() == "Main()").Single(); var type = model.GetTypeInfo(mainCall); Assert.Equal(SpecialType.System_Void, type.Type.SpecialType); Assert.Equal(SpecialType.System_Void, type.ConvertedType.SpecialType); Assert.Equal(ConversionKind.Identity, model.GetConversion(mainCall).Kind); var symbols = model.GetSymbolInfo(mainCall); Assert.Equal(symbols.Symbol, main.GetPublicSymbol()); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); // the ArgumentSyntax above a tuple element doesn't support GetTypeInfo or GetSymbolInfo. var argument = (ArgumentSyntax)mainCall.Parent; type = model.GetTypeInfo(argument); Assert.Null(type.Type); Assert.Null(type.ConvertedType); symbols = model.GetSymbolInfo(argument); Assert.Null(symbols.Symbol); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); } [Fact, WorkItem(17921, "https://github.com/dotnet/roslyn/issues/17921")] public void DeconstructVoid_02() { var source = @"class C { static void Main() { var (x, y) = (1, Main()); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,26): error CS8210: A tuple may not contain a value of type 'void'. // var (x, y) = (1, Main()); Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 26) ); var main = comp.GetMember<MethodSymbol>("C.Main"); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainCall = tree.GetRoot().DescendantNodes().OfType<ExpressionSyntax>().Where(n => n.ToString() == "Main()").Single(); var type = model.GetTypeInfo(mainCall); Assert.Equal(SpecialType.System_Void, type.Type.SpecialType); Assert.Equal(SpecialType.System_Void, type.ConvertedType.SpecialType); Assert.Equal(ConversionKind.Identity, model.GetConversion(mainCall).Kind); var symbols = model.GetSymbolInfo(mainCall); Assert.Equal(symbols.Symbol, main.GetPublicSymbol()); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); // the ArgumentSyntax above a tuple element doesn't support GetTypeInfo or GetSymbolInfo. var argument = (ArgumentSyntax)mainCall.Parent; type = model.GetTypeInfo(argument); Assert.Null(type.Type); Assert.Null(type.ConvertedType); symbols = model.GetSymbolInfo(argument); Assert.Null(symbols.Symbol); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); } [Fact, WorkItem(17921, "https://github.com/dotnet/roslyn/issues/17921")] public void DeconstructVoid_03() { var source = @"class C { static void Main() { (int x, void y) = (1, 2); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,17): error CS1547: Keyword 'void' cannot be used in this context // (int x, void y) = (1, 2); Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(5, 17), // (5,31): error CS0029: Cannot implicitly convert type 'int' to 'void' // (int x, void y) = (1, 2); Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "void").WithLocation(5, 31) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var two = tree.GetRoot().DescendantNodes().OfType<ExpressionSyntax>().Where(n => n.ToString() == "2").Single(); var type = model.GetTypeInfo(two); Assert.Equal(SpecialType.System_Int32, type.Type.SpecialType); Assert.Equal(SpecialType.System_Int32, type.ConvertedType.SpecialType); Assert.Equal(ConversionKind.Identity, model.GetConversion(two).Kind); var symbols = model.GetSymbolInfo(two); Assert.Null(symbols.Symbol); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); // the ArgumentSyntax above a tuple element doesn't support GetTypeInfo or GetSymbolInfo. var argument = (ArgumentSyntax)two.Parent; type = model.GetTypeInfo(argument); Assert.Null(type.Type); Assert.Null(type.ConvertedType); symbols = model.GetSymbolInfo(argument); Assert.Null(symbols.Symbol); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); } [Fact, WorkItem(17921, "https://github.com/dotnet/roslyn/issues/17921")] public void DeconstructVoid_04() { var source = @"class C { static void Main() { (int x, int y) = (1, Main()); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,30): error CS8210: A tuple may not contain a value of type 'void'. // (int x, int y) = (1, Main()); Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 30) ); var main = comp.GetMember<MethodSymbol>("C.Main"); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainCall = tree.GetRoot().DescendantNodes().OfType<ExpressionSyntax>().Where(n => n.ToString() == "Main()").Single(); var type = model.GetTypeInfo(mainCall); Assert.Equal(SpecialType.System_Void, type.Type.SpecialType); Assert.Equal(SpecialType.System_Void, type.ConvertedType.SpecialType); Assert.Equal(ConversionKind.Identity, model.GetConversion(mainCall).Kind); var symbols = model.GetSymbolInfo(mainCall); Assert.Equal(symbols.Symbol, main.GetPublicSymbol()); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); // the ArgumentSyntax above a tuple element doesn't support GetTypeInfo or GetSymbolInfo. var argument = (ArgumentSyntax)mainCall.Parent; type = model.GetTypeInfo(argument); Assert.Null(type.Type); Assert.Null(type.ConvertedType); symbols = model.GetSymbolInfo(argument); Assert.Null(symbols.Symbol); Assert.Empty(symbols.CandidateSymbols); Assert.Equal(CandidateReason.None, symbols.CandidateReason); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DiscardDeclarationExpression_IOperation() { string source = @" class C { void M() { /*<bind>*/var (_, _) = (0, 0)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32, System.Int32)) (Syntax: 'var (_, _) = (0, 0)') Left: IDeclarationExpressionOperation (OperationKind.DeclarationExpression, Type: (System.Int32, System.Int32)) (Syntax: 'var (_, _)') ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(_, _)') NaturalType: (System.Int32, System.Int32) Elements(2): IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(0, 0)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DiscardDeclarationAssignment_IOperation() { string source = @" class C { void M() { int x; /*<bind>*/(x, _) = (0, 0)/*</bind>*/; } } "; string expectedOperationTree = @" IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int32 x, System.Int32)) (Syntax: '(x, _) = (0, 0)') Left: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 x, System.Int32)) (Syntax: '(x, _)') NaturalType: (System.Int32 x, System.Int32) Elements(2): ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') Right: ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(0, 0)') NaturalType: (System.Int32, System.Int32) Elements(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void DiscardOutVarDeclaration_IOperation() { string source = @" class C { void M() { M2(out /*<bind>*/var _/*</bind>*/); } void M2(out int x) { x = 0; } } "; string expectedOperationTree = @" IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: 'var _') "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<DeclarationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics); } [Fact] [WorkItem(46165, "https://github.com/dotnet/roslyn/issues/46165")] public void Issue46165_1() { var text = @" class C { static void Main() { foreach ((var i, i)) } }"; CreateCompilation(text).VerifyEmitDiagnostics( // (6,18): error CS8186: A foreach loop must declare its iteration variables. // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(var i, i)").WithLocation(6, 18), // (6,23): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'i'. // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "i").WithArguments("i").WithLocation(6, 23), // (6,26): error CS0841: Cannot use local variable 'i' before it is declared // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "i").WithArguments("i").WithLocation(6, 26), // (6,28): error CS1515: 'in' expected // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_InExpected, ")").WithLocation(6, 28), // (6,28): error CS1525: Invalid expression term ')' // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 28), // (6,29): error CS1525: Invalid expression term '}' // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(6, 29), // (6,29): error CS1002: ; expected // foreach ((var i, i)) Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 29) ); } [Fact] [WorkItem(46165, "https://github.com/dotnet/roslyn/issues/46165")] public void Issue46165_2() { var text = @" class C { static void Main() { (var i, i) = ; } }"; CreateCompilation(text).VerifyEmitDiagnostics( // (6,14): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'i'. // (var i, i) = ; Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "i").WithArguments("i").WithLocation(6, 14), // (6,17): error CS0841: Cannot use local variable 'i' before it is declared // (var i, i) = ; Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "i").WithArguments("i").WithLocation(6, 17), // (6,22): error CS1525: Invalid expression term ';' // (var i, i) = ; Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 22) ); } [Fact] [WorkItem(46165, "https://github.com/dotnet/roslyn/issues/46165")] public void Issue46165_3() { var text = @" class C { static void Main() { foreach ((int i, i)) } }"; CreateCompilation(text).VerifyEmitDiagnostics( // (6,18): error CS8186: A foreach loop must declare its iteration variables. // foreach ((int i, i)) Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(int i, i)").WithLocation(6, 18), // (6,26): error CS1656: Cannot assign to 'i' because it is a 'foreach iteration variable' // foreach ((int i, i)) Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "i").WithArguments("i", "foreach iteration variable").WithLocation(6, 26), // (6,28): error CS1515: 'in' expected // foreach ((int i, i)) Diagnostic(ErrorCode.ERR_InExpected, ")").WithLocation(6, 28), // (6,28): error CS1525: Invalid expression term ')' // foreach ((int i, i)) Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 28), // (6,29): error CS1525: Invalid expression term '}' // foreach ((int i, i)) Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(6, 29), // (6,29): error CS1002: ; expected // foreach ((int i, i)) Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 29) ); } [Fact] [WorkItem(46165, "https://github.com/dotnet/roslyn/issues/46165")] public void Issue46165_4() { var text = @" class C { static void Main() { (int i, i) = ; } }"; CreateCompilation(text).VerifyEmitDiagnostics( // (6,22): error CS1525: Invalid expression term ';' // (int i, i) = ; Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 22) ); } [Fact] public void ObsoleteConversions_01() { var source = @" var x = (1, new C()); (int i, bool c) = x; (i, c) = (1, new C()); (i, c) = new C2(); class C { [System.Obsolete()] public static implicit operator bool(C c) => true; } class C2 { public void Deconstruct(out int i, out C c) => throw null; }"; CreateCompilation(source).VerifyEmitDiagnostics( // (4,1): warning CS0612: 'C.implicit operator bool(C)' is obsolete // (int i, bool c) = x; Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "(int i, bool c) = x").WithArguments("C.implicit operator bool(C)").WithLocation(4, 1), // (5,14): warning CS0612: 'C.implicit operator bool(C)' is obsolete // (i, c) = (1, new C()); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()").WithArguments("C.implicit operator bool(C)").WithLocation(5, 14), // (6,1): warning CS0612: 'C.implicit operator bool(C)' is obsolete // (i, c) = new C2(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "(i, c) = new C2()").WithArguments("C.implicit operator bool(C)").WithLocation(6, 1) ); } [Fact] public void ObsoleteConversions_02() { var source = @" var x = (1, new C()); (int i, bool c) = x; (i, c) = (1, new C()); (i, c) = new C2(); class C { [System.Obsolete(""Obsolete error"", true)] public static implicit operator bool(C c) => true; } class C2 { public void Deconstruct(out int i, out C c) => throw null; }"; CreateCompilation(source).VerifyEmitDiagnostics( // (4,1): error CS0619: 'C.implicit operator bool(C)' is obsolete: 'Obsolete error' // (int i, bool c) = x; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "(int i, bool c) = x").WithArguments("C.implicit operator bool(C)", "Obsolete error").WithLocation(4, 1), // (5,14): error CS0619: 'C.implicit operator bool(C)' is obsolete: 'Obsolete error' // (i, c) = (1, new C()); Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new C()").WithArguments("C.implicit operator bool(C)", "Obsolete error").WithLocation(5, 14), // (6,1): error CS0619: 'C.implicit operator bool(C)' is obsolete: 'Obsolete error' // (i, c) = new C2(); Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "(i, c) = new C2()").WithArguments("C.implicit operator bool(C)", "Obsolete error").WithLocation(6, 1) ); } [Fact, WorkItem(58472, "https://github.com/dotnet/roslyn/issues/58472")] public void DeconstructionIntoImplicitIndexers() { var source = @" var x = new int[1]; C.M(x); var y = new int[1]; C.M2(y); System.Console.Write((x[^1], y[^1])); class C { public static void M<T>(T[] a) { (a[0], a[^1]) = (default, default); } public static void M2(int[] a) { (a[0], a[^1]) = (default, default); } } "; var comp = CreateCompilationWithIndex(source); // No IndexOutOfRangeException thrown var verifier = CompileAndVerify(comp, expectedOutput: "(0, 0)"); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M<T>(T[])", @" { // Code size 41 (0x29) .maxstack 3 .locals init (T[] V_0, int V_1, T V_2) IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: dup IL_0003: stloc.0 IL_0004: ldlen IL_0005: conv.i4 IL_0006: ldc.i4.1 IL_0007: sub IL_0008: stloc.1 IL_0009: ldc.i4.0 IL_000a: ldloca.s V_2 IL_000c: initobj ""T"" IL_0012: ldloc.2 IL_0013: stelem ""T"" IL_0018: ldloc.0 IL_0019: ldloc.1 IL_001a: ldloca.s V_2 IL_001c: initobj ""T"" IL_0022: ldloc.2 IL_0023: stelem ""T"" IL_0028: ret } "); verifier.VerifyIL("C.M2", @" { // Code size 25 (0x19) .maxstack 3 .locals init (int& V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: ldelema ""int"" IL_0007: stloc.0 IL_0008: ldarg.0 IL_0009: dup IL_000a: ldlen IL_000b: conv.i4 IL_000c: ldc.i4.1 IL_000d: sub IL_000e: ldelema ""int"" IL_0013: ldloc.0 IL_0014: ldc.i4.0 IL_0015: stind.i4 IL_0016: ldc.i4.0 IL_0017: stind.i4 IL_0018: ret } "); } [Fact, WorkItem(61332, "https://github.com/dotnet/roslyn/issues/61332")] public void NestedNullableConversions() { var code = """ float? _startScrollPosition, _endScrollPosition; (_startScrollPosition, _endScrollPosition) = GetScrollPositions(); (float, float) GetScrollPositions() => (0, 0); """; var comp = CreateCompilation(code); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var assignment = tree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>().Single(); var deconstructionInfo = model.GetDeconstructionInfo(assignment); var nestedConversions = deconstructionInfo.Nested; Assert.Equal(2, nestedConversions.Length); Assert.All(nestedConversions, n => Assert.Empty(n.Nested)); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/68026")] public void ErrorForeachVariable_01() { var source = @" foreach Console.Write($""{1 switch { _ => 1 }}""); "; CreateCompilation(source).VerifyDiagnostics( // (2,8): error CS1003: Syntax error, '(' expected // foreach Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(2, 8), // (3,40): error CS1515: 'in' expected // Console.Write($"{1 switch { _ => 1 }}"); Diagnostic(ErrorCode.ERR_InExpected, ";").WithLocation(3, 40), // (3,40): error CS0230: Type and identifier are both required in a foreach statement // Console.Write($"{1 switch { _ => 1 }}"); Diagnostic(ErrorCode.ERR_BadForeachDecl, ";").WithLocation(3, 40), // (3,40): error CS1525: Invalid expression term ';' // Console.Write($"{1 switch { _ => 1 }}"); Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(3, 40), // (3,40): error CS1026: ) expected // Console.Write($"{1 switch { _ => 1 }}"); Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(3, 40) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/68026")] public void ErrorForeachVariable_02() { var source = @" foreach (m(out var x) in new[]{1,2}) { x++; // 1 } x++; // 2 void m(out int x) => x = 0; "; CreateCompilation(source).VerifyDiagnostics( // (2,23): error CS0230: Type and identifier are both required in a foreach statement // foreach (m(out var x) in new[]{1,2}) Diagnostic(ErrorCode.ERR_BadForeachDecl, "in").WithLocation(2, 23), // (7,1): error CS0103: The name 'x' does not exist in the current context // x++; // 2 Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(7, 1), // (9,6): warning CS8321: The local function 'm' is declared but never used // void m(out int x) => x = 0; Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "m").WithArguments("m").WithLocation(9, 6) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68979")] public void GetDeconstructionInfo_01() { string source = """ var c1 = new C(); var c2 = new C(); c1 = (_, _) = c2; class C { public void Deconstruct(out int a, out string b) => throw null; public static implicit operator C((int i, string s) tuple) => throw null; } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var discardAssignment = GetSyntax<AssignmentExpressionSyntax>(tree, "(_, _) = c2"); var deconstructionInfo = model.GetDeconstructionInfo(discardAssignment); Assert.Equal("void C.Deconstruct(out System.Int32 a, out System.String b)", deconstructionInfo.Method.ToTestDisplayString()); } } }