Skip to content

Commit

Permalink
Merge pull request #1152 from gaschd/issue-1147-large-hex-literals
Browse files Browse the repository at this point in the history
Fixed conversion of large uint/ulong/long hex literals
  • Loading branch information
GrahamTheCoder authored Nov 9, 2024
2 parents 950ee9b + cfcd020 commit a232218
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 18 deletions.
54 changes: 36 additions & 18 deletions CodeConverter/CSharp/LiteralConversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -68,7 +68,7 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor
}

private static LiteralExpressionSyntax NumericLiteral(SyntaxToken literal) => SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, literal);

/// <summary>
/// See LiteralConversions.GetLiteralExpression
/// These are all the literals where the type will already be correct from the literal declaration
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
}
}
21 changes: 21 additions & 0 deletions Tests/CSharp/SpecialConversionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down

0 comments on commit a232218

Please sign in to comment.