Skip to content

Commit 3edd2d6

Browse files
Merge pull request #28896 from JoshuaKGoldberg/n-identifier-after-numeric-literal-error
Better error message for invalid bigint literals ending with n
2 parents 4f7184a + 0d7e3f8 commit 3edd2d6

9 files changed

+58
-43
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,14 @@
10151015
"category": "Error",
10161016
"code": 1351
10171017
},
1018+
"A bigint literal cannot use exponential notation.": {
1019+
"category": "Error",
1020+
"code": 1352
1021+
},
1022+
"A bigint literal must be an integer.": {
1023+
"category": "Error",
1024+
"code": 1353
1025+
},
10181026

10191027
"Duplicate identifier '{0}'.": {
10201028
"category": "Error",

src/compiler/scanner.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ namespace ts {
976976
}
977977

978978
if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) {
979-
checkForIdentifierStartAfterNumericLiteral();
979+
checkForIdentifierStartAfterNumericLiteral(start, decimalFragment === undefined && !!(tokenFlags & TokenFlags.Scientific));
980980
return {
981981
type: SyntaxKind.NumericLiteral,
982982
value: "" + +result // if value is not an integer, it can be safely coerced to a number
@@ -985,20 +985,31 @@ namespace ts {
985985
else {
986986
tokenValue = result;
987987
const type = checkBigIntSuffix(); // if value is an integer, check whether it is a bigint
988-
checkForIdentifierStartAfterNumericLiteral();
988+
checkForIdentifierStartAfterNumericLiteral(start);
989989
return { type, value: tokenValue };
990990
}
991991
}
992992

993-
function checkForIdentifierStartAfterNumericLiteral() {
993+
function checkForIdentifierStartAfterNumericLiteral(numericStart: number, isScientific?: boolean) {
994994
if (!isIdentifierStart(text.charCodeAt(pos), languageVersion)) {
995995
return;
996996
}
997997

998998
const identifierStart = pos;
999999
const { length } = scanIdentifierParts();
1000-
error(Diagnostics.An_identifier_or_keyword_cannot_immediately_follow_a_numeric_literal, identifierStart, length);
1001-
pos = identifierStart;
1000+
1001+
if (length === 1 && text[identifierStart] === "n") {
1002+
if (isScientific) {
1003+
error(Diagnostics.A_bigint_literal_cannot_use_exponential_notation, numericStart, identifierStart - numericStart + 1);
1004+
}
1005+
else {
1006+
error(Diagnostics.A_bigint_literal_must_be_an_integer, numericStart, identifierStart - numericStart + 1);
1007+
}
1008+
}
1009+
else {
1010+
error(Diagnostics.An_identifier_or_keyword_cannot_immediately_follow_a_numeric_literal, identifierStart, length);
1011+
pos = identifierStart;
1012+
}
10021013
}
10031014

10041015
function scanOctalDigits(): number {

tests/baselines/reference/identifierStartAfterNumericLiteral.errors.txt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ tests/cases/compiler/identifierStartAfterNumericLiteral.ts(6,5): error TS1124: D
99
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(6,6): error TS2538: Type 'null' cannot be used as an index type.
1010
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(7,2): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
1111
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(8,4): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
12+
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(9,1): error TS1352: A bigint literal cannot use exponential notation.
1213
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(9,3): error TS1124: Digit expected.
13-
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(9,3): error TS2304: Cannot find name 'n'.
14+
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(9,5): error TS2538: Type 'null' cannot be used as an index type.
15+
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(10,1): error TS1352: A bigint literal cannot use exponential notation.
1416
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(10,5): error TS1124: Digit expected.
15-
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(10,5): error TS2304: Cannot find name 'n'.
17+
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(10,7): error TS2538: Type 'null' cannot be used as an index type.
1618
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(11,2): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
1719
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(11,2): error TS2304: Cannot find name 'a'.
1820
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(12,4): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
@@ -35,7 +37,7 @@ tests/cases/compiler/identifierStartAfterNumericLiteral.ts(26,5): error TS1351:
3537
tests/cases/compiler/identifierStartAfterNumericLiteral.ts(26,5): error TS2304: Cannot find name 'abc'.
3638

3739

38-
==== tests/cases/compiler/identifierStartAfterNumericLiteral.ts (35 errors) ====
40+
==== tests/cases/compiler/identifierStartAfterNumericLiteral.ts (37 errors) ====
3941
let valueIn = 3in[null];
4042
~~
4143
!!! error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
@@ -67,15 +69,19 @@ tests/cases/compiler/identifierStartAfterNumericLiteral.ts(26,5): error TS2304:
6769
~~
6870
!!! error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
6971
3en[null]
72+
~~~
73+
!!! error TS1352: A bigint literal cannot use exponential notation.
7074

7175
!!! error TS1124: Digit expected.
72-
~
73-
!!! error TS2304: Cannot find name 'n'.
76+
~~~~
77+
!!! error TS2538: Type 'null' cannot be used as an index type.
7478
123en[null]
79+
~~~~~
80+
!!! error TS1352: A bigint literal cannot use exponential notation.
7581

7682
!!! error TS1124: Digit expected.
77-
~
78-
!!! error TS2304: Cannot find name 'n'.
83+
~~~~
84+
!!! error TS2538: Type 'null' cannot be used as an index type.
7985
1a
8086
~
8187
!!! error TS1351: An identifier or keyword cannot immediately follow a numeric literal.

tests/baselines/reference/identifierStartAfterNumericLiteral.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,8 @@ a[null];
3737
123e[null];
3838
3 in [null];
3939
123 in [null];
40-
3e;
41-
n[null];
42-
123e;
43-
n[null];
40+
3en[null];
41+
123en[null];
4442
1;
4543
a;
4644
123;

tests/baselines/reference/identifierStartAfterNumericLiteral.types

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,13 @@ let valueIn = 3in[null];
4141
>null : null
4242

4343
3en[null]
44-
>3e : 3
45-
>n[null] : any
46-
>n : any
44+
>3en[null] : any
45+
>3en : 3
4746
>null : null
4847

4948
123en[null]
50-
>123e : 123
51-
>n[null] : any
52-
>n : any
49+
>123en[null] : any
50+
>123en : 123
5351
>null : null
5452

5553
1a

tests/baselines/reference/parseBigInt.errors.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
tests/cases/compiler/parseBigInt.ts(51,20): error TS2736: Operator '+' cannot be applied to type '123n'.
22
tests/cases/compiler/parseBigInt.ts(52,23): error TS2736: Operator '+' cannot be applied to type '291n'.
33
tests/cases/compiler/parseBigInt.ts(56,25): error TS1005: ',' expected.
4-
tests/cases/compiler/parseBigInt.ts(57,25): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
5-
tests/cases/compiler/parseBigInt.ts(58,22): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
6-
tests/cases/compiler/parseBigInt.ts(59,28): error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
4+
tests/cases/compiler/parseBigInt.ts(57,22): error TS1352: A bigint literal cannot use exponential notation.
5+
tests/cases/compiler/parseBigInt.ts(58,19): error TS1353: A bigint literal must be an integer.
6+
tests/cases/compiler/parseBigInt.ts(59,26): error TS1353: A bigint literal must be an integer.
77
tests/cases/compiler/parseBigInt.ts(60,23): error TS1177: Binary digit expected.
88
tests/cases/compiler/parseBigInt.ts(61,20): error TS1178: Octal digit expected.
99
tests/cases/compiler/parseBigInt.ts(62,20): error TS1125: Hexadecimal digit expected.
@@ -81,14 +81,14 @@ tests/cases/compiler/parseBigInt.ts(70,72): error TS2345: Argument of type '3' i
8181
~
8282
!!! error TS1005: ',' expected.
8383
{ const scientific = 1e2n; }
84-
~
85-
!!! error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
84+
~~~~
85+
!!! error TS1352: A bigint literal cannot use exponential notation.
8686
{ const decimal = 4.1n; }
87-
~
88-
!!! error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
87+
~~~~
88+
!!! error TS1353: A bigint literal must be an integer.
8989
{ const leadingDecimal = .1n; }
90-
~
91-
!!! error TS1351: An identifier or keyword cannot immediately follow a numeric literal.
90+
~~~
91+
!!! error TS1353: A bigint literal must be an integer.
9292
const emptyBinary = 0bn; // should error but infer 0n
9393

9494
!!! error TS1177: Binary digit expected.

tests/baselines/reference/parseBigInt.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ const unaryPlusHex = +0x123n;
123123
const legacyOct = 0123, n;
124124
}
125125
{
126-
const scientific = 1e2, n;
126+
const scientific = 1e2n;
127127
}
128128
{
129-
const decimal = 4.1, n;
129+
const decimal = 4.1n;
130130
}
131131
{
132-
const leadingDecimal = .1, n;
132+
const leadingDecimal = .1n;
133133
}
134134
const emptyBinary = 0n; // should error but infer 0n
135135
const emptyOct = 0n; // should error but infer 0n

tests/baselines/reference/parseBigInt.symbols

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,12 @@ const unaryPlusHex = +0x123n;
131131

132132
{ const scientific = 1e2n; }
133133
>scientific : Symbol(scientific, Decl(parseBigInt.ts, 56, 7))
134-
>n : Symbol(n, Decl(parseBigInt.ts, 56, 24))
135134

136135
{ const decimal = 4.1n; }
137136
>decimal : Symbol(decimal, Decl(parseBigInt.ts, 57, 7))
138-
>n : Symbol(n, Decl(parseBigInt.ts, 57, 21))
139137

140138
{ const leadingDecimal = .1n; }
141139
>leadingDecimal : Symbol(leadingDecimal, Decl(parseBigInt.ts, 58, 7))
142-
>n : Symbol(n, Decl(parseBigInt.ts, 58, 27))
143140

144141
const emptyBinary = 0bn; // should error but infer 0n
145142
>emptyBinary : Symbol(emptyBinary, Decl(parseBigInt.ts, 59, 5))

tests/baselines/reference/parseBigInt.types

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,15 @@ const unaryPlusHex = +0x123n;
180180

181181
{ const scientific = 1e2n; }
182182
>scientific : 100
183-
>1e2 : 100
184-
>n : any
183+
>1e2n : 100
185184

186185
{ const decimal = 4.1n; }
187186
>decimal : 4.1
188-
>4.1 : 4.1
189-
>n : any
187+
>4.1n : 4.1
190188

191189
{ const leadingDecimal = .1n; }
192190
>leadingDecimal : 0.1
193-
>.1 : 0.1
194-
>n : any
191+
>.1n : 0.1
195192

196193
const emptyBinary = 0bn; // should error but infer 0n
197194
>emptyBinary : 0n

0 commit comments

Comments
 (0)