Skip to content
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

fix35982: allow BigIntLiteral to parse as PropertyName for literal object and indices #58608

Merged
merged 17 commits into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18753,14 +18753,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
if (accessNode) {
const indexNode = getIndexNodeForAccessExpression(accessNode);
if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
if (indexNode.kind !== SyntaxKind.BigIntLiteral && indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, "" + (indexType as StringLiteralType | NumberLiteralType).value, typeToString(objectType));
}
else if (indexType.flags & (TypeFlags.String | TypeFlags.Number)) {
error(indexNode, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, typeToString(objectType), typeToString(indexType));
}
else {
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
const typeString = indexNode.kind === SyntaxKind.BigIntLiteral ? "bigint" : typeToString(indexType);
iisaduan marked this conversation as resolved.
Show resolved Hide resolved
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeString);
}
}
if (isTypeAny(indexType)) {
Expand Down Expand Up @@ -43889,6 +43890,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return;
}

if (node.name.kind === SyntaxKind.BigIntLiteral) {
error(node.name, Diagnostics.A_bigint_literal_cannot_be_used_as_a_property_name);
}

const type = convertAutoToAny(getTypeOfSymbol(symbol));
if (node === symbol.valueDeclaration) {
// Node is the primary declaration of the symbol, just validate the initializer
Expand Down Expand Up @@ -51088,6 +51093,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (name.kind === SyntaxKind.NumericLiteral) {
checkGrammarNumericLiteral(name);
}
if (name.kind === SyntaxKind.BigIntLiteral) {
addErrorOrSuggestion(/*isError*/ true, createDiagnosticForNode(name, Diagnostics.A_bigint_literal_cannot_be_used_as_a_property_name));
}
currentKind = DeclarationMeaning.PropertyAssignment;
break;
case SyntaxKind.MethodDeclaration:
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,10 @@
"category": "Error",
"code": 1538
},
"A 'bigint' literal cannot be used as a property name.": {
"category": "Error",
"code": 1539
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down
9 changes: 6 additions & 3 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
attachFileToDiagnostics,
AwaitExpression,
BaseNodeFactory,
BigIntLiteral,
BinaryExpression,
BinaryOperatorToken,
BindingElement,
Expand Down Expand Up @@ -2692,16 +2693,17 @@ namespace Parser {
function isLiteralPropertyName(): boolean {
return tokenIsIdentifierOrKeyword(token()) ||
token() === SyntaxKind.StringLiteral ||
token() === SyntaxKind.NumericLiteral;
token() === SyntaxKind.NumericLiteral ||
token() === SyntaxKind.BigIntLiteral;
}

function isImportAttributeName(): boolean {
return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.StringLiteral;
}

function parsePropertyNameWorker(allowComputedPropertyNames: boolean): PropertyName {
if (token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral) {
const node = parseLiteralNode() as StringLiteral | NumericLiteral;
if (token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral) {
const node = parseLiteralNode() as StringLiteral | NumericLiteral | BigIntLiteral;
node.text = internIdentifier(node.text);
return node;
}
Expand Down Expand Up @@ -8058,6 +8060,7 @@ namespace Parser {
tokenIsIdentifierOrKeyword(token()) ||
token() === SyntaxKind.StringLiteral ||
token() === SyntaxKind.NumericLiteral ||
token() === SyntaxKind.BigIntLiteral ||
token() === SyntaxKind.AsteriskToken ||
token() === SyntaxKind.OpenBracketToken
) {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/transformers/destructuring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
isArrayBindingElement,
isArrayBindingOrAssignmentElement,
isArrayBindingOrAssignmentPattern,
isBigIntLiteral,
isBindingElement,
isBindingName,
isBindingOrAssignmentElement,
Expand Down Expand Up @@ -558,7 +559,7 @@ function createDestructuringPropertyAccess(flattenContext: FlattenContext, value
const argumentExpression = ensureIdentifier(flattenContext, Debug.checkDefined(visitNode(propertyName.expression, flattenContext.visitor, isExpression)), /*reuseIdentifierExpressions*/ false, /*location*/ propertyName);
return flattenContext.context.factory.createElementAccessExpression(value, argumentExpression);
}
else if (isStringOrNumericLiteralLike(propertyName)) {
else if (isStringOrNumericLiteralLike(propertyName) || isBigIntLiteral(propertyName)) {
const argumentExpression = factory.cloneNode(propertyName);
return flattenContext.context.factory.createElementAccessExpression(value, argumentExpression);
}
Expand Down
14 changes: 11 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1721,7 +1721,14 @@ export interface QualifiedName extends Node, FlowContainer {

export type EntityName = Identifier | QualifiedName;

export type PropertyName = Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier;
export type PropertyName =
| Identifier
| StringLiteral
| NoSubstitutionTemplateLiteral
| NumericLiteral
| ComputedPropertyName
| PrivateIdentifier
| BigIntLiteral;
iisaduan marked this conversation as resolved.
Show resolved Hide resolved

export type MemberName = Identifier | PrivateIdentifier;

Expand Down Expand Up @@ -2340,7 +2347,8 @@ export interface LiteralTypeNode extends TypeNode {

export interface StringLiteral extends LiteralExpression, Declaration {
readonly kind: SyntaxKind.StringLiteral;
/** @internal */ readonly textSourceNode?: Identifier | StringLiteralLike | NumericLiteral | PrivateIdentifier | JsxNamespacedName; // Allows a StringLiteral to get its text from another node (used by transforms).
/** @internal */
readonly textSourceNode?: Identifier | StringLiteralLike | NumericLiteral | PrivateIdentifier | JsxNamespacedName | BigIntLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
/**
* Note: this is only set when synthesizing a node, not during parsing.
*
Expand All @@ -2350,7 +2358,7 @@ export interface StringLiteral extends LiteralExpression, Declaration {
}

export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral;
export type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral | JsxNamespacedName;
export type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral | JsxNamespacedName | BigIntLiteral;

export interface TemplateLiteralTypeNode extends TypeNode {
kind: SyntaxKind.TemplateLiteralType;
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,7 @@ export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemp
return name.emitNode?.autoGenerate ? undefined : name.escapedText;
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return escapeLeadingUnderscores(name.text);
case SyntaxKind.ComputedPropertyName:
Expand Down Expand Up @@ -5220,6 +5221,7 @@ export function getPropertyNameForPropertyNameNode(name: PropertyName | JsxAttri
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:
return escapeLeadingUnderscores(name.text);
case SyntaxKind.ComputedPropertyName:
const nameExpression = name.expression;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4422,7 +4422,7 @@ declare namespace ts {
readonly right: Identifier;
}
type EntityName = Identifier | QualifiedName;
type PropertyName = Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier;
type PropertyName = Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier | BigIntLiteral;
type MemberName = Identifier | PrivateIdentifier;
type DeclarationName = PropertyName | JsxAttributeName | StringLiteralLike | ElementAccessExpression | BindingPattern | EntityNameExpression;
interface Declaration extends Node {
Expand Down Expand Up @@ -4773,7 +4773,7 @@ declare namespace ts {
readonly kind: SyntaxKind.StringLiteral;
}
type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral;
type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral | JsxNamespacedName;
type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral | JsxNamespacedName | BigIntLiteral;
interface TemplateLiteralTypeNode extends TypeNode {
kind: SyntaxKind.TemplateLiteralType;
readonly head: TemplateHead;
Expand Down
69 changes: 69 additions & 0 deletions tests/baselines/reference/bigintArbirtraryIdentifier.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
badExport.ts(1,10): error TS2304: Cannot find name 'foo'.
badExport.ts(1,17): error TS1003: Identifier expected.
badExport.ts(1,20): error TS1128: Declaration or statement expected.
badExport2.ts(1,10): error TS1003: Identifier expected.
badExport2.ts(1,16): error TS2304: Cannot find name 'foo'.
badExport2.ts(1,20): error TS1128: Declaration or statement expected.
badImport.ts(1,10): error TS1003: Identifier expected.
badImport.ts(1,10): error TS1141: String literal expected.
badImport.ts(1,20): error TS1128: Declaration or statement expected.
badImport.ts(1,22): error TS1434: Unexpected keyword or identifier.
badImport.ts(1,22): error TS2304: Cannot find name 'from'.
badImport2.ts(1,17): error TS1003: Identifier expected.
badImport2.ts(1,17): error TS1141: String literal expected.
badImport2.ts(1,20): error TS1128: Declaration or statement expected.
badImport2.ts(1,22): error TS1434: Unexpected keyword or identifier.
badImport2.ts(1,22): error TS2304: Cannot find name 'from'.


==== foo.ts (0 errors) ====
const foo = 0n;
export { foo as "0n" };

==== correctUse.ts (0 errors) ====
import { "0n" as foo } from "./foo";
export { foo as "0n" };

==== badImport.ts (5 errors) ====
import { 0n as foo } from "./foo";
~~
!!! error TS1003: Identifier expected.
~~~~~~~~~
!!! error TS1141: String literal expected.
~
!!! error TS1128: Declaration or statement expected.
~~~~
!!! error TS1434: Unexpected keyword or identifier.
~~~~
!!! error TS2304: Cannot find name 'from'.

==== badImport2.ts (5 errors) ====
import { foo as 0n } from "./foo";
~~
!!! error TS1003: Identifier expected.
~~
!!! error TS1141: String literal expected.
~
!!! error TS1128: Declaration or statement expected.
~~~~
!!! error TS1434: Unexpected keyword or identifier.
~~~~
!!! error TS2304: Cannot find name 'from'.

==== badExport.ts (3 errors) ====
export { foo as 0n };
~~~
!!! error TS2304: Cannot find name 'foo'.
~~
!!! error TS1003: Identifier expected.
~
!!! error TS1128: Declaration or statement expected.

==== badExport2.ts (3 errors) ====
export { 0n as foo };
~~
!!! error TS1003: Identifier expected.
~~~
!!! error TS2304: Cannot find name 'foo'.
~
!!! error TS1128: Declaration or statement expected.
44 changes: 44 additions & 0 deletions tests/baselines/reference/bigintArbirtraryIdentifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//// [tests/cases/compiler/bigintArbirtraryIdentifier.ts] ////

//// [foo.ts]
const foo = 0n;
export { foo as "0n" };

//// [correctUse.ts]
import { "0n" as foo } from "./foo";
export { foo as "0n" };

//// [badImport.ts]
import { 0n as foo } from "./foo";

//// [badImport2.ts]
import { foo as 0n } from "./foo";

//// [badExport.ts]
export { foo as 0n };

//// [badExport2.ts]
export { 0n as foo };

//// [foo.js]
const foo = 0n;
export { foo as "0n" };
//// [correctUse.js]
import { "0n" as foo } from "./foo";
export { foo as "0n" };
//// [badImport.js]
from;
"./foo";
export {};
//// [badImport2.js]
from;
"./foo";
export {};
//// [badExport.js]
export { foo as };
0n;
;
//// [badExport2.js]
0n;
;
export {};
34 changes: 34 additions & 0 deletions tests/baselines/reference/bigintArbirtraryIdentifier.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [tests/cases/compiler/bigintArbirtraryIdentifier.ts] ////

=== foo.ts ===
const foo = 0n;
>foo : Symbol(foo, Decl(foo.ts, 0, 5))

export { foo as "0n" };
>foo : Symbol(foo, Decl(foo.ts, 0, 5))
>"0n" : Symbol("0n", Decl(foo.ts, 1, 8))

=== correctUse.ts ===
import { "0n" as foo } from "./foo";
>foo : Symbol(foo, Decl(correctUse.ts, 0, 8))

export { foo as "0n" };
>foo : Symbol(foo, Decl(correctUse.ts, 0, 8))
>"0n" : Symbol("0n", Decl(correctUse.ts, 1, 8))

=== badImport.ts ===
import { 0n as foo } from "./foo";
>foo : Symbol(foo)

=== badImport2.ts ===
import { foo as 0n } from "./foo";
> : Symbol((Missing), Decl(badImport2.ts, 0, 8))

=== badExport.ts ===
export { foo as 0n };
> : Symbol((Missing), Decl(badExport.ts, 0, 8))

=== badExport2.ts ===
export { 0n as foo };
>foo : Symbol(foo)

64 changes: 64 additions & 0 deletions tests/baselines/reference/bigintArbirtraryIdentifier.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//// [tests/cases/compiler/bigintArbirtraryIdentifier.ts] ////

=== foo.ts ===
const foo = 0n;
>foo : 0n
> : ^^
>0n : 0n
> : ^^

export { foo as "0n" };
>foo : 0n
> : ^^
>"0n" : 0n
> : ^^

=== correctUse.ts ===
import { "0n" as foo } from "./foo";
>foo : 0n
> : ^^

export { foo as "0n" };
>foo : 0n
> : ^^
>"0n" : 0n
> : ^^

=== badImport.ts ===
import { 0n as foo } from "./foo";
>0n as foo : foo
> : ^^^
>0n : 0n
> : ^^
>from : any
> : ^^^
>"./foo" : "./foo"
> : ^^^^^^^

=== badImport2.ts ===
import { foo as 0n } from "./foo";
>foo : any
> : ^^^
> : any
> : ^^^
>from : any
> : ^^^
>"./foo" : "./foo"
> : ^^^^^^^

=== badExport.ts ===
export { foo as 0n };
>foo : any
> : ^^^
> : any
> : ^^^
>0n : 0n
> : ^^

=== badExport2.ts ===
export { 0n as foo };
>0n as foo : foo
> : ^^^
>0n : 0n
> : ^^

Loading