Skip to content

Commit 0e51cda

Browse files
helixbassGeoffreyBooth
authored andcommitted
AST: allow assignment to nontrailing splat (#5263)
* allow assignment to nontrailing splat * allow assignment to empty array * complex object splat * add explanatory comment
1 parent d4b7bd3 commit 0e51cda

File tree

3 files changed

+139
-22
lines changed

3 files changed

+139
-22
lines changed

lib/coffeescript/nodes.js

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

src/nodes.coffee

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,7 +2442,7 @@ exports.Obj = class Obj extends Base
24422442

24432443
children: ['properties']
24442444

2445-
isAssignable: ->
2445+
isAssignable: (opts) ->
24462446
for prop in @properties
24472447
# Check for reserved words.
24482448
message = isUnassignable prop.unwrapAll().value
@@ -2451,7 +2451,7 @@ exports.Obj = class Obj extends Base
24512451
prop = prop.value if prop instanceof Assign and
24522452
prop.context is 'object' and
24532453
prop.value?.base not instanceof Arr
2454-
return no unless prop.isAssignable()
2454+
return no unless prop.isAssignable opts
24552455
yes
24562456

24572457
shouldCache: ->
@@ -2594,7 +2594,7 @@ exports.Obj = class Obj extends Base
25942594
# Shorthand property with default, e.g. `{a = 1} = b`.
25952595
property.nestedLhs = yes
25962596
else if property instanceof Splat
2597-
property.lhs = yes
2597+
property.propagateLhs yes
25982598

25992599
astNode: (o) ->
26002600
@getAndCheckSplatProps()
@@ -2661,11 +2661,11 @@ exports.Arr = class Arr extends Base
26612661
return yes for obj in @objects when obj instanceof Elision
26622662
no
26632663

2664-
isAssignable: ({allowExpansion} = {}) ->
2665-
return no unless @objects.length
2664+
isAssignable: ({allowExpansion, allowNontrailingSplat, allowEmptyArray = no} = {}) ->
2665+
return allowEmptyArray unless @objects.length
26662666

26672667
for obj, i in @objects
2668-
return no if obj instanceof Splat and i + 1 isnt @objects.length
2668+
return no if not allowNontrailingSplat and obj instanceof Splat and i + 1 isnt @objects.length
26692669
return no unless (allowExpansion and obj instanceof Expansion) or (obj.isAssignable() and (not obj.isAtomic or obj.isAtomic()))
26702670
yes
26712671

@@ -3455,11 +3455,24 @@ exports.Assign = class Assign extends Base
34553455
unfoldSoak: (o) ->
34563456
unfoldSoak o, this, 'variable'
34573457

3458-
addScopeVariables: (o, {allowAssignmentToExpansion = no} = {}) ->
3458+
addScopeVariables: (o, {
3459+
# During AST generation, we need to allow assignment to these constructs
3460+
# that are considered “unassignable” during compile-to-JS, while still
3461+
# flagging things like `[null] = b`.
3462+
allowAssignmentToExpansion = no,
3463+
allowAssignmentToNontrailingSplat = no,
3464+
allowAssignmentToEmptyArray = no,
3465+
allowAssignmentToComplexSplat = no
3466+
} = {}) ->
34593467
return unless not @context or @context is '**='
34603468

34613469
varBase = @variable.unwrapAll()
3462-
if not varBase.isAssignable allowExpansion: allowAssignmentToExpansion
3470+
if not varBase.isAssignable {
3471+
allowExpansion: allowAssignmentToExpansion
3472+
allowNontrailingSplat: allowAssignmentToNontrailingSplat
3473+
allowEmptyArray: allowAssignmentToEmptyArray
3474+
allowComplexSplat: allowAssignmentToComplexSplat
3475+
}
34633476
@variable.error "'#{@variable.compile o}' can't be assigned"
34643477

34653478
varBase.eachName (name) =>
@@ -3805,7 +3818,7 @@ exports.Assign = class Assign extends Base
38053818
variable = @variable.unwrap()
38063819
if variable instanceof IdentifierLiteral and not o.scope.check variable.value
38073820
@throwUnassignableConditionalError variable.value
3808-
@addScopeVariables o, allowAssignmentToExpansion: yes
3821+
@addScopeVariables o, allowAssignmentToExpansion: yes, allowAssignmentToNontrailingSplat: yes, allowAssignmentToEmptyArray: yes, allowAssignmentToComplexSplat: yes
38093822
super o
38103823

38113824
astType: ->
@@ -4403,8 +4416,8 @@ exports.Splat = class Splat extends Base
44034416

44044417
shouldCache: -> no
44054418

4406-
isAssignable: ->
4407-
return no if @name instanceof Obj or @name instanceof Parens
4419+
isAssignable: ({allowComplexSplat = no} = {})->
4420+
return allowComplexSplat if @name instanceof Obj or @name instanceof Parens
44084421
@name.isAssignable() and (not @name.isAtomic or @name.isAtomic())
44094422

44104423
assigns: (name) ->
@@ -4417,6 +4430,11 @@ exports.Splat = class Splat extends Base
44174430

44184431
unwrap: -> @name
44194432

4433+
propagateLhs: (setLhs) ->
4434+
@lhs = yes if setLhs
4435+
return unless @lhs
4436+
@name.propagateLhs? yes
4437+
44204438
astType: ->
44214439
if @jsx
44224440
'JSXSpreadAttribute'

test/abstract_syntax_tree.coffee

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,85 @@ test "AST as expected for Assign node", ->
22752275
operator: '?='
22762276
]
22772277

