Skip to content

Commit a232218

Browse files
Merge pull request #1152 from gaschd/issue-1147-large-hex-literals
Fixed conversion of large uint/ulong/long hex literals
2 parents 950ee9b + cfcd020 commit a232218

File tree

2 files changed

+57
-18
lines changed

2 files changed

+57
-18
lines changed

CodeConverter/CSharp/LiteralConversions.cs

+36-18
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor
3030
var (maybeTextForUser, maybeFullExpression) = ConvertNumericLiteralValueText(textForUser ?? value.ToString(), value);
3131
if (maybeFullExpression != null) return maybeFullExpression;
3232
textForUser = maybeTextForUser;
33-
33+
3434
switch (value)
3535
{
3636
case byte b:
@@ -68,7 +68,7 @@ public static ExpressionSyntax GetLiteralExpression(object value, string textFor
6868
}
6969

7070
private static LiteralExpressionSyntax NumericLiteral(SyntaxToken literal) => SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, literal);
71-
71+
7272
/// <summary>
7373
/// See LiteralConversions.GetLiteralExpression
7474
/// 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
149149
string hexValue = textForUser.Substring(2);
150150
textForUser = "0x" + hexValue;
151151

152-
int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
152+
bool replaceWithUncheckedExpr = false;
153+
LiteralExpressionSyntax hexValueExpr = default;
154+
CSSyntaxKind csSyntaxKind = CSSyntaxKind.None;
155+
156+
if (value is long) {
157+
long parsedHexValue = long.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
158+
if (parsedHexValue < 0L) {
159+
hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue));
160+
csSyntaxKind = CSSyntaxKind.LongKeyword;
161+
replaceWithUncheckedExpr = true;
162+
}
163+
} else if (value is int) {
164+
int parsedHexValue = int.Parse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
165+
if (parsedHexValue < 0) {
166+
hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue));
167+
csSyntaxKind = CSSyntaxKind.IntKeyword;
168+
replaceWithUncheckedExpr = true;
169+
}
170+
}
153171

154172
// This is a very special case where for 8 digit hex strings, C# interprets them as unsigned ints, but VB interprets them as ints
155173
// 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)`
156174
// This way the value looks pretty close to before and remains a compile time constant
157-
if (parsedHexValue < 0) {
158-
var hexValueExpr = NumericLiteral(SyntaxFactory.Literal(textForUser, parsedHexValue));
175+
// The same applies to 16 digit hex strings that are converted to long
176+
if (replaceWithUncheckedExpr) {
159177
var checkedExpr = SyntaxFactory.CheckedExpression(
160178
CSSyntaxKind.UncheckedExpression,
161179
SyntaxFactory.CastExpression(
162-
SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.IntKeyword)),
180+
SyntaxFactory.PredefinedType(SyntaxFactory.Token(csSyntaxKind)),
163181
hexValueExpr));
164182
return (null, checkedExpr);
165183
}
@@ -172,20 +190,20 @@ private static (string textForUser, ExpressionSyntax MaybeFullExpression) Conver
172190
}
173191

174192
if (value switch {
175-
ulong _ => "UL",
176-
long _ => "L",
177-
uint _ => "U",
178-
int _ => "",
179-
ushort _ => "",
180-
short _ => "",
181-
double _ => "d",
182-
float _ => "f",
183-
decimal _ => "m",
184-
_ => default
185-
} is {} suffix ) {
193+
ulong _ => "UL",
194+
long _ => "L",
195+
uint _ => "U",
196+
int _ => "",
197+
ushort _ => "",
198+
short _ => "",
199+
double _ => "d",
200+
float _ => "f",
201+
decimal _ => "m",
202+
_ => default
203+
} is { } suffix) {
186204
textForUser += suffix;
187205
}
188206

189207
return (textForUser, null);
190208
}
191-
}
209+
}

Tests/CSharp/SpecialConversionTests.cs

+21
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,27 @@ private string numstr(double aDouble)
219219
}");
220220
}
221221

222+
[Fact]
223+
public async Task Issue1147_LargeNumericHexLiteralsAsync()
224+
{
225+
await TestConversionVisualBasicToCSharpAsync(
226+
@"
227+
Public Class Issue1147
228+
Private Const LargeUInt As UInteger = &HFFFFFFFEUI
229+
Private Const LargeULong As ULong = &HFFFFFFFFFFFFFFFEUL
230+
Private Const LargeInt As Integer = &HFFFFFFFE
231+
Private Const LargeLong As Long = &HFFFFFFFFFFFFFFFEL
232+
End Class", @"
233+
public partial class Issue1147
234+
{
235+
private const uint LargeUInt = 0xFFFFFFFEU;
236+
private const ulong LargeULong = 0xFFFFFFFFFFFFFFFEUL;
237+
private const int LargeInt = unchecked((int)0xFFFFFFFE);
238+
private const long LargeLong = unchecked((long)0xFFFFFFFFFFFFFFFE);
239+
}");
240+
}
241+
242+
222243
[Fact]
223244
public async Task TestConstCharacterConversionsAsync()
224245
{

0 commit comments

Comments
 (0)