diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index c518e4f73b291..94b750cab51bb 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -1,19 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.Semantics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { - internal partial class BoundTupleExpression - { - protected override ImmutableArray Children => StaticCast.From(this.Arguments); - } - internal partial class BoundDelegateCreationExpression { protected override ImmutableArray Children => ImmutableArray.Create(this.Argument); diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index a99f0e98bd54e..d98de48c7e0f8 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -165,6 +165,9 @@ private static IOperation CreateInternal(BoundNode boundNode) return CreateBoundLabeledStatementOperation((BoundLabeledStatement)boundNode); case BoundKind.ExpressionStatement: return CreateBoundExpressionStatementOperation((BoundExpressionStatement)boundNode); + case BoundKind.TupleLiteral: + case BoundKind.ConvertedTupleLiteral: + return CreateBoundTupleExpressionOperation((BoundTupleExpression)boundNode); default: var constantValue = ConvertToOptional((boundNode as BoundExpression)?.ConstantValue); return Operation.CreateOperationNone(boundNode.HasErrors, boundNode.Syntax, constantValue, getChildren: () => GetIOperationChildren(boundNode)); @@ -985,5 +988,15 @@ private static IExpressionStatement CreateBoundExpressionStatementOperation(Boun Optional constantValue = default(Optional); return new LazyExpressionStatement(expression, isInvalid, syntax, type, constantValue); } + + private static ITupleExpression CreateBoundTupleExpressionOperation(BoundTupleExpression boundTupleExpression) + { + Lazy> elements = new Lazy>(() => GetTupleElements(boundTupleExpression)); + bool isInvalid = boundTupleExpression.HasErrors; + SyntaxNode syntax = boundTupleExpression.Syntax; + ITypeSymbol type = boundTupleExpression.Type; + Optional constantValue = ConvertToOptional(boundTupleExpression.ConstantValue); + return new LazyTupleExpression(elements, isInvalid, syntax, type, constantValue); + } } } diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs index 836fd727d3638..2a598800ce69b 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs @@ -340,6 +340,15 @@ private static ImmutableArray GetVariableDeclarationStatem OperationFactory.CreateVariableDeclaration(declaration.LocalSymbol, Create(declaration.InitializerOpt), declaration.Syntax))); } + private static readonly ConditionalWeakTable s_tupleElementsMappings = + new ConditionalWeakTable(); + + private static ImmutableArray GetTupleElements(BoundTupleExpression boundTupleExpression) + { + return (ImmutableArray)s_tupleElementsMappings.GetValue(boundTupleExpression, + tupleExpr => tupleExpr.Arguments.SelectAsArray(element => Create(element))); + } + // TODO: We need to reuse the logic in `LocalRewriter.MakeArguments` instead of using private implementation. // Also. this implementation here was for the (now removed) API `ArgumentsInParameter`, which doesn't fulfill // the contract of `ArgumentsInEvaluationOrder` plus it doesn't handle various scenarios correctly even for parameter order, diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index e22c2eb023be8..2a97ca3ac4577 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -62,6 +62,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs index b6f9d22b84c9d..856e6ebbd0652 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -23,8 +23,8 @@ public void M(int x, int y) } "; string expectedOperationTree = @" -IOperation: (OperationKind.None) (Syntax: '(x, x + y)') - Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32 x, System.Int32)) (Syntax: '(x, x + y)') + Elements(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') IBinaryOperatorExpression (BinaryOperationKind.IntegerAdd) (OperationKind.BinaryOperatorExpression, Type: System.Int32) (Syntax: 'x + y') Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') @@ -66,8 +66,8 @@ public void M(Point point) "; string expectedOperationTree = @" IOperation: (OperationKind.None) (Syntax: 'var (x, y) = point') - Children(2): IOperation: (OperationKind.None) (Syntax: 'var (x, y)') - Children(2): ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'x') + Children(2): ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32 x, System.Int32 y)) (Syntax: 'var (x, y)') + Elements(2): ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'x') ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32 x, System.Int32 y)) (Syntax: 'point') IParameterReferenceExpression: point (OperationKind.ParameterReferenceExpression, Type: Point) (Syntax: 'point') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs new file mode 100644 index 0000000000000..2b99c9ca0fc1a --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs @@ -0,0 +1,473 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +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 +{ + public partial class IOperationTests : SemanticModelTestBase + { + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NoConversions() + { + string source = @" +using System; + +class C +{ + static void Main() + { + (int, int) t = /**/(1, 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') + Elements(2): ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_ImplicitConversions() + { + string source = @" +using System; + +class C +{ + static void Main() + { + (uint, uint) t = /**/(1, 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.UInt32, System.UInt32)) (Syntax: '(1, 2)') + Elements(2): IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.UInt32, Constant: 1) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.UInt32, Constant: 2) (Syntax: '2') + ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_ImplicitConversionFromNull() + { + string source = @" +using System; + +class C +{ + static void Main() + { + (uint, string) t = /**/(1, null)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.UInt32, System.String)) (Syntax: '(1, null)') + Elements(2): IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.UInt32, Constant: 1) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + ILiteralExpression (Text: null) (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NamedArguments() + { + string source = @" +using System; + +class C +{ + static void Main() + { + var t = /**/(A: 1, B: 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32 A, System.Int32 B)) (Syntax: '(A: 1, B: 2)') + Elements(2): ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NamedElementsInTupleType() + { + string source = @" +using System; + +class C +{ + static void Main() + { + (int A, int B) t = /**/(1, 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') + Elements(2): ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NamedElementsAndImplicitConversions() + { + string source = @" +using System; + +class C +{ + static void Main() + { + (short, string) t = /**/(A: 1, B: null)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int16 A, System.String B)) (Syntax: '(A: 1, B: null)') + Elements(2): IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Int16, Constant: 1) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') + IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + ILiteralExpression (Text: null) (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS8123: The tuple element name 'A' is ignored because a different name or no name is specified by the target type '(short, string)'. + // (short, string) t = /**/(A: 1, B: null)/**/; + Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "A: 1").WithArguments("A", "(short, string)").WithLocation(8, 40), + // CS8123: The tuple element name 'B' is ignored because a different name or no name is specified by the target type '(short, string)'. + // (short, string) t = /**/(A: 1, B: null)/**/; + Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "B: null").WithArguments("B", "(short, string)").WithLocation(8, 46) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_UserDefinedConversionsForArguments() + { + string source = @" +using System; + +class C +{ + private readonly int _x; + public C(int x) + { + _x = x; + } + + public static implicit operator C(int value) + { + return new C(value); + } + + public static implicit operator short(C c) + { + return (short)c._x; + } + + public static implicit operator string(C c) + { + return c._x.ToString(); + } + + public void M(C c1) + { + (short, string) t = /**/(new C(0), c1)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int16, System.String c1)) (Syntax: '(new C(0), c1)') + Elements(2): IConversionExpression (ConversionKind.OperatorMethod, Implicit) (OperatorMethod: System.Int16 C.op_Implicit(C c)) (OperationKind.ConversionExpression, Type: System.Int16) (Syntax: 'new C(0)') + IObjectCreationExpression (Constructor: C..ctor(System.Int32 x)) (OperationKind.ObjectCreationExpression, Type: C) (Syntax: 'new C(0)') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: '0') + ILiteralExpression (Text: 0) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + IConversionExpression (ConversionKind.OperatorMethod, Implicit) (OperatorMethod: System.String C.op_Implicit(C c)) (OperationKind.ConversionExpression, Type: System.String) (Syntax: 'c1') + IParameterReferenceExpression: c1 (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'c1') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_UserDefinedConversionFromTupleExpression() + { + string source = @" +using System; + +class C +{ + private readonly int _x; + public C(int x) + { + _x = x; + } + + public static implicit operator C((int, string) x) + { + return new C(x.Item1); + } + + public static implicit operator (int, string)(C c) + { + return (c._x, c._x.ToString()); + } + + public void M(C c1) + { + C t /**/= (0, null)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'C t /**/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'C t /**/;') + Variables: Local_1: C t + Initializer: IConversionExpression (ConversionKind.OperatorMethod, Implicit) (OperatorMethod: C C.op_Implicit((System.Int32, System.String) x)) (OperationKind.ConversionExpression, Type: C) (Syntax: '(0, null)') + IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32, System.String)) (Syntax: '(0, null)') + ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32, System.String)) (Syntax: '(0, null)') + Elements(2): ILiteralExpression (Text: 0) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + ILiteralExpression (Text: null) (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_UserDefinedConversionToTupleType() + { + string source = @" +using System; + +class C +{ + private readonly int _x; + public C(int x) + { + _x = x; + } + + public static implicit operator C((int, string) x) + { + return new C(x.Item1); + } + + public static implicit operator (int, string)(C c) + { + return (c._x, c._x.ToString()); + } + + public void M(C c1) + { + (int, string) t /**/= c1/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(int, strin ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(int, strin ... **/;') + Variables: Local_1: (System.Int32, System.String) t + Initializer: IConversionExpression (ConversionKind.OperatorMethod, Implicit) (OperatorMethod: (System.Int32, System.String) C.op_Implicit(C c)) (OperationKind.ConversionExpression, Type: (System.Int32, System.String)) (Syntax: 'c1') + IParameterReferenceExpression: c1 (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'c1') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_InvalidConversion() + { + string source = @" +using System; + +class C +{ + private readonly int _x; + public C(int x) + { + _x = x; + } + + public static implicit operator C(int value) + { + return new C(value); + } + + public static implicit operator int(C c) + { + return (short)c._x; + } + + public static implicit operator string(C c) + { + return c._x.ToString(); + } + + public void M(C c1) + { + /**/(short, string) t = (new C(0), c1);/**/ + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: '(short, str ... C(0), c1);') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: '(short, str ... C(0), c1);') + Variables: Local_1: (System.Int16, System.String) t + Initializer: IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.Int16, System.String), IsInvalid) (Syntax: '(new C(0), c1)') + ITupleExpression (OperationKind.TupleExpression, Type: (C, C c1)) (Syntax: '(new C(0), c1)') + Elements(2): IObjectCreationExpression (Constructor: C..ctor(System.Int32 x)) (OperationKind.ObjectCreationExpression, Type: C) (Syntax: 'new C(0)') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: '0') + ILiteralExpression (Text: 0) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + IParameterReferenceExpression: c1 (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'c1') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0029: Cannot implicitly convert type 'C' to 'short' + // /**/(short, string) t = (new C(0), c1);/**/ + Diagnostic(ErrorCode.ERR_NoImplicitConv, "new C(0)").WithArguments("C", "short").WithLocation(29, 40) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_Deconstruction() + { + string source = @" +class Point +{ + public int X { get; } + public int Y { get; } + + public Point(int x, int y) + { + X = x; + Y = y; + } + + public void Deconstruct(out int x, out int y) + { + x = X; + y = Y; + } +} + +class Class1 +{ + public void M() + { + /**/var (x, y) = new Point(0, 1)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'var (x, y) ... Point(0, 1)') + Children(2): ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32 x, System.Int32 y)) (Syntax: 'var (x, y)') + Elements(2): ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'x') + ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') + IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32 x, System.Int32 y)) (Syntax: 'new Point(0, 1)') + IObjectCreationExpression (Constructor: Point..ctor(System.Int32 x, System.Int32 y)) (OperationKind.ObjectCreationExpression, Type: Point) (Syntax: 'new Point(0, 1)') + Arguments(2): IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: '0') + ILiteralExpression (Text: 0) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + IArgument (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_DeconstructionWithConversion() + { + string source = @" +class Point +{ + public int X { get; } + public int Y { get; } + + public Point(int x, int y) + { + X = x; + Y = y; + } + + public void Deconstruct(out uint x, out uint y) + { + x = X; + y = Y; + } +} + +class Class1 +{ + public void M() + { + /**/(uint x, uint y) = new Point(0, 1)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '(uint x, ui ... Point(0, 1)') + Children(2): ITupleExpression (OperationKind.TupleExpression, Type: (System.UInt32 x, System.UInt32 y)) (Syntax: '(uint x, uint y)') + Elements(2): ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.UInt32) (Syntax: 'uint x') + ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.UInt32) (Syntax: 'uint y') + IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: (System.UInt32 x, System.UInt32 y)) (Syntax: 'new Point(0, 1)') + IObjectCreationExpression (Constructor: Point..ctor(System.Int32 x, System.Int32 y)) (OperationKind.ObjectCreationExpression, Type: Point) (Syntax: 'new Point(0, 1)') + Arguments(2): IArgument (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument) (Syntax: '0') + ILiteralExpression (Text: 0) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + IArgument (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument) (Syntax: '1') + ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0266: Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?) + // x = X; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "X").WithArguments("int", "uint").WithLocation(15, 13), + // CS0266: Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?) + // y = Y; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "Y").WithArguments("int", "uint").WithLocation(16, 13) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 3c79c5c114b41..3872525a6b3f3 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -116,6 +116,7 @@ + diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index faf2b2dde7043..c95a7f50f06be 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -3836,6 +3836,63 @@ public LazyTryStatement(Lazy body, Lazy _lazyFinallyHandler.Value; } + /// + /// Represents a tuple expression. + /// + internal abstract partial class BaseTupleExpression : Operation, ITupleExpression + { + protected BaseTupleExpression(bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : + base(OperationKind.TupleExpression, isInvalid, syntax, type, constantValue) + { + } + /// + /// Elements for tuple expression. + /// + public abstract ImmutableArray Elements { get; } + public override void Accept(OperationVisitor visitor) + { + visitor.VisitTupleExpression(this); + } + public override TResult Accept(OperationVisitor visitor, TArgument argument) + { + return visitor.VisitTupleExpression(this, argument); + } + } + + /// + /// Represents a tuple expression. + /// + internal sealed partial class TupleExpression : BaseTupleExpression, ITupleExpression + { + public TupleExpression(ImmutableArray elements, bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : + base(isInvalid, syntax, type, constantValue) + { + Elements = elements; + } + /// + /// Elements for tuple expression. + /// + public override ImmutableArray Elements { get; } + } + + /// + /// Represents a C# try or a VB Try statement. + /// + internal sealed partial class LazyTupleExpression : BaseTupleExpression, ITupleExpression + { + private readonly Lazy> _lazyElements; + + public LazyTupleExpression(Lazy> elements, bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : + base(isInvalid, syntax, type, constantValue) + { + _lazyElements = elements; + } + /// + /// Elements for tuple expression. + /// + public override ImmutableArray Elements => _lazyElements.Value; + } + /// /// Represents a TypeOf expression. /// diff --git a/src/Compilers/Core/Portable/Operations/IOperationKind.cs b/src/Compilers/Core/Portable/Operations/IOperationKind.cs index 9b271fb2afe68..f14884a519e56 100644 --- a/src/Compilers/Core/Portable/Operations/IOperationKind.cs +++ b/src/Compilers/Core/Portable/Operations/IOperationKind.cs @@ -126,6 +126,8 @@ public enum OperationKind ConditionalAccessExpression = 0x11c, /// Indicates an . ConditionalAccessInstanceExpression = 0x11d, + /// Indicates an . + TupleExpression = 0x11f, // Expressions that occur only in C#. diff --git a/src/Compilers/Core/Portable/Operations/ITupleExpression.cs b/src/Compilers/Core/Portable/Operations/ITupleExpression.cs new file mode 100644 index 0000000000000..39769c4034bd2 --- /dev/null +++ b/src/Compilers/Core/Portable/Operations/ITupleExpression.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.Semantics +{ + /// + /// Represents a tuple expression. + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface ITupleExpression : IOperation + { + /// + /// Elements for tuple expression. + /// + ImmutableArray Elements { get; } + } +} + diff --git a/src/Compilers/Core/Portable/Operations/OperationVisitor.cs b/src/Compilers/Core/Portable/Operations/OperationVisitor.cs index 317a2efc745ba..f5cced26b7c85 100644 --- a/src/Compilers/Core/Portable/Operations/OperationVisitor.cs +++ b/src/Compilers/Core/Portable/Operations/OperationVisitor.cs @@ -394,6 +394,11 @@ public virtual void VisitLocalFunctionStatement(IOperation operation) { DefaultVisit(operation); } + + public virtual void VisitTupleExpression(ITupleExpression operation) + { + DefaultVisit(operation); + } } /// @@ -794,5 +799,10 @@ public virtual TResult VisitLocalFunctionStatement(IOperation operation, TArgume { return DefaultVisit(operation, argument); } + + public virtual TResult VisitTupleExpression(ITupleExpression operation, TArgument argument) + { + return DefaultVisit(operation, argument); + } } } diff --git a/src/Compilers/Core/Portable/Operations/OperationWalker.cs b/src/Compilers/Core/Portable/Operations/OperationWalker.cs index f9798628922b0..6cf6f59864989 100644 --- a/src/Compilers/Core/Portable/Operations/OperationWalker.cs +++ b/src/Compilers/Core/Portable/Operations/OperationWalker.cs @@ -420,5 +420,10 @@ public override void VisitInvalidExpression(IInvalidExpression operation) { VisitArray(operation.Children); } + + public override void VisitTupleExpression(ITupleExpression operation) + { + VisitArray(operation.Elements); + } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 9364170ce5761..49b1ed6b34226 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -116,6 +116,7 @@ Microsoft.CodeAnalysis.OperationKind.SwitchStatement = 4 -> Microsoft.CodeAnalys Microsoft.CodeAnalysis.OperationKind.SyntheticLocalReferenceExpression = 263 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.ThrowStatement = 10 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TryStatement = 14 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.OperationKind.TupleExpression = 287 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TypeOfExpression = 513 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TypeParameterObjectCreationExpression = 275 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.UnaryOperatorExpression = 269 -> Microsoft.CodeAnalysis.OperationKind @@ -503,6 +504,8 @@ Microsoft.CodeAnalysis.Semantics.ITryStatement Microsoft.CodeAnalysis.Semantics.ITryStatement.Body.get -> Microsoft.CodeAnalysis.Semantics.IBlockStatement Microsoft.CodeAnalysis.Semantics.ITryStatement.Catches.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Semantics.ITryStatement.FinallyHandler.get -> Microsoft.CodeAnalysis.Semantics.IBlockStatement +Microsoft.CodeAnalysis.Semantics.ITupleExpression +Microsoft.CodeAnalysis.Semantics.ITupleExpression.Elements.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Semantics.ITypeOfExpression Microsoft.CodeAnalysis.Semantics.ITypeOperationExpression Microsoft.CodeAnalysis.Semantics.ITypeOperationExpression.TypeOperand.get -> Microsoft.CodeAnalysis.ITypeSymbol @@ -729,6 +732,7 @@ override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitSwitchStatement(M override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitSyntheticLocalReferenceExpression(Microsoft.CodeAnalysis.Semantics.ISyntheticLocalReferenceExpression operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitThrowStatement(Microsoft.CodeAnalysis.Semantics.IThrowStatement operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitTryStatement(Microsoft.CodeAnalysis.Semantics.ITryStatement operation) -> void +override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation) -> void override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitUnaryOperatorExpression(Microsoft.CodeAnalysis.Semantics.IUnaryOperatorExpression operation) -> void @@ -828,6 +832,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSwitchStatement(M virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSyntheticLocalReferenceExpression(Microsoft.CodeAnalysis.Semantics.ISyntheticLocalReferenceExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitThrowStatement(Microsoft.CodeAnalysis.Semantics.IThrowStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTryStatement(Microsoft.CodeAnalysis.Semantics.ITryStatement operation) -> void +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitUnaryOperatorExpression(Microsoft.CodeAnalysis.Semantics.IUnaryOperatorExpression operation) -> void @@ -904,6 +909,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSyntheticLocalReferenceExpression(Microsoft.CodeAnalysis.Semantics.ISyntheticLocalReferenceExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitThrowStatement(Microsoft.CodeAnalysis.Semantics.IThrowStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTryStatement(Microsoft.CodeAnalysis.Semantics.ITryStatement operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitUnaryOperatorExpression(Microsoft.CodeAnalysis.Semantics.IUnaryOperatorExpression operation, TArgument argument) -> TResult diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb index 92176d78b672a..6f464975bb8eb 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property End Class - Friend Partial Class BoundAttribute + Partial Friend Class BoundAttribute Protected Overrides ReadOnly Property Children As ImmutableArray(Of BoundNode) Get Return StaticCast(Of BoundNode).From(Me.ConstructorArguments.AddRange(Me.NamedArguments)) @@ -35,14 +35,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property End Class - Friend Partial MustInherit Class BoundTupleExpression - Protected Overrides ReadOnly Property Children As ImmutableArray(Of BoundNode) - Get - Return StaticCast(Of BoundNode).From(Me.Arguments) - End Get - End Property - End Class - Friend Partial Class BoundLateInvocation Protected Overrides ReadOnly Property Children As ImmutableArray(Of BoundNode) Get diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index 86de7a626feff..be189e5a6d7a8 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -161,6 +161,8 @@ Namespace Microsoft.CodeAnalysis.Semantics Return CreateBoundAddHandlerStatementOperation(DirectCast(boundNode, BoundAddHandlerStatement)) Case BoundKind.RemoveHandlerStatement Return CreateBoundRemoveHandlerStatementOperation(DirectCast(boundNode, BoundRemoveHandlerStatement)) + Case BoundKind.TupleLiteral, BoundKind.ConvertedTupleLiteral + Return CreateBoundTupleExpressionOperation(DirectCast(boundNode, BoundTupleExpression)) Case Else Dim constantValue = ConvertToOptional(TryCast(boundNode, BoundExpression)?.ConstantValueOpt) Return Operation.CreateOperationNone(boundNode.HasErrors, boundNode.Syntax, constantValue, Function() GetIOperationChildren(boundNode)) @@ -1016,6 +1018,15 @@ Namespace Microsoft.CodeAnalysis.Semantics Dim constantValue As [Optional](Of Object) = New [Optional](Of Object)() Return New LazyExpressionStatement(expression, isInvalid, syntax, type, constantValue) End Function + + Private Shared Function CreateBoundTupleExpressionOperation(boundTupleExpression As BoundTupleExpression) As ITupleExpression + Dim elements As New Lazy(Of ImmutableArray(Of IOperation))(Function() GetTupleElements(boundTupleExpression)) + Dim isInvalid As Boolean = boundTupleExpression.HasErrors + Dim syntax As SyntaxNode = boundTupleExpression.Syntax + Dim type As ITypeSymbol = boundTupleExpression.Type + Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundTupleExpression.ConstantValueOpt) + Return New LazyTupleExpression(elements, isInvalid, syntax, type, constantValue) + End Function End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb index 821880ca6b1b9..dc2b2796fbd53 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb @@ -489,6 +489,14 @@ Namespace Microsoft.CodeAnalysis.Semantics End Function) End Function + Private Shared ReadOnly s_tupleElementsMappings As New ConditionalWeakTable(Of BoundTupleExpression, Object)() + + Private Shared Function GetTupleElements(boundTupleExpression As BoundTupleExpression) As ImmutableArray(Of IOperation) + Return DirectCast(s_tupleElementsMappings.GetValue( + boundTupleExpression, + Function(tupleExpr) tupleExpr.Arguments.SelectAsArray(Function(element) Create(element))), ImmutableArray(Of IOperation)) + End Function + Friend Class Helper Friend Shared Function DeriveUnaryOperationKind(operatorKind As UnaryOperatorKind) As UnaryOperationKind Select Case operatorKind And UnaryOperatorKind.OpMask diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index c2ef6593119e5..680229f6ce057 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -109,6 +109,7 @@ + diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb index 0787c1bd2a34e..3a5ebb3007586 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb @@ -20,8 +20,8 @@ Class Class1 End Class]]>.Value Dim expectedOperationTree = + Public Sub TupleExpression_NoConversions() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_ImplicitConversions() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_ImplicitConversionFromNull() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_NamedArguments() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_NamedElementsInTupleType() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_NamedElementsAndImplicitConversions() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_UserDefinedConversionsForArguments() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_UserDefinedConversionFromTupleExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_UserDefinedConversionToTupleType() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_InvalidConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 4a0e6df980f3c..415045016ce53 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -1100,6 +1100,14 @@ public override void VisitRangeCaseClause(IRangeCaseClause operation) Visit(operation.MaximumValue, "Max"); } + public override void VisitTupleExpression(ITupleExpression operation) + { + LogString(nameof(ITupleExpression)); + LogCommonPropertiesAndNewLine(operation); + + VisitArray(operation.Elements, "Elements", logElementCount: true); + } + #endregion } } \ No newline at end of file diff --git a/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs b/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs index 08532f9ff3067..4cfa35c27db26 100644 --- a/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs +++ b/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs @@ -512,5 +512,10 @@ public override void VisitInvalidExpression(IInvalidExpression operation) { base.VisitInvalidExpression(operation); } + + public override void VisitTupleExpression(ITupleExpression operation) + { + base.VisitTupleExpression(operation); + } } } \ No newline at end of file