-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow digit separator after base specifier #20449
Changes from all commits
8d7fb2b
75365a4
ee697a5
b268dc1
4bad4d7
2726b4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,8 @@ public class LexicalTests | |
{ | ||
private readonly CSharpParseOptions _options; | ||
private readonly CSharpParseOptions _options6; | ||
private readonly CSharpParseOptions _options7; | ||
private readonly CSharpParseOptions _options72; | ||
private readonly CSharpParseOptions _binaryOptions; | ||
private readonly CSharpParseOptions _underscoreOptions; | ||
private readonly CSharpParseOptions _binaryUnderscoreOptions; | ||
|
@@ -24,8 +26,10 @@ public LexicalTests() | |
{ | ||
_options = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp3); | ||
_options6 = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp6); | ||
_binaryOptions = _options.WithLanguageVersion(LanguageVersion.CSharp7); | ||
_underscoreOptions = _options.WithLanguageVersion(LanguageVersion.CSharp7); | ||
_options7 = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp7); | ||
_options72 = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp7_2); | ||
_binaryOptions = _options7; | ||
_underscoreOptions = _options7; | ||
_binaryUnderscoreOptions = _binaryOptions; | ||
} | ||
|
||
|
@@ -2614,6 +2618,89 @@ public void TestNumericWithUnderscoresWithoutFeatureFlag() | |
Assert.Equal(text, token.Text); | ||
} | ||
|
||
[Fact] | ||
[Trait("Feature", "Literals")] | ||
public void TestNumericWithLeadingUnderscores() | ||
{ | ||
var text = "0x_A"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please test with multiple underscores. @gafter can confirm expected language/spec behavior. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
var token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
Assert.Equal(0, token.Errors().Length); | ||
Assert.Equal(0xA, token.Value); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0b_1"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
Assert.Equal(0, token.Errors().Length); | ||
Assert.Equal(1, token.Value); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0x__A_1L"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
Assert.Equal(0, token.Errors().Length); | ||
Assert.Equal(0xA1L, token.Value); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0b__1_1L"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
Assert.Equal(0, token.Errors().Length); | ||
Assert.Equal(0b11L, token.Value); | ||
Assert.Equal(text, token.Text); | ||
} | ||
|
||
[Fact] | ||
[Trait("Feature", "Literals")] | ||
public void TestNumericWithLeadingUnderscoresWithoutFeatureFlag() | ||
{ | ||
var text = "0x_A"; | ||
var token = LexToken(text, _options7); | ||
|
||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
var errors = token.Errors(); | ||
Assert.Equal(1, errors.Length); | ||
Assert.Equal((int)ErrorCode.ERR_FeatureNotAvailableInVersion7, errors[0].Code); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0b_1"; | ||
token = LexToken(text, _options7); | ||
|
||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
errors = token.Errors(); | ||
Assert.Equal(1, errors.Length); | ||
Assert.Equal((int)ErrorCode.ERR_FeatureNotAvailableInVersion7, errors[0].Code); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0x_1"; | ||
token = LexToken(text, _options6); | ||
|
||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
errors = token.Errors(); | ||
Assert.Equal(1, errors.Length); | ||
Assert.Equal((int)ErrorCode.ERR_FeatureNotAvailableInVersion6, errors[0].Code); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0x_123_456_789_ABC_DEF_123"; | ||
token = LexToken(text, _options6); | ||
|
||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
errors = token.Errors(); | ||
Assert.Equal(2, errors.Length); | ||
Assert.Equal((int)ErrorCode.ERR_FeatureNotAvailableInVersion6, errors[0].Code); | ||
Assert.Equal((int)ErrorCode.ERR_IntOverflow, errors[1].Code); | ||
Assert.Equal(text, token.Text); | ||
} | ||
|
||
[Fact] | ||
[Trait("Feature", "Literals")] | ||
public void TestNumericWithBadUnderscores() | ||
|
@@ -2695,7 +2782,7 @@ public void TestNumericWithBadUnderscores() | |
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add tests for |
||
|
||
text = "0x_A"; | ||
text = "0xA_"; | ||
token = LexToken(text, _underscoreOptions); | ||
|
||
Assert.NotNull(token); | ||
|
@@ -2706,8 +2793,8 @@ public void TestNumericWithBadUnderscores() | |
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0xA_"; | ||
token = LexToken(text, _underscoreOptions); | ||
text = "0b1_"; | ||
token = LexToken(text, _binaryUnderscoreOptions); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
|
@@ -2717,8 +2804,8 @@ public void TestNumericWithBadUnderscores() | |
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0b_1"; | ||
token = LexToken(text, _binaryUnderscoreOptions); | ||
text = "0x_"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
|
@@ -2728,8 +2815,8 @@ public void TestNumericWithBadUnderscores() | |
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "0b1_"; | ||
token = LexToken(text, _binaryUnderscoreOptions); | ||
text = "1E+_2"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
|
@@ -2738,6 +2825,29 @@ public void TestNumericWithBadUnderscores() | |
Assert.Equal((int)ErrorCode.ERR_InvalidNumber, errors[0].Code); | ||
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "1E-_2"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
errors = token.Errors(); | ||
Assert.Equal(1, errors.Length); | ||
Assert.Equal((int)ErrorCode.ERR_InvalidNumber, errors[0].Code); | ||
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
|
||
text = "1E_"; | ||
token = LexToken(text, _options72); | ||
|
||
Assert.NotNull(token); | ||
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind()); | ||
errors = token.Errors(); | ||
Assert.Equal(2, errors.Length); | ||
Assert.Equal((int)ErrorCode.ERR_InvalidNumber, errors[0].Code); | ||
Assert.Equal((int)ErrorCode.ERR_FloatOverflow, errors[1].Code); | ||
Assert.Equal("error CS1013: Invalid number", errors[0].ToString(EnsureEnglishUICulture.PreferredOrNull)); | ||
Assert.Equal(text, token.Text); | ||
} | ||
|
||
[Fact] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1673,6 +1673,7 @@ FullWidthRepeat: | |
Dim IntegerLiteralStart As Integer | ||
Dim UnderscoreInWrongPlace As Boolean | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider assigning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, crossed my mind, shouldn't VB flag those cases in the first place? |
||
Dim UnderscoreUsed As Boolean = False | ||
Dim LeadingUnderscoreUsed = False | ||
|
||
Dim Base As LiteralBase = LiteralBase.Decimal | ||
Dim literalKind As NumericLiteralKind = NumericLiteralKind.Integral | ||
|
@@ -1695,7 +1696,10 @@ FullWidthRepeat: | |
IntegerLiteralStart = Here | ||
Base = LiteralBase.Hexadecimal | ||
|
||
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c) | ||
If CanGet(Here) AndAlso Peek(Here) = "_"c Then | ||
LeadingUnderscoreUsed = True | ||
End If | ||
|
||
While CanGet(Here) | ||
ch = Peek(Here) | ||
If Not IsHexDigit(ch) AndAlso ch <> "_"c Then | ||
|
@@ -1713,7 +1717,10 @@ FullWidthRepeat: | |
IntegerLiteralStart = Here | ||
Base = LiteralBase.Binary | ||
|
||
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c) | ||
If CanGet(Here) AndAlso Peek(Here) = "_"c Then | ||
LeadingUnderscoreUsed = True | ||
End If | ||
|
||
While CanGet(Here) | ||
ch = Peek(Here) | ||
If Not IsBinaryDigit(ch) AndAlso ch <> "_"c Then | ||
|
@@ -1731,7 +1738,10 @@ FullWidthRepeat: | |
IntegerLiteralStart = Here | ||
Base = LiteralBase.Octal | ||
|
||
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c) | ||
If CanGet(Here) AndAlso Peek(Here) = "_"c Then | ||
LeadingUnderscoreUsed = True | ||
End If | ||
|
||
While CanGet(Here) | ||
ch = Peek(Here) | ||
If Not IsOctalDigit(ch) AndAlso ch <> "_"c Then | ||
|
@@ -2085,13 +2095,16 @@ FullWidthRepeat2: | |
|
||
If Overflows Then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI The order is now in sync with C#, an interesting case is an overflowing integer with a trailing underscore which gives 1 and 2 errors in VB and C# respectively. This is now fixed. Any other case that needs to be verified here? |
||
result = DirectCast(result.AddError(ErrorFactory.ErrorInfo(ERRID.ERR_Overflow)), SyntaxToken) | ||
ElseIf UnderscoreInWrongPlace Then | ||
result = DirectCast(result.AddError(ErrorFactory.ErrorInfo(ERRID.ERR_Syntax)), SyntaxToken) | ||
End If | ||
|
||
If UnderscoreUsed Then | ||
If UnderscoreInWrongPlace Then | ||
result = DirectCast(result.AddError(ErrorFactory.ErrorInfo(ERRID.ERR_Syntax)), SyntaxToken) | ||
ElseIf LeadingUnderscoreUsed Then | ||
result = CheckFeatureAvailability(result, Feature.LeadingDigitSeparator) | ||
ElseIf UnderscoreUsed Then | ||
result = CheckFeatureAvailability(result, Feature.DigitSeparators) | ||
End If | ||
|
||
If Base = LiteralBase.Binary Then | ||
result = CheckFeatureAvailability(result, Feature.BinaryLiterals) | ||
End If | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider switching order:
if (firstCharWasUnderscore) { ... } else if (lastCharWasUnderscore) { ... }
.#Resolved