Fix late bound method name assignment, added tests (#43344)

* Fix late bound method name assignment, added tests

* Refactor bindDynamicallyNamedthisPropertyAssignment

* PR comments

* Rollback allowJscheck fix
This commit is contained in:
Armando Aguirre 2021-06-16 12:46:00 -07:00 committed by GitHub
parent 971133d5d0
commit d0159a8891
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 8 deletions

View File

@ -410,13 +410,15 @@ namespace ts {
* @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
* @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
*/
function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean): Symbol {
Debug.assert(!hasDynamicName(node));
function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): Symbol {
Debug.assert(isComputedName || !hasDynamicName(node));
const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default";
// The exported symbol for an export default function/class node is always named "default"
const name = isDefaultExport && parent ? InternalSymbolName.Default : getDeclarationName(node);
const name = isComputedName ? InternalSymbolName.Computed
: isDefaultExport && parent ? InternalSymbolName.Default
: getDeclarationName(node);
let symbol: Symbol | undefined;
if (name === undefined) {
@ -2929,7 +2931,7 @@ namespace ts {
constructorSymbol.members = constructorSymbol.members || createSymbolTable();
// It's acceptable for multiple 'this' assignments of the same identifier to occur
if (hasDynamicName(node)) {
bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol);
bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol, constructorSymbol.members);
}
else {
declareSymbol(constructorSymbol.members, constructorSymbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
@ -2948,7 +2950,7 @@ namespace ts {
const containingClass = thisContainer.parent;
const symbolTable = hasSyntacticModifier(thisContainer, ModifierFlags.Static) ? containingClass.symbol.exports! : containingClass.symbol.members!;
if (hasDynamicName(node)) {
bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol);
bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol, symbolTable);
}
else {
declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.None, /*isReplaceableByMethod*/ true);
@ -2972,8 +2974,8 @@ namespace ts {
}
}
function bindDynamicallyNamedThisPropertyAssignment(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol) {
bindAnonymousDeclaration(node, SymbolFlags.Property, InternalSymbolName.Computed);
function bindDynamicallyNamedThisPropertyAssignment(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol, symbolTable: SymbolTable) {
declareSymbol(symbolTable, symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true, /*isComputedName*/ true);
addLateBoundAssignmentDeclarationToSymbol(node, symbol);
}

View File

@ -10243,7 +10243,7 @@ namespace ts {
if (!symbol.declarations) {
symbol.declarations = [member];
}
else {
else if(!member.symbol.isReplaceableByMethod) {
symbol.declarations.push(member);
}
if (symbolFlags & SymbolFlags.Value) {

View File

@ -0,0 +1,18 @@
//// [lateBoundMethodNameAssigmentJS.js]
const _symbol = Symbol("_sym");
export class MyClass {
constructor() {
this[_symbol] = this[_symbol].bind(this);
}
async [_symbol]() { }
}
//// [lateBoundMethodNameAssigmentJS.d.ts]
export class MyClass {
[_symbol]: any;
}
declare const _symbol: unique symbol;
export {};

View File

@ -0,0 +1,21 @@
=== tests/cases/compiler/lateBoundMethodNameAssigmentJS.js ===
const _symbol = Symbol("_sym");
>_symbol : Symbol(_symbol, Decl(lateBoundMethodNameAssigmentJS.js, 0, 5))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
export class MyClass {
>MyClass : Symbol(MyClass, Decl(lateBoundMethodNameAssigmentJS.js, 0, 31))
constructor() {
this[_symbol] = this[_symbol].bind(this);
>this : Symbol(MyClass, Decl(lateBoundMethodNameAssigmentJS.js, 0, 31))
>_symbol : Symbol(_symbol, Decl(lateBoundMethodNameAssigmentJS.js, 0, 5))
>this : Symbol(MyClass, Decl(lateBoundMethodNameAssigmentJS.js, 0, 31))
>_symbol : Symbol(_symbol, Decl(lateBoundMethodNameAssigmentJS.js, 0, 5))
>this : Symbol(MyClass, Decl(lateBoundMethodNameAssigmentJS.js, 0, 31))
}
async [_symbol]() { }
>[_symbol] : Symbol(MyClass[_symbol], Decl(lateBoundMethodNameAssigmentJS.js, 4, 5))
>_symbol : Symbol(_symbol, Decl(lateBoundMethodNameAssigmentJS.js, 0, 5))
}

View File

@ -0,0 +1,29 @@
=== tests/cases/compiler/lateBoundMethodNameAssigmentJS.js ===
const _symbol = Symbol("_sym");
>_symbol : unique symbol
>Symbol("_sym") : unique symbol
>Symbol : SymbolConstructor
>"_sym" : "_sym"
export class MyClass {
>MyClass : MyClass
constructor() {
this[_symbol] = this[_symbol].bind(this);
>this[_symbol] = this[_symbol].bind(this) : error
>this[_symbol] : error
>this : this
>_symbol : unique symbol
>this[_symbol].bind(this) : error
>this[_symbol].bind : error
>this[_symbol] : any
>this : this
>_symbol : unique symbol
>bind : any
>this : this
}
async [_symbol]() { }
>[_symbol] : error
>_symbol : unique symbol
}

View File

@ -0,0 +1,15 @@
// @allowJs: true
// @checkJs: true
// @emitDeclarationOnly: true
// @strict: true
// @target: es6
// @declaration: true
// @filename: lateBoundMethodNameAssigmentJS.js
const _symbol = Symbol("_sym");
export class MyClass {
constructor() {
this[_symbol] = this[_symbol].bind(this);
}
async [_symbol]() { }
}