From 50fee94d7d1cdbd9daf6b493eca2ba6e028becb1 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 17 May 2017 14:58:00 -0700 Subject: [PATCH 1/5] Add IOperation support for tuple expressions Fixes #10856 ``` public interface ITupleExpression : IOperation { /// /// Elements for tuple expression. /// ImmutableArray Elements { get; } } ``` --- .../CSharp/Portable/BoundTree/Expression.cs | 10 - .../Operations/CSharpOperationFactory.cs | 13 + .../CSharpOperationFactory_Methods.cs | 9 + .../CSharpCompilerSemanticTest.csproj | 1 + ...tionTests_IParameterReferenceExpression.cs | 8 +- .../IOperationTests_ITupleExpression.cs | 473 ++++++++++++++++++ .../Core/Portable/CodeAnalysis.csproj | 1 + .../Generated/Operations.xml.Generated.cs | 57 +++ .../Portable/Operations/IOperationKind.cs | 2 + .../Portable/Operations/ITupleExpression.cs | 22 + .../Portable/Operations/OperationVisitor.cs | 10 + .../Portable/Operations/OperationWalker.cs | 5 + .../Core/Portable/PublicAPI.Unshipped.txt | 6 + .../Portable/BoundTree/Expression.vb | 10 +- .../Operations/VisualBasicOperationFactory.vb | 11 + .../VisualBasicOperationFactory_Methods.vb | 8 + .../Semantic/BasicCompilerSemanticTest.vbproj | 1 + ...tionTests_IParameterReferenceExpression.vb | 4 +- .../IOperationTests_ITupleExpression.vb | 330 ++++++++++++ .../Compilation/OperationTreeVerifier.cs | 8 + .../Compilation/TestOperationWalker.cs | 5 + 21 files changed, 969 insertions(+), 25 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs create mode 100644 src/Compilers/Core/Portable/Operations/ITupleExpression.cs create mode 100644 src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb 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 From b240fdde0b2f825076102d84aa153c82ab56e03a Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 10 Jul 2017 12:37:19 -0700 Subject: [PATCH 2/5] Address PR feedback from Aleksey and add unit tests for both the tuple expression and parenting variable declaration so we test the operation tree for tuple conversions. --- .../IOperationTests_ITupleExpression.cs | 402 +++++++++++++++++- .../Operations/VisualBasicOperationFactory.vb | 3 +- .../IOperationTests_ITupleExpression.vb | 335 +++++++++++++++ 3 files changed, 724 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs index 2b99c9ca0fc1a..67caa981cf8fa 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs @@ -35,6 +35,34 @@ static void Main() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NoConversions_ParentVariableDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + /**/(int, int) t = (1, 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(int, int) ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(int, int) ... **/;') + Variables: Local_1: (System.Int32, System.Int32) t + Initializer: 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() { @@ -62,6 +90,37 @@ static void Main() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_ImplicitConversions_ParentVariableDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + /**/(uint, uint) t = (1, 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(uint, uint ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(uint, uint ... **/;') + Variables: Local_1: (System.UInt32, System.UInt32) t + Initializer: IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.UInt32, System.UInt32)) (Syntax: '(1, 2)') + 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() { @@ -90,7 +149,38 @@ static void Main() } [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] - public void TupleExpression_NamedArguments() + public void TupleExpression_ImplicitConversionFromNull_ParentVariableDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + /**/(uint, string) t = (1, null)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(uint, stri ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(uint, stri ... **/;') + Variables: Local_1: (System.UInt32, System.String) t + Initializer: IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.UInt32, System.String)) (Syntax: '(1, null)') + 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_NamedElements() { string source = @" using System; @@ -114,6 +204,34 @@ static void Main() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NamedElements_ParentVariableDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + /**/var t = (A: 1, B: 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var t = (A: ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var t = (A: ... **/;') + Variables: Local_1: (System.Int32 A, System.Int32 B) t + Initializer: 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() { @@ -139,6 +257,35 @@ static void Main() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NamedElementsInTupleType_ParentVariableDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + /**/(int A, int B) t = (1, 2)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(int A, int ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(int A, int ... **/;') + Variables: Local_1: (System.Int32 A, System.Int32 B) t + Initializer: IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32 A, System.Int32 B)) (Syntax: '(1, 2)') + 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() { @@ -173,6 +320,44 @@ static void Main() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_NamedElementsAndImplicitConversions_ParentVariableDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + /**/(short, string) t = (A: 1, B: null)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(short, str ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(short, str ... **/;') + Variables: Local_1: (System.Int16, System.String) t + Initializer: IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.Int16, System.String)) (Syntax: '(A: 1, B: null)') + 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() { @@ -223,6 +408,60 @@ public void M(C c1) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_UserDefinedConversionsForArguments_ParentVariableDeclaration() + { + 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 = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(short, str ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(short, str ... **/;') + Variables: Local_1: (System.Int16, System.String) t + Initializer: IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.Int16, System.String)) (Syntax: '(new C(0), c1)') + 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() { @@ -242,21 +481,63 @@ public static implicit operator C((int, string) x) return new C(x.Item1); } - public static implicit operator (int, string)(C c) + public static implicit operator (int, string) (C c) { return (c._x, c._x.ToString()); } public void M(C c1) { - C t /**/= (0, null)/**/; + 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 /**/;') +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_UserDefinedConversionFromTupleExpression_ParentVariableDeclaration() + { + 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 = (0, n ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'C t = (0, n ... **/;') 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)') @@ -267,7 +548,7 @@ public void M(C c1) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] @@ -289,14 +570,53 @@ public static implicit operator C((int, string) x) return new C(x.Item1); } - public static implicit operator (int, string)(C c) + 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 = @" +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_UserDefinedConversionToTupleType_ParentVariableDeclaration() + { + 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/**/; + /**/(int, string) t = c1/**/; Console.WriteLine(t); } } @@ -310,7 +630,7 @@ public void M(C c1) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] @@ -344,16 +664,68 @@ public static implicit operator string(C c) public void M(C c1) { - /**/(short, string) t = (new C(0), c1);/**/ + (short, string) t = /**/(new C(0), c1)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +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_InvalidConversion_ParentVariableDeclaration() + { + 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);') +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: '(short, str ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: '(short, str ... **/;') 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)') + Initializer: IConversionExpression (ConversionKind.Invalid, 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') @@ -362,11 +734,11 @@ public void M(C c1) "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0029: Cannot implicitly convert type 'C' to 'short' - // /**/(short, string) t = (new C(0), c1);/**/ + // /**/(short, string) t = (new C(0), c1)/**/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "new C(0)").WithArguments("C", "short").WithLocation(29, 40) }; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index f785961666671..d55a564bb891e 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -163,7 +163,8 @@ Namespace Microsoft.CodeAnalysis.Semantics Return CreateBoundAddHandlerStatementOperation(DirectCast(boundNode, BoundAddHandlerStatement)) Case BoundKind.RemoveHandlerStatement Return CreateBoundRemoveHandlerStatementOperation(DirectCast(boundNode, BoundRemoveHandlerStatement)) - Case BoundKind.TupleLiteral, BoundKind.ConvertedTupleLiteral + Case BoundKind.TupleLiteral, + BoundKind.ConvertedTupleLiteral Return CreateBoundTupleExpressionOperation(DirectCast(boundNode, BoundTupleExpression)) Case BoundKind.InterpolatedStringExpression Return CreateBoundInterpolatedStringExpressionOperation(DirectCast(boundNode, BoundInterpolatedStringExpression)) diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb index 780662859e5a4..5fb9565485cd3 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb @@ -30,6 +30,31 @@ ITupleExpression (OperationKind.TupleExpression, Type: (System.Int32, System.Int VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + Public Sub TupleExpression_NoConversions_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_ImplicitConversions() Dim source = + Public Sub TupleExpression_ImplicitConversions_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_ImplicitConversionFromNull() Dim source = + Public Sub TupleExpression_ImplicitConversionFromNull_ParentVaraibleDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_NamedArguments() Dim source = + Public Sub TupleExpression_NamedArguments_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_NamedElementsInTupleType() Dim source = + Public Sub TupleExpression_NamedElementsInTupleType_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_NamedElementsAndImplicitConversions() Dim source = + Public Sub TupleExpression_NamedElementsAndImplicitConversions_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_UserDefinedConversionsForArguments() Dim source = + Public Sub TupleExpression_UserDefinedConversionsForArguments_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_UserDefinedConversionFromTupleExpression() Dim source = + Public Sub TupleExpression_UserDefinedConversionFromTupleExpression_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of LocalDeclarationStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub TupleExpression_UserDefinedConversionToTupleType() Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of IdentifierNameSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_UserDefinedConversionToTupleType_ParentVariableDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub TupleExpression_InvalidConversion_ParentVariableDeclaration() + Dim source = Date: Fri, 14 Jul 2017 11:00:00 -0700 Subject: [PATCH 3/5] Address PR feedback and add unit tests --- .../IOperationTests_ITupleExpression.cs | 125 ++++++++++++++++-- .../Generated/Operations.xml.Generated.cs | 2 +- .../IOperationTests_ITupleExpression.vb | 2 +- 3 files changed, 117 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs index 67caa981cf8fa..9f086dfd217fe 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ITupleExpression.cs @@ -121,6 +121,68 @@ static void Main() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_ImplicitConversionsWithTypedExpression() + { + string source = @" +using System; + +class C +{ + static void Main() + { + int a = 1; + int b = 2; + (long, long) t = /**/(a, b)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +ITupleExpression (OperationKind.TupleExpression, Type: (System.Int64 a, System.Int64 b)) (Syntax: '(a, b)') + Elements(2): IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Int64) (Syntax: 'a') + ILocalReferenceExpression: a (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'a') + IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Int64) (Syntax: 'b') + ILocalReferenceExpression: b (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'b') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_ImplicitConversionsWithTypedExpression_WithParentDeclaration() + { + string source = @" +using System; + +class C +{ + static void Main() + { + int a = 1; + int b = 2; + /**/(long, long) t = (a, b)/**/; + Console.WriteLine(t); + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: '(long, long ... **/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(long, long ... **/;') + Variables: Local_1: (System.Int64, System.Int64) t + Initializer: IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: (System.Int64, System.Int64)) (Syntax: '(a, b)') + ITupleExpression (OperationKind.TupleExpression, Type: (System.Int64 a, System.Int64 b)) (Syntax: '(a, b)') + Elements(2): IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Int64) (Syntax: 'a') + ILocalReferenceExpression: a (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'a') + IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Int64) (Syntax: 'b') + ILocalReferenceExpression: b (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'b') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] public void TupleExpression_ImplicitConversionFromNull() { @@ -788,6 +850,56 @@ public void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] + public void TupleExpression_Deconstruction_ForEach() + { + 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 = 0; + y = 0; + } +} + +class Class1 +{ + public void M() + { + /**/foreach (var (x, y) in new Point[]{ new Point(0, 1) }) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +IForEachLoopStatement (Iteration variable: null) (LoopKind.ForEach) (OperationKind.LoopStatement) (Syntax: 'foreach (va ... }') + Collection: IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.Collections.IEnumerable) (Syntax: 'new Point[] ... int(0, 1) }') + IArrayCreationExpression (Element Type: Point) (OperationKind.ArrayCreationExpression, Type: Point[]) (Syntax: 'new Point[] ... int(0, 1) }') + Dimension Sizes(1): ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: 'new Point[] ... int(0, 1) }') + Initializer: IArrayInitializer (1 elements) (OperationKind.ArrayInitializer) (Syntax: '{ new Point(0, 1) }') + Element Values(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') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + [Fact, WorkItem(10856, "https://github.com/dotnet/roslyn/issues/10856")] public void TupleExpression_DeconstructionWithConversion() { @@ -805,8 +917,8 @@ public Point(int x, int y) public void Deconstruct(out uint x, out uint y) { - x = X; - y = Y; + x = 0; + y = 0; } } @@ -830,14 +942,7 @@ public void M() 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) - }; + var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index 8c6d9e01f9012..a55e7557e2903 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -4256,7 +4256,7 @@ public TupleExpression(ImmutableArray elements, bool isInvalid, Synt } /// - /// Represents a C# try or a VB Try statement. + /// Represents a tuple expression. /// internal sealed partial class LazyTupleExpression : BaseTupleExpression, ITupleExpression { diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb index 5fb9565485cd3..1bd5843155449 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ITupleExpression.vb @@ -134,7 +134,7 @@ IConversionExpression (ConversionKind.Basic, Implicit) (OperationKind.Conversion End Sub - Public Sub TupleExpression_ImplicitConversionFromNull_ParentVaraibleDeclaration() + Public Sub TupleExpression_ImplicitConversionFromNull_ParentVariableDeclaration() Dim source = Date: Sat, 15 Jul 2017 17:25:48 -0700 Subject: [PATCH 4/5] Fix build break --- .../Portable/Generated/Operations.xml.Generated.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index 5b56c18dcdef6..5d56ccb3003aa 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -4771,6 +4771,16 @@ protected BaseTupleExpression(bool isInvalid, SyntaxNode syntax, ITypeSymbol typ /// Elements for tuple expression. /// public abstract ImmutableArray Elements { get; } + public override IEnumerable Children + { + get + { + foreach (var element in Elements) + { + yield return element; + } + } + } public override void Accept(OperationVisitor visitor) { visitor.VisitTupleExpression(this); From 1d7fd67181914d1c359bcd49cb384b286709a247 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 17 Jul 2017 11:06:47 -0700 Subject: [PATCH 5/5] Address feedback: tuples cannot have constant value --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 2 +- .../Portable/Operations/VisualBasicOperationFactory.vb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 7b6cc64dc82b4..e1a81a888e2cf 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1197,7 +1197,7 @@ private ITupleExpression CreateBoundTupleExpressionOperation(BoundTupleExpressio Lazy> elements = new Lazy>(() => boundTupleExpression.Arguments.SelectAsArray(element => Create(element))); SyntaxNode syntax = boundTupleExpression.Syntax; ITypeSymbol type = boundTupleExpression.Type; - Optional constantValue = ConvertToOptional(boundTupleExpression.ConstantValue); + Optional constantValue = default(Optional); return new LazyTupleExpression(elements, syntax, type, constantValue); } diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index 5e3a03a18ee7d..6d1bb62aee84d 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -1015,7 +1015,7 @@ Namespace Microsoft.CodeAnalysis.Semantics Dim elements As New Lazy(Of ImmutableArray(Of IOperation))(Function() boundTupleExpression.Arguments.SelectAsArray(Function(element) Create(element))) Dim syntax As SyntaxNode = boundTupleExpression.Syntax Dim type As ITypeSymbol = boundTupleExpression.Type - Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundTupleExpression.ConstantValueOpt) + Dim constantValue As [Optional](Of Object) = Nothing Return New LazyTupleExpression(elements, syntax, type, constantValue) End Function