Skip to content

Commit 6250c52

Browse files
Correct logic for conversion objWithOverloads Is Nothing - fixes #591
1 parent 2c9ea37 commit 6250c52

6 files changed

Lines changed: 67 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
99
* Fix file extension and location of single converted file [#589](https://github.com/icsharpcode/CodeConverter/issues/589)
1010

1111
### VB -> C#
12-
12+
* Correct logic for conversion "objectWithOverloadedEquals Is Nothing" [#591](https://github.com/icsharpcode/CodeConverter/issues/591)
1313

1414
### C# -> VB
1515

CodeConverter/CSharp/BuiltInVisualBasicOperatorSubstitutions.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,20 @@ public async Task<ExpressionSyntax> ConvertNothingComparisonOrNullAsync(VBSyntax
4949
if (!(exprNode is VBSyntax.BinaryExpressionSyntax node) || !node.IsKind(VBasic.SyntaxKind.IsExpression, VBasic.SyntaxKind.EqualsExpression, VBasic.SyntaxKind.IsNotExpression, VBasic.SyntaxKind.NotEqualsExpression)) {
5050
return null;
5151
}
52-
ExpressionSyntax otherArgument;
52+
53+
VBSyntax.ExpressionSyntax vbOtherArg;
5354
if (node.Left.IsKind(VBasic.SyntaxKind.NothingLiteralExpression)) {
54-
otherArgument = (ExpressionSyntax)await ConvertIsOrIsNotExpressionArgAsync(node.Right);
55+
vbOtherArg = node.Right;
5556
} else if (node.Right.IsKind(VBasic.SyntaxKind.NothingLiteralExpression)) {
56-
otherArgument = (ExpressionSyntax)await ConvertIsOrIsNotExpressionArgAsync(node.Left);
57+
vbOtherArg = node.Left;
5758
} else {
5859
return null;
5960
}
60-
61-
var isReference = node.IsKind(VBasic.SyntaxKind.IsExpression, VBasic.SyntaxKind.IsNotExpression);
61+
var csOtherArg = (ExpressionSyntax)await ConvertIsOrIsNotExpressionArgAsync(vbOtherArg);
62+
var couldHaveOverloadedOperators = _semanticModel.GetTypeInfo(vbOtherArg).Type.SpecialType == SpecialType.None;
63+
var isReferenceComparison = node.IsKind(VBasic.SyntaxKind.IsExpression, VBasic.SyntaxKind.IsNotExpression);
6264
var notted = node.IsKind(VBasic.SyntaxKind.IsNotExpression, VBasic.SyntaxKind.NotEqualsExpression) || negateExpression;
63-
return notted ? CommonConversions.NotNothingComparison(otherArgument, isReference) : CommonConversions.NothingComparison(otherArgument, isReference);
65+
return notted ? CommonConversions.NotNothingComparison(csOtherArg, isReferenceComparison) : CommonConversions.NothingComparison(csOtherArg, isReferenceComparison, couldHaveOverloadedOperators);
6466
}
6567

6668
private async Task<CSharpSyntaxNode> ConvertIsOrIsNotExpressionArgAsync(VBSyntax.ExpressionSyntax binaryExpressionArg)

CodeConverter/CSharp/CommonConversions.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -658,16 +658,17 @@ public static CSSyntax.ParameterListSyntax CreateParameterList(IEnumerable<Synta
658658
public static CSSyntax.BinaryExpressionSyntax NotNothingComparison(ExpressionSyntax otherArgument, bool isReferenceType)
659659
{
660660
if (isReferenceType) {
661-
return SyntaxFactory.BinaryExpression(CSSyntaxKind.IsExpression, otherArgument, SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.ObjectKeyword)));
661+
return SyntaxFactory.BinaryExpression(CSSyntaxKind.IsExpression, otherArgument, ValidSyntaxFactory.ObjectType);
662662
}
663-
return SyntaxFactory.BinaryExpression(CSSyntaxKind.NotEqualsExpression, otherArgument, SyntaxFactory.LiteralExpression(CSSyntaxKind.DefaultLiteralExpression));
663+
return SyntaxFactory.BinaryExpression(CSSyntaxKind.NotEqualsExpression, otherArgument, ValidSyntaxFactory.DefaultExpression);
664664
}
665665

666-
public static ExpressionSyntax NothingComparison(ExpressionSyntax otherArgument, bool isReferenceType)
666+
public static ExpressionSyntax NothingComparison(ExpressionSyntax otherArgument, bool isReferenceType, bool couldHaveOverloadedOperators)
667667
{
668668
// Old project style doesn't support is pattern expressions (or indeed anything beyond c#7.3), so can't use "x is null"
669-
var literalKind = isReferenceType ? CSSyntaxKind.NullLiteralExpression : CSSyntaxKind.DefaultLiteralExpression;
670-
return SyntaxFactory.BinaryExpression(CSSyntaxKind.EqualsExpression, otherArgument, SyntaxFactory.LiteralExpression(literalKind));
669+
var literal = isReferenceType ? ValidSyntaxFactory.NullExpression : (ExpressionSyntax)ValidSyntaxFactory.DefaultExpression;
670+
if (isReferenceType && couldHaveOverloadedOperators) otherArgument = SyntaxFactory.ParenthesizedExpression(SyntaxFactory.CastExpression(ValidSyntaxFactory.ObjectType, otherArgument));
671+
return SyntaxFactory.BinaryExpression(CSSyntaxKind.EqualsExpression, otherArgument, literal);
671672
}
672673
}
673674
}

