@@ -2467,8 +2467,7 @@ exports.Obj = class Obj extends Base
24672467 # `foo = ({a, rest..., b}) ->` -> `foo = {a, b, rest...}) ->`
24682468 reorderProperties : ->
24692469 props = @properties
2470- splatProps = (i for prop, i in props when prop instanceof Splat)
2471- props[splatProps[1 ]].error " multiple spread elements are disallowed" if splatProps ? .length > 1
2470+ splatProps = @ getAndCheckSplatProps ()
24722471 splatProp = props .splice splatProps[0 ], 1
24732472 @objects = @properties = [].concat props, splatProp
24742473
@@ -2538,6 +2537,13 @@ exports.Obj = class Obj extends Base
25382537 answer = @ wrapInBraces answer
25392538 if @front then @ wrapInParentheses answer else answer
25402539
2540+ getAndCheckSplatProps : ->
2541+ return unless @ hasSplat () and @lhs
2542+ props = @properties
2543+ splatProps = (i for prop, i in props when prop instanceof Splat)
2544+ props[splatProps[1 ]].error " multiple spread elements are disallowed" if splatProps ? .length > 1
2545+ splatProps
2546+
25412547 assigns : (name ) ->
25422548 for prop in @properties when prop .assigns name then return yes
25432549 no
@@ -2590,6 +2596,10 @@ exports.Obj = class Obj extends Base
25902596 else if property instanceof Splat
25912597 property .lhs = yes
25922598
2599+ astNode : (o ) ->
2600+ @ getAndCheckSplatProps ()
2601+ super o
2602+
25932603 astType : ->
25942604 if @lhs
25952605 ' ObjectPattern'
@@ -2731,7 +2741,7 @@ exports.Arr = class Arr extends Base
27312741 @lhs = yes if setLhs
27322742 return unless @lhs
27332743 for object in @objects
2734- object .lhs = yes if object instanceof Splat
2744+ object .lhs = yes if object instanceof Splat or object instanceof Expansion
27352745 unwrappedObject = object .unwrapAll ()
27362746 if unwrappedObject instanceof Arr or unwrappedObject instanceof Obj
27372747 unwrappedObject .propagateLhs yes
@@ -2995,6 +3005,10 @@ exports.Class = class Class extends Base
29953005 isStatementAst : -> yes
29963006
29973007 astNode : (o ) ->
3008+ if jumpNode = @body .jumps ()
3009+ jumpNode .error ' Class bodies cannot contain pure statements'
3010+ if argumentsNode = @body .contains isLiteralArguments
3011+ argumentsNode .error " Class bodies shouldn't reference arguments"
29983012 @ declareName o
29993013 @name = @ determineName ()
30003014 @body .isClassBody = yes
@@ -3502,7 +3516,7 @@ exports.Assign = class Assign extends Base
35023516 return @ compileDestructuring o
35033517
35043518 return @ compileSplice o if @variable .isSplice ()
3505- return @ compileConditional o if @context in [ ' ||= ' , ' &&= ' , ' ?= ' ]
3519+ return @ compileConditional o if @ isConditional ()
35063520 return @ compileSpecialMath o if @context in [' //=' , ' %%=' ]
35073521
35083522 @ addScopeVariables o
@@ -3561,18 +3575,7 @@ exports.Assign = class Assign extends Base
35613575 [obj ] = objects
35623576
35633577 @ disallowLoneExpansion ()
3564-
3565- # Count all `Splats`: [a, b, c..., d, e]
3566- splats = (i for obj, i in objects when obj instanceof Splat)
3567- # Count all `Expansions`: [a, b, ..., c, d]
3568- expans = (i for obj, i in objects when obj instanceof Expansion)
3569- # Combine splats and expansions.
3570- splatsAndExpans = [splats... , expans... ]
3571- # Show error if there is more than one `Splat`, or `Expansion`.
3572- # Examples: [a, b, c..., d, e, f...], [a, b, ..., c, d, ...], [a, b, ..., c, d, e...]
3573- if splatsAndExpans .length > 1
3574- # Sort 'splatsAndExpans' so we can show error at first disallowed token.
3575- objects[splatsAndExpans .sort ()[1 ]].error " multiple splats/expansions are disallowed in an assignment"
3578+ {splats , expans , splatsAndExpans } = @ getAndCheckSplatsAndExpansions ()
35763579
35773580 isSplat = splats ? .length > 0
35783581 isExpans = expans ? .length > 0
@@ -3710,6 +3713,23 @@ exports.Assign = class Assign extends Base
37103713 if loneObject instanceof Expansion
37113714 loneObject .error ' Destructuring assignment has no target'
37123715
3716+ # Show error if there is more than one `Splat`, or `Expansion`.
3717+ # Examples: [a, b, c..., d, e, f...], [a, b, ..., c, d, ...], [a, b, ..., c, d, e...]
3718+ getAndCheckSplatsAndExpansions : ->
3719+ return {splats : [], expans : [], splatsAndExpans : []} unless @variable .base instanceof Arr
3720+ {objects } = @variable .base
3721+
3722+ # Count all `Splats`: [a, b, c..., d, e]
3723+ splats = (i for obj, i in objects when obj instanceof Splat)
3724+ # Count all `Expansions`: [a, b, ..., c, d]
3725+ expans = (i for obj, i in objects when obj instanceof Expansion)
3726+ # Combine splats and expansions.
3727+ splatsAndExpans = [splats... , expans... ]
3728+ if splatsAndExpans .length > 1
3729+ # Sort 'splatsAndExpans' so we can show error at first disallowed token.
3730+ objects[splatsAndExpans .sort ()[1 ]].error " multiple splats/expansions are disallowed in an assignment"
3731+ {splats, expans, splatsAndExpans}
3732+
37133733 # When compiling a conditional assignment, take care to ensure that the
37143734 # operands are only evaluated once, even though we have to reference them
37153735 # more than once.
@@ -3718,7 +3738,7 @@ exports.Assign = class Assign extends Base
37183738 # Disallow conditional assignment of undefined variables.
37193739 if not left .properties .length and left .base instanceof Literal and
37203740 left .base not instanceof ThisLiteral and not o .scope .check left .base .value
3721- @variable . error " the variable \" #{ left .base .value } \" can't be assigned with #{ @context } because it has not been declared before "
3741+ @ throwUnassignableConditionalError left .base .value
37223742 if " ?" in @context
37233743 o .isExistentialEquals = true
37243744 new If (new Existence (left), right, type : ' if' ).addElse (new Assign (right, @value , ' =' )).compileToFragments o
@@ -3770,10 +3790,21 @@ exports.Assign = class Assign extends Base
37703790 # destructured variables.
37713791 @variable .base .propagateLhs yes
37723792
3793+ throwUnassignableConditionalError : (name ) ->
3794+ @variable .error " the variable \" #{ name} \" can't be assigned with #{ @context } because it has not been declared before"
3795+
3796+ isConditional : ->
3797+ @context in [' ||=' , ' &&=' , ' ?=' ]
3798+
37733799 isStatementAst : NO
37743800
37753801 astNode : (o ) ->
37763802 @ disallowLoneExpansion ()
3803+ @ getAndCheckSplatsAndExpansions ()
3804+ if @ isConditional ()
3805+ variable = @variable .unwrap ()
3806+ if variable instanceof IdentifierLiteral and not o .scope .check variable .value
3807+ @ throwUnassignableConditionalError variable .value
37773808 @ addScopeVariables o, allowAssignmentToExpansion : yes
37783809 super o
37793810
@@ -3855,6 +3886,7 @@ exports.Code = class Code extends Base
38553886 haveBodyParam = no
38563887
38573888 @ checkForDuplicateParams ()
3889+ @ disallowLoneExpansionAndMultipleSplats ()
38583890
38593891 # Separate `this` assignments.
38603892 @eachParamName (name , node , param , obj ) ->
@@ -3883,14 +3915,9 @@ exports.Code = class Code extends Base
38833915 # declare and assign all subsequent parameters in the function body so that
38843916 # any non-idempotent parameters are evaluated in the correct order.
38853917 for param, i in @params
3886- # Was `...` used with this parameter? (Only one such parameter is allowed
3887- # per function.) Splat/expansion parameters cannot have default values,
3888- # so we need not worry about that.
3918+ # Was `...` used with this parameter? Splat/expansion parameters cannot
3919+ # have default values, so we need not worry about that.
38893920 if param .splat or param instanceof Expansion
3890- if haveSplatParam
3891- param .error ' only one splat or expansion parameter is allowed per function definition'
3892- else if param instanceof Expansion and @params .length is 1
3893- param .error ' an expansion parameter cannot be the only parameter in a function definition'
38943921 haveSplatParam = yes
38953922 if param .splat
38963923 if param .name instanceof Arr or param .name instanceof Obj
@@ -4102,6 +4129,18 @@ exports.Code = class Code extends Base
41024129 @name .error ' Class constructor may not be async' if @isAsync
41034130 @name .error ' Class constructor may not be a generator' if @isGenerator
41044131
4132+ disallowLoneExpansionAndMultipleSplats : ->
4133+ seenSplatParam = no
4134+ for param in @params
4135+ # Was `...` used with this parameter? (Only one such parameter is allowed
4136+ # per function.)
4137+ if param .splat or param instanceof Expansion
4138+ if seenSplatParam
4139+ param .error ' only one splat or expansion parameter is allowed per function definition'
4140+ else if param instanceof Expansion and @params .length is 1
4141+ param .error ' an expansion parameter cannot be the only parameter in a function definition'
4142+ seenSplatParam = yes
4143+
41054144 expandCtorSuper : (thisAssignments ) ->
41064145 return false unless @ctor
41074146
@@ -4141,8 +4180,12 @@ exports.Code = class Code extends Base
41414180 seenSuper
41424181
41434182 propagateLhs : ->
4144- for {name} in @params when name instanceof Arr or name instanceof Obj
4145- name .propagateLhs yes
4183+ for param in @params
4184+ {name } = param
4185+ if name instanceof Arr or name instanceof Obj
4186+ name .propagateLhs yes
4187+ else if param instanceof Expansion
4188+ param .lhs = yes
41464189
41474190 astAddParamsToScope : (o ) ->
41484191 @eachParamName (name ) ->
@@ -4153,6 +4196,7 @@ exports.Code = class Code extends Base
41534196 @ checkForAsyncOrGeneratorConstructor ()
41544197 @ checkForDuplicateParams ()
41554198 @ disallowSuperInParamDefaults forAst : yes
4199+ @ disallowLoneExpansionAndMultipleSplats ()
41564200 seenSuper = @ checkSuperCallsInConstructorBody ()
41574201 if @ctor is ' derived' and not seenSuper
41584202 @eachParamName (name , node ) =>
@@ -4281,6 +4325,9 @@ exports.Param = class Param extends Base
42814325 literal .error " '#{ literal .value } ' can't be assigned"
42824326
42834327 atParam = (obj , originalObj = null ) => iterator " @#{ obj .properties [0 ].name .value } " , obj, @ , originalObj
4328+ if name instanceof Call
4329+ name .error " Function invocation can't be assigned"
4330+
42844331 # * simple literals `foo`
42854332 if name instanceof Literal
42864333 checkAssignabilityOfLiteral name
@@ -4392,13 +4439,22 @@ exports.Expansion = class Expansion extends Base
43924439 shouldCache : NO
43934440
43944441 compileNode : (o ) ->
4395- @ error ' Expansion must be used inside a destructuring assignment or parameter list '
4442+ @ throwLhsError ()
43964443
43974444 asReference : (o ) ->
43984445 this
43994446
44004447 eachName : (iterator ) ->
44014448
4449+ throwLhsError : ->
4450+ @ error ' Expansion must be used inside a destructuring assignment or parameter list'
4451+
4452+ astNode : (o ) ->
4453+ unless @lhs
4454+ @ throwLhsError ()
4455+
4456+ super o
4457+
44024458 astType : -> ' RestElement'
44034459
44044460 astProperties : ->
@@ -4630,8 +4686,7 @@ exports.Op = class Op extends Base
46304686 # In chains, there's no need to wrap bare obj literals in parens,
46314687 # as the chained expression is wrapped.
46324688 @first .front = @front unless isChain
4633- if @operator is ' delete' and o .scope .check (@first .unwrapAll ().value )
4634- @ error ' delete operand may not be argument or var'
4689+ @ checkDeleteOperand o
46354690 return @ compileContinuation o if @ isYield () or @ isAwait ()
46364691 return @ compileUnary o if @ isUnary ()
46374692 return @ compileChain o if isChain
@@ -4719,8 +4774,13 @@ exports.Op = class Op extends Base
47194774 toString : (idt ) ->
47204775 super idt, @constructor .name + ' ' + @operator
47214776
4777+ checkDeleteOperand : (o ) ->
4778+ if @operator is ' delete' and o .scope .check (@first .unwrapAll ().value )
4779+ @ error ' delete operand may not be argument or var'
4780+
47224781 astNode : (o ) ->
47234782 @ checkContinuation o if @ isYield () or @ isAwait ()
4783+ @ checkDeleteOperand o
47244784 super o
47254785
47264786 astType : ->
@@ -4902,14 +4962,19 @@ exports.Catch = class Catch extends Base
49024962 o .indent += TAB
49034963 generatedErrorVariableName = o .scope .freeVariable ' error' , reserve : no
49044964 placeholder = new IdentifierLiteral generatedErrorVariableName
4965+ @ checkUnassignable ()
49054966 if @errorVariable
4906- message = isUnassignable @errorVariable .unwrapAll ().value
4907- @errorVariable .error message if message
49084967 @recovery .unshift new Assign @errorVariable , placeholder
49094968 [].concat @ makeCode (" catch (" ), placeholder .compileToFragments (o), @ makeCode (" ) {\n " ),
49104969 @recovery .compileToFragments (o, LEVEL_TOP), @ makeCode (" \n #{ @tab } }" )
49114970
4971+ checkUnassignable : ->
4972+ if @errorVariable
4973+ message = isUnassignable @errorVariable .unwrapAll ().value
4974+ @errorVariable .error message if message
4975+
49124976 astNode : (o ) ->
4977+ @ checkUnassignable ()
49134978 @errorVariable ? .eachName (name) ->
49144979 alreadyDeclared = o .scope .find name .value
49154980 name .isDeclaration = not alreadyDeclared
0 commit comments