diff --git a/CodeConverter/CSharp/LiteralConversions.cs b/CodeConverter/CSharp/LiteralConversions.cs index 41c9b940..50af9f1a 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: @@ -68,7 +68,7 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor } 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,17 +149,35 @@ private static (string textForUser, ExpressionSyntax MaybeFullExpression) Conver string hexValue = textForUser.Substring(2); textForUser = "0x" + hexValue; - int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + bool replaceWithUncheckedExpr = false; + LiteralExpressionSyntax hexValueExpr = default; + CSSyntaxKind csSyntaxKind = CSSyntaxKind.None; + + 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) { + 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 - if (parsedHexValue < 0) { - var hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue)); + // 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.IntKeyword)), + SyntaxFactory.PredefinedType(SyntaxFactory.Token(csSyntaxKind)), hexValueExpr)); return (null, checkedExpr); } @@ -172,20 +190,20 @@ 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; } return (textForUser, null); } -} \ No newline at end of file +} diff --git a/Tests/CSharp/SpecialConversionTests.cs b/Tests/CSharp/SpecialConversionTests.cs index b07f5025..9b0713ca 100644 --- a/Tests/CSharp/SpecialConversionTests.cs +++ b/Tests/CSharp/SpecialConversionTests.cs @@ -219,6 +219,27 @@ private string numstr(double aDouble) }"); } + [Fact] + public async Task Issue1147_LargeNumericHexLiteralsAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Class Issue1147 + Private Const LargeUInt As UInteger = &HFFFFFFFEUI + 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 int LargeInt = unchecked((int)0xFFFFFFFE); + private const long LargeLong = unchecked((long)0xFFFFFFFFFFFFFFFE); +}"); + } + + [Fact] public async Task TestConstCharacterConversionsAsync() {