test "#4798 destructuring of objects with splat within arrays", ->
  arr = [1, {a:1, b:2}]
  [...,{a, r...}] = arr
  eq a, 1
  deepEqual r, {b:2}
  [b, {q...}] = arr
  eq b, 1
  deepEqual q, arr[1]
  eq q.b, r.b
  eq q.a, a

  arr2 = [arr[1]]
  [{a2...}] = arr2
  eq a2.a, arr2[0].a

test "destructuring assignment with objects and splats: ES2015", ->
  obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
  throwsCompileError "{a, r..., s...} = x", null, null, "multiple rest elements are disallowed"
  throwsCompileError "{a, r..., s..., b} = x", null, null, "multiple rest elements are disallowed"
  prop = "b"
  {a, b, r...} = obj
  eq a, 1
  eq b, 2
  eq r.e, obj.e
  eq r.a, undefined
  {d, c: x, r...} = obj
  eq x, 3
  eq d, 4
  eq r.c, undefined
  eq r.b, 2
  {a, 'b': z, g = 9, r...} = obj
  eq g, 9
  eq z, 2
  eq r.b, undefined

test "destructuring assignment with splats and default values", ->
  obj = {}
  c = {b: 1}
  { a: {b} = c, d...} = obj

  eq b, 1
  deepEqual d, {}

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  {
    a: {b} = c
    d ...
  } = obj

  eq b, 1
  deepEqual d, {}

test "destructuring assignment with splat with default value", ->
  obj = {}
  c = {val: 1}
  { a: {b...} = c } = obj

  deepEqual b, val: 1

test "destructuring assignment with multiple splats in different objects", ->
  obj = { a: {val: 1}, b: {val: 2} }
  { a: {a...}, b: {b...} } = obj
  deepEqual a, val: 1
  deepEqual b, val: 2

  o = {
    props: {
      p: {
        n: 1
        m: 5
      }
      s: 6
    }
  }
  {p: {m, q..., t = {obj...}}, r...} = o.props
  eq m, o.props.p.m
  deepEqual r, s: 6
  deepEqual q, n: 1
  deepEqual t, obj

  @props = o.props
  {p: {m}, r...} = @props
  eq m, @props.p.m
  deepEqual r, s: 6

  {p: {m}, r...} = {o.props..., p:{m:9}}
  eq m, 9

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  {
    a: {
      a ...
    }
    b: {
      b ...
    }
  } = obj
  deepEqual a, val: 1
  deepEqual b, val: 2

test "destructuring assignment with dynamic keys and splats", ->
  i = 0
  foo = -> ++i

  obj = {1: 'a', 2: 'b'}
  { "#{foo()}": a, b... } = obj

  eq a, 'a'
  eq i, 1
  deepEqual b, 2: 'b'

# Tests from https://babeljs.io/docs/plugins/transform-object-rest-spread/.
test "destructuring assignment with objects and splats: Babel tests", ->
  # What Babel calls “rest properties:”
  { x, y, z... } = { x: 1, y: 2, a: 3, b: 4 }
  eq x, 1
  eq y, 2
  deepEqual z, { a: 3, b: 4 }

  # What Babel calls “spread properties:”
  n = { x, y, z... }
  deepEqual n, { x: 1, y: 2, a: 3, b: 4 }

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  { x, y, z ... } = { x: 1, y: 2, a: 3, b: 4 }
  eq x, 1
  eq y, 2
  deepEqual z, { a: 3, b: 4 }

  n = { x, y, z ... }
  deepEqual n, { x: 1, y: 2, a: 3, b: 4 }

test "deep destructuring assignment with objects: ES2015", ->
  a1={}; b1={}; c1={}; d1={}
  obj = {
    a: a1
    b: {
      'c': {
        d: {
          b1
          e: c1
          f: d1
        }
      }
    }
    b2: {b1, c1}
  }
  {a: w, b: {c: {d: {b1: bb, r1...}}}, r2...} = obj
  eq r1.e, c1
  eq r2.b, undefined
  eq bb, b1
  eq r2.b2, obj.b2

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  {a: w, b: {c: {d: {b1: bb, r1 ...}}}, r2 ...} = obj
  eq r1.e, c1
  eq r2.b, undefined
  eq bb, b1
  eq r2.b2, obj.b2

