diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index d36a5465..8ff9b99c 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -1501,10 +1501,13 @@ }; Assign.prototype.compileConditional = function(o) { - var left, rite, _ref3; - _ref3 = this.variable.cacheReference(o), left = _ref3[0], rite = _ref3[1]; + var left, right, _ref3; + _ref3 = this.variable.cacheReference(o), left = _ref3[0], right = _ref3[1]; + if (left.base instanceof Literal && left.base.value !== "this" && !o.scope.check(left.base.value)) { + throw new Error("the variable \"" + left.base.value + "\" can't be assigned with " + this.context + " because it has not been defined."); + } if (__indexOf.call(this.context, "?") >= 0) o.isExistentialEquals = true; - return new Op(this.context.slice(0, -1), left, new Assign(rite, this.value, '=')).compile(o); + return new Op(this.context.slice(0, -1), left, new Assign(right, this.value, '=')).compile(o); }; Assign.prototype.compileSplice = function(o) { diff --git a/src/nodes.coffee b/src/nodes.coffee index b19f35b1..6aee1e96 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1117,9 +1117,12 @@ exports.Assign = class Assign extends Base # operands are only evaluated once, even though we have to reference them # more than once. compileConditional: (o) -> - [left, rite] = @variable.cacheReference o + [left, right] = @variable.cacheReference o + # Disallow conditional assignment of undefined variables. + if left.base instanceof Literal and left.base.value != "this" and not o.scope.check left.base.value + throw new Error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been defined." if "?" in @context then o.isExistentialEquals = true - new Op(@context[...-1], left, new Assign(rite, @value, '=') ).compile o + new Op(@context[...-1], left, new Assign(right, @value, '=') ).compile o # Compile the assignment from an array splice literal, using JavaScript's # `Array#splice` method. diff --git a/test/assignment.coffee b/test/assignment.coffee index 3e8b9783..dd4bbb0c 100644 --- a/test/assignment.coffee +++ b/test/assignment.coffee @@ -19,12 +19,6 @@ test "unassignable values", -> for nonref in ['', '""', '0', 'f()'].concat CoffeeScript.RESERVED eq nonce, (try CoffeeScript.compile "#{nonref} = v" catch e then nonce) -test "compound assignments should not declare", -> - # TODO: make description more clear - # TODO: remove reference to Math - eq Math, (-> Math or= 0)() - - # Compound Assignment test "boolean operators", -> @@ -285,8 +279,23 @@ test "existential assignment", -> c = null c ?= nonce eq nonce, c - d ?= nonce - eq nonce, d + +test "#1627: prohibit conditional assignment of undefined variables", -> + throws (-> CoffeeScript.compile "x ?= 10"), null, "prohibit (x ?= 10)" + throws (-> CoffeeScript.compile "x ||= 10"), null, "prohibit (x ||= 10)" + throws (-> CoffeeScript.compile "x or= 10"), null, "prohibit (x or= 10)" + throws (-> CoffeeScript.compile "do -> x ?= 10"), null, "prohibit (do -> x ?= 10)" + throws (-> CoffeeScript.compile "do -> x ||= 10"), null, "prohibit (do -> x ||= 10)" + throws (-> CoffeeScript.compile "do -> x or= 10"), null, "prohibit (do -> x or= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; x ?= 10"), "allow (x = null; x ?= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; x ||= 10"), "allow (x = null; x ||= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; x or= 10"), "allow (x = null; x or= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ?= 10"), "allow (x = null; do -> x ?= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ||= 10"), "allow (x = null; do -> x ||= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; do -> x or= 10"), "allow (x = null; do -> x or= 10)" + + throws (-> CoffeeScript.compile "-> -> -> x ?= 10"), null, "prohibit (-> -> -> x ?= 10)" + doesNotThrow (-> CoffeeScript.compile "x = null; -> -> -> x ?= 10"), "allow (x = null; -> -> -> x ?= 10)" test "#1348, #1216: existential assignment compilation", -> nonce = {} @@ -295,14 +304,7 @@ test "#1348, #1216: existential assignment compilation", -> eq nonce, b #the first ?= compiles into a statement; the second ?= compiles to a ternary expression eq a ?= b ?= 1, nonce - - e ?= f ?= g ?= 1 - eq e + g, 2 - - #need to ensure the two vars are not defined, hence the strange names; - # broke earlier when using c ?= d ?= 1 because `d` is declared elsewhere - eq und1_1348 ?= und2_1348 ?= 1, 1 - + if a then a ?= 2 else a = 3 eq a, nonce