Skip to content

Commit 72ab6fe

Browse files
zdenkoGeoffreyBooth
authored andcommitted
Fix #4889: for...range loop condition (#4891)
* fix #4889 * test * move test from 'control_flow' to 'ranges' * More range tests
1 parent eb70092 commit 72ab6fe

File tree

3 files changed

+75
-41
lines changed

3 files changed

+75
-41
lines changed

lib/coffeescript/nodes.js

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

src/nodes.coffee

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,23 +1365,18 @@ exports.Range = class Range extends Base
13651365
# Generate the condition.
13661366
[from, to] = [@fromNum, @toNum]
13671367
# Always check if the `step` isn't zero to avoid the infinite loop.
1368-
stepCond = if @stepNum then "#{@stepNum} !== 0" else "#{@stepVar} !== 0"
1368+
stepNotZero = "#{ @stepNum ? @stepVar } !== 0"
1369+
stepCond = "#{ @stepNum ? @stepVar } > 0"
1370+
lowerBound = "#{lt} #{ if known then to else @toVar }"
1371+
upperBound = "#{gt} #{ if known then to else @toVar }"
13691372
condPart =
1370-
if known
1371-
unless @step?
1372-
if from <= to then "#{lt} #{to}" else "#{gt} #{to}"
1373-
else
1374-
# from < to
1375-
lowerBound = "#{from} <= #{idx} && #{lt} #{to}"
1376-
# from > to
1377-
upperBound = "#{from} >= #{idx} && #{gt} #{to}"
1378-
if from <= to then "#{stepCond} && #{lowerBound}" else "#{stepCond} && #{upperBound}"
1379-
else
1380-
# from < to
1381-
lowerBound = "#{@fromVar} <= #{idx} && #{lt} #{@toVar}"
1382-
# from > to
1383-
upperBound = "#{@fromVar} >= #{idx} && #{gt} #{@toVar}"
1384-
"#{stepCond} && (#{@fromVar} <= #{@toVar} ? #{lowerBound} : #{upperBound})"
1373+
if @step?
1374+
"#{stepNotZero} && (#{stepCond} ? #{lowerBound} : #{upperBound})"
1375+
else
1376+
if known
1377+
"#{ if from <= to then lt else gt } #{to}"
1378+
else
1379+
"(#{@fromVar} <= #{@toVar} ? #{lowerBound} : #{upperBound})"
13851380

13861381
cond = if @stepVar then "#{@stepVar} > 0" else "#{@fromVar} <= #{@toVar}"
13871382

test/ranges.coffee

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -130,43 +130,43 @@ test "#2047: Infinite loop possible when `for` loop with `range` uses variables"
130130

131131
testData = [
132132
[1, 5, 1, [1..5]]
133-
[1, 5, -1, [1]]
133+
[1, 5, -1, []]
134134
[1, 5, up, [1..5]]
135-
[1, 5, down, [1]]
135+
[1, 5, down, []]
136136

137137
[a, 5, 1, [1..5]]
138-
[a, 5, -1, [1]]
138+
[a, 5, -1, []]
139139
[a, 5, up, [1..5]]
140-
[a, 5, down, [1]]
140+
[a, 5, down, []]
141141

142142
[1, b, 1, [1..5]]
143-
[1, b, -1, [1]]
143+
[1, b, -1, []]
144144
[1, b, up, [1..5]]
145-
[1, b, down, [1]]
145+
[1, b, down, []]
146146

147147
[a, b, 1, [1..5]]
148-
[a, b, -1, [1]]
148+
[a, b, -1, []]
149149
[a, b, up, [1..5]]
150-
[a, b, down, [1]]
150+
[a, b, down, []]
151151

152-
[5, 1, 1, [5]]
152+
[5, 1, 1, []]
153153
[5, 1, -1, [5..1]]
154-
[5, 1, up, [5]]
154+
[5, 1, up, []]
155155
[5, 1, down, [5..1]]
156156

157-
[5, a, 1, [5]]
157+
[5, a, 1, []]
158158
[5, a, -1, [5..1]]
159-
[5, a, up, [5]]
159+
[5, a, up, []]
160160
[5, a, down, [5..1]]
161161

162-
[b, 1, 1, [5]]
162+
[b, 1, 1, []]
163163
[b, 1, -1, [5..1]]
164-
[b, 1, up, [5]]
164+
[b, 1, up, []]
165165
[b, 1, down, [5..1]]
166166

167-
[b, a, 1, [5]]
167+
[b, a, 1, []]
168168
[b, a, -1, [5..1]]
169-
[b, a, up, [5]]
169+
[b, a, up, []]
170170
[b, a, down, [5..1]]
171171
]
172172

@@ -182,10 +182,10 @@ test "#2047: from, to and step as variables", ->
182182
arrayEq r, [1..5]
183183

184184
r = (x for x in [a..b] by down)
185-
arrayEq r, [1]
185+
arrayEq r, []
186186

187187
r = (x for x in [b..a] by up)
188-
arrayEq r, [5]
188+
arrayEq r, []
189189

190190
r = (x for x in [b..a] by down)
191191
arrayEq r, [5..1]
@@ -202,3 +202,43 @@ test "#4884: Range not declaring var for the 'i'", ->
202202
idx + 1
203203

204204
eq global.i, undefined
205+
206+
test "#4889: `for` loop unexpected behavior", ->
207+
n = 1
208+
result = []
209+
for i in [0..n]
210+
result.push i
211+
for j in [(i+1)..n]
212+
result.push j
213+
214+
arrayEq result, [0,1,1,2,1]
215+
216+
test "#4889: `for` loop unexpected behavior with `by 1` on second loop", ->
217+
n = 1
218+
result = []
219+
for i in [0..n]
220+
result.push i
221+
for j in [(i+1)..n] by 1
222+
result.push j
223+
224+
arrayEq result, [0,1,1]
225+
226+
test "countdown example from docs", ->
227+
countdown = (num for num in [10..1])
228+
arrayEq countdown, [10,9,8,7,6,5,4,3,2,1]
229+
230+
test "counting up when the range goes down returns an empty array", ->
231+
countdown = (num for num in [10..1] by 1)
232+
arrayEq countdown, []
233+
234+
test "counting down when the range goes up returns an empty array", ->
235+
countup = (num for num in [1..10] by -1)
236+
arrayEq countup, []
237+
238+
test "counting down by too much returns just the first value", ->
239+
countdown = (num for num in [10..1] by -100)
240+
arrayEq countdown, [10]
241+
242+
test "counting up by too much returns just the first value", ->
243+
countup = (num for num in [1..10] by 100)
244+
arrayEq countup, [1]

0 commit comments

Comments
 (0)