Permalink
Browse files

Allow `@` values as indices in `for` expressions

This loosens the compilation of `for` expressions to allow the index
variable to be an `@` value, e.g.

    do @visit for @node, @index in nodes

Within `@visit`, the index of the current node (`@node`) would be
available as `@index`.

Fixes #4411.
1 parent 0a6aeef commit 1143ac0268f3d96354ef156e78435716368053f6 @connec connec committed Dec 29, 2016
Showing with 21 additions and 4 deletions.
  1. +2 −2 lib/coffee-script/nodes.js
  2. +2 −2 src/nodes.coffee
  3. +10 −0 test/comprehensions.coffee
  4. +7 −0 test/error_messages.coffee
@@ -3415,7 +3415,7 @@
if (this.object) {
ref3 = [this.index, this.name], this.name = ref3[0], this.index = ref3[1];
}
- if (this.index instanceof Value) {
+ if (this.index instanceof Value && !this.index.isAssignable()) {
this.index.error('index cannot be a pattern matching expression');
}
this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length && !this.from;
@@ -3447,7 +3447,7 @@
if (name && !this.pattern) {
scope.find(name);
}
- if (index) {
+ if (index && !(this.index instanceof Value)) {
scope.find(index);
}
if (this.returns) {
View
@@ -2312,7 +2312,7 @@ exports.For = class For extends While
@index.error 'cannot use index with for-from' if @from and @index
source.ownTag.error "cannot use own with for-#{if @from then 'from' else 'in'}" if @own and not @object
[@name, @index] = [@index, @name] if @object
- @index.error 'index cannot be a pattern matching expression' if @index instanceof Value
+ @index.error 'index cannot be a pattern matching expression' if @index instanceof Value and not @index.isAssignable()
@range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length and not @from
@pattern = @name instanceof Value
@index.error 'indexes do not apply to range loops' if @range and @index
@@ -2334,7 +2334,7 @@ exports.For = class For extends While
name = @name and (@name.compile o, LEVEL_LIST) if not @pattern
index = @index and (@index.compile o, LEVEL_LIST)
scope.find(name) if name and not @pattern
- scope.find(index) if index
+ scope.find(index) if index and @index not instanceof Value
rvar = scope.freeVariable 'results' if @returns
if @from
ivar = scope.freeVariable 'x', single: true if @pattern
@@ -515,6 +515,16 @@ test "#2274: Allow @values as loop variables", ->
obj.method()
eq obj.item, 3
+test "#4411: Allow @values as loop indices", ->
+ obj =
+ index: null
+ get: -> @index
+ method: ->
+ @get() for _, @index in [1, 2, 3]
+ eq obj.index, null
+ arrayEq obj.method(), [0, 1, 2]
+ eq obj.index, 3
+
test "#2525, #1187, #1208, #1758, looping over an array forwards", ->
list = [0, 1, 2, 3, 4]
@@ -1179,3 +1179,10 @@ test "tagged template literals must be called by an identifier", ->
1"""#{b}"""
^
'''
+
+test "can't use pattern matches for loop indices", ->
+ assertErrorFormat 'a for b, {c} in d', '''
+ [stdin]:1:10: error: index cannot be a pattern matching expression
+ a for b, {c} in d
+ ^^^
+ '''

0 comments on commit 1143ac0

Please sign in to comment.