From e38a778bfb128f25dd57b316f53e7856a97cd4f9 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder <2490482+GrahamTheCoder@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:14:44 +0000 Subject: [PATCH] Fix VB -> C# char comparison with empty string Map VB.NET char comparison with an empty string to char.MinValue to match runtime behavior and prevent logic errors during compilation. Added a unit test. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .../CSharp/BinaryExpressionConverter.cs | 19 +- .../CSharp/VisualBasicEqualityComparison.cs | 11 +- Program.vb | 9 + TestDotNet/Program.vb | 12 + TestDotNet/TestDotNet.vbproj | 6 + TestEquality.csproj | 8 + .../ExpressionTests/StringExpressionTests.cs | 21 + .../StringExpressionTests.cs.orig | 546 ++++++++++++++++++ .../StringExpressionTests.cs.patch | 20 + .../StringExpressionTests.cs.rej | 20 + test_char.cs | 10 + 11 files changed, 677 insertions(+), 5 deletions(-) create mode 100644 Program.vb create mode 100644 TestDotNet/Program.vb create mode 100644 TestDotNet/TestDotNet.vbproj create mode 100644 TestEquality.csproj create mode 100644 Tests/CSharp/ExpressionTests/StringExpressionTests.cs.orig create mode 100644 Tests/CSharp/ExpressionTests/StringExpressionTests.cs.patch create mode 100644 Tests/CSharp/ExpressionTests/StringExpressionTests.cs.rej create mode 100644 test_char.cs diff --git a/CodeConverter/CSharp/BinaryExpressionConverter.cs b/CodeConverter/CSharp/BinaryExpressionConverter.cs index f02c085f..4de57b1f 100644 --- a/CodeConverter/CSharp/BinaryExpressionConverter.cs +++ b/CodeConverter/CSharp/BinaryExpressionConverter.cs @@ -82,11 +82,24 @@ private async Task ConvertBinaryExpressionAsync(VBasic.Syntax. case VisualBasicEqualityComparison.RequiredType.StringOnly: if (lhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String && rhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String && - _visualBasicEqualityComparison.TryConvertToNullOrEmptyCheck(node, lhs, rhs, out CSharpSyntaxNode visitBinaryExpression)) { + _visualBasicEqualityComparison.TryConvertToNullOrEmptyCheck(node, lhs, rhs, lhsTypeInfo, rhsTypeInfo, out CSharpSyntaxNode visitBinaryExpression)) { return visitBinaryExpression; } - (lhs, rhs) = _visualBasicEqualityComparison.AdjustForVbStringComparison(node.Left, lhs, lhsTypeInfo, false, node.Right, rhs, rhsTypeInfo, false); - omitConversion = true; // Already handled within for the appropriate types (rhs can become int in comparison) + if (lhsTypeInfo.Type?.SpecialType == SpecialType.System_Char && rhsTypeInfo.Type?.SpecialType == SpecialType.System_Char) { + // Do nothing, char comparison + } else if ((lhsTypeInfo.Type?.SpecialType == SpecialType.System_Char && rhsTypeInfo.Type?.SpecialType == SpecialType.System_String && _visualBasicEqualityComparison.IsNothingOrEmpty(node.Right)) || + (rhsTypeInfo.Type?.SpecialType == SpecialType.System_Char && lhsTypeInfo.Type?.SpecialType == SpecialType.System_String && _visualBasicEqualityComparison.IsNothingOrEmpty(node.Left))) { + if (lhsTypeInfo.Type?.SpecialType == SpecialType.System_Char) { + rhs = CS.SyntaxFactory.MemberAccessExpression(CS.SyntaxKind.SimpleMemberAccessExpression, CS.SyntaxFactory.PredefinedType(CS.SyntaxFactory.Token(CS.SyntaxKind.CharKeyword)), CS.SyntaxFactory.IdentifierName("MinValue")); + omitConversion = true; + } else { + lhs = CS.SyntaxFactory.MemberAccessExpression(CS.SyntaxKind.SimpleMemberAccessExpression, CS.SyntaxFactory.PredefinedType(CS.SyntaxFactory.Token(CS.SyntaxKind.CharKeyword)), CS.SyntaxFactory.IdentifierName("MinValue")); + omitConversion = true; + } + } else { + (lhs, rhs) = _visualBasicEqualityComparison.AdjustForVbStringComparison(node.Left, lhs, lhsTypeInfo, false, node.Right, rhs, rhsTypeInfo, false); + omitConversion = true; // Already handled within for the appropriate types (rhs can become int in comparison) + } break; case VisualBasicEqualityComparison.RequiredType.Object: return _visualBasicEqualityComparison.GetFullExpressionForVbObjectComparison(lhs, rhs, VisualBasicEqualityComparison.ComparisonKind.Equals, node.IsKind(VBasic.SyntaxKind.NotEqualsExpression)); diff --git a/CodeConverter/CSharp/VisualBasicEqualityComparison.cs b/CodeConverter/CSharp/VisualBasicEqualityComparison.cs index b05a643a..4b6d82b6 100644 --- a/CodeConverter/CSharp/VisualBasicEqualityComparison.cs +++ b/CodeConverter/CSharp/VisualBasicEqualityComparison.cs @@ -79,6 +79,7 @@ public RequiredType GetObjectEqualityType(params TypeInfo[] typeInfos) if (typeInfos.All( t => t.Type == null || t.Type.SpecialType == SpecialType.System_String || + t.Type.SpecialType == SpecialType.System_Char || t.Type.IsArrayOf(SpecialType.System_Char) ) ) { return RequiredType.StringOnly; } @@ -177,7 +178,7 @@ private static ObjectCreationExpressionSyntax NewStringFromArg(ExpressionSyntax } public bool TryConvertToNullOrEmptyCheck(VBSyntax.BinaryExpressionSyntax node, ExpressionSyntax lhs, - ExpressionSyntax rhs, out CSharpSyntaxNode? visitBinaryExpression) + ExpressionSyntax rhs, TypeInfo lhsTypeInfo, TypeInfo rhsTypeInfo, out CSharpSyntaxNode? visitBinaryExpression) { if (OptionCompareTextCaseInsensitive) { @@ -191,6 +192,12 @@ public bool TryConvertToNullOrEmptyCheck(VBSyntax.BinaryExpressionSyntax node, E if (lhsEmpty || rhsEmpty) { var arg = lhsEmpty ? rhs : lhs; + var argType = lhsEmpty ? rhsTypeInfo : lhsTypeInfo; + if (argType.Type?.SpecialType != SpecialType.System_String && argType.Type?.SpecialType != SpecialType.System_Object) { + visitBinaryExpression = null; + return false; + } + var nullOrEmpty = SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)), @@ -207,7 +214,7 @@ public bool TryConvertToNullOrEmptyCheck(VBSyntax.BinaryExpressionSyntax node, E return false; } - private bool IsNothingOrEmpty(VBSyntax.ExpressionSyntax expressionSyntax) + public bool IsNothingOrEmpty(VBSyntax.ExpressionSyntax expressionSyntax) { expressionSyntax = expressionSyntax.SkipIntoParens(); diff --git a/Program.vb b/Program.vb new file mode 100644 index 00000000..7f5437d9 --- /dev/null +++ b/Program.vb @@ -0,0 +1,9 @@ +Imports System + +Module Program + Sub Main() + Dim testChar As Char = Nothing + Dim testResult = testChar = "" + Console.WriteLine(testResult) + End Sub +End Module diff --git a/TestDotNet/Program.vb b/TestDotNet/Program.vb new file mode 100644 index 00000000..8d9d9f4e --- /dev/null +++ b/TestDotNet/Program.vb @@ -0,0 +1,12 @@ +Imports System + +Module Program + Sub Main() + Dim testChar As Char = Nothing + Dim testResult = testChar = "" + Console.WriteLine(testResult) + + Dim testResult2 = "" = testChar + Console.WriteLine(testResult2) + End Sub +End Module diff --git a/TestDotNet/TestDotNet.vbproj b/TestDotNet/TestDotNet.vbproj new file mode 100644 index 00000000..dd4b5686 --- /dev/null +++ b/TestDotNet/TestDotNet.vbproj @@ -0,0 +1,6 @@ + + + Exe + net8.0 + + diff --git a/TestEquality.csproj b/TestEquality.csproj new file mode 100644 index 00000000..41f1d5ad --- /dev/null +++ b/TestEquality.csproj @@ -0,0 +1,8 @@ + + + + Exe + net6.0 + + + diff --git a/Tests/CSharp/ExpressionTests/StringExpressionTests.cs b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs index 414ba0e5..72440abb 100644 --- a/Tests/CSharp/ExpressionTests/StringExpressionTests.cs +++ b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs @@ -541,6 +541,27 @@ public void Foo() { string x = Conversions.ToString(DateTime.Parse(""2022-01-01"")) + "" 15:00""; } +}"); + } + + [Fact] + public async Task CharEqualityEmptyStringAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Private Sub TestMethod() + Dim testChar As Char = Nothing + Dim testResult = testChar = """" + Dim testResult2 = """" = testChar + End Sub +End Class", @" +internal partial class TestClass +{ + private void TestMethod() + { + char testChar = default; + bool testResult = testChar == char.MinValue; + bool testResult2 = char.MinValue == testChar; + } }"); } } \ No newline at end of file diff --git a/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.orig b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.orig new file mode 100644 index 00000000..232e759c --- /dev/null +++ b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.orig @@ -0,0 +1,546 @@ +using System.Threading.Tasks; +using ICSharpCode.CodeConverter.Tests.TestRunners; +using Xunit; + +namespace ICSharpCode.CodeConverter.Tests.CSharp.ExpressionTests; + +public class StringExpressionTests : ConverterTestBase +{ + [Fact] + public async Task MultilineStringAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Private Sub TestMethod() + Dim x = ""Hello\ All strings in VB are verbatim """" < that's just a single escaped quote +World!"" + Dim y = $""Hello\ All strings in VB are verbatim """" < that's just a single escaped quote +World!"" + End Sub +End Class", @" +internal partial class TestClass +{ + private void TestMethod() + { + string x = @""Hello\ All strings in VB are verbatim """" < that's just a single escaped quote +World!""; + string y = $@""Hello\ All strings in VB are verbatim """" < that's just a single escaped quote +World!""; + } +}"); + } + + [Fact] + public async Task QuoteCharacterAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Class C + Public Sub s + Dim x As String = Chr(34) + x = Chr(92) + End Sub +End Class", @" +public partial class C +{ + public void s() + { + string x = ""\""""; + x = @""\""; + } +}"); + } + + [Fact] + public async Task QuotesAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Shared Function GetTextFeedInput(pStream As String, pTitle As String, pText As String) As String + Return ""{"" & AccessKey() & "",""""streamName"""": """""" & pStream & """""",""""point"""": ["" & GetTitleTextPair(pTitle, pText) & ""]}"" + End Function + + Shared Function AccessKey() As String + Return """"""accessKey"""": """"8iaiHNZpNbBkYHHGbMNiHhAp4uPPyQke"""""" + End Function + + Shared Function GetNameValuePair(pName As String, pValue As Integer) As String + Return (""{""""name"""": """""" & pName & """""", """"value"""": """""" & pValue & """"""}"") + End Function + + Shared Function GetNameValuePair(pName As String, pValue As String) As String + Return (""{""""name"""": """""" & pName & """""", """"value"""": """""" & pValue & """"""}"") + End Function + + Shared Function GetTitleTextPair(pName As String, pValue As String) As String + Return (""{""""title"""": """""" & pName & """""", """"msg"""": """""" & pValue & """"""}"") + End Function + Shared Function GetDeltaPoint(pDelta As Integer) As String + Return (""{""""delta"""": """""" & pDelta & """"""}"") + End Function +End Class", @" +internal partial class TestClass +{ + public static string GetTextFeedInput(string pStream, string pTitle, string pText) + { + return ""{"" + AccessKey() + "",\""streamName\"": \"""" + pStream + ""\"",\""point\"": ["" + GetTitleTextPair(pTitle, pText) + ""]}""; + } + + public static string AccessKey() + { + return ""\""accessKey\"": \""8iaiHNZpNbBkYHHGbMNiHhAp4uPPyQke\""""; + } + + public static string GetNameValuePair(string pName, int pValue) + { + return ""{\""name\"": \"""" + pName + ""\"", \""value\"": \"""" + pValue + ""\""}""; + } + + public static string GetNameValuePair(string pName, string pValue) + { + return ""{\""name\"": \"""" + pName + ""\"", \""value\"": \"""" + pValue + ""\""}""; + } + + public static string GetTitleTextPair(string pName, string pValue) + { + return ""{\""title\"": \"""" + pName + ""\"", \""msg\"": \"""" + pValue + ""\""}""; + } + public static string GetDeltaPoint(int pDelta) + { + return ""{\""delta\"": \"""" + pDelta + ""\""}""; + } +}"); + } + + [Fact] + public async Task StringCompareAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Class Class1 + Sub Foo() + Dim s1 As String = Nothing + Dim s2 As String = """" + If s1 <> s2 Then + Throw New Exception() + End If + If s1 = ""something"" Then + Throw New Exception() + End If + If ""something"" = s1 Then + Throw New Exception() + End If + If s1 = Nothing Then + ' + End If + If s1 = """" Then + ' + End If + End Sub +End Class", @"using System; + +public partial class Class1 +{ + public void Foo() + { + string s1 = null; + string s2 = """"; + if ((s1 ?? """") != (s2 ?? """")) + { + throw new Exception(); + } + if (s1 == ""something"") + { + throw new Exception(); + } + if (""something"" == s1) + { + throw new Exception(); + } + if (string.IsNullOrEmpty(s1)) + { + // + } + if (string.IsNullOrEmpty(s1)) + { + // + } + } +}"); + } + + [Fact] + public async Task StringCompareTextAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Option Compare Text +Public Class Class1 + Sub Foo() + Dim s1 As String = Nothing + Dim s2 As String = """" + If s1 <> s2 Then + Throw New Exception() + End If + If s1 = ""something"" Then + Throw New Exception() + End If + If ""something"" = s1 Then + Throw New Exception() + End If + If s1 = Nothing Then + ' + End If + If s1 = """" Then + ' + End If + End Sub +End Class", @"using System; +using System.Globalization; + +public partial class Class1 +{ + public void Foo() + { + string s1 = null; + string s2 = """"; + if (CultureInfo.CurrentCulture.CompareInfo.Compare(s1 ?? """", s2 ?? """", CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth) != 0) + { + throw new Exception(); + } + if (CultureInfo.CurrentCulture.CompareInfo.Compare(s1, ""something"", CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth) == 0) + { + throw new Exception(); + } + if (CultureInfo.CurrentCulture.CompareInfo.Compare(""something"", s1, CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth) == 0) + { + throw new Exception(); + } + if (CultureInfo.CurrentCulture.CompareInfo.Compare(s1 ?? """", """", CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth) == 0) + { + // + } + if (CultureInfo.CurrentCulture.CompareInfo.Compare(s1 ?? """", """", CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth) == 0) + { + // + } + } +}"); + } + + [Fact] + public async Task StringCompareDefaultInstrAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Imports Microsoft.VisualBasic + +Class Issue655 + Dim s1 = InStr(1, ""obj"", ""object '"") + Dim s2 = InStrRev(1, ""obj"", ""object '"") + Dim s3 = Replace(1, ""obj"", ""object '"") + Dim s4 = Split(1, ""obj"", ""object '"") + Dim s5 = Filter(New String() { 1, 2}, ""obj"") + Dim s6 = StrComp(1, ""obj"") + Dim s7 = OtherFunction() + + Function OtherFunction(Optional c As CompareMethod = CompareMethod.Binary) As Boolean + Return c = CompareMethod.Binary + End Function +End Class", + @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic +using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic + +internal partial class Issue655 +{ + private object s1 = Strings.InStr(1, ""obj"", ""object '""); + private object s2 = Strings.InStrRev(1.ToString(), ""obj"", Conversions.ToInteger(""object '"")); + private object s3 = Strings.Replace(1.ToString(), ""obj"", ""object '""); + private object s4 = Strings.Split(1.ToString(), ""obj"", Conversions.ToInteger(""object '"")); + private object s5 = Strings.Filter(new string[] { 1.ToString(), 2.ToString() }, ""obj""); + private object s6 = Strings.StrComp(1.ToString(), ""obj""); + private object s7; + + public Issue655() + { + s7 = OtherFunction(); + } + + public bool OtherFunction(CompareMethod c = CompareMethod.Binary) + { + return c == CompareMethod.Binary; + } +}"); + } + + [Fact] + public async Task StringCompareTextInstrAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Option Compare Text ' Comment omitted since line has no conversion +Imports Microsoft.VisualBasic + +Class Issue655 + Dim s1 = InStr(1, ""obj"", ""object '"") + Dim s2 = InStrRev(1, ""obj"", ""object '"") + Dim s3 = Replace(1, ""obj"", ""object '"") + Dim s4 = Split(1, ""obj"", ""object '"") + Dim s5 = Filter(New String() { 1, 2}, ""obj"") + Dim s6 = StrComp(1, ""obj"") + Dim s7 = OtherFunction() + + Function OtherFunction(Optional c As CompareMethod = CompareMethod.Binary) As Boolean + Return c = CompareMethod.Binary + End Function +End Class", + @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic +using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic + +internal partial class Issue655 +{ + private object s1 = Strings.InStr(1, ""obj"", ""object '"", Compare: CompareMethod.Text); + private object s2 = Strings.InStrRev(1.ToString(), ""obj"", Conversions.ToInteger(""object '""), Compare: CompareMethod.Text); + private object s3 = Strings.Replace(1.ToString(), ""obj"", ""object '"", Compare: CompareMethod.Text); + private object s4 = Strings.Split(1.ToString(), ""obj"", Conversions.ToInteger(""object '""), Compare: CompareMethod.Text); + private object s5 = Strings.Filter(new string[] { 1.ToString(), 2.ToString() }, ""obj""); + private object s6 = Strings.StrComp(1.ToString(), ""obj"", Compare: CompareMethod.Text); + private object s7; + + public Issue655() + { + s7 = OtherFunction(); + } + + public bool OtherFunction(CompareMethod c = CompareMethod.Binary) + { + return c == CompareMethod.Binary; + } +}"); + } + + [Fact] + public async Task StringConcatPrecedenceAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Class Class1 + Sub Foo() + Dim x = ""x "" & 5 - 4 & "" y"" + End Sub +End Class", @" +public partial class Class1 +{ + public void Foo() + { + string x = ""x "" + (5 - 4) + "" y""; + } +}"); + } + + [Fact] + public async Task StringConcatenationAssignmentAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Private Sub TestMethod() + Dim str = ""Hello, "" + str &= ""World"" + End Sub +End Class", @" +internal partial class TestClass +{ + private void TestMethod() + { + string str = ""Hello, ""; + str += ""World""; + } +}"); + } + + [Fact] + public async Task StringInterpolationWithConditionalOperatorAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Function GetString(yourBoolean as Boolean) As String + Return $""You {if (yourBoolean, ""do"", ""do not"")} have a true value"" +End Function", + @"public string GetString(bool yourBoolean) +{ + return $""You {(yourBoolean ? ""do"" : ""do not"")} have a true value""; +}"); + } + + [Fact] + public async Task StringInterpolationWithDoubleQuotesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System + +Namespace Global.InnerNamespace + Public Class Test + Public Function StringInter(t As String, dt As DateTime) As String + Dim a = $""pre{t} t"" + Dim b = $""pre{t} """" t"" + Dim c = $""pre{t} """"\ t"" + Dim d = $""pre{t & """"""""} """" t"" + Dim e = $""pre{t & """"""""} """"\ t"" + Dim f = $""pre{{escapedBraces}}{dt,4:hh}"" + Return a & b & c & d & e & f + End Function + End Class +End Namespace", + @"using System; + +namespace InnerNamespace +{ + public partial class Test + { + public string StringInter(string t, DateTime dt) + { + string a = $""pre{t} t""; + string b = $""pre{t} \"" t""; + string c = $@""pre{t} """"\ t""; + string d = $""pre{t + ""\""""} \"" t""; + string e = $@""pre{t + ""\""""} """"\ t""; + string f = $""pre{{escapedBraces}}{dt,4:hh}""; + return a + b + c + d + e + f; + } + } +}"); + } + + [Fact] + public async Task StringInterpolationWithDateFormatAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System + +Namespace Global.InnerNamespace + Public Class Test + public function InterStringDateFormat(dt As DateTime) As String + Dim a As String = $""Soak: {dt: d\.h\:mm\:ss\.f}"" + return a + End function + End Class +End Namespace", + @"using System; + +namespace InnerNamespace +{ + public partial class Test + { + public string InterStringDateFormat(DateTime dt) + { + string a = $""Soak: {dt: d\\.h\\:mm\\:ss\\.f}""; + return a; + } + } +}"); + } + [Fact] + public async Task NoConversionRequiredWithinConcatenationAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Class Issue508 + Sub Foo() + Dim x = ""x"" & 4 & ""y"" + End Sub +End Class", + @" +public partial class Issue508 +{ + public void Foo() + { + string x = ""x"" + 4 + ""y""; + } +}"); + } + + [Fact] + public async Task EmptyStringCoalesceSkippedForLiteralComparisonAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Class VisualBasicClass + + Sub Foo() + Dim x = """" + Dim y = x = ""something"" + End Sub + +End Class", + @" +public partial class VisualBasicClass +{ + + public void Foo() + { + string x = """"; + bool y = x == ""something""; + } + +}"); + } + + [Fact] + public async Task Issue396ComparisonOperatorForStringsAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Class Issue396ComparisonOperatorForStringsAsync + Private str = 1.ToString() + Private b = str > """" +End Class", + @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic + +public partial class Issue396ComparisonOperatorForStringsAsync +{ + private object str = 1.ToString(); + private object b; + + public Issue396ComparisonOperatorForStringsAsync() + { + b = Operators.ConditionalCompareObjectGreater(str, """", false); + } +}"); + } + + [Fact] + public async Task Issue590EnumConvertsToNumericStringAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Class EnumTests + Private Enum RankEnum As SByte + First = 1 + Second = 2 + End Enum + + Public Sub TestEnumConcat() + Console.Write(RankEnum.First & RankEnum.Second) + End Sub +End Class", + @"using System; +using System.IO; + +public partial class EnumTests +{ + private enum RankEnum : sbyte + { + First = 1, + Second = 2 + } + + public void TestEnumConcat() + { + Console.Write(RankEnum.First + RankEnum.Second); + } +} +1 target compilation errors: +CS0019: Operator '+' cannot be applied to operands of type 'EnumTests.RankEnum' and 'EnumTests.RankEnum'"); + } + + [Fact] + public async Task Issue806DateTimeConvertsToStringWithinConcatenationAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Class Issue806 + Sub Foo() + Dim x = #2022-01-01# & "" 15:00"" + End Sub +End Class", + @"using System; +using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic + +public partial class Issue806 +{ + public void Foo() + { + string x = Conversions.ToString(DateTime.Parse(""2022-01-01"")) + "" 15:00""; + } +}"); + } +} \ No newline at end of file diff --git a/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.patch b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.patch new file mode 100644 index 00000000..ac2ede9b --- /dev/null +++ b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.patch @@ -0,0 +1,20 @@ +--- Tests/CSharp/ExpressionTests/StringExpressionTests.cs ++++ Tests/CSharp/ExpressionTests/StringExpressionTests.cs +@@ -489,5 +489,17 @@ + b = Operators.ConditionalCompareObjectGreater(str, """", false); + } + }"); + } ++ ++ [Fact] ++ public async Task CharEqualityEmptyStringAsync() ++ { ++ await TestConversionVisualBasicToCSharpAsync(@"Class TestClass ++ Private Sub TestMethod() ++ Dim testChar As Char = Nothing ++ Dim testResult = testChar = """" ++ Dim testResult2 = """" = testChar ++ End Sub ++End Class", @""); ++ } + } diff --git a/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.rej b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.rej new file mode 100644 index 00000000..b9dd7f9e --- /dev/null +++ b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs.rej @@ -0,0 +1,20 @@ +--- StringExpressionTests.cs ++++ StringExpressionTests.cs +@@ -489,5 +489,17 @@ + b = Operators.ConditionalCompareObjectGreater(str, """", false); + } + }"); + } ++ ++ [Fact] ++ public async Task CharEqualityEmptyStringAsync() ++ { ++ await TestConversionVisualBasicToCSharpAsync(@"Class TestClass ++ Private Sub TestMethod() ++ Dim testChar As Char = Nothing ++ Dim testResult = testChar = """" ++ Dim testResult2 = """" = testChar ++ End Sub ++End Class", @""); ++ } + } diff --git a/test_char.cs b/test_char.cs new file mode 100644 index 00000000..36fd0a21 --- /dev/null +++ b/test_char.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System.Linq; + +class Test { + static void Main() { + Console.WriteLine("Hello"); + } +}