diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index eadedbb2740344fafc537c36182794267398be63..42648bd0353c2b8a2a65e3ce422072171cbe01d6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -94,7 +94,7 @@ internal partial class Binder } if (conversion.IsTupleLiteralConversion || - (conversion.Kind == ConversionKind.ImplicitNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) + (conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) { return CreateTupleLiteralConversion(syntax, (BoundTupleLiteral)source, conversion, isCast, destination, diagnostics); } @@ -343,12 +343,12 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup { // We have a successful tuple conversion; rather than producing a separate conversion node // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments. - Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType()); + Debug.Assert(conversion.IsNullable == destination.IsNullableType()); var destinationWithoutNullable = destination; var conversionWithoutNullable = conversion; - if (conversion.Kind == ConversionKind.ImplicitNullable) + if (conversion.IsNullable) { destinationWithoutNullable = destination.GetNullableUnderlyingType(); conversionWithoutNullable = conversion.UnderlyingConversions[0]; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index c783ac88ae433760caf2553c81a73eb8831d7c3e..d50de799103477460982da7e0403f09d5c22aad2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -3009,6 +3009,14 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa } return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); + + case BoundKind.TupleLiteral: + if ((object)operand.Type == null) + { + Error(diagnostics, ErrorCode.ERR_TypelessTupleInAs, node); + return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); + } + break; } if (operand.HasAnyErrors || targetTypeKind == TypeKind.Error) @@ -3073,12 +3081,6 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa type: GetSpecialType(SpecialType.System_Object, diagnostics, node)); } - if (operand.Kind == BoundKind.MethodGroup) - { - Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, MessageID.IDS_MethodGroup.Localize(), targetType); - return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); - } - var operandType = operand.Type; Debug.Assert((object)operandType != null); var operandTypeKind = operandType.TypeKind; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 7a1975a98f5f2d5aeee3a9d91c2534093fdd1bde..65d87f9cc823228c76954abaf9d7e029e827d251 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -9151,6 +9151,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to The first operand of an 'as' operator may not be a tuple literal without a natural type.. + /// + internal static string ERR_TypelessTupleInAs { + get { + return ResourceManager.GetString("ERR_TypelessTupleInAs", resourceCulture); + } + } + /// /// Looks up a localized string similar to Type parameter declaration must be an identifier not a type. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 9227afe9d86e806e4369852a1c8969cfe97905ce..13a64ea0367ed764d5afcdf8f17fbad8b500037d 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2165,6 +2165,9 @@ If such a class is used as a base class and if the deriving class defines a dest The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group. + + The first operand of an 'as' operator may not be a tuple literal without a natural type. + An expression tree may not contain a multidimensional array initializer diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 40cbe2de7be995367e407f34fbe9648f2ba005a5..6c6cbeb3ee268f32307f12d3cfb496b61dcaecc4 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1475,6 +1475,7 @@ internal enum ErrorCode ERR_CompilerAndLanguageVersion = 8304, WRN_Experimental = 8305, ERR_TupleInferredNamesNotAvailable = 8306, + ERR_TypelessTupleInAs = 8307, #region diagnostics for C# 7.1 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 2032304befd1c88b3141c3bced54078db05e3594..4448de074facf0ec05df2ced93b5ba0151eb9848 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -6605,6 +6605,176 @@ static void Main() ); } + [Fact] + public void TupleExplicitNullableConversionWithTypelessTuple() + { + var source = @" +class C +{ + static void Main() + { + var x = ((int, string)?) (1, null); + System.Console.Write(x); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(1, )"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var declaration = tree.GetRoot().DescendantNodes().OfType().First(); + var value = declaration.Declaration.Variables.First().Initializer.Value; + Assert.Equal("((int, string)?) (1, null)", value.ToString()); + var castConversion = model.GetConversion(value); + Assert.Equal(ConversionKind.Identity, castConversion.Kind); + var tuple = ((CastExpressionSyntax)value).Expression; + Assert.Equal("(1, null)", tuple.ToString()); + var tupleConversion = model.GetConversion(tuple); + Assert.Equal(ConversionKind.ExplicitNullable, tupleConversion.Kind); + Assert.Equal(1, tupleConversion.UnderlyingConversions.Length); + Assert.Equal(ConversionKind.ExplicitTupleLiteral, tupleConversion.UnderlyingConversions[0].Kind); + } + + [Fact] + public void TupleImplicitNullableConversionWithTypelessTuple() + { + var source = @" +class C +{ + static void Main() + { + (int, string)? x = (1, null); + System.Console.Write(x); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(1, )"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var declaration = tree.GetRoot().DescendantNodes().OfType().First(); + var value = declaration.Declaration.Variables.First().Initializer.Value; + Assert.Equal("(1, null)", value.ToString()); + var tupleConversion = model.GetConversion(value); + Assert.Equal(ConversionKind.ImplicitNullable, tupleConversion.Kind); + Assert.Equal(1, tupleConversion.UnderlyingConversions.Length); + Assert.Equal(ConversionKind.ImplicitTupleLiteral, tupleConversion.UnderlyingConversions[0].Kind); + } + + [Fact] + public void TupleImplicitNullableAndCustomConversionsWithTypelessTuple() + { + var source = @" +struct C +{ + static void Main() + { + C? x = (1, null); + System.Console.Write(x); + var x2 = (C)(2, null); + System.Console.Write(x2); + var x3 = (C?)(3, null); + System.Console.Write(x3); + } + public static implicit operator C((int, string) x) + { + return new C(); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "CCC"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tuples = tree.GetRoot().DescendantNodes().OfType(); + + var tuple1 = tuples.ElementAt(0); + Assert.Equal("(1, null)", tuple1.ToString()); + Assert.Null(model.GetTypeInfo(tuple1).Type); + Assert.Equal("C?", model.GetTypeInfo(tuple1).ConvertedType.ToTestDisplayString()); + + var conversion1 = model.GetConversion(tuple1); + Assert.Equal(ConversionKind.ImplicitUserDefined, conversion1.Kind); + Assert.True(conversion1.UnderlyingConversions.IsDefault); + + var tuple2 = tuples.ElementAt(1); + Assert.Equal("(2, null)", tuple2.ToString()); + Assert.Null(model.GetTypeInfo(tuple2).Type); + Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(tuple2).ConvertedType.ToTestDisplayString()); + + var conversion2 = model.GetConversion(tuple2); + Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion2.Kind); + Assert.False(conversion2.UnderlyingConversions.IsDefault); + + var tuple3 = tuples.ElementAt(2); + Assert.Equal("(3, null)", tuple3.ToString()); + Assert.Null(model.GetTypeInfo(tuple3).Type); + Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(tuple3).ConvertedType.ToTestDisplayString()); + + var conversion3 = model.GetConversion(tuple3); + Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion3.Kind); + Assert.False(conversion3.UnderlyingConversions.IsDefault); + } + + [Fact] + public void TupleImplicitNullableAndCustomConversionsWithTypelessTupleInAsOperator() + { + var source = @" +struct C +{ + static void Main() + { + var x4 = (1, null) as C; + System.Console.Write(x4); + var x5 = (2, null) as C?; + System.Console.Write(x5); + } + public static implicit operator C((int, string) x) + { + return new C(); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (6,18): error CS8305: The first operand of an 'as' operator may not be a tuple literal without a natural type. + // var x4 = (1, null) as C; + Diagnostic(ErrorCode.ERR_TypelessTupleInAs, "(1, null) as C").WithLocation(6, 18), + // (8,18): error CS8305: The first operand of an 'as' operator may not be a tuple literal without a natural type. + // var x5 = (2, null) as C?; + Diagnostic(ErrorCode.ERR_TypelessTupleInAs, "(2, null) as C?").WithLocation(8, 18) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tuples = tree.GetRoot().DescendantNodes().OfType(); + verifyTuple("(1, null)", tuples.ElementAt(0)); + verifyTuple("(2, null)", tuples.ElementAt(1)); + + void verifyTuple(string expected, TupleExpressionSyntax tuple) + { + Assert.Equal(expected, tuple.ToString()); + Assert.Null(model.GetTypeInfo(tuple).Type); + Assert.Null(model.GetTypeInfo(tuple).ConvertedType); + + var conversion = model.GetConversion(tuple); + Assert.Equal(ConversionKind.Identity, conversion.Kind); + Assert.True(conversion.UnderlyingConversions.IsDefault); + } + } + [Fact] public void TupleTargetTypeLambda() { @@ -7069,8 +7239,8 @@ static void Main() Assert.Equal(@"(e: 1, f: ""hello"")", node.ToString()); Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).Type.ToTestDisplayString()); - Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.Identity, model.GetConversion(node)); + Assert.Equal("(System.Int16 c, System.String d)?", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + Assert.Equal(ConversionKind.ExplicitNullable, model.GetConversion(node).Kind); // semantic model returns topmost conversion from the sequence of conversions for // ((short c, string d)?)(e: 1, f: ""hello"") @@ -7295,8 +7465,8 @@ static void Main() Assert.Equal(@"(e: 1, f: ""hello"")", node.ToString()); Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).Type.ToTestDisplayString()); - Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.Identity, model.GetConversion(node)); + Assert.Equal("(System.Int32 c, System.String d)?", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + Assert.Equal(ConversionKind.ExplicitNullable, model.GetConversion(node).Kind); // semantic model returns topmost conversion from the sequence of conversions for // ((int c, string d)?)(e: 1, f: ""hello"") @@ -22680,5 +22850,27 @@ public struct ValueTuple Assert.False(tuple3.IsErrorType()); Assert.Equal(libWithVTRef.Display, tuple3.ContainingAssembly.MetadataName.ToString()); } + + [Fact] + [WorkItem(17962, "https://github.com/dotnet/roslyn/issues/17962")] + public void TupleWithAsOperator() + { + var source = @" +class C +{ + void M() + { + var x = (0, null) as (int, T)?; + System.Console.WriteLine(x == null); + } +}"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + comp.VerifyDiagnostics( + // (6,17): error CS8304: The first operand of an 'as' operator may not be a tuple literal without a natural type. + // var x = (0, null) as (int, T)?; + Diagnostic(ErrorCode.ERR_TypelessTupleInAs, "(0, null) as (int, T)?").WithLocation(6, 17) + ); + } } } \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb index 05b2da99f587c9bf61cb25069814970419763204..63170568905e5d2e4130fb3dde3bbc24cfc2b438 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb @@ -7863,6 +7863,336 @@ BC30512: Option Strict On disallows implicit conversions from 'Double' to 'Strin End Sub + + Public Sub TupleCTypeNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertNoDiagnostics() + CompileAndVerify(comp, expectedOutput:="(1, )") + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningNullableTuple, model.GetConversion(node).Kind) + + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleDirectCastNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertNoDiagnostics() + CompileAndVerify(comp, expectedOutput:="(1, )") + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningNullableTuple, model.GetConversion(node).Kind) + + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleTryCastNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30792: 'TryCast' operand must be reference type, but '(Integer, String)?' is a value type. + Dim x As (Integer, String)? = TryCast((1, Nothing), (Integer, String)?) + ~~~~~~~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(node).Kind) + + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleTryCastNullableConversionWithTypelessTuple2() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C(Of Integer, T)'. + Dim x = TryCast((0, Nothing), C(Of Integer, T)) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(0, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(node).Kind) + + Assert.Equal("C(Of System.Int32, T)", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("C(Of System.Int32, T)", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleImplicitNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertNoDiagnostics() + CompileAndVerify(comp, expectedOutput:="(1, )") + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningNullableTuple, model.GetConversion(node).Kind) + + End Sub + + + Public Sub ImplicitConversionOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. + Dim y As C? = (2, Nothing) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("C", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.Narrowing Or ConversionKind.UserDefined, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("System.Nullable(Of C)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.DelegateRelaxationLevelNone, model.GetConversion(secondTuple).Kind) + + End Sub + + + Public Sub DirectCastOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C'. + Dim x = DirectCast((1, Nothing), C) + ~~~~~~~~~~~~ +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. + Dim y = DirectCast((2, Nothing), C?) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(secondTuple).Kind) + + End Sub + + + Public Sub TryCastOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30792: 'TryCast' operand must be reference type, but 'C' is a value type. + Dim x = TryCast((1, Nothing), C) + ~ +BC30792: 'TryCast' operand must be reference type, but 'C?' is a value type. + Dim y = TryCast((2, Nothing), C?) + ~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(secondTuple).Kind) + + End Sub + + + Public Sub CTypeOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. + Dim y = CType((2, Nothing), C?) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(secondTuple).Kind) + + End Sub + Public Sub TupleTargetTypeLambda() @@ -8261,6 +8591,35 @@ End Module End Sub + + + + Public Sub GetSymbolInfo_01() + Dim source = " + Class C + Shared Sub Main() + Dim x1 = (Alice:=1, ""hello"") + + Dim Alice = x1.Alice + End Sub +End Class + " + + Dim tree = Parse(source, options:=TestOptions.Regular) + Dim comp = CreateCompilationWithMscorlib(tree) + + Dim model = comp.GetSemanticModel(tree, ignoreAccessibility:=False) + Dim nodes = tree.GetCompilationUnitRoot().DescendantNodes() + + Dim nc = nodes.OfType(Of NameColonEqualsSyntax)().ElementAt(0) + + Dim sym = model.GetSymbolInfo(nc.Name) + + Assert.Equal("Alice", sym.Symbol.Name) + Assert.Equal(SymbolKind.Field, sym.Symbol.Kind) ' Incorrectly returns Local + Assert.Equal(nc.Name.GetLocation(), sym.Symbol.Locations(0)) ' Incorrect location + End Sub + Public Sub RetargetTupleErrorType() Dim libComp = CreateCompilationWithMscorlibAndVBRuntime( diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb index ee0735d40dc86a7abd4fcc9ec35be7c49f4204d6..5d369567f276e7f47111f89c0631f07de3a45201 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb @@ -4397,6 +4397,29 @@ Class C } End Sub End Class +" + Await TestInRegularAndScriptAsync(code, expected, ignoreTrivia:=False) + End Function + + + + Public Async Function TupleElementNameIsNotReplaced() As Task + ' The name of the named element has bad symbol info and gets replaced with (1 + 2) + Dim code = " +Class C + Sub M() + Dim [||]i = 1 + 2 + Dim t = (i, i:=3) + End Sub +End Class +" + + Dim expected = " +Class C + Sub M() + Dim t = (1 + 2, i:=3) + End Sub +End Class " Await TestInRegularAndScriptAsync(code, expected, ignoreTrivia:=False) End Function