diff --git a/CodeConverter/CSharp/BinaryExpressionConverter.cs b/CodeConverter/CSharp/BinaryExpressionConverter.cs index f02c085f..113e930a 100644 --- a/CodeConverter/CSharp/BinaryExpressionConverter.cs +++ b/CodeConverter/CSharp/BinaryExpressionConverter.cs @@ -82,11 +82,15 @@ 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 { + (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..3e936b50 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,20 +178,38 @@ 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) - { - visitBinaryExpression = null; - return false; - } - bool lhsEmpty = IsNothingOrEmpty(node.Left); bool rhsEmpty = IsNothingOrEmpty(node.Right); if (lhsEmpty || rhsEmpty) { var arg = lhsEmpty ? rhs : lhs; + var argType = lhsEmpty ? rhsTypeInfo : lhsTypeInfo; + + if (argType.Type?.SpecialType == SpecialType.System_Char) { + // char = "" in VB means char == '\0' (char.MinValue) in C#; not affected by OptionCompareText + var charMinValue = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.CharKeyword)), + ValidSyntaxFactory.IdentifierName("MinValue")); + var equalityKind = node.IsKind(VBasic.SyntaxKind.NotEqualsExpression) ? SyntaxKind.NotEqualsExpression : SyntaxKind.EqualsExpression; + var opToken = SyntaxFactory.Token(node.IsKind(VBasic.SyntaxKind.NotEqualsExpression) ? SyntaxKind.ExclamationEqualsToken : SyntaxKind.EqualsEqualsToken); + visitBinaryExpression = SyntaxFactory.BinaryExpression(equalityKind, arg, opToken, charMinValue); + return true; + } + + if (OptionCompareTextCaseInsensitive) + { + visitBinaryExpression = null; + return false; + } + + 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 +226,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/Tests/CSharp/ExpressionTests/StringExpressionTests.cs b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs index 414ba0e5..09481594 100644 --- a/Tests/CSharp/ExpressionTests/StringExpressionTests.cs +++ b/Tests/CSharp/ExpressionTests/StringExpressionTests.cs @@ -541,6 +541,53 @@ 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 + Dim testResult3 = testChar <> """" + End Sub +End Class", @" +internal partial class TestClass +{ + private void TestMethod() + { + char testChar = default; + bool testResult = testChar == char.MinValue; + bool testResult2 = testChar == char.MinValue; + bool testResult3 = testChar != char.MinValue; + } +}"); + } + + [Fact] + public async Task CharEqualityInConditionAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Private Function IsEmpty(c As Char) As Boolean + If c = """" Then + Return True + End If + Return False + End Function +End Class", @" +internal partial class TestClass +{ + private bool IsEmpty(char c) + { + if (c == char.MinValue) + { + return true; + } + return false; + } }"); } } \ No newline at end of file