test "deep destructuring assignment with defaults: ES2015", ->
  obj =
    b: { c: 1, baz: 'qux' }
    foo: 'bar'
  j =
    f: 'world'
  i =
    some: 'prop'
  {
    a...
    b: { c, d... }
    e: {
      f: hello
      g: { h... } = i
    } = j
  } = obj

  deepEqual a, foo: 'bar'
  eq c, 1
  deepEqual d, baz: 'qux'
  eq hello, 'world'
  deepEqual h, some: 'prop'

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  {
    a ...
    b: {
      c,
      d ...
    }
    e: {
      f: hello
      g: {
        h ...
      } = i
    } = j
  } = obj

  deepEqual a, foo: 'bar'
  eq c, 1
  deepEqual d, baz: 'qux'
  eq hello, 'world'
  deepEqual h, some: 'prop'

test "object spread properties: ES2015", ->
  obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
  obj2 = {obj..., c:9}
  eq obj2.c, 9
  eq obj.a, obj2.a

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  obj2 = {
    obj ...
    c:9
  }
  eq obj2.c, 9
  eq obj.a, obj2.a

  obj2 = {obj..., a: 8, c: 9, obj...}
  eq obj2.c, 3
  eq obj.a, obj2.a

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  obj2 = {
    obj ...
    a: 8
    c: 9
    obj ...
  }
  eq obj2.c, 3
  eq obj.a, obj2.a

  obj3 = {obj..., b: 7, g: {obj2..., c: 1}}
  eq obj3.g.c, 1
  eq obj3.b, 7
  deepEqual obj3.g, {obj..., c: 1}

  (({a, b, r...}) ->
    eq 1, a
    deepEqual r, {c: 3, d: 44, e: 55}
  ) {obj2..., d: 44, e: 55}

  obj = {a: 1, b: 2, c: {d: 3, e: 4, f: {g: 5}}}
  obj4 = {a: 10, obj.c...}
  eq obj4.a, 10
  eq obj4.d, 3
  eq obj4.f.g, 5
  deepEqual obj4.f, obj.c.f

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  (({
    a
    b
    r ...
    }) ->
    eq 1, a
    deepEqual r, {c: 3, d: 44, e: 55}
  ) {
    obj2 ...
    d: 44
    e: 55
  }

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  obj4 = {
    a: 10
    obj.c ...
  }
  eq obj4.a, 10
  eq obj4.d, 3
  eq obj4.f.g, 5
  deepEqual obj4.f, obj.c.f

  obj5 = {obj..., ((k) -> {b: k})(99)...}
  eq obj5.b, 99
  deepEqual obj5.c, obj.c

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  obj5 = {
    obj ...
    ((k) -> {b: k})(99) ...
  }
  eq obj5.b, 99
  deepEqual obj5.c, obj.c

  fn = -> {c: {d: 33, e: 44, f: {g: 55}}}
  obj6 = {obj..., fn()...}
  eq obj6.c.d, 33
  deepEqual obj6.c, {d: 33, e: 44, f: {g: 55}}

  obj7 = {obj..., fn()..., {c: {d: 55, e: 66, f: {77}}}...}
  eq obj7.c.d, 55
  deepEqual obj6.c, {d: 33, e: 44, f: {g: 55}}

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  obj7 = {
    obj ...
    fn() ...
    {c: {d: 55, e: 66, f: {77}}} ...
  }
  eq obj7.c.d, 55
  deepEqual obj6.c, {d: 33, e: 44, f: {g: 55}}

  obj =
    a:
      b:
        c:
          d:
            e: {}
  obj9 = {a:1, obj.a.b.c..., g:3}
  deepEqual obj9.d, {e: {}}

  a = "a"
  c = "c"
  obj9 = {a:1, obj[a].b[c]..., g:3}
  deepEqual obj9.d, {e: {}}

  obj9 = {a:1, obj.a["b"].c["d"]..., g:3}
  deepEqual obj9["e"], {}

