From c6f11f39e2c04fdc8dc1da136ad81f5407d27081 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 23 Mar 2015 23:01:27 -0700 Subject: [PATCH 1/2] disallow assignments to imported exports --- src/compiler/checker.ts | 24 ++- ...externalModuleImmutableBindings.errors.txt | 164 ++++++++++++++++++ .../externalModuleImmutableBindings.js | 112 ++++++++++++ .../externalModuleImmutableBindings.ts | 48 +++++ 4 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/externalModuleImmutableBindings.errors.txt create mode 100644 tests/baselines/reference/externalModuleImmutableBindings.js create mode 100644 tests/cases/compiler/externalModuleImmutableBindings.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d50e440c5e935..b9e2b23559d75 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7239,7 +7239,6 @@ module ts { case SyntaxKind.ElementAccessExpression: { let index = (n).argumentExpression; let symbol = findSymbol((n).expression); - if (symbol && index && index.kind === SyntaxKind.StringLiteral) { let name = (index).text; let prop = getPropertyOfType(getTypeOfSymbol(symbol), name); @@ -7254,14 +7253,37 @@ module ts { } } + function isImportedNameFromEternalModule(n: Node): boolean { + switch (n.kind) { + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.PropertyAccessExpression: { + // all bindings for external module should be immutable + // so attempt to use a.b or a[b] as lhs will always fail + // no matter what b is + let symbol = findSymbol((n).expression); + return symbol && symbol.flags & SymbolFlags.Alias && isExternalModuleSymbol(resolveAlias(symbol)); + } + case SyntaxKind.ParenthesizedExpression: + return isImportedNameFromEternalModule((n).expression); + default: + return false; + } + } + if (!isReferenceOrErrorExpression(n)) { error(n, invalidReferenceMessage); return false; } + if (isConstVariableReference(n)) { error(n, constantVariableMessage); return false; } + + if (isImportedNameFromEternalModule(n)) { + error(n, invalidReferenceMessage); + } + return true; } diff --git a/tests/baselines/reference/externalModuleImmutableBindings.errors.txt b/tests/baselines/reference/externalModuleImmutableBindings.errors.txt new file mode 100644 index 0000000000000..09cc7b3329578 --- /dev/null +++ b/tests/baselines/reference/externalModuleImmutableBindings.errors.txt @@ -0,0 +1,164 @@ +tests/cases/compiler/f2.ts(5,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(6,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(7,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(7,7): error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. +tests/cases/compiler/f2.ts(8,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(10,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(11,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(12,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(13,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(15,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(16,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(17,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(17,8): error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. +tests/cases/compiler/f2.ts(18,1): error TS2364: Invalid left-hand side of assignment expression. +tests/cases/compiler/f2.ts(20,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(21,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(22,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(23,1): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. +tests/cases/compiler/f2.ts(25,6): error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. +tests/cases/compiler/f2.ts(26,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(27,6): error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. +tests/cases/compiler/f2.ts(28,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(29,6): error TS2406: Invalid left-hand side in 'for...in' statement. +tests/cases/compiler/f2.ts(29,12): error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. +tests/cases/compiler/f2.ts(30,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(30,12): error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. +tests/cases/compiler/f2.ts(31,6): error TS2406: Invalid left-hand side in 'for...in' statement. +tests/cases/compiler/f2.ts(32,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(34,6): error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. +tests/cases/compiler/f2.ts(35,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(36,6): error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. +tests/cases/compiler/f2.ts(37,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(38,6): error TS2406: Invalid left-hand side in 'for...in' statement. +tests/cases/compiler/f2.ts(38,13): error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. +tests/cases/compiler/f2.ts(39,6): error TS2487: Invalid left-hand side in 'for...of' statement. +tests/cases/compiler/f2.ts(39,13): error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. +tests/cases/compiler/f2.ts(40,6): error TS2406: Invalid left-hand side in 'for...in' statement. +tests/cases/compiler/f2.ts(41,6): error TS2487: Invalid left-hand side in 'for...of' statement. + + +==== tests/cases/compiler/f1.ts (0 errors) ==== + export var x = 1; + +==== tests/cases/compiler/f2.ts (38 errors) ==== + import * as stuff from 'f1'; + + var n = 'baz'; + + stuff.x = 0; + ~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + stuff['x'] = 1; + ~~~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + stuff.blah = 2; + ~~~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + ~~~~ +!!! error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. + stuff[n] = 3; + ~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + + stuff.x++; + ~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + stuff['x']++; + ~~~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + stuff['blah']++; + ~~~~~~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + stuff[n]++; + ~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + + (stuff.x) = 0; + ~~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + (stuff['x']) = 1; + ~~~~~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + (stuff.blah) = 2; + ~~~~~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + ~~~~ +!!! error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. + (stuff[n]) = 3; + ~~~~~~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + + (stuff.x)++; + ~~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + (stuff['x'])++; + ~~~~~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + (stuff['blah'])++; + ~~~~~~~~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + (stuff[n])++; + ~~~~~~~~~~ +!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. + + for (stuff.x in []) {} + ~~~~~~~ +!!! error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. + for (stuff.x of []) {} + ~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + for (stuff['x'] in []) {} + ~~~~~~~~~~ +!!! error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. + for (stuff['x'] of []) {} + ~~~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + for (stuff.blah in []) {} + ~~~~~~~~~~ +!!! error TS2406: Invalid left-hand side in 'for...in' statement. + ~~~~ +!!! error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. + for (stuff.blah of []) {} + ~~~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + ~~~~ +!!! error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. + for (stuff[n] in []) {} + ~~~~~~~~ +!!! error TS2406: Invalid left-hand side in 'for...in' statement. + for (stuff[n] of []) {} + ~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + + for ((stuff.x) in []) {} + ~~~~~~~~~ +!!! error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. + for ((stuff.x) of []) {} + ~~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + for ((stuff['x']) in []) {} + ~~~~~~~~~~~~ +!!! error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'. + for ((stuff['x']) of []) {} + ~~~~~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + for ((stuff.blah) in []) {} + ~~~~~~~~~~~~ +!!! error TS2406: Invalid left-hand side in 'for...in' statement. + ~~~~ +!!! error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. + for ((stuff.blah) of []) {} + ~~~~~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + ~~~~ +!!! error TS2339: Property 'blah' does not exist on type 'typeof "tests/cases/compiler/f1"'. + for ((stuff[n]) in []) {} + ~~~~~~~~~~ +!!! error TS2406: Invalid left-hand side in 'for...in' statement. + for ((stuff[n]) of []) {} + ~~~~~~~~~~ +!!! error TS2487: Invalid left-hand side in 'for...of' statement. + + + \ No newline at end of file diff --git a/tests/baselines/reference/externalModuleImmutableBindings.js b/tests/baselines/reference/externalModuleImmutableBindings.js new file mode 100644 index 0000000000000..546b593fa77b7 --- /dev/null +++ b/tests/baselines/reference/externalModuleImmutableBindings.js @@ -0,0 +1,112 @@ +//// [tests/cases/compiler/externalModuleImmutableBindings.ts] //// + +//// [f1.ts] +export var x = 1; + +//// [f2.ts] +import * as stuff from 'f1'; + +var n = 'baz'; + +stuff.x = 0; +stuff['x'] = 1; +stuff.blah = 2; +stuff[n] = 3; + +stuff.x++; +stuff['x']++; +stuff['blah']++; +stuff[n]++; + +(stuff.x) = 0; +(stuff['x']) = 1; +(stuff.blah) = 2; +(stuff[n]) = 3; + +(stuff.x)++; +(stuff['x'])++; +(stuff['blah'])++; +(stuff[n])++; + +for (stuff.x in []) {} +for (stuff.x of []) {} +for (stuff['x'] in []) {} +for (stuff['x'] of []) {} +for (stuff.blah in []) {} +for (stuff.blah of []) {} +for (stuff[n] in []) {} +for (stuff[n] of []) {} + +for ((stuff.x) in []) {} +for ((stuff.x) of []) {} +for ((stuff['x']) in []) {} +for ((stuff['x']) of []) {} +for ((stuff.blah) in []) {} +for ((stuff.blah) of []) {} +for ((stuff[n]) in []) {} +for ((stuff[n]) of []) {} + + + + +//// [f1.js] +exports.x = 1; +//// [f2.js] +var stuff = require('f1'); +var n = 'baz'; +stuff.x = 0; +stuff['x'] = 1; +stuff.blah = 2; +stuff[n] = 3; +stuff.x++; +stuff['x']++; +stuff['blah']++; +stuff[n]++; +(stuff.x) = 0; +(stuff['x']) = 1; +(stuff.blah) = 2; +(stuff[n]) = 3; +(stuff.x)++; +(stuff['x'])++; +(stuff['blah'])++; +(stuff[n])++; +for (stuff.x in []) { +} +for (var _i = 0, _a = []; _i < _a.length; _i++) { + stuff.x = _a[_i]; +} +for (stuff['x'] in []) { +} +for (var _b = 0, _c = []; _b < _c.length; _b++) { + stuff['x'] = _c[_b]; +} +for (stuff.blah in []) { +} +for (var _d = 0, _e = []; _d < _e.length; _d++) { + stuff.blah = _e[_d]; +} +for (stuff[n] in []) { +} +for (var _f = 0, _g = []; _f < _g.length; _f++) { + stuff[n] = _g[_f]; +} +for ((stuff.x) in []) { +} +for (var _h = 0, _j = []; _h < _j.length; _h++) { + (stuff.x) = _j[_h]; +} +for ((stuff['x']) in []) { +} +for (var _k = 0, _l = []; _k < _l.length; _k++) { + (stuff['x']) = _l[_k]; +} +for ((stuff.blah) in []) { +} +for (var _m = 0, _o = []; _m < _o.length; _m++) { + (stuff.blah) = _o[_m]; +} +for ((stuff[n]) in []) { +} +for (var _p = 0, _q = []; _p < _q.length; _p++) { + (stuff[n]) = _q[_p]; +} diff --git a/tests/cases/compiler/externalModuleImmutableBindings.ts b/tests/cases/compiler/externalModuleImmutableBindings.ts new file mode 100644 index 0000000000000..9ed3d278a3af3 --- /dev/null +++ b/tests/cases/compiler/externalModuleImmutableBindings.ts @@ -0,0 +1,48 @@ +// @module: commonjs +// @Filename: f1.ts +export var x = 1; + +// @Filename: f2.ts +import * as stuff from 'f1'; + +var n = 'baz'; + +stuff.x = 0; +stuff['x'] = 1; +stuff.blah = 2; +stuff[n] = 3; + +stuff.x++; +stuff['x']++; +stuff['blah']++; +stuff[n]++; + +(stuff.x) = 0; +(stuff['x']) = 1; +(stuff.blah) = 2; +(stuff[n]) = 3; + +(stuff.x)++; +(stuff['x'])++; +(stuff['blah'])++; +(stuff[n])++; + +for (stuff.x in []) {} +for (stuff.x of []) {} +for (stuff['x'] in []) {} +for (stuff['x'] of []) {} +for (stuff.blah in []) {} +for (stuff.blah of []) {} +for (stuff[n] in []) {} +for (stuff[n] of []) {} + +for ((stuff.x) in []) {} +for ((stuff.x) of []) {} +for ((stuff['x']) in []) {} +for ((stuff['x']) of []) {} +for ((stuff.blah) in []) {} +for ((stuff.blah) of []) {} +for ((stuff[n]) in []) {} +for ((stuff[n]) of []) {} + + From 3a8df4106de50ea7c929f53629836e02d59a4bc9 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 23 Mar 2015 23:03:02 -0700 Subject: [PATCH 2/2] fix typo in function name --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b9e2b23559d75..05ea4099e2d5f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7253,7 +7253,7 @@ module ts { } } - function isImportedNameFromEternalModule(n: Node): boolean { + function isImportedNameFromExternalModule(n: Node): boolean { switch (n.kind) { case SyntaxKind.ElementAccessExpression: case SyntaxKind.PropertyAccessExpression: { @@ -7264,7 +7264,7 @@ module ts { return symbol && symbol.flags & SymbolFlags.Alias && isExternalModuleSymbol(resolveAlias(symbol)); } case SyntaxKind.ParenthesizedExpression: - return isImportedNameFromEternalModule((n).expression); + return isImportedNameFromExternalModule((n).expression); default: return false; } @@ -7280,7 +7280,7 @@ module ts { return false; } - if (isImportedNameFromEternalModule(n)) { + if (isImportedNameFromExternalModule(n)) { error(n, invalidReferenceMessage); }