Skip to content

Commit acb0756

Browse files
GrahamTheCodergoogle-labs-jules[bot]
authored andcommitted
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>
1 parent 2bb1568 commit acb0756

File tree

11 files changed

+677
-5
lines changed

11 files changed

+677
-5
lines changed

CodeConverter/CSharp/BinaryExpressionConverter.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,24 @@ private async Task<CSharpSyntaxNode> ConvertBinaryExpressionAsync(VBasic.Syntax.
8282
case VisualBasicEqualityComparison.RequiredType.StringOnly:
8383
if (lhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String &&
8484
rhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String &&
85-
_visualBasicEqualityComparison.TryConvertToNullOrEmptyCheck(node, lhs, rhs, out CSharpSyntaxNode visitBinaryExpression)) {
85+
_visualBasicEqualityComparison.TryConvertToNullOrEmptyCheck(node, lhs, rhs, lhsTypeInfo, rhsTypeInfo, out CSharpSyntaxNode visitBinaryExpression)) {
8686
return visitBinaryExpression;
8787
}
88-
(lhs, rhs) = _visualBasicEqualityComparison.AdjustForVbStringComparison(node.Left, lhs, lhsTypeInfo, false, node.Right, rhs, rhsTypeInfo, false);
89-
omitConversion = true; // Already handled within for the appropriate types (rhs can become int in comparison)
88+
if (lhsTypeInfo.Type?.SpecialType == SpecialType.System_Char && rhsTypeInfo.Type?.SpecialType == SpecialType.System_Char) {
89+
// Do nothing, char comparison
90+
} else if ((lhsTypeInfo.Type?.SpecialType == SpecialType.System_Char && rhsTypeInfo.Type?.SpecialType == SpecialType.System_String && _visualBasicEqualityComparison.IsNothingOrEmpty(node.Right)) ||
91+
(rhsTypeInfo.Type?.SpecialType == SpecialType.System_Char && lhsTypeInfo.Type?.SpecialType == SpecialType.System_String && _visualBasicEqualityComparison.IsNothingOrEmpty(node.Left))) {
92+
if (lhsTypeInfo.Type?.SpecialType == SpecialType.System_Char) {
93+
rhs = CS.SyntaxFactory.MemberAccessExpression(CS.SyntaxKind.SimpleMemberAccessExpression, CS.SyntaxFactory.PredefinedType(CS.SyntaxFactory.Token(CS.SyntaxKind.CharKeyword)), CS.SyntaxFactory.IdentifierName("MinValue"));
94+
omitConversion = true;
95+
} else {
96+
lhs = CS.SyntaxFactory.MemberAccessExpression(CS.SyntaxKind.SimpleMemberAccessExpression, CS.SyntaxFactory.PredefinedType(CS.SyntaxFactory.Token(CS.SyntaxKind.CharKeyword)), CS.SyntaxFactory.IdentifierName("MinValue"));
97+
omitConversion = true;
98+
}
99+
} else {
100+
(lhs, rhs) = _visualBasicEqualityComparison.AdjustForVbStringComparison(node.Left, lhs, lhsTypeInfo, false, node.Right, rhs, rhsTypeInfo, false);
101+
omitConversion = true; // Already handled within for the appropriate types (rhs can become int in comparison)
102+
}
90103
break;
91104
case VisualBasicEqualityComparison.RequiredType.Object:
92105
return _visualBasicEqualityComparison.GetFullExpressionForVbObjectComparison(lhs, rhs, VisualBasicEqualityComparison.ComparisonKind.Equals, node.IsKind(VBasic.SyntaxKind.NotEqualsExpression));

CodeConverter/CSharp/VisualBasicEqualityComparison.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public RequiredType GetObjectEqualityType(params TypeInfo[] typeInfos)
7979

8080
if (typeInfos.All(
8181
t => t.Type == null || t.Type.SpecialType == SpecialType.System_String ||
82+
t.Type.SpecialType == SpecialType.System_Char ||
8283
t.Type.IsArrayOf(SpecialType.System_Char) ) ) {
8384
return RequiredType.StringOnly;
8485
}
@@ -177,7 +178,7 @@ private static ObjectCreationExpressionSyntax NewStringFromArg(ExpressionSyntax
177178
}
178179

179180
public bool TryConvertToNullOrEmptyCheck(VBSyntax.BinaryExpressionSyntax node, ExpressionSyntax lhs,
180-
ExpressionSyntax rhs, out CSharpSyntaxNode? visitBinaryExpression)
181+
ExpressionSyntax rhs, TypeInfo lhsTypeInfo, TypeInfo rhsTypeInfo, out CSharpSyntaxNode? visitBinaryExpression)
181182
{
182183
if (OptionCompareTextCaseInsensitive)
183184
{
@@ -191,6 +192,12 @@ public bool TryConvertToNullOrEmptyCheck(VBSyntax.BinaryExpressionSyntax node, E
191192
if (lhsEmpty || rhsEmpty)
192193
{
193194
var arg = lhsEmpty ? rhs : lhs;
195+
var argType = lhsEmpty ? rhsTypeInfo : lhsTypeInfo;
196+
if (argType.Type?.SpecialType != SpecialType.System_String && argType.Type?.SpecialType != SpecialType.System_Object) {
197+
visitBinaryExpression = null;
198+
return false;
199+
}
200+
194201
var nullOrEmpty = SyntaxFactory.InvocationExpression(
195202
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
196203
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)),
@@ -207,7 +214,7 @@ public bool TryConvertToNullOrEmptyCheck(VBSyntax.BinaryExpressionSyntax node, E
207214
return false;
208215
}
209216

210-
private bool IsNothingOrEmpty(VBSyntax.ExpressionSyntax expressionSyntax)
217+
public bool IsNothingOrEmpty(VBSyntax.ExpressionSyntax expressionSyntax)
211218
{
212219
expressionSyntax = expressionSyntax.SkipIntoParens();
213220

Program.vb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Imports System
2+
3+
Module Program
4+
Sub Main()
5+
Dim testChar As Char = Nothing
6+
Dim testResult = testChar = ""
7+
Console.WriteLine(testResult)
8+
End Sub
9+
End Module

TestDotNet/Program.vb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Imports System
2+
3+
Module Program
4+
Sub Main()
5+
Dim testChar As Char = Nothing
6+
Dim testResult = testChar = ""
7+
Console.WriteLine(testResult)
8+
9+
Dim testResult2 = "" = testChar
10+
Console.WriteLine(testResult2)
11+
End Sub
12+
End Module

TestDotNet/TestDotNet.vbproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
</PropertyGroup>
6+
</Project>

TestEquality.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
</Project>

Tests/CSharp/ExpressionTests/StringExpressionTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,27 @@ public void Foo()
541541
{
542542
string x = Conversions.ToString(DateTime.Parse(""2022-01-01"")) + "" 15:00"";
543543
}
544+
}");
545+
}
546+
547+
[Fact]
548+
public async Task CharEqualityEmptyStringAsync()
549+
{
550+
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
551+
Private Sub TestMethod()
552+
Dim testChar As Char = Nothing
553+
Dim testResult = testChar = """"
554+
Dim testResult2 = """" = testChar
555+
End Sub
556+
End Class", @"
557+
internal partial class TestClass
558+
{
559+
private void TestMethod()
560+
{
561+
char testChar = default;
562+
bool testResult = testChar == char.MinValue;
563+
bool testResult2 = char.MinValue == testChar;
564+
}
544565
}");
545566
}
546567
}

0 commit comments

Comments
 (0)