Skip to content

Commit

Permalink
Use Roslyn to parse literal values
Browse files Browse the repository at this point in the history
Pick the right overload of SyntaxFactory.Literal() based on the parsed type of the literal. Otherwise when we interpret the tree we could have called SyntaxFactory.Literal(double) for "1UL".

Add a test for binary literal.

Fixes #78
  • Loading branch information
KirillOsenkov committed Feb 10, 2023
1 parent 0b90c3e commit 92890f9
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 76 deletions.
6 changes: 6 additions & 0 deletions src/Quoter.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,12 @@ public void TestIssue77()
Test("Foo(0x0000800000000000)", NodeKind.Expression);
}

[Fact]
public void TestBinaryLiteral()
{
Test("0b_0010_1010", NodeKind.Expression);
}

private void Test(
string sourceText,
string expected,
Expand Down
113 changes: 37 additions & 76 deletions src/Quoter/Quoter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1565,6 +1565,13 @@ private object CreateArrayOfType(IList list, Type elementType)
}
else if (argument is string str)
{
var token = SyntaxFactory.ParseToken(str);

// WARNING: the order of these checks matters, because
// we're effectively emulating overload resolution here.
// We have a list of 9 overloads of SyntaxFactory.Literal()
// and we need to pick the right overload given the literal
// type.
if (parameterType == typeof(string))
{
if (str == "null")
Expand All @@ -1583,60 +1590,45 @@ private object CreateArrayOfType(IList list, Type elementType)
return (argument, false);
}

return (ParseStringLiteral(str), true);
}
else if (parameterType == typeof(int))
{
if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
if (token.IsKind(SyntaxKind.StringLiteralToken) ||
token.IsKind(SyntaxKind.SingleLineRawStringLiteralToken) ||
token.IsKind(SyntaxKind.MultiLineRawStringLiteralToken))
{
str = str.Remove(0, 2);
return (token.ValueText, true);
}

if (int.TryParse(str, out var int32))
}
else if (
parameterType == typeof(int))
{
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is int)
{
return (int32, true);
return (token.Value, true);
}
}
else if (parameterType == typeof(double))
{
if (str.EndsWith("d", StringComparison.OrdinalIgnoreCase))
{
str = str.Substring(0, str.Length - 1);
}

if (double.TryParse(str, out var dbl))
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is double)
{
return (dbl, true);
return (token.Value, true);
}
}
else if (parameterType == typeof(float))
{
if (str.EndsWith("f", StringComparison.OrdinalIgnoreCase))
{
str = str.Substring(0, str.Length - 1);
}

if (float.TryParse(str, out var fl))
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is float)
{
return (fl, true);
return (token.Value, true);
}
}
else if (parameterType == typeof(decimal))
{
if (str.EndsWith("m", StringComparison.OrdinalIgnoreCase))
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is decimal)
{
str = str.Substring(0, str.Length - 1);
}

if (decimal.TryParse(str, out var d))
{
return (d, true);
return (token.Value, true);
}
}
else if (parameterType == typeof(char))
{
var token = SyntaxFactory.ParseToken(str);
if (token.IsKind(SyntaxKind.CharacterLiteralToken))
if (token.IsKind(SyntaxKind.CharacterLiteralToken) && token.Value is char)
{
return (token.Value, true);
}
Expand All @@ -1652,56 +1644,25 @@ private object CreateArrayOfType(IList list, Type elementType)
return (false, true);
}
}
else if (
parameterType == typeof(uint) ||
parameterType == typeof(ulong) ||
parameterType == typeof(long))
else if (parameterType == typeof(uint))
{
NumberStyles numberStyles = NumberStyles.Integer;
if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
str = str.Remove(0, 2);
numberStyles = NumberStyles.HexNumber;
}

if (str.EndsWith("lu", StringComparison.OrdinalIgnoreCase) ||
str.EndsWith("ul", StringComparison.OrdinalIgnoreCase))
{
str = str.Substring(0, str.Length - 2);
if (parameterType != typeof(ulong))
{
return (argument, false);
}
}
else if (str.EndsWith("u", StringComparison.OrdinalIgnoreCase))
{
str = str.Substring(0, str.Length - 1);
}
else if (str.EndsWith("l", StringComparison.OrdinalIgnoreCase))
{
str = str.Substring(0, str.Length - 1);
if (parameterType != typeof(long))
{
return (argument, false);
}
}

if (parameterType == typeof(uint) &&
uint.TryParse(str, numberStyles, null, out var ui))
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is uint)
{
return (ui, true);
return (token.Value, true);
}

if (parameterType == typeof(ulong) &&
ulong.TryParse(str, numberStyles, null, out var ul))
}
else if (parameterType == typeof(ulong))
{
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is ulong)
{
return (ul, true);
return (token.Value, true);
}

if (parameterType == typeof(long) &&
long.TryParse(str, numberStyles, null, out var l))
}
else if (parameterType == typeof(long))
{
if (token.IsKind(SyntaxKind.NumericLiteralToken) && token.Value is long)
{
return (l, true);
return (token.Value, true);
}
}
}
Expand Down

1 comment on commit 92890f9

@benediktfrings
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that was quick. Thank you and nice work btw!

Please sign in to comment.