2278+
testExpression '[a..., b] = c',
2279+
type: 'AssignmentExpression'
2280+
left:
2281+
type: 'ArrayPattern'
2282+
elements: [
2283+
type: 'RestElement'
2284+
argument: ID 'a', declaration: yes
2285+
postfix: yes
2286+
,
2287+
ID 'b'
2288+
]
2289+
right:
2290+
ID 'c'
2291+
2292+
testExpression '[] = c',
2293+
type: 'AssignmentExpression'
2294+
left:
2295+
type: 'ArrayPattern'
2296+
elements: []
2297+
right:
2298+
ID 'c'
2299+
2300+
testExpression '{{a...}...} = b',
2301+
type: 'AssignmentExpression'
2302+
left:
2303+
type: 'ObjectPattern'
2304+
properties: [
2305+
type: 'RestElement'
2306+
argument:
2307+
type: 'ObjectPattern'
2308+
properties: [
2309+
type: 'RestElement'
2310+
argument: ID 'a'
2311+
]
2312+
postfix: yes
2313+
]
2314+
right: ID 'b'
2315+
2316+
testExpression '{a..., b} = c',
2317+
type: 'AssignmentExpression'
2318+
left:
2319+
type: 'ObjectPattern'
2320+
properties: [
2321+
type: 'RestElement'
2322+
argument: ID 'a'
2323+
postfix: yes
2324+
,
2325+
type: 'ObjectProperty'
2326+
]
2327+
right: ID 'c'
2328+
2329+
testExpression '{a.b...} = c',
2330+
type: 'AssignmentExpression'
2331+
left:
2332+
type: 'ObjectPattern'
2333+
properties: [
2334+
type: 'RestElement'
2335+
argument:
2336+
type: 'MemberExpression'
2337+
postfix: yes
2338+
]
2339+
right: ID 'c'
2340+
2341+
testExpression '{{a}...} = b',
2342+
type: 'AssignmentExpression'
2343+
left:
2344+
type: 'ObjectPattern'
2345+
properties: [
2346+
type: 'RestElement'
2347+
argument:
2348+
type: 'ObjectPattern'
2349+
properties: [
2350+
type: 'ObjectProperty'
2351+
shorthand: yes
2352+
]
2353+
postfix: yes
2354+
]
2355+
right: ID 'b'
2356+
22782357
test "AST as expected for Code node", ->
22792358
testExpression '=>',
22802359
type: 'ArrowFunctionExpression'

0 commit comments

Comments
 (0)