From c2b11702b3aee7abd1159c06369a4864689d30be Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 18 Oct 2019 09:51:21 -0700 Subject: [PATCH 1/2] Fix crash in expando assignment to alias This PR disallows expando assignments Fixes #34493, but disallows the prototype assignment nonetheless. --- src/compiler/binder.ts | 5 ++- .../importAliasModuleExports.errors.txt | 20 ++++++++++ .../importAliasModuleExports.symbols | 30 ++++++++++++++ .../reference/importAliasModuleExports.types | 40 +++++++++++++++++++ .../salsa/importAliasModuleExports.ts | 15 +++++++ 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/importAliasModuleExports.errors.txt create mode 100644 tests/baselines/reference/importAliasModuleExports.symbols create mode 100644 tests/baselines/reference/importAliasModuleExports.types create mode 100644 tests/cases/conformance/salsa/importAliasModuleExports.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e3fcc4624cf57..7a2117f772b72 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2691,7 +2691,8 @@ namespace ts { const flags = exportAssignmentIsAlias(node) ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class : SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule; - declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); + const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); + setValueDeclaration(symbol, node); } function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) { @@ -2870,7 +2871,7 @@ namespace ts { } }); } - if (containerIsClass && namespaceSymbol) { + if (containerIsClass && namespaceSymbol && namespaceSymbol.valueDeclaration) { addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class); } return namespaceSymbol; diff --git a/tests/baselines/reference/importAliasModuleExports.errors.txt b/tests/baselines/reference/importAliasModuleExports.errors.txt new file mode 100644 index 0000000000000..ea3dd9ef0e018 --- /dev/null +++ b/tests/baselines/reference/importAliasModuleExports.errors.txt @@ -0,0 +1,20 @@ +tests/cases/conformance/salsa/main.js(2,13): error TS2339: Property 'foo' does not exist on type 'Alias'. +tests/cases/conformance/salsa/main.js(4,9): error TS2339: Property 'foo' does not exist on type 'Alias'. + + +==== tests/cases/conformance/salsa/mod1.js (0 errors) ==== + class Alias { + bar() { return 1 } + } + module.exports = Alias; + +==== tests/cases/conformance/salsa/main.js (2 errors) ==== + import A from './mod1' + A.prototype.foo = 0 + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'Alias'. + new A().bar + new A().foo + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'Alias'. + \ No newline at end of file diff --git a/tests/baselines/reference/importAliasModuleExports.symbols b/tests/baselines/reference/importAliasModuleExports.symbols new file mode 100644 index 0000000000000..4ab081d608f8f --- /dev/null +++ b/tests/baselines/reference/importAliasModuleExports.symbols @@ -0,0 +1,30 @@ +=== tests/cases/conformance/salsa/mod1.js === +class Alias { +>Alias : Symbol(Alias, Decl(mod1.js, 0, 0)) + + bar() { return 1 } +>bar : Symbol(Alias.bar, Decl(mod1.js, 0, 13)) +} +module.exports = Alias; +>module.exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0)) +>module : Symbol(export=, Decl(mod1.js, 2, 1)) +>exports : Symbol(export=, Decl(mod1.js, 2, 1)) +>Alias : Symbol(Alias, Decl(mod1.js, 0, 0)) + +=== tests/cases/conformance/salsa/main.js === +import A from './mod1' +>A : Symbol(A, Decl(main.js, 0, 6)) + +A.prototype.foo = 0 +>A.prototype : Symbol(A.prototype) +>A : Symbol(A, Decl(main.js, 0, 6)) +>prototype : Symbol(A.prototype) + +new A().bar +>new A().bar : Symbol(A.bar, Decl(mod1.js, 0, 13)) +>A : Symbol(A, Decl(main.js, 0, 6)) +>bar : Symbol(A.bar, Decl(mod1.js, 0, 13)) + +new A().foo +>A : Symbol(A, Decl(main.js, 0, 6)) + diff --git a/tests/baselines/reference/importAliasModuleExports.types b/tests/baselines/reference/importAliasModuleExports.types new file mode 100644 index 0000000000000..1303ba6e8c4b4 --- /dev/null +++ b/tests/baselines/reference/importAliasModuleExports.types @@ -0,0 +1,40 @@ +=== tests/cases/conformance/salsa/mod1.js === +class Alias { +>Alias : Alias + + bar() { return 1 } +>bar : () => number +>1 : 1 +} +module.exports = Alias; +>module.exports = Alias : typeof Alias +>module.exports : typeof Alias +>module : { "tests/cases/conformance/salsa/mod1": typeof Alias; } +>exports : typeof Alias +>Alias : typeof Alias + +=== tests/cases/conformance/salsa/main.js === +import A from './mod1' +>A : typeof A + +A.prototype.foo = 0 +>A.prototype.foo = 0 : 0 +>A.prototype.foo : any +>A.prototype : A +>A : typeof A +>prototype : A +>foo : any +>0 : 0 + +new A().bar +>new A().bar : () => number +>new A() : A +>A : typeof A +>bar : () => number + +new A().foo +>new A().foo : any +>new A() : A +>A : typeof A +>foo : any + diff --git a/tests/cases/conformance/salsa/importAliasModuleExports.ts b/tests/cases/conformance/salsa/importAliasModuleExports.ts new file mode 100644 index 0000000000000..5e850206676a2 --- /dev/null +++ b/tests/cases/conformance/salsa/importAliasModuleExports.ts @@ -0,0 +1,15 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @esModuleInterop: true +// @filename: mod1.js +class Alias { + bar() { return 1 } +} +module.exports = Alias; + +// @filename: main.js +import A from './mod1' +A.prototype.foo = 0 +new A().bar +new A().foo From f70a8b63f55202f6bac2650c16085b55cbf10235 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 18 Oct 2019 10:35:50 -0700 Subject: [PATCH 2/2] Revert mistaken changes --- src/compiler/binder.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7a2117f772b72..0b0ef4825b385 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2691,8 +2691,7 @@ namespace ts { const flags = exportAssignmentIsAlias(node) ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class : SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule; - const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); - setValueDeclaration(symbol, node); + declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); } function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) {