From c24caebf79828ec446aaa87704c3af6dc943d619 Mon Sep 17 00:00:00 2001 From: gaschd Date: Mon, 4 Nov 2024 22:53:21 +0100 Subject: [PATCH 1/5] Added test case for issue #1147 containing large hex literals --- Tests/CSharp/SpecialConversionTests.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Tests/CSharp/SpecialConversionTests.cs b/Tests/CSharp/SpecialConversionTests.cs index b07f5025..df2a4023 100644 --- a/Tests/CSharp/SpecialConversionTests.cs +++ b/Tests/CSharp/SpecialConversionTests.cs @@ -219,6 +219,25 @@ private string numstr(double aDouble) }"); } + [Fact] + public async Task Issue1147_LargeNumericHexAndBinaryLiteralsAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Class Issue1147 + Private Const LargeUInt As UInteger = &HFFFFFFFEUI + Private Const LargeULong As ULong = &HFFFFFFFFFFFFFFFEUL + Private Const LargeLong As Long = &HFFFFFFFFFFFFFFFEL +End Class", @" +public partial class Issue1147 +{ + private const uint LargeUInt = 0xFFFFFFFEU; + private const ulong LargeULong = 0xFFFFFFFFFFFFFFFEUL; + private const long LargeLong = 0xFFFFFFFFFFFFFFFEL; +}"); + } + + [Fact] public async Task TestConstCharacterConversionsAsync() { From fb0301b247ac83c0714982863963d06b4e67ca82 Mon Sep 17 00:00:00 2001 From: gaschd Date: Mon, 4 Nov 2024 22:55:58 +0100 Subject: [PATCH 2/5] constraining special hex literal case to integer values --- CodeConverter/CSharp/LiteralConversions.cs | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/CodeConverter/CSharp/LiteralConversions.cs b/CodeConverter/CSharp/LiteralConversions.cs index 41c9b940..6ecf7050 100644 --- a/CodeConverter/CSharp/LiteralConversions.cs +++ b/CodeConverter/CSharp/LiteralConversions.cs @@ -149,19 +149,22 @@ private static (string textForUser, ExpressionSyntax MaybeFullExpression) Conver string hexValue = textForUser.Substring(2); textForUser = "0x" + hexValue; - int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); - - // This is a very special case where for 8 digit hex strings, C# interprets them as unsigned ints, but VB interprets them as ints - // This can lead to a compile error if assigned to an int in VB. So in a case like 0x91234567, we generate `unchecked((int)0x91234567)` - // This way the value looks pretty close to before and remains a compile time constant - if (parsedHexValue < 0) { - var hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue)); - var checkedExpr = SyntaxFactory.CheckedExpression( - CSSyntaxKind.UncheckedExpression, - SyntaxFactory.CastExpression( - SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.IntKeyword)), - hexValueExpr)); - return (null, checkedExpr); + // ulong/long/uint hex literals have suffixes, so we just handle ints + if (value is int) { + int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + + // This is a very special case where for 8 digit hex strings, C# interprets them as unsigned ints, but VB interprets them as ints + // This can lead to a compile error if assigned to an int in VB. So in a case like 0x91234567, we generate `unchecked((int)0x91234567)` + // This way the value looks pretty close to before and remains a compile time constant + if (parsedHexValue < 0) { + var hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue)); + var checkedExpr = SyntaxFactory.CheckedExpression( + CSSyntaxKind.UncheckedExpression, + SyntaxFactory.CastExpression( + SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.IntKeyword)), + hexValueExpr)); + return (null, checkedExpr); + } } } else if (canBeBinaryOrHex && textForUser.StartsWith("&B", StringComparison.OrdinalIgnoreCase)) { textForUser = "0b" + textForUser.Substring(2); From 586c35e9a3e17c85b5e6a0fba4f71cb183e9628c Mon Sep 17 00:00:00 2001 From: gaschd Date: Mon, 4 Nov 2024 23:17:40 +0100 Subject: [PATCH 3/5] changing test case to also check large int and long hex literals --- Tests/CSharp/SpecialConversionTests.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/CSharp/SpecialConversionTests.cs b/Tests/CSharp/SpecialConversionTests.cs index df2a4023..9b0713ca 100644 --- a/Tests/CSharp/SpecialConversionTests.cs +++ b/Tests/CSharp/SpecialConversionTests.cs @@ -220,20 +220,22 @@ private string numstr(double aDouble) } [Fact] - public async Task Issue1147_LargeNumericHexAndBinaryLiteralsAsync() + public async Task Issue1147_LargeNumericHexLiteralsAsync() { await TestConversionVisualBasicToCSharpAsync( @" Public Class Issue1147 Private Const LargeUInt As UInteger = &HFFFFFFFEUI - Private Const LargeULong As ULong = &HFFFFFFFFFFFFFFFEUL + Private Const LargeULong As ULong = &HFFFFFFFFFFFFFFFEUL + Private Const LargeInt As Integer = &HFFFFFFFE Private Const LargeLong As Long = &HFFFFFFFFFFFFFFFEL End Class", @" public partial class Issue1147 { private const uint LargeUInt = 0xFFFFFFFEU; private const ulong LargeULong = 0xFFFFFFFFFFFFFFFEUL; - private const long LargeLong = 0xFFFFFFFFFFFFFFFEL; + private const int LargeInt = unchecked((int)0xFFFFFFFE); + private const long LargeLong = unchecked((long)0xFFFFFFFFFFFFFFFE); }"); } From 565886fef3b48fad736a107f25a7e8d9f1fea217 Mon Sep 17 00:00:00 2001 From: gaschd Date: Mon, 4 Nov 2024 23:21:47 +0100 Subject: [PATCH 4/5] handling special case of 16 digit long hex literal --- CodeConverter/CSharp/LiteralConversions.cs | 73 +++++++++++++--------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/CodeConverter/CSharp/LiteralConversions.cs b/CodeConverter/CSharp/LiteralConversions.cs index 6ecf7050..4da8c158 100644 --- a/CodeConverter/CSharp/LiteralConversions.cs +++ b/CodeConverter/CSharp/LiteralConversions.cs @@ -30,7 +30,7 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor var (maybeTextForUser, maybeFullExpression) = ConvertNumericLiteralValueText(textForUser ?? value.ToString(), value); if (maybeFullExpression != null) return maybeFullExpression; textForUser = maybeTextForUser; - + switch (value) { case byte b: @@ -59,16 +59,16 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor return SyntaxFactory.LiteralExpression(CSSyntaxKind.CharacterLiteralExpression, SyntaxFactory.Literal(c)); case DateTime dt: { - var valueToOutput = dt.Date.Equals(dt) ? dt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) : dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); - return SyntaxFactory.ParseExpression("DateTime.Parse(\"" + valueToOutput + "\")"); - } + var valueToOutput = dt.Date.Equals(dt) ? dt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) : dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + return SyntaxFactory.ParseExpression("DateTime.Parse(\"" + valueToOutput + "\")"); + } default: throw new ArgumentOutOfRangeException(nameof(value), value, null); } } private static LiteralExpressionSyntax NumericLiteral(SyntaxToken literal) => SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, literal); - + /// /// See LiteralConversions.GetLiteralExpression /// These are all the literals where the type will already be correct from the literal declaration @@ -149,23 +149,38 @@ private static (string textForUser, ExpressionSyntax MaybeFullExpression) Conver string hexValue = textForUser.Substring(2); textForUser = "0x" + hexValue; - // ulong/long/uint hex literals have suffixes, so we just handle ints - if (value is int) { - int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + bool replaceWithUncheckedExpr = false; + LiteralExpressionSyntax hexValueExpr = default; + CSSyntaxKind csSyntaxKind = CSSyntaxKind.None; - // This is a very special case where for 8 digit hex strings, C# interprets them as unsigned ints, but VB interprets them as ints - // This can lead to a compile error if assigned to an int in VB. So in a case like 0x91234567, we generate `unchecked((int)0x91234567)` - // This way the value looks pretty close to before and remains a compile time constant + if (value is long) { + long parsedHexValue = long.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + if (parsedHexValue < 0L) { + hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue)); + csSyntaxKind = CSSyntaxKind.LongKeyword; + replaceWithUncheckedExpr = true; + } + } else if (value is int) { + int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); if (parsedHexValue < 0) { - var hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue)); - var checkedExpr = SyntaxFactory.CheckedExpression( - CSSyntaxKind.UncheckedExpression, - SyntaxFactory.CastExpression( - SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.IntKeyword)), - hexValueExpr)); - return (null, checkedExpr); + hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue)); + csSyntaxKind = CSSyntaxKind.IntKeyword; + replaceWithUncheckedExpr = true; } } + + // This is a very special case where for 8 digit hex strings, C# interprets them as unsigned ints, but VB interprets them as ints + // This can lead to a compile error if assigned to an int in VB. So in a case like 0x91234567, we generate `unchecked((int)0x91234567)` + // This way the value looks pretty close to before and remains a compile time constant + // The same applies to 16 digit hex strings that are converted to long + if (replaceWithUncheckedExpr) { + var checkedExpr = SyntaxFactory.CheckedExpression( + CSSyntaxKind.UncheckedExpression, + SyntaxFactory.CastExpression( + SyntaxFactory.PredefinedType(SyntaxFactory.Token(csSyntaxKind)), + hexValueExpr)); + return (null, checkedExpr); + } } else if (canBeBinaryOrHex && textForUser.StartsWith("&B", StringComparison.OrdinalIgnoreCase)) { textForUser = "0b" + textForUser.Substring(2); } else if (textForUser.StartsWith("&", StringComparison.OrdinalIgnoreCase)) { @@ -175,17 +190,17 @@ private static (string textForUser, ExpressionSyntax MaybeFullExpression) Conver } if (value switch { - ulong _ => "UL", - long _ => "L", - uint _ => "U", - int _ => "", - ushort _ => "", - short _ => "", - double _ => "d", - float _ => "f", - decimal _ => "m", - _ => default - } is {} suffix ) { + ulong _ => "UL", + long _ => "L", + uint _ => "U", + int _ => "", + ushort _ => "", + short _ => "", + double _ => "d", + float _ => "f", + decimal _ => "m", + _ => default + } is { } suffix) { textForUser += suffix; } From cfcd02081f94d829cb38fb98e35bff5e5eaef2e1 Mon Sep 17 00:00:00 2001 From: Graham Date: Sat, 9 Nov 2024 10:59:14 +0000 Subject: [PATCH 5/5] Fix whitespace --- CodeConverter/CSharp/LiteralConversions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CodeConverter/CSharp/LiteralConversions.cs b/CodeConverter/CSharp/LiteralConversions.cs index 4da8c158..50af9f1a 100644 --- a/CodeConverter/CSharp/LiteralConversions.cs +++ b/CodeConverter/CSharp/LiteralConversions.cs @@ -59,9 +59,9 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor return SyntaxFactory.LiteralExpression(CSSyntaxKind.CharacterLiteralExpression, SyntaxFactory.Literal(c)); case DateTime dt: { - var valueToOutput = dt.Date.Equals(dt) ? dt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) : dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); - return SyntaxFactory.ParseExpression("DateTime.Parse(\"" + valueToOutput + "\")"); - } + var valueToOutput = dt.Date.Equals(dt) ? dt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) : dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + return SyntaxFactory.ParseExpression("DateTime.Parse(\"" + valueToOutput + "\")"); + } default: throw new ArgumentOutOfRangeException(nameof(value), value, null); } @@ -206,4 +206,4 @@ private static (string textForUser, ExpressionSyntax MaybeFullExpression) Conver return (textForUser, null); } -} \ No newline at end of file +}