diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 3c7c59d2ef..614e21bcec 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1470,15 +1470,15 @@ exports.Obj = Obj = (function() { class Obj extends Base { - constructor(props, generated = false) { + constructor(props, generated = false, lhs1 = false) { super(); this.generated = generated; + this.lhs = lhs1; this.objects = this.properties = props || []; } isAssignable() { var j, len1, message, prop, ref1; - this.lhs = true; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; @@ -1603,8 +1603,9 @@ exports.Arr = Arr = (function() { class Arr extends Base { - constructor(objs) { + constructor(objs, lhs1 = false) { super(); + this.lhs = lhs1; this.objects = objs || []; } @@ -1631,23 +1632,33 @@ } compileNode(o) { - var answer, compiledObjs, fragments, index, j, len1, obj; + var answer, compiledObjs, fragments, index, j, k, len1, len2, obj, ref1, unwrappedObj; if (!this.objects.length) { return [this.makeCode('[]')]; } o.indent += TAB; answer = []; - compiledObjs = (function() { - var j, len1, ref1, results; + if (this.lhs) { ref1 = this.objects; - results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { obj = ref1[j]; + unwrappedObj = obj.unwrapAll(); + if (unwrappedObj instanceof Arr || unwrappedObj instanceof Obj) { + unwrappedObj.lhs = true; + } + } + } + compiledObjs = (function() { + var k, len2, ref2, results; + ref2 = this.objects; + results = []; + for (k = 0, len2 = ref2.length; k < len2; k++) { + obj = ref2[k]; results.push(obj.compileToFragments(o, LEVEL_LIST)); } return results; }).call(this); - for (index = j = 0, len1 = compiledObjs.length; j < len1; index = ++j) { + for (index = k = 0, len2 = compiledObjs.length; k < len2; index = ++k) { fragments = compiledObjs[index]; if (index) { answer.push(this.makeCode(", ")); @@ -2350,9 +2361,11 @@ compileNode(o) { var answer, compiledName, isValue, j, name, properties, prototype, ref1, ref2, ref3, ref4, ref5, ref6, val, varBase; - if (isValue = this.variable instanceof Value) { + isValue = this.variable instanceof Value; + if (isValue) { this.variable.param = this.param; if (this.variable.isArray() || this.variable.isObject()) { + this.variable.base.lhs = true; if (!this.variable.isAssignable()) { return this.compileDestructuring(o); } @@ -2377,14 +2390,14 @@ if (typeof name.hasProperties === "function" ? name.hasProperties() : void 0) { return; } - if (message = isUnassignable(name.value)) { + message = isUnassignable(name.value); + if (message) { name.error(message); } + this.checkAssignability(o, name); if (this.moduleDeclaration) { - this.checkAssignability(o, name); return o.scope.add(name.value, this.moduleDeclaration); } else { - this.checkAssignability(o, name); return o.scope.find(name.value); } }); @@ -2782,6 +2795,7 @@ } } if (param.name instanceof Arr || param.name instanceof Obj) { + param.name.lhs = true; param.name.eachName(function(prop) { return o.scope.parameter(prop.value); }); diff --git a/src/nodes.coffee b/src/nodes.coffee index 918b7d9207..4dd66e5b17 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1108,7 +1108,7 @@ exports.Slice = class Slice extends Base # An object literal, nothing fancy. exports.Obj = class Obj extends Base - constructor: (props, @generated = no) -> + constructor: (props, @generated = no, @lhs = no) -> super() @objects = @properties = props or [] @@ -1116,10 +1116,6 @@ exports.Obj = class Obj extends Base children: ['properties'] isAssignable: -> - # Is this object the left-hand side of an assignment? If it is, this object - # is part of a destructuring assignment. - @lhs = yes - for prop in @properties # Check for reserved words. message = isUnassignable prop.unwrapAll().value @@ -1199,7 +1195,7 @@ exports.Obj = class Obj extends Base # An array literal. exports.Arr = class Arr extends Base - constructor: (objs) -> + constructor: (objs, @lhs = no) -> super() @objects = objs or [] @@ -1222,6 +1218,12 @@ exports.Arr = class Arr extends Base o.indent += TAB answer = [] + # If this array is the left-hand side of an assignment, all its children + # are too. + if @lhs + for obj in @objects + unwrappedObj = obj.unwrapAll() + unwrappedObj.lhs = yes if unwrappedObj instanceof Arr or unwrappedObj instanceof Obj compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects) for fragments, index in compiledObjs if index @@ -1727,7 +1729,8 @@ exports.Assign = class Assign extends Base # we've been assigned to, for correct internal references. If the variable # has not been seen yet within the current scope, declare it. compileNode: (o) -> - if isValue = @variable instanceof Value + isValue = @variable instanceof Value + if isValue # When compiling `@variable`, remember if it is part of a function parameter. @variable.param = @param @@ -1736,6 +1739,10 @@ exports.Assign = class Assign extends Base # in ES and we can output it as is; otherwise we `@compileDestructuring` # and convert this ES-unsupported destructuring into acceptable output. if @variable.isArray() or @variable.isObject() + # This is the left-hand side of an assignment; let `Arr` and `Obj` + # know that, so that those nodes know that they’re assignable as + # destructured variables. + @variable.base.lhs = yes return @compileDestructuring o unless @variable.isAssignable() return @compileSplice o if @variable.isSplice() @@ -1750,14 +1757,14 @@ exports.Assign = class Assign extends Base varBase.eachName (name) => return if name.hasProperties?() - name.error message if message = isUnassignable name.value + message = isUnassignable name.value + name.error message if message # `moduleDeclaration` can be `'import'` or `'export'` + @checkAssignability o, name if @moduleDeclaration - @checkAssignability o, name o.scope.add name.value, @moduleDeclaration else - @checkAssignability o, name o.scope.find name.value if @value instanceof Code @@ -2122,6 +2129,7 @@ exports.Code = class Code extends Base # Add this parameter’s reference(s) to the function scope. if param.name instanceof Arr or param.name instanceof Obj # This parameter is destructured. + param.name.lhs = yes param.name.eachName (prop) -> o.scope.parameter prop.value else