diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 1d5609bd96..44da97411a 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -4193,21 +4193,21 @@ // or as part of a destructuring assignment. exports.Splat = Splat = (function() { class Splat extends Base { - isAssignable() { - return this.name.isAssignable() && (!this.name.isAtomic || this.name.isAtomic()); - } - constructor(name) { super(); this.name = name.compile ? name : new Literal(name); } + isAssignable() { + return this.name.isAssignable() && (!this.name.isAtomic || this.name.isAtomic()); + } + assigns(name) { return this.name.assigns(name); } - compileToFragments(o) { - return [this.makeCode('...'), ...this.name.compileToFragments(o)]; + compileNode(o) { + return [this.makeCode('...'), ...this.name.compileToFragments(o, LEVEL_OP)]; } unwrap() { diff --git a/src/nodes.coffee b/src/nodes.coffee index 677d4df028..1edeeb3b8a 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2859,22 +2859,20 @@ exports.Param = class Param extends Base # A splat, either as a parameter to a function, an argument to a call, # or as part of a destructuring assignment. exports.Splat = class Splat extends Base + constructor: (name) -> + super() + @name = if name.compile then name else new Literal name children: ['name'] isAssignable: -> @name.isAssignable() and (not @name.isAtomic or @name.isAtomic()) - constructor: (name) -> - super() - @name = if name.compile then name else new Literal name - assigns: (name) -> @name.assigns name - compileToFragments: (o) -> - [ @makeCode('...') - @name.compileToFragments(o)... ] + compileNode: (o) -> + [@makeCode('...'), @name.compileToFragments(o, LEVEL_OP)...] unwrap: -> @name diff --git a/test/arrays.coffee b/test/arrays.coffee index 5c509b28d9..0963d3e591 100644 --- a/test/arrays.coffee +++ b/test/arrays.coffee @@ -38,7 +38,6 @@ test "array splat expansions with assignments", -> test "mixed shorthand objects in array lists", -> - arr = [ a:1 'b' @@ -58,7 +57,6 @@ test "mixed shorthand objects in array lists", -> eq arr[2].b, 1 eq arr[3], 'b' - test "array splats with nested arrays", -> nonce = {} a = [nonce] @@ -70,6 +68,54 @@ test "array splats with nested arrays", -> list = [1, 2, a...] arrayEq list, [1, 2, [nonce]] +test "#4260: splat after existential operator soak", -> + a = {b: [3]} + foo = (a) -> [a] + arrayEq [a?.b...], [3] + arrayEq [c?.b ? []...], [] + arrayEq [...a?.b], [3] + arrayEq [...c?.b ? []], [] + arrayEq foo(a?.b...), [3] + arrayEq foo(...a?.b), [3] + arrayEq foo(c?.b ? []...), [undefined] + arrayEq foo(...c?.b ? []), [undefined] + e = yes + f = null + arrayEq [(a if e)?.b...], [3] + arrayEq [(a if f)?.b ? []...], [] + arrayEq [...(a if e)?.b], [3] + arrayEq [...(a if f)?.b ? []], [] + arrayEq foo((a if e)?.b...), [3] + arrayEq foo(...(a if e)?.b), [3] + arrayEq foo((a if f)?.b ? []...), [undefined] + arrayEq foo(...(a if f)?.b ? []), [undefined] + + # Should not trigger implicit call, e.g. rest ... => rest(...) + arrayEq [... a?.b], [3] + arrayEq [... c?.b ? []], [] + arrayEq [a?.b ...], [3] + arrayEq [(a if e)?.b ...], [3] + arrayEq foo(a?.b ...), [3] + arrayEq foo(... a?.b), [3] + +test "#1349: trailing if after splat", -> + a = [3] + b = yes + c = null + foo = (a) -> [a] + arrayEq [a if b...], [3] + arrayEq [(a if c) ? []...], [] + arrayEq [...a if b], [3] + arrayEq [...(a if c) ? []], [] + arrayEq foo((a if b)...), [3] + arrayEq foo(...(a if b)), [3] + arrayEq foo((a if c) ? []...), [undefined] + arrayEq foo(...(a if c) ? []), [undefined] + + # Should not trigger implicit call, e.g. rest ... => rest(...) + arrayEq [... a if b], [3] + arrayEq [a if b ...], [3] + test "#1274: `[] = a()` compiles to `false` instead of `a()`", -> a = false fn = -> a = true