test "#4673: complex destructured object spread variables", ->
  b = c: 1
  {{a...}...} = b
  eq a.c, 1

  d = {}
  {d.e...} = f: 1
  eq d.e.f, 1

  {{g}...} = g: 1
  eq g, 1

test "rest element destructuring in function definition", ->
  obj = {a: 1, b: 2, c: 3, d: 4, e: 5}

  (({a, b, r...}) ->
    eq 1, a
    eq 2, b,
    deepEqual r, {c: 3, d: 4, e: 5}
  ) obj

  (({a: p, b, r...}, q) ->
    eq p, 1
    eq q, 9
    deepEqual r, {c: 3, d: 4, e: 5}
  ) {a:1, b:2, c:3, d:4, e:5}, 9

  # Should not trigger implicit call, e.g. rest ... => rest(...)
  (({
      a: p
      b
      r ...
    }, q) ->
    eq p, 1
    eq q, 9
    deepEqual r, {c: 3, d: 4, e: 5}
  ) {a:1, b:2, c:3, d:4, e:5}, 9

  a1={}; b1={}; c1={}; d1={}
  obj1 = {
    a: a1
    b: {
      'c': {
        d: {
          b1
          e: c1
          f: d1
        }
      }
    }
    b2: {b1, c1}
  }

  (({a: w, b: {c: {d: {b1: bb, r1...}}}, r2...}) ->
    eq a1, w
    eq bb, b1
    eq r2.b, undefined
    deepEqual r1, {e: c1, f: d1}
    deepEqual r2.b2, {b1, c1}
  ) obj1

  b = 3
  f = ({a, b...}) ->
  f {}
  eq 3, b

  (({a, r...} = {}) ->
    eq a, undefined
    deepEqual r, {}
  )()

  (({a, r...} = {}) ->
    eq a, 1
    deepEqual r, {b: 2, c: 3}
  ) {a: 1, b: 2, c: 3}

  f = ({a, r...} = {}) -> [a, r]
  deepEqual [undefined, {}], f()
  deepEqual [1, {b: 2}], f {a: 1, b: 2}
  deepEqual [1, {}], f {a: 1}

  f = ({a, r...} = {a: 1, b: 2}) -> [a, r]
  deepEqual [1, {b:2}], f()
  deepEqual [2, {}], f {a:2}
  deepEqual [3, {c:5}], f {a:3, c:5}

  f = ({ a: aa = 0, b: bb = 0 }) -> [aa, bb]
  deepEqual [0, 0], f {}
  deepEqual [0, 42], f {b:42}
  deepEqual [42, 0], f {a:42}
  deepEqual [42, 43], f {a:42, b:43}

test "#4673: complex destructured object spread variables", ->
  f = ({{a...}...}) ->
    a
  eq f(c: 1).c, 1

  g = ({@y...}) ->
    eq @y.b, 1
  g b: 1

test "#4834: dynamic import can technically be object spread", ->
  eqJS """
    x = {...import('module')}
  """,
  """
    var x;

    x = {...import('module')};
  """

test "#5168: allow indented property index", ->
  a = b: c: 3

  eq 3, {
    ...a[
      if yes
        'b'
      else
        'c'
    ]
  }.c

test "#5291: soaks/prototype shorthands in object spread variables", ->
  withPrototype =
    prototype:
      b: {c: 1}
  eq {withPrototype::b...}.c, 1
  eq {...withPrototype::b}.c, 1

  withSoak =
    b:
      c: 2
  eq {withSoak?.b...}.c, 2
  eq {...withSoak?.b}.c, 2

  soakedCall = ->
    b:
      c: 3
  eq {soakedCall?().b...}.c, 3
  eq {...soakedCall?().b}.c, 3

  assignToPrototype =
    prototype: {}
  {...assignToPrototype::b} = c: 4
  eq assignToPrototype::b.c, 4
