From 5590951276d69c84c2e9ad13f8b72282d0c2ac13 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Mon, 29 Jan 2024 00:53:29 +0000 Subject: [PATCH] Only cast for boxing when it's not implicit - fixes #1071 But since this removes some casts, do still ensure that predefined cast expressions always cast since otherwise casting to T via object gets broken --- CHANGELOG.md | 1 + CodeConverter/CSharp/ExpressionNodeVisitor.cs | 2 +- .../CSharp/TypeConversionAnalyzer.cs | 2 +- .../ExpressionTests/BinaryExpressionTests.cs | 2 +- .../CSharp/ExpressionTests/ExpressionTests.cs | 34 ++++++++++++++++--- .../ExpressionTests.cs | 2 +- .../MyNamespace.Static.2.Designer.cs | 2 +- .../MyNamespace.Static.2.Designer.cs | 2 +- .../MyNamespace.Static.2.Designer.cs | 2 +- 9 files changed, 38 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6d525e0..7153a3960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Conversion of explicit interface implementations now converts optional parameters [#1062](https://github.com/icsharpcode/CodeConverter/issues/1062) * Constant chars are converted to constant strings where needed * Select case for a mixture of strings and characters converts correctly [#1062](https://github.com/icsharpcode/CodeConverter/issues/1062) +* Implicit boxing conversion converted correctly to no-op [#1071](https://github.com/icsharpcode/CodeConverter/issues/1071) ### C# -> VB diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.cs index 5f4c899d7..b3abaf9ef 100644 --- a/CodeConverter/CSharp/ExpressionNodeVisitor.cs +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.cs @@ -291,7 +291,7 @@ public override async Task VisitPredefinedCastExpression(VBasi SyntaxFactory.Argument(expressionSyntax)))); } - var withConversion = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, expressionSyntax, false, false, forceTargetType: _semanticModel.GetTypeInfo(node).Type); + var withConversion = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, expressionSyntax, false, true, forceTargetType: _semanticModel.GetTypeInfo(node).Type); return node.ParenthesizeIfPrecedenceCouldChange(withConversion); // Use context of outer node, rather than just its exprssion, as the above method call would do if allowed to add parenthesis } diff --git a/CodeConverter/CSharp/TypeConversionAnalyzer.cs b/CodeConverter/CSharp/TypeConversionAnalyzer.cs index 37702e34b..38c360b09 100644 --- a/CodeConverter/CSharp/TypeConversionAnalyzer.cs +++ b/CodeConverter/CSharp/TypeConversionAnalyzer.cs @@ -311,7 +311,7 @@ private ITypeSymbol GetCSType(ITypeSymbol vbType, VBSyntax.ExpressionSyntax vbNo } else if (vbConversion.IsNumeric && (csConversion.IsNumeric || nullableVbConvertedType.IsEnumType()) && isConvertFractionalToInt) { typeConversionKind = TypeConversionKind.FractionalNumberRoundThenCast; return true; - } else if (csConversion.IsExplicit && csConversion.IsEnumeration || csConversion.IsBoxing) { + } else if (csConversion is {IsExplicit: true, IsEnumeration: true} or {IsBoxing: true, IsImplicit: false}) { typeConversionKind = TypeConversionKind.NonDestructiveCast; return true; } else if (vbConversion.IsNumeric && csConversion.IsNumeric) { diff --git a/Tests/CSharp/ExpressionTests/BinaryExpressionTests.cs b/Tests/CSharp/ExpressionTests/BinaryExpressionTests.cs index 9240c8f5e..ee2493d45 100644 --- a/Tests/CSharp/ExpressionTests/BinaryExpressionTests.cs +++ b/Tests/CSharp/ExpressionTests/BinaryExpressionTests.cs @@ -668,7 +668,7 @@ private object TestMethod() { bool? var1 = default; bool? var2 = default; - return (object)(var1.GetValueOrDefault() ? true : !var2 is not { } arg1 ? null : arg1 ? true : var1); + return var1.GetValueOrDefault() ? true : !var2 is not { } arg1 ? null : arg1 ? true : var1; } }"); } diff --git a/Tests/CSharp/ExpressionTests/ExpressionTests.cs b/Tests/CSharp/ExpressionTests/ExpressionTests.cs index 7ceff4eb2..4e7670058 100644 --- a/Tests/CSharp/ExpressionTests/ExpressionTests.cs +++ b/Tests/CSharp/ExpressionTests/ExpressionTests.cs @@ -80,6 +80,33 @@ public bool IsPointWithinBoundaryBox(double dblLat, double dblLon, object boundb }"); } + [Fact] + public async Task DynamicAccessAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Option Strict Off ' Directive gets removed + +Public Class TestDynamicUsage + Property Prop As Integer + + Sub S() + Dim o As Object + o = New TestDynamicUsage + o.Prop = 1 'Must not cast to object here + End Sub +End Class", @" +public partial class TestDynamicUsage +{ + public int Prop { get; set; } + + public void S() + { + object o; + o = new TestDynamicUsage(); + ((dynamic)o).Prop = 1; // Must not cast to object here + } +}"); + } + [Fact] public async Task ConversionOfNotUsesParensIfNeededAsync() { @@ -267,12 +294,11 @@ public partial class VisualBasicClass public void Rounding() { object o = 3.0f; - var x = Math.Round(o, (object)2); + var x = Math.Round(o, 2); } } -2 target compilation errors: -CS1503: Argument 1: cannot convert from 'object' to 'double' -CS1503: Argument 2: cannot convert from 'object' to 'int'"); +1 target compilation errors: +CS1503: Argument 1: cannot convert from 'object' to 'double'"); } [Fact] diff --git a/Tests/CSharp/MissingSemanticModelInfo/ExpressionTests.cs b/Tests/CSharp/MissingSemanticModelInfo/ExpressionTests.cs index 5a8d26e07..32b73591e 100644 --- a/Tests/CSharp/MissingSemanticModelInfo/ExpressionTests.cs +++ b/Tests/CSharp/MissingSemanticModelInfo/ExpressionTests.cs @@ -391,7 +391,7 @@ public void Test() { SomeUnknownType x = default; int y = 3; - if (x == null || (object)y == null) + if (x == null || y == null) { } diff --git a/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertVbLibraryOnly/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs b/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertVbLibraryOnly/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs index bd7913e6f..7ff83277c 100644 --- a/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertVbLibraryOnly/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs +++ b/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertVbLibraryOnly/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs @@ -1624,7 +1624,7 @@ public static T ToGenericParameter(object Value) } else if (Equals(reflectedType, typeof(string))) { - return (T)ToString(Value); + return (T)(object)ToString(Value); } else { diff --git a/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs b/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs index bd7913e6f..7ff83277c 100644 --- a/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs +++ b/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbLibrary/My Project/MyNamespace.Static.2.Designer.cs @@ -1624,7 +1624,7 @@ public static T ToGenericParameter(object Value) } else if (Equals(reflectedType, typeof(string))) { - return (T)ToString(Value); + return (T)(object)ToString(Value); } else { diff --git a/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbNetStandardLib/My Project/MyNamespace.Static.2.Designer.cs b/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbNetStandardLib/My Project/MyNamespace.Static.2.Designer.cs index bd7913e6f..7ff83277c 100644 --- a/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbNetStandardLib/My Project/MyNamespace.Static.2.Designer.cs +++ b/Tests/TestData/MultiFileCharacterization/VBToCSResults/ConvertWholeSolution/VbNetStandardLib/My Project/MyNamespace.Static.2.Designer.cs @@ -1624,7 +1624,7 @@ public static T ToGenericParameter(object Value) } else if (Equals(reflectedType, typeof(string))) { - return (T)ToString(Value); + return (T)(object)ToString(Value); } else {