icsharpcode / CodeConverter

Convert code from C# to VB.NET and vice versa using Roslyn
https://icsharpcode.github.io/CodeConverter/
MIT License
826 stars 216 forks source link

VB -> C#: `Select Case [object]` can have incorrect converted code #1100

Closed TymurGubayev closed 1 month ago

TymurGubayev commented 5 months ago

VB.Net input code

Option Strict Off

Public Class TestSelectObjectCaseInt
    Enum E
        A
        B
        C
    End Enum
    Shared Sub Test()
        Dim o As Object
        Dim j As Integer

        o = E.C
        Select Case o
            Case 1
                j = 1
            Case 2
                j = 2
            Case Else
                j = -1
        End Select
        System.Diagnostics.Debug.Assert(j = 2)
    End Sub
End Class

Erroneous output

public class TestSelectObjectCaseInt
{
    public enum E
    {
        A,
        B,
        C
    }
    public static void Test()
    {
        object o;
        int j;

        o = E.C;
        switch (o)
        {
            case 1:
                {
                    j = 1;
                    break;
                }

            case 2:
                {
                    j = 2;
                    break;
                }

            default:
                {
                    j = -1;
                    break;
                }
        }
        System.Diagnostics.Debug.Assert(j == 2);
    }
}

Expected output

public class TestSelectObjectCaseInt
{
    public enum E
    {
        A,
        B,
        C
    }
    public static void Test()
    {
        object o;
        int j;

        o = E.C;
        switch (o is int ? (int)o : o)
        {
            case 1:
                {
                    j = 1;
                    break;
                }

            case 2:
                {
                    j = 2;
                    break;
                }

            default:
                {
                    j = -1;
                    break;
                }
        }
        System.Diagnostics.Debug.Assert(j == 2);
    }
}

Note with Option Strict On the VB.NET example doesn't compile. The issue is, in C# default branch is executed. Cast to int (=type of the cases) fixes it.

Changing the expression into (int)o can result in runtime errors in case the object can't be converted to an int, which would be better than executing wrong branch without errors. On the other hand, something like (o is int ? (int)o : o) would work correctly (~I think~most of the time*), but would require introducing temporary variable (in case o is a property, an invocation, or an index access - do I have them all?)

*) o = "2" is same as 2, which is a problem. The code decompiled to C# uses Operators.ConditionalCompareObjectEqual(o, 1, false)

Details