CodeConverter/CSharp/ValidSyntaxFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,9 @@ public static DeclarationPatternSyntax VarPattern(SyntaxToken varName)
9999
return SyntaxFactory.DeclarationPattern(
100100
ValidSyntaxFactory.VarType, SyntaxFactory.SingleVariableDesignation(varName));
101101
}
102+
103+
public static PredefinedTypeSyntax ObjectType => SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword));
104+
public static LiteralExpressionSyntax NullExpression => SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression);
105+
public static LiteralExpressionSyntax DefaultExpression => SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression);
102106
}
103107
}

Tests/CSharp/ExpressionTests/ExpressionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,7 @@ public partial class Foo
13471347
13481348
protected void OnBar(EventArgs e)
13491349
{
1350-
if (Bar == null)
1350+
if ((object)Bar == null)
13511351
{
13521352
Debug.WriteLine(""No subscriber"");
13531353
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
Imports System
2+
Imports System.Collections.Generic
3+
Imports System.Linq
4+
Imports Xunit
5+
6+
7+
Friend Class CTst
8+
Public Shared Operator <>(ByVal aCls1 As CTst, ByVal aCls2 As CTst) As Boolean
9+
Assert.Empty("<> operator should not be called")
10+
Return True
11+
End Operator
12+
Public Shared Operator =(ByVal aCls1 As CTst, ByVal aCls2 As CTst) As Boolean
13+
Assert.Empty("= operator should not be called")
14+
Return True
15+
End Operator
16+
End Class
17+
18+
Public Class ObjectEqualityTests
19+
20+
<Fact>
21+
Public Sub ComparingToNothingDoesNotCallOperatorOverload()
22+
Dim Test1 As CTst
23+
24+
If Not Test1 Is Nothing Then
25+
Assert.Empty("First branch should not be taken")
26+
ElseIf Test1 IsNot Nothing Then
27+
Assert.Empty("Second branch should not be taken")
28+
ElseIf Test1 Is Nothing Then
29+
Assert.True(Test1 Is Nothing)
30+
Assert.False(Not Test1 Is Nothing)
31+
Assert.False(Test1 IsNot Nothing)
32+
Else
33+
Assert.Empty("Else branch should not be taken")
34+
End If
35+
End Sub
36+
37+
<Fact>
38+
Public Sub GenericCanBeAssignedNothing()
39+
Generic(5, False)
40+
Generic("5", True)
41+
End Sub
42+
43+
Private Sub Generic(Of T)(input As T, isReference As Boolean)
44+
input = Nothing
45+
If isReference Then Assert.True(input Is Nothing) Else Assert.True(input IsNot Nothing)
46+
End Sub
47+
End Class

0 commit comments

Comments
 (0)