Skip to content

Commit 6f29db9

Browse files
Improve numeric casts - fixes #580
1 parent 05eb57e commit 6f29db9

5 files changed

Lines changed: 63 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1111
### VB -> C#
1212
* Handle Option Compare Text case insensitive comparisons in switch statements [#579](https://github.com/icsharpcode/CodeConverter/issues/579)
1313
* Fix compilation error when switching with enum cases [#549](https://github.com/icsharpcode/CodeConverter/issues/549)
14+
* Improve numeric casts [#580](https://github.com/icsharpcode/CodeConverter/issues/580)
1415

1516
### C# -> VB
1617

CodeConverter/CSharp/BuiltInVisualBasicOperatorSubsitutions.cs renamed to CodeConverter/CSharp/BuiltInVisualBasicOperatorSubstitutions.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,26 @@ namespace ICSharpCode.CodeConverter.CSharp
2222
{
2323
internal static class VbOperatorConversion
2424
{
25-
public static IOperatorConverter Create(CommentConvertingVisitorWrapper expressionVisitor, SemanticModel semanticModel, VisualBasicEqualityComparison visualBasicEqualityComparison)
25+
public static IOperatorConverter Create(CommentConvertingVisitorWrapper expressionVisitor, SemanticModel semanticModel, VisualBasicEqualityComparison visualBasicEqualityComparison, TypeConversionAnalyzer typeConversionAnalyzer)
2626
{
27-
return new BuiltInVisualBasicOperatorSubsitutions(expressionVisitor, semanticModel, visualBasicEqualityComparison);
27+
return new BuiltInVisualBasicOperatorSubstitutions(expressionVisitor, semanticModel, visualBasicEqualityComparison, typeConversionAnalyzer);
2828
}
2929

30-
private class BuiltInVisualBasicOperatorSubsitutions : IOperatorConverter
30+
private class BuiltInVisualBasicOperatorSubstitutions : IOperatorConverter
3131
{
3232
private const string _compilerServices = nameof(Microsoft) + "." + nameof(Microsoft.VisualBasic) + "." + nameof(Microsoft.VisualBasic.CompilerServices);
3333
private const string _operators = nameof(Operators);
3434
private readonly SemanticModel _semanticModel;
3535
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
3636
private readonly CommentConvertingVisitorWrapper _triviaConvertingVisitor;
37+
private readonly TypeConversionAnalyzer _typeConversionAnalyzer;
3738

38-
public BuiltInVisualBasicOperatorSubsitutions(CommentConvertingVisitorWrapper triviaConvertingVisitor, SemanticModel semanticModel, VisualBasicEqualityComparison visualBasicEqualityComparison)
39+
public BuiltInVisualBasicOperatorSubstitutions(CommentConvertingVisitorWrapper triviaConvertingVisitor, SemanticModel semanticModel, VisualBasicEqualityComparison visualBasicEqualityComparison, TypeConversionAnalyzer typeConversionAnalyzer)
3940
{
4041
_semanticModel = semanticModel;
4142
_visualBasicEqualityComparison = visualBasicEqualityComparison;
4243
_triviaConvertingVisitor = triviaConvertingVisitor;
44+
_typeConversionAnalyzer = typeConversionAnalyzer;
4345
}
4446

4547
public async Task<ExpressionSyntax> ConvertNothingComparisonOrNullAsync(VBSyntax.ExpressionSyntax exprNode, bool negateExpression = false)
@@ -93,11 +95,18 @@ private async Task<ExpressionSyntax> ConvertToLikeOperatorAsync(VBSyntax.BinaryE
9395

9496
private async Task<ExpressionSyntax> ConvertToPowOperatorAsync(VBSyntax.BinaryExpressionSyntax node)
9597
{
96-
var (lhs, rhs) = await AcceptSidesAsync(node);
98+
var (lhs, rhs) = await AcceptSidesAsync(node);
99+
lhs = ConvertTo(node.Left, lhs, SpecialType.System_Double);
100+
rhs = ConvertTo(node.Right, rhs, SpecialType.System_Double);
97101
return new KnownMethod(nameof(System), nameof(Math), nameof(Math.Pow))
98102
.Invoke(_visualBasicEqualityComparison.ExtraUsingDirectives, lhs, rhs);
99103
}
100104

105+
private ExpressionSyntax ConvertTo(VBSyntax.ExpressionSyntax node, ExpressionSyntax lhs, SpecialType targetType)
106+
{
107+
return _typeConversionAnalyzer.AddExplicitConversion(node, lhs, forceTargetType: _semanticModel.Compilation.GetSpecialType(targetType));
108+
}
109+
101110
/// <remarks>No need to implement these since this is only called for things that are already decimal and hence will resolve operator in C#</remarks>
102111
private async Task<ExpressionSyntax> ConvertToDecimalBinaryOperatorAsync(VBSyntax.BinaryExpressionSyntax node, KnownMethod member) =>
103112
default;

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
5656
_csCompilation = csCompilation;
5757
_typeContext = typeContext;
5858
_extraUsingDirectives = extraUsingDirectives;
59-
_operatorConverter = VbOperatorConversion.Create(TriviaConvertingExpressionVisitor, semanticModel, visualBasicEqualityComparison);
59+
_operatorConverter = VbOperatorConversion.Create(TriviaConvertingExpressionVisitor, semanticModel, visualBasicEqualityComparison, commonConversions.TypeConversionAnalyzer);
6060
// If this isn't needed, the assembly with Conversions may not be referenced, so this must be done lazily
6161
_convertMethodsLookupByReturnType =
6262
new Lazy<IDictionary<ITypeSymbol, string>>(() => CreateConvertMethodsLookupByReturnType(semanticModel));
@@ -237,12 +237,13 @@ public override async Task<CSharpSyntaxNode> VisitPredefinedCastExpression(VBasi
237237
SyntaxFactory.Argument(expressionSyntax))));
238238
}
239239

240-
return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, expressionSyntax, true, false, forceTargetType: _semanticModel.GetTypeInfo(node).Type);
240+
var withConversion = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, expressionSyntax, false, false, forceTargetType: _semanticModel.GetTypeInfo(node).Type);
241+
return node.ParenthesizeIfPrecedenceCouldChange(withConversion); // Use context of outer node, rather than just its exprssion, as the above method call would do if allowed to add parenthesis
241242
}
242243

243244
public override async Task<CSharpSyntaxNode> VisitTryCastExpression(VBasic.Syntax.TryCastExpressionSyntax node)
244245
{
245-
return VbSyntaxNodeExtensions.ParenthesizeIfPrecedenceCouldChange(node, SyntaxFactory.BinaryExpression(
246+
return node.ParenthesizeIfPrecedenceCouldChange(SyntaxFactory.BinaryExpression(
246247
SyntaxKind.AsExpression,
247248
(ExpressionSyntax) await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor),
248249
(TypeSyntax) await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor)
Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,26 @@
1-
using ICSharpCode.CodeConverter.Util;
2-
using Microsoft.CodeAnalysis;
3-
using Microsoft.CodeAnalysis.CSharp.Syntax;
4-
using Microsoft.CodeAnalysis.VisualBasic;
5-
using ArgumentSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentSyntax;
6-
using BinaryExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.BinaryExpressionSyntax;
7-
using LambdaExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.LambdaExpressionSyntax;
8-
using ParenthesizedExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ParenthesizedExpressionSyntax;
9-
using ReturnStatementSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ReturnStatementSyntax;
1+
using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax;
2+
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
3+
using VBasic = Microsoft.CodeAnalysis.VisualBasic;
104
using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
115

126
namespace ICSharpCode.CodeConverter.CSharp
137
{
148
internal static class VbSyntaxNodeExtensions
159
{
16-
public static ExpressionSyntax ParenthesizeIfPrecedenceCouldChange(this VisualBasicSyntaxNode node, ExpressionSyntax expression)
10+
public static CSSyntax.ExpressionSyntax ParenthesizeIfPrecedenceCouldChange(this VBasic.VisualBasicSyntaxNode node, CSSyntax.ExpressionSyntax expression)
1711
{
1812
return PrecedenceCouldChange(node) ? SyntaxFactory.ParenthesizedExpression(expression) : expression;
1913
}
2014

21-
public static bool PrecedenceCouldChange(this VisualBasicSyntaxNode node)
15+
public static bool PrecedenceCouldChange(this VBasic.VisualBasicSyntaxNode node)
2216
{
23-
bool parentIsBinaryExpression = node is BinaryExpressionSyntax;
24-
bool parentIsReturn = node.Parent is ReturnStatementSyntax;
25-
bool parentIsLambda = node.Parent is LambdaExpressionSyntax;
26-
bool parentIsNonArgumentExpression = node.Parent is Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax && !(node.Parent is ArgumentSyntax);
27-
bool parentIsParenthesis = node.Parent is ParenthesizedExpressionSyntax;
17+
bool parentIsBinaryExpression = node is VBSyntax.BinaryExpressionSyntax;
18+
bool parentIsLambda = node.Parent is VBSyntax.LambdaExpressionSyntax;
19+
bool parentIsNonArgumentExpression = node.Parent is VBSyntax.ExpressionSyntax && !(node.Parent is VBSyntax.ArgumentSyntax);
20+
bool parentIsParenthesis = node.Parent is VBSyntax.ParenthesizedExpressionSyntax;
21+
bool parentIsMemberAccessExpression = node.Parent is VBSyntax.MemberAccessExpressionSyntax;
2822

29-
return parentIsNonArgumentExpression && !parentIsBinaryExpression && !parentIsReturn && !parentIsLambda && !parentIsParenthesis;
23+
return parentIsMemberAccessExpression || parentIsNonArgumentExpression && !parentIsBinaryExpression && !parentIsLambda && !parentIsParenthesis;
3024
}
3125
}
3226
}

Tests/CSharp/ExpressionTests/ExpressionTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,39 @@ public void Bar(int x)
102102
}");
103103
}
104104

105+
[Fact]
106+
public async Task Issue580_EnumCastsAsync()
107+
{
108+
await TestConversionVisualBasicToCSharpAsync(@"
109+
Public Class EnumToString
110+
Enum Tes As Short
111+
None = 0
112+
TEST2 = 2
113+
End Enum
114+
Private Sub TEest2(aEnum As Tes)
115+
Dim sxtr_Tmp As String = ""Use"" & CShort(aEnum).ToString
116+
Dim si_Txt As Short = CShort(2 ^ Tes.TEST2)
117+
End Sub
118+
End Class",
119+
@"using System;
120+
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
121+
122+
public partial class EnumToString
123+
{
124+
public enum Tes : short
125+
{
126+
None = 0,
127+
TEST2 = 2
128+
}
129+
130+
private void TEest2(Tes aEnum)
131+
{
132+
string sxtr_Tmp = ""Use"" + ((short)aEnum).ToString();
133+
short si_Txt = Conversions.ToShort(Math.Pow(2, (double)Tes.TEST2));
134+
}
135+
}");
136+
}
137+
105138
[Fact]
106139
public async Task IntToEnumArgAsync()
107140
{

0 commit comments

Comments
 (0)