Skip to content

Commit 537aa2d

Browse files
committed
destructuring fixes [Fixes jashkenas#4673] [Fixes jashkenas#4657]
1 parent df9d4a2 commit 537aa2d

File tree

4 files changed

+72
-80
lines changed

4 files changed

+72
-80
lines changed

lib/coffeescript/nodes.js

Lines changed: 27 additions & 47 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/nodes.coffee

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,8 +1427,8 @@ exports.Obj = class Obj extends Base
14271427

14281428
# Check if object contains splat.
14291429
hasSplat: ->
1430-
splat = yes for prop in @properties when prop instanceof Splat
1431-
splat ? no
1430+
return yes for prop in @properties when prop instanceof Splat
1431+
no
14321432

14331433
compileNode: (o) ->
14341434
props = @properties
@@ -1510,7 +1510,7 @@ exports.Obj = class Obj extends Base
15101510
prop.eachName iterator if prop.eachName?
15111511

15121512
# Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
1513-
# `obj2 = {a: 1, obj..., c: 3, d: 4}` → `obj2 = Object.assign({}, {a: 1}, obj, {c: 3, d: 4})`
1513+
# `obj2 = {a: 1, obj..., c: 3, d: 4}` → `obj2 = _extends({}, {a: 1}, obj, {c: 3, d: 4})`
15141514
compileSpread: (o) ->
15151515
props = @properties
15161516
# Store object spreads.
@@ -2144,6 +2144,8 @@ exports.Assign = class Assign extends Base
21442144
@checkAssignability o, name
21452145
if @moduleDeclaration
21462146
o.scope.add name.value, @moduleDeclaration
2147+
else if @param
2148+
o.scope.add name.value, 'var'
21472149
else
21482150
o.scope.find name.value
21492151

@@ -2167,31 +2169,14 @@ exports.Assign = class Assign extends Base
21672169
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
21682170
# Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration,
21692171
# if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses.
2170-
if o.level > LEVEL_LIST or (o.level is LEVEL_TOP and isValue and @variable.base instanceof Obj and not @nestedLhs and not @param)
2172+
if o.level > LEVEL_LIST or o.level is LEVEL_TOP and isValue and @variable.base instanceof Obj and not @nestedLhs and not (@param is yes)
21712173
@wrapInParentheses answer
21722174
else
21732175
answer
21742176

21752177
# Check object destructuring variable for rest elements;
21762178
# can be removed once ES proposal hits Stage 4.
21772179
compileObjectDestruct: (o) ->
2178-
# Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration,
2179-
# if we’re destructuring without declaring, the destructuring assignment
2180-
# must be wrapped in parentheses: `({a, b} = obj)`. Helper function
2181-
# `setScopeVar()` declares variables `a` and `b` at the top of the
2182-
# current scope.
2183-
setScopeVar = (prop) ->
2184-
newVar = false
2185-
return if prop instanceof Assign and prop.value.base instanceof Obj
2186-
if prop instanceof Assign
2187-
if prop.value.base instanceof IdentifierLiteral
2188-
newVar = prop.value.base.compileWithoutComments o
2189-
else
2190-
newVar = prop.variable.base.compileWithoutComments o
2191-
else
2192-
newVar = prop.compileWithoutComments o
2193-
o.scope.add(newVar, 'var', true) if newVar
2194-
21952180
# Returns a safe (cached) reference to the key for a given property
21962181
getPropKey = (prop) ->
21972182
if prop instanceof Assign
@@ -2220,7 +2205,6 @@ exports.Assign = class Assign extends Base
22202205

22212206
for prop, index in properties
22222207
nestedSourceDefault = nestedSource = nestedProperties = null
2223-
setScopeVar prop.unwrap()
22242208
if prop instanceof Assign
22252209
# prop is `k: expr`, we need to check `expr` for nested splats
22262210
if prop.value.isObject?()
@@ -2252,10 +2236,11 @@ exports.Assign = class Assign extends Base
22522236
restElements
22532237

22542238
# Cache the value for reuse with rest elements.
2255-
if @value.shouldCache()
2256-
valueRefTemp = new IdentifierLiteral o.scope.freeVariable 'ref', reserve: false
2257-
else
2258-
valueRefTemp = @value.base
2239+
valueRefTemp =
2240+
if @value.shouldCache()
2241+
new IdentifierLiteral o.scope.freeVariable 'ref', reserve: false
2242+
else
2243+
@value.base
22592244

22602245
# Find all rest elements.
22612246
restElements = traverseRest @variable.base.properties, valueRefTemp
@@ -2266,7 +2251,7 @@ exports.Assign = class Assign extends Base
22662251

22672252
for restElement in restElements
22682253
value = new Call new Value(new Literal utility 'objectWithoutKeys', o), [restElement.source, restElement.excludeProps]
2269-
result.push new Assign restElement.name, value
2254+
result.push new Assign new Value(restElement.name), value, null, param: if @param then 'alwaysDeclare' else null
22702255

22712256
fragments = result.compileToFragments o
22722257
if o.level is LEVEL_TOP
@@ -2600,7 +2585,7 @@ exports.Code = class Code extends Base
26002585
ifTrue = new Assign new Value(param.name), param.value
26012586
exprs.push new If condition, ifTrue
26022587
else
2603-
exprs.push new Assign new Value(param.name), param.asReference(o)
2588+
exprs.push new Assign new Value(param.name), param.asReference(o), null, param: 'alwaysDeclare'
26042589

26052590
# If this parameter comes before the splat or expansion, it will go
26062591
# in the function definition parameter list.
@@ -2613,25 +2598,26 @@ exports.Code = class Code extends Base
26132598
ref = param.asReference o
26142599
else
26152600
if param.value? and not param.assignedInBody
2616-
ref = new Assign new Value(param.name), param.value, null, param: yes
2601+
ref = new Assign new Value(param.name), param.value, null, param: 'alwaysDeclare'
26172602
else
26182603
ref = param
26192604
# Add this parameter’s reference(s) to the function scope.
26202605
if param.name instanceof Arr or param.name instanceof Obj
26212606
# This parameter is destructured.
26222607
param.name.lhs = yes
2623-
param.name.eachName (prop) ->
2624-
o.scope.parameter prop.value
26252608
# Compile `foo({a, b...}) ->` to `foo(arg) -> {a, b...} = arg`.
26262609
# Can be removed once ES proposal hits Stage 4.
26272610
if param.name instanceof Obj and param.name.hasSplat()
26282611
splatParamName = o.scope.freeVariable 'arg'
26292612
o.scope.parameter splatParamName
26302613
ref = new Value new IdentifierLiteral splatParamName
2631-
exprs.push new Assign new Value(param.name), ref
2614+
exprs.push new Assign new Value(param.name), ref, null, param: 'alwaysDeclare'
26322615
# Compile `foo({a, b...} = {}) ->` to `foo(arg = {}) -> {a, b...} = arg`.
2633-
if param.value? and not param.assignedInBody
2616+
if param.value? and not param.assignedInBody
26342617
ref = new Assign ref, param.value, null, param: yes
2618+
else unless param.shouldCache()
2619+
param.name.eachName (prop) ->
2620+
o.scope.parameter prop.value
26352621
else
26362622
# This compilation of the parameter is only to get its name to add
26372623
# to the scope name tracking; since the compilation output here

test/assignment.coffee

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,3 +915,15 @@ test "#4674: _extends utility for object spreads 2", ->
915915
e = {a..., c...}
916916
eq e.b, 1
917917
eq e.d, 2
918+
919+
test "#4673: complex destructured object spread variables", ->
920+
b = c: 1
921+
{{a...}...} = b
922+
eq a.c, 1
923+
924+
d = {}
925+
{d.e...} = f: 1
926+
eq d.e.f, 1
927+
928+
{{g}...} = g: 1
929+
eq g, 1

test/functions.coffee

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,3 +549,17 @@ test "#4413: expressions in function parameters that create generated variables
549549
g = (a = foo() ? bar()) -> a + 1
550550
eq f(), 33
551551
eq g(), 34
552+
553+
test "#4673: complex destructured object spread variables", ->
554+
b = c: 1
555+
f = ({{a...}...}) ->
556+
a
557+
eq f(c: 1).c, 1
558+
559+
test "#4657: destructured array param declarations", ->
560+
a = 1
561+
b = 2
562+
f = ([a..., b]) ->
563+
f [3, 4, 5]
564+
eq a, 1
565+
eq b, 2

0 commit comments

Comments
 (0)