提交 51e2f2dd 编写于 作者: E Evan Hauck

Add features flag to binary literals and digit separators

上级 4badaf18
......@@ -8828,6 +8828,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to binary literals.
/// </summary>
internal static string IDS_FeatureBinaryLiteral {
get {
return ResourceManager.GetString("IDS_FeatureBinaryLiteral", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to collection initializer.
/// </summary>
......@@ -8864,6 +8873,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to digit separators.
/// </summary>
internal static string IDS_FeatureDigitSeparator {
get {
return ResourceManager.GetString("IDS_FeatureDigitSeparator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to dynamic.
/// </summary>
......
......@@ -4599,6 +4599,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="IDS_AwaitInCatchAndFinally" xml:space="preserve">
<value>await in catch blocks and finally blocks</value>
</data>
<data name="IDS_FeatureBinaryLiteral" xml:space="preserve">
<value>binary literals</value>
</data>
<data name="IDS_FeatureDigitSeparator" xml:space="preserve">
<value>digit separators</value>
</data>
<data name="ERR_UnescapedCurly" xml:space="preserve">
<value>A '{0}' character must be escaped (by doubling) in an interpolated string.</value>
</data>
......
......@@ -111,6 +111,9 @@ internal enum MessageID
IDS_FeatureInterpolatedStrings = MessageBase + 12702,
IDS_OperationCausedStackOverflow = MessageBase + 12703,
IDS_AwaitInCatchAndFinally = MessageBase + 12704,
IDS_FeatureBinaryLiteral = MessageBase + 12705,
IDS_FeatureDigitSeparator = MessageBase + 12706,
}
// Message IDs may refer to strings that need to be localized.
......@@ -144,10 +147,21 @@ public static LocalizableErrorArgument Localize(this MessageID id)
return new LocalizableErrorArgument(id);
}
// Returns the string to be used in the /features flag switch to enable the MessageID feature.
// Always call this before RequiredVersion:
// If this method returns null, call RequiredVersion and use that.
// If this method returns non-null, use that.
// Features should be mutually exclusive between RequiredFeature and RequiredVersion.
// (hence the above rule - RequiredVersion throws when RequiredFeature returns non-null)
internal static string RequiredFeature(this MessageID feature)
{
switch (feature)
{
case MessageID.IDS_FeatureBinaryLiteral:
return "binaryLiterals";
case MessageID.IDS_FeatureDigitSeparator:
return "digitSeparators";
default:
return null;
}
......
......@@ -29,7 +29,7 @@ internal static bool IsHexDigit(char c)
/// <returns>true if the character is a binary digit.</returns>
internal static bool IsBinaryDigit(char c)
{
return c == '0' || c == '1';
return c == '0' | c == '1';
}
/// <summary>
......@@ -52,6 +52,16 @@ internal static int HexValue(char c)
return (c >= '0' && c <= '9') ? c - '0' : (c & 0xdf) - 'A' + 10;
}
/// <summary>
/// Returns the value of a binary Unicode character.
/// </summary>
/// <param name="c">The Unicode character.</param>
internal static int BinaryValue(char c)
{
Debug.Assert(IsBinaryDigit(c));
return c - '0';
}
/// <summary>
/// Returns the value of a decimal Unicode character.
/// </summary>
......
......@@ -921,27 +921,37 @@ private bool ScanInteger()
}
// Allows underscores in integers, except at beginning and end
private void ScanNumericLiteralSingleInteger(StringBuilder builder, ref bool underscoreInWrongPlace)
private void ScanNumericLiteralSingleInteger(StringBuilder builder, ref bool underscoreInWrongPlace, ref bool usedUnderscore, bool isHex, bool isBinary)
{
char ch;
bool lastCharWasUnderscore = false;
if (TextWindow.PeekChar() == '_')
{
underscoreInWrongPlace = true;
}
while (((ch = TextWindow.PeekChar()) >= '0' && ch <= '9') || ch == '_')
char ch;
var lastCharWasUnderscore = false;
while (true)
{
if (ch != '_')
ch = TextWindow.PeekChar();
if (ch == '_')
{
builder.Append(ch);
lastCharWasUnderscore = false;
usedUnderscore = true;
lastCharWasUnderscore = true;
}
else if ((isHex && !SyntaxFacts.IsHexDigit(ch))
|| (isBinary && !SyntaxFacts.IsBinaryDigit(ch))
|| (!isHex && !isBinary && !SyntaxFacts.IsDecDigit(ch)))
{
break;
}
else
{
lastCharWasUnderscore = true;
_builder.Append(ch);
lastCharWasUnderscore = false;
}
TextWindow.AdvanceChar();
}
if (lastCharWasUnderscore)
{
underscoreInWrongPlace = true;
......@@ -962,6 +972,7 @@ private bool ScanNumericLiteral(ref TokenInfo info)
bool hasUSuffix = false;
bool hasLSuffix = false;
bool underscoreInWrongPlace = false;
bool usedUnderscore = false;
ch = TextWindow.PeekChar();
if (ch == '0')
......@@ -974,6 +985,7 @@ private bool ScanNumericLiteral(ref TokenInfo info)
}
else if (ch == 'b' || ch == 'B')
{
CheckFeatureAvailability(MessageID.IDS_FeatureBinaryLiteral);
TextWindow.AdvanceChar(2);
isBinary = true;
}
......@@ -983,40 +995,7 @@ private bool ScanNumericLiteral(ref TokenInfo info)
{
// It's OK if it has no digits after the '0x' -- we'll catch it in ScanNumericLiteral
// and give a proper error then.
if (TextWindow.PeekChar() == '_')
{
underscoreInWrongPlace = true;
}
var lastCharWasUnderscore = false;
while (true)
{
ch = TextWindow.PeekChar();
if (ch == '_')
{
lastCharWasUnderscore = true;
}
else
{
if (isHex && !SyntaxFacts.IsHexDigit(ch))
{
break;
}
if (isBinary && !SyntaxFacts.IsBinaryDigit(ch))
{
break;
}
_builder.Append(ch);
lastCharWasUnderscore = false;
}
TextWindow.AdvanceChar();
}
if (lastCharWasUnderscore)
{
underscoreInWrongPlace = true;
}
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace, ref usedUnderscore, isHex, isBinary);
if ((ch = TextWindow.PeekChar()) == 'L' || ch == 'l')
{
......@@ -1046,7 +1025,7 @@ private bool ScanNumericLiteral(ref TokenInfo info)
}
else
{
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace);
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace, ref usedUnderscore, isHex: false, isBinary: false);
if (this.ModeIs(LexerMode.DebuggerSyntax) && TextWindow.PeekChar() == '#')
{
......@@ -1067,7 +1046,7 @@ private bool ScanNumericLiteral(ref TokenInfo info)
_builder.Append(ch);
TextWindow.AdvanceChar();
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace);
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace, ref usedUnderscore, isHex: false, isBinary: false);
}
else if (_builder.Length == 0)
{
......@@ -1089,7 +1068,7 @@ private bool ScanNumericLiteral(ref TokenInfo info)
TextWindow.AdvanceChar();
}
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace);
ScanNumericLiteralSingleInteger(_builder, ref underscoreInWrongPlace, ref usedUnderscore, isHex: false, isBinary: false);
}
if (hasExponent || hasDecimal)
......@@ -1160,6 +1139,10 @@ private bool ScanNumericLiteral(ref TokenInfo info)
{
this.AddError(MakeError(start, TextWindow.Position - start, ErrorCode.ERR_InvalidNumber));
}
if (usedUnderscore)
{
CheckFeatureAvailability(MessageID.IDS_FeatureDigitSeparator);
}
info.Kind = SyntaxKind.NumericLiteralToken;
info.Text = TextWindow.GetText(true);
......@@ -1285,18 +1268,16 @@ private bool ScanNumericLiteral(ref TokenInfo info)
private bool TryParseBinaryUInt64(string text, out ulong value)
{
value = 0;
for (int i = 0; i < text.Length; i++)
foreach (char c in text)
{
// if uppermost bit is set, then the next bitshift will overflow
if ((value & 0x8000000000000000) != 0)
{
return false;
}
if (!SyntaxFacts.IsBinaryDigit(text[i]))
{
return false;
}
var bit = (ulong)SyntaxFacts.DecValue(text[i]);
// We shouldn't ever get a string that's nonbinary (see ScanNumericLiteral),
// so don't explicitly check for it (there's a debug assert in SyntaxFacts)
var bit = (ulong)SyntaxFacts.BinaryValue(c);
value = (value << 1) | bit;
}
return true;
......
......@@ -1046,7 +1046,6 @@ protected TNode CheckFeatureAvailability<TNode>(TNode node, MessageID feature, b
}
var featureName = feature.Localize();
var requiredVersion = feature.RequiredVersion();
if (feature.RequiredFeature() != null)
{
......@@ -1060,6 +1059,8 @@ protected TNode CheckFeatureAvailability<TNode>(TNode node, MessageID feature, b
}
else
{
var requiredVersion = feature.RequiredVersion();
if (forceWarning)
{
SyntaxDiagnosticInfo rawInfo = new SyntaxDiagnosticInfo(availableVersion.GetErrorCode(), featureName, requiredVersion.Localize());
......
......@@ -17,20 +17,48 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
public class LexicalTests
{
private readonly CSharpParseOptions _options;
private readonly CSharpParseOptions _binaryOptions;
private readonly CSharpParseOptions _underscoreOptions;
private readonly CSharpParseOptions _binaryUnderscoreOptions;
public LexicalTests()
{
_options = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp3);
var binaryLiterals = new[] { new KeyValuePair<string, string>("binaryLiterals", "true") };
var digitSeparators = new[] { new KeyValuePair<string, string>("digitSeparators", "true") };
_binaryOptions = _options.WithFeatures(binaryLiterals);
_underscoreOptions = _options.WithFeatures(digitSeparators);
_binaryUnderscoreOptions = _options.WithFeatures(binaryLiterals.Concat(digitSeparators));
}
private SyntaxToken Lex(string text)
private IEnumerable<SyntaxToken> Lex(string text, CSharpParseOptions options = null)
{
return SyntaxFactory.ParseToken(text);
return SyntaxFactory.ParseTokens(text, options: options);
}
private SyntaxToken LexToken(string text)
private SyntaxToken LexToken(string text, CSharpParseOptions options = null)
{
return Lex(text);
SyntaxToken result = default(SyntaxToken);
foreach (var token in Lex(text, options))
{
if (result.Kind() == SyntaxKind.None)
{
result = token;
}
else if (token.Kind() == SyntaxKind.EndOfFileToken)
{
continue;
}
else
{
Assert.True(false, "More than one token was lexed: " + token);
}
}
if (result.Kind() == SyntaxKind.None)
{
Assert.True(false, "No tokens were lexed");
}
return result;
}
private SyntaxToken DebuggerLex(string text)
......@@ -671,7 +699,7 @@ public void TestMultiLineVerbatimStringLiteral()
public void TestStringLiteralWithNewLine()
{
var text = "\"literal\r\nwith new line\"";
var token = LexToken(text);
var token = Lex(text).First();
Assert.NotNull(token);
Assert.Equal(SyntaxKind.StringLiteralToken, token.Kind());
......@@ -1075,7 +1103,7 @@ public void TestCharacterLiteralWithNewline()
{
var value = "a";
var text = "'a\r'";
var token = LexToken(text);
var token = Lex(text).First();
Assert.NotNull(token);
Assert.Equal(SyntaxKind.CharacterLiteralToken, token.Kind());
......@@ -1091,7 +1119,7 @@ public void TestCharacterLiteralWithNewline()
public void TestCharacterLiteralThatsTooSmallWithNewline()
{
var text = "'\r'";
var token = LexToken(text);
var token = Lex(text).First();
Assert.NotNull(token);
Assert.Equal(SyntaxKind.CharacterLiteralToken, token.Kind());
......@@ -1109,7 +1137,7 @@ public void TestCharacterLiteralThatsTooBigWithNewline()
{
var value = "a";
var text = "'ab\r'";
var token = LexToken(text);
var token = Lex(text).First();
Assert.NotNull(token);
Assert.Equal(SyntaxKind.CharacterLiteralToken, token.Kind());
......@@ -1914,13 +1942,27 @@ public void TestNumericHexLiteralWithUpperUnsignedAndUpperLongSpecifier()
Assert.Equal(value, token.Value);
}
[Fact]
[Trait("Feature", "Literals")]
public void TestNumericBinaryLiteralWithoutFeatureFlag()
{
var text = "0b1";
var token = LexToken(text);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
var errors = token.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_FeatureIsExperimental, errors[0].Code);
Assert.Equal(text, token.Text);
}
[Fact]
[Trait("Feature", "Literals")]
public void TestNumericBinaryLiteralWithUnsignedAndLongSpecifier()
{
var value = 0x123ul;
var text = "0b100100011ul";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -1930,7 +1972,7 @@ public void TestNumericBinaryLiteralWithUnsignedAndLongSpecifier()
Assert.Equal(value, token.Value);
text = "0b100100011lu";
token = LexToken(text);
token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -1946,7 +1988,7 @@ public void TestNumericBinaryLiteralWithUpperUnsignedAndLongSpecifier()
{
var value = 0x123Ul;
var text = "0b100100011Ul";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -1956,7 +1998,7 @@ public void TestNumericBinaryLiteralWithUpperUnsignedAndLongSpecifier()
Assert.Equal(value, token.Value);
text = "0b100100011lU";
token = LexToken(text);
token = LexToken(text, _binaryOptions);
errors = token.Errors();
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -1971,7 +2013,7 @@ public void TestNumericBinaryLiteralWithUnsignedAndUpperLongSpecifier()
{
var value = 0x123uL;
var text = "0b100100011uL";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -1981,7 +2023,7 @@ public void TestNumericBinaryLiteralWithUnsignedAndUpperLongSpecifier()
Assert.Equal(value, token.Value);
text = "0b100100011Lu";
token = LexToken(text);
token = LexToken(text, _binaryOptions);
errors = token.Errors();
Assert.NotNull(token);
......@@ -1997,7 +2039,7 @@ public void TestNumericBinaryLiteralWithUpperUnsignedAndUpperLongSpecifier()
{
var value = 0x123UL;
var text = "0b100100011UL";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2007,7 +2049,7 @@ public void TestNumericBinaryLiteralWithUpperUnsignedAndUpperLongSpecifier()
Assert.Equal(value, token.Value);
text = "0b100100011LU";
token = LexToken(text);
token = LexToken(text, _binaryOptions);
errors = token.Errors();
Assert.NotNull(token);
......@@ -2320,7 +2362,7 @@ public void TestNumericBinaryLiteral()
{
var value = 0x123;
var text = "0b100100011";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2336,7 +2378,7 @@ public void TestNumericBinaryLiteralWithUnsignedSpecifier()
{
var value = 0x123u;
var text = "0b100100011u";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2352,7 +2394,7 @@ public void TestNumericBinaryLiteralWithLongSpecifier()
{
var value = 0x123L;
var text = "0b100100011L";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2368,7 +2410,7 @@ public void TestNumeric8DigitBinaryLiteral()
{
var value = 0xAA;
var text = "0b10101010";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2384,7 +2426,7 @@ public void TestNumeric8DigitMaxBinaryLiteral()
{
var value = 0x7FFFFFFF;
var text = "0b1111111111111111111111111111111";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2400,7 +2442,7 @@ public void TestNumeric8DigitMaxUnsignedBinaryLiteral()
{
var value = 0xFFFFFFFF;
var text = "0b11111111111111111111111111111111";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2416,7 +2458,7 @@ public void TestNumeric8DigitMaxUnsignedBinaryLiteralWithUnsignedSpecifier()
{
var value = 0xFFFFFFFFu;
var text = "0b11111111111111111111111111111111u";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2432,7 +2474,7 @@ public void TestNumeric16DigitMaxBinaryLiteral()
{
var value = 0x7FFFFFFFFFFFFFFF;
var text = "0b111111111111111111111111111111111111111111111111111111111111111";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2448,7 +2490,7 @@ public void TestNumeric16DigitMaxUnsignedBinaryLiteral()
{
var value = 0xFFFFFFFFFFFFFFFF;
var text = "0b1111111111111111111111111111111111111111111111111111111111111111";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2463,7 +2505,7 @@ public void TestNumeric16DigitMaxUnsignedBinaryLiteral()
public void TestNumericOverflowBinaryLiteral()
{
var text = "0b10000000000000000000000000000000000000000000000000000000000000000";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2479,7 +2521,7 @@ public void TestNumericOverflowBinaryLiteral()
public void TestNumericEmptyBinaryLiteral()
{
var text = "0b";
var token = LexToken(text);
var token = LexToken(text, _binaryOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2495,7 +2537,7 @@ public void TestNumericEmptyBinaryLiteral()
public void TestNumericWithUnderscores()
{
var text = "1_000";
var token = LexToken(text);
var token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2504,7 +2546,7 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
text = "1___0_0___0";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2513,7 +2555,7 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
text = "1_000.000_1";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2522,7 +2564,7 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
text = "1_01__0.0__10_1f";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2531,7 +2573,7 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
text = "1_01__0.0__10_1e0_1";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2540,7 +2582,7 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
text = "0xA_A";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2549,7 +2591,7 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
text = "0b1_1";
token = LexToken(text);
token = LexToken(text, _binaryUnderscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2558,19 +2600,33 @@ public void TestNumericWithUnderscores()
Assert.Equal(text, token.Text);
}
[Fact]
[Trait("Feature", "Literals")]
public void TestNumericWithUnderscoresWithoutFeatureFlag()
{
var text = "1_000";
var token = LexToken(text);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
var errors = token.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_FeatureIsExperimental, errors[0].Code);
Assert.Equal(text, token.Text);
}
[Fact]
[Trait("Feature", "Literals")]
public void TestNumericWithBadUnderscores()
{
var text = "_1000";
var token = LexToken(text);
var token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.IdentifierToken, token.Kind());
Assert.Equal(0, token.Errors().Length);
text = "1000_";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2581,7 +2637,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "1000_.0";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2596,7 +2652,7 @@ public void TestNumericWithBadUnderscores()
// text = "1000._0";
text = "1000.0_";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2607,7 +2663,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "1000.0_e1";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2618,7 +2674,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "1000.0e_1";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2629,7 +2685,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "1000.0e1_";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2640,7 +2696,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "0x_A";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2651,7 +2707,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "0xA_";
token = LexToken(text);
token = LexToken(text, _underscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2662,7 +2718,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "0b_1";
token = LexToken(text);
token = LexToken(text, _binaryUnderscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2673,7 +2729,7 @@ public void TestNumericWithBadUnderscores()
Assert.Equal(text, token.Text);
text = "0b1_";
token = LexToken(text);
token = LexToken(text, _binaryUnderscoreOptions);
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2690,7 +2746,7 @@ public void TestNumericWithTrailingDot()
{
var value = 3;
var text = "3.";
var token = LexToken(text);
var token = Lex(text).First();
Assert.NotNull(token);
Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind());
......@@ -2792,7 +2848,7 @@ public void TestDebuggerDollarIdentifiers()
[Fact]
public void TestDebuggerObjectAddressIdentifiers()
{
var token = LexToken("@0x0");
var token = Lex("@0x0").First();
Assert.Equal(SyntaxKind.BadToken, token.Kind());
VerifyError(token, ErrorCode.ERR_ExpectedVerbatimLiteral);
Assert.Equal("@", token.Text);
......@@ -2828,7 +2884,7 @@ public void TestDebuggerObjectAddressIdentifiers()
Assert.Equal("@", token.Text);
Assert.Equal("@", token.Value);
token = LexToken("@0b1c2d3e4f");
token = Lex("@0b1c2d3e4f").First();
Assert.Equal(SyntaxKind.BadToken, token.Kind());
VerifyError(token, ErrorCode.ERR_ExpectedVerbatimLiteral);
Assert.Equal("@", token.Text);
......
......@@ -1935,5 +1935,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
FEATURE_GlobalNamespace
FEATURE_NullPropagatingOperator
FEATURE_NameOfExpressions
FEATURE_DigitSeparators
FEATURE_BinaryLiterals
End Enum
End Namespace
......@@ -5998,37 +5998,20 @@ checkNullable:
''' of the parser. If it is not available a diagnostic will be added to the returned value.
''' </summary>
Private Function CheckFeatureAvailability(Of TNode As VisualBasicSyntaxNode)(feature As Feature, node As TNode) As TNode
Dim languageVersion = _scanner.Options.LanguageVersion
If CheckFeatureAvailability(languageVersion, feature) Then
If _scanner.CheckFeatureAvailability(feature) Then
Return node
End If
If feature = Feature.InterpolatedStrings Then
' Bug: It is too late in the release cycle to update localized strings. As a short term measure we will output
' an unlocalized string and fix this to be localized in the next release.
Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, languageVersion.GetErrorName(), "interpolated strings")
Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, _scanner.Options.LanguageVersion.GetErrorName(), "interpolated strings")
Else
Dim featureName = ErrorFactory.ErrorInfo(feature.GetResourceId())
Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, languageVersion.GetErrorName(), featureName)
Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, _scanner.Options.LanguageVersion.GetErrorName(), featureName)
End If
End Function
Private Function CheckFeatureAvailability(feature As Feature) As Boolean
Return CheckFeatureAvailability(_scanner.Options.LanguageVersion, feature)
End Function
Private Shared Function CheckFeatureAvailability(languageVersion As LanguageVersion, feature As Feature) As Boolean
Dim required = feature.GetLanguageVersion()
Return CInt(required) <= CInt(languageVersion)
End Function
Friend Shared Sub CheckFeatureAvailability(diagnostics As DiagnosticBag, location As Location, languageVersion As LanguageVersion, feature As Feature)
If Not CheckFeatureAvailability(languageVersion, feature) Then
Dim featureName = ErrorFactory.ErrorInfo(feature.GetResourceId())
diagnostics.Add(ERRID.ERR_LanguageVersion, location, languageVersion.GetErrorName(), featureName)
End If
End Sub
End Class
'TODO - These should be removed. Checks should be in binding.
......
......@@ -18,12 +18,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
NullPropagatingOperator
NameOfExpressions
InterpolatedStrings
DigitSeparators
BinaryLiterals
End Enum
Friend Module FeatureExtensions
<Extension>
Friend Function GetFeatureFlag(feature As Feature) As String
Select Case feature
Case Feature.DigitSeparators
Return "digitSeparators"
Case Feature.BinaryLiterals
Return "binaryLiterals"
Case Else
Return Nothing
......@@ -86,6 +93,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
Return ERRID.FEATURE_NullPropagatingOperator
Case Feature.NameOfExpressions
Return ERRID.FEATURE_NameOfExpressions
Case Feature.DigitSeparators
Return ERRID.FEATURE_DigitSeparators
Case Feature.BinaryLiterals
Return ERRID.FEATURE_BinaryLiterals
Case Else
Throw ExceptionUtilities.UnexpectedValue(feature)
End Select
......
......@@ -1555,6 +1555,7 @@ FullWidthRepeat:
Dim Here As Integer = 0
Dim IntegerLiteralStart As Integer
Dim UnderscoreInWrongPlace As Boolean
Dim UnderscoreUsed As Boolean = False
Dim Base As LiteralBase = LiteralBase.Decimal
Dim literalKind As NumericLiteralKind = NumericLiteralKind.Integral
......@@ -1577,12 +1578,15 @@ FullWidthRepeat:
IntegerLiteralStart = Here
Base = LiteralBase.Hexadecimal
UnderscoreInWrongPlace = Peek(Here) = "_"c
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c)
While CanGet(Here)
ch = Peek(Here)
If Not IsHexDigit(ch) AndAlso ch <> "_"c Then
Exit While
End If
If ch = "_"c Then
UnderscoreUsed = True
End If
Here += 1
End While
UnderscoreInWrongPlace = UnderscoreInWrongPlace Or (Peek(Here - 1) = "_"c)
......@@ -1592,12 +1596,15 @@ FullWidthRepeat:
IntegerLiteralStart = Here
Base = LiteralBase.Binary
UnderscoreInWrongPlace = Peek(Here) = "_"c
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c)
While CanGet(Here)
ch = Peek(Here)
If Not IsBinaryDigit(ch) AndAlso ch <> "_"c Then
Exit While
End If
If ch = "_"c Then
UnderscoreUsed = True
End If
Here += 1
End While
UnderscoreInWrongPlace = UnderscoreInWrongPlace Or (Peek(Here - 1) = "_"c)
......@@ -1607,12 +1614,15 @@ FullWidthRepeat:
IntegerLiteralStart = Here
Base = LiteralBase.Octal
UnderscoreInWrongPlace = Peek(Here) = "_"c
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c)
While CanGet(Here)
ch = Peek(Here)
If Not IsOctalDigit(ch) AndAlso ch <> "_"c Then
Exit While
End If
If ch = "_"c Then
UnderscoreUsed = True
End If
Here += 1
End While
UnderscoreInWrongPlace = UnderscoreInWrongPlace Or (Peek(Here - 1) = "_"c)
......@@ -1628,12 +1638,15 @@ FullWidthRepeat:
Else
' no base specifier - just go through decimal digits.
IntegerLiteralStart = Here
UnderscoreInWrongPlace = Peek(Here) = "_"c
UnderscoreInWrongPlace = (CanGet(Here) AndAlso Peek(Here) = "_"c)
While CanGet(Here)
ch = Peek(Here)
If Not IsDecimalDigit(ch) AndAlso ch <> "_"c Then
Exit While
End If
If ch = "_"c Then
UnderscoreUsed = True
End If
Here += 1
End While
If Here <> IntegerLiteralStart Then
......@@ -1959,6 +1972,13 @@ FullWidthRepeat2:
result = DirectCast(result.AddError(ErrorFactory.ErrorInfo(ERRID.ERR_Syntax)), SyntaxToken)
End If
If UnderscoreUsed Then
result = CheckFeatureAvailability(result, Feature.DigitSeparators)
End If
If Base = LiteralBase.Binary Then
result = CheckFeatureAvailability(result, Feature.BinaryLiterals)
End If
Return result
End Function
......@@ -2489,5 +2509,28 @@ baddate:
Private Function IsIdentifierStartCharacter(c As Char) As Boolean
Return (_isScanningForExpressionCompiler AndAlso c = "$"c) OrElse SyntaxFacts.IsIdentifierStartCharacter(c)
End Function
Private Function CheckFeatureAvailability(token As SyntaxToken, feature As Feature) As SyntaxToken
If CheckFeatureAvailability(feature) Then
Return token
End If
Dim errorInfo = ErrorFactory.ErrorInfo(ERRID.ERR_LanguageVersion, _options.LanguageVersion.GetErrorName(), ErrorFactory.ErrorInfo(feature.GetResourceId()))
Return DirectCast(token.AddError(errorInfo), SyntaxToken)
End Function
Friend Function CheckFeatureAvailability(feature As Feature) As Boolean
Return CheckFeatureAvailability(Me.Options, feature)
End Function
Private Shared Function CheckFeatureAvailability(parseOptions As VisualBasicParseOptions, feature As Feature) As Boolean
Dim featureFlag = feature.GetFeatureFlag()
If featureFlag IsNot Nothing Then
Return parseOptions.Features.ContainsKey(featureFlag)
End If
Dim required = feature.GetLanguageVersion()
Dim actual = parseOptions.LanguageVersion
Return CInt(required) <= CInt(actual)
End Function
End Class
End Namespace
......@@ -11604,6 +11604,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
'''<summary>
''' Looks up a localized string similar to binary literals.
'''</summary>
Friend ReadOnly Property FEATURE_BinaryLiterals() As String
Get
Return ResourceManager.GetString("FEATURE_BinaryLiterals", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to variance.
'''</summary>
......@@ -11622,6 +11631,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
'''<summary>
''' Looks up a localized string similar to digit separators.
'''</summary>
Friend ReadOnly Property FEATURE_DigitSeparators() As String
Get
Return ResourceManager.GetString("FEATURE_DigitSeparators", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to declaring a Global namespace.
'''</summary>
......
......@@ -5295,4 +5295,10 @@
<data name="FEATURE_NameOfExpressions" xml:space="preserve">
<value>'nameof' expressions</value>
</data>
<data name="FEATURE_DigitSeparators" xml:space="preserve">
<value>digit separators</value>
</data>
<data name="FEATURE_BinaryLiterals" xml:space="preserve">
<value>binary literals</value>
</data>
</root>
\ No newline at end of file
......@@ -99,7 +99,6 @@ Public Class ParseExpressionTest
Public Sub ParseIntegerLiteralTest()
ParseExpression("&H1")
ParseExpression("&O1")
ParseExpression("&B1")
End Sub
<Fact>
......
......@@ -700,6 +700,7 @@ End If]]>.Value,
Assert.Equal(LiteralBase.Decimal, tk.GetBase())
Assert.Equal(42, tk.Value)
Assert.Equal(" 4_2 ", tk.ToFullString())
Assert.Equal("error BC36716: Visual Basic 14.0 does not support digit separators.", tk.Errors().Single().ToString())
Str = " &H42L "
tk = ScanOnce(Str)
......@@ -728,6 +729,7 @@ End If]]>.Value,
Assert.Equal(LiteralBase.Binary, tk.GetBase())
Assert.Equal(&HAL, tk.Value)
Assert.Equal(" &B1010L ", tk.ToFullString())
Assert.Equal("error BC36716: Visual Basic 14.0 does not support binary literals.", tk.Errors().Single().ToString())
Str = " &B1_0_1_0L "
tk = ScanOnce(Str)
......@@ -735,6 +737,9 @@ End If]]>.Value,
Assert.Equal(LiteralBase.Binary, tk.GetBase())
Assert.Equal(&HAL, tk.Value)
Assert.Equal(" &B1_0_1_0L ", tk.ToFullString())
Assert.Equal(2, tk.Errors().Count)
Assert.Equal("error BC36716: Visual Basic 14.0 does not support digit separators.", tk.Errors()(0).ToString())
Assert.Equal("error BC36716: Visual Basic 14.0 does not support binary literals.", tk.Errors()(1).ToString())
End Sub
<Fact>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册