Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CodeConverter/CSharp/BinaryExpressionConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,15 @@ private async Task<CSharpSyntaxNode> 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));
Expand Down
35 changes: 27 additions & 8 deletions CodeConverter/CSharp/VisualBasicEqualityComparison.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)),
Expand All @@ -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();

Expand Down
23 changes: 23 additions & 0 deletions Tests/CSharp/ExpressionTests/StringExpressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,29 @@ 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 = char.MinValue == testChar;
bool testResult3 = testChar != char.MinValue;
}
}");
}
}
9 changes: 0 additions & 9 deletions Tests/Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,4 @@
<ItemGroup>
<ProjectReference Include="..\CodeConv\CodeConv.csproj" />
</ItemGroup>
<!--
The Vsix project is a net472 Windows-only project and only builds under an MSBuild that has
the WindowsDesktop SDK available. We reference it so that VsixAssemblyCompatibilityTests can
statically verify the Vsix output, but only in environments that can actually build it
(i.e. Windows). Elsewhere the tests that depend on Vsix output will skip.
-->
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<ProjectReference Include="..\Vsix\Vsix.csproj" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" PrivateAssets="all" />
</ItemGroup>
</Project>
Loading