zombie-killer 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +28 -0
- data/bin/zk +45 -0
- data/lib/zombie_killer.rb +4 -0
- data/lib/zombie_killer/code_histogram.rb +50 -0
- data/lib/zombie_killer/killer.rb +35 -0
- data/lib/zombie_killer/niceness.rb +60 -0
- data/lib/zombie_killer/node_type_counter.rb +29 -0
- data/lib/zombie_killer/rewriter.rb +303 -0
- data/lib/zombie_killer/variable_scope.rb +62 -0
- data/lib/zombie_killer/version.rb +3 -0
- data/spec/rspec_renderer.rb +172 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/zombie_killer_spec.md +1101 -0
- data/spec/zombie_killer_spec.rb +1148 -0
- metadata +145 -0
@@ -0,0 +1,1148 @@
|
|
1
|
+
# Generated from spec/zombie_killer_spec.md -- do not change!
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe "ZombieKiller:" do
|
6
|
+
describe "table of contents:" do
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "concepts:" do
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "literals:" do
|
13
|
+
it "translates `Ops.add` of two string literals" do
|
14
|
+
original_code = cleanup(<<-EOT)
|
15
|
+
Ops.add("Hello", "World")
|
16
|
+
EOT
|
17
|
+
|
18
|
+
translated_code = cleanup(<<-EOT)
|
19
|
+
"Hello" + "World"
|
20
|
+
EOT
|
21
|
+
|
22
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "translates `Ops.add` of two integer literals" do
|
26
|
+
original_code = cleanup(<<-EOT)
|
27
|
+
Ops.add(40, 2)
|
28
|
+
EOT
|
29
|
+
|
30
|
+
translated_code = cleanup(<<-EOT)
|
31
|
+
40 + 2
|
32
|
+
EOT
|
33
|
+
|
34
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "translates assignment of `Ops.add` of two string literals" do
|
38
|
+
original_code = cleanup(<<-EOT)
|
39
|
+
v = Ops.add("Hello", "World")
|
40
|
+
EOT
|
41
|
+
|
42
|
+
translated_code = cleanup(<<-EOT)
|
43
|
+
v = "Hello" + "World"
|
44
|
+
EOT
|
45
|
+
|
46
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not translate Ops.add if any argument is ugly" do
|
50
|
+
original_code = cleanup(<<-EOT)
|
51
|
+
Ops.add("Hello", world)
|
52
|
+
EOT
|
53
|
+
|
54
|
+
translated_code = cleanup(<<-EOT)
|
55
|
+
Ops.add("Hello", world)
|
56
|
+
EOT
|
57
|
+
|
58
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "does not translate Ops.add if any argument is the nil literal" do
|
62
|
+
original_code = cleanup(<<-EOT)
|
63
|
+
Ops.add("Hello", nil)
|
64
|
+
EOT
|
65
|
+
|
66
|
+
translated_code = cleanup(<<-EOT)
|
67
|
+
Ops.add("Hello", nil)
|
68
|
+
EOT
|
69
|
+
|
70
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "variables:" do
|
75
|
+
it "translates `Ops.add(nice_variable, literal)`" do
|
76
|
+
original_code = cleanup(<<-EOT)
|
77
|
+
v = "Hello"
|
78
|
+
Ops.add(v, "World")
|
79
|
+
EOT
|
80
|
+
|
81
|
+
translated_code = cleanup(<<-EOT)
|
82
|
+
v = "Hello"
|
83
|
+
v + "World"
|
84
|
+
EOT
|
85
|
+
|
86
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "doesn't translate `Ops.add(nice_variable, literal)` when the variable got it's niceness via multiple assignemnt" do
|
90
|
+
original_code = cleanup(<<-EOT)
|
91
|
+
v1, v2 = "Hello", "World"
|
92
|
+
Ops.add(v1, v2)
|
93
|
+
EOT
|
94
|
+
|
95
|
+
translated_code = cleanup(<<-EOT)
|
96
|
+
v1, v2 = "Hello", "World"
|
97
|
+
Ops.add(v1, v2)
|
98
|
+
EOT
|
99
|
+
|
100
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "translates `Ops.add(nontrivially_nice_variable, literal)`" do
|
104
|
+
original_code = cleanup(<<-EOT)
|
105
|
+
v = "Hello"
|
106
|
+
v2 = v
|
107
|
+
v = uglify
|
108
|
+
Ops.add(v2, "World")
|
109
|
+
EOT
|
110
|
+
|
111
|
+
translated_code = cleanup(<<-EOT)
|
112
|
+
v = "Hello"
|
113
|
+
v2 = v
|
114
|
+
v = uglify
|
115
|
+
v2 + "World"
|
116
|
+
EOT
|
117
|
+
|
118
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "does not translate `Ops.add(mutated_variable, literal)`" do
|
122
|
+
original_code = cleanup(<<-EOT)
|
123
|
+
v = "Hello"
|
124
|
+
v = f(v)
|
125
|
+
Ops.add(v, "World")
|
126
|
+
EOT
|
127
|
+
|
128
|
+
translated_code = cleanup(<<-EOT)
|
129
|
+
v = "Hello"
|
130
|
+
v = f(v)
|
131
|
+
Ops.add(v, "World")
|
132
|
+
EOT
|
133
|
+
|
134
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "does not confuse variables across `def`s" do
|
138
|
+
original_code = cleanup(<<-EOT)
|
139
|
+
def a
|
140
|
+
v = "literal"
|
141
|
+
end
|
142
|
+
|
143
|
+
def b(v)
|
144
|
+
Ops.add(v, "literal")
|
145
|
+
end
|
146
|
+
EOT
|
147
|
+
|
148
|
+
translated_code = cleanup(<<-EOT)
|
149
|
+
def a
|
150
|
+
v = "literal"
|
151
|
+
end
|
152
|
+
|
153
|
+
def b(v)
|
154
|
+
Ops.add(v, "literal")
|
155
|
+
end
|
156
|
+
EOT
|
157
|
+
|
158
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "does not confuse variables across `def self.`s" do
|
162
|
+
original_code = cleanup(<<-EOT)
|
163
|
+
v = 1
|
164
|
+
|
165
|
+
def self.foo(v)
|
166
|
+
Ops.add(v, 1)
|
167
|
+
end
|
168
|
+
EOT
|
169
|
+
|
170
|
+
translated_code = cleanup(<<-EOT)
|
171
|
+
v = 1
|
172
|
+
|
173
|
+
def self.foo(v)
|
174
|
+
Ops.add(v, 1)
|
175
|
+
end
|
176
|
+
EOT
|
177
|
+
|
178
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "does not confuse variables across `module`s" do
|
182
|
+
original_code = cleanup(<<-EOT)
|
183
|
+
module A
|
184
|
+
v = "literal"
|
185
|
+
end
|
186
|
+
|
187
|
+
module B
|
188
|
+
# The assignment is needed to convince Ruby parser that the "v" reference in
|
189
|
+
# the "Ops.add" call later refers to a variable, not a method. This means it
|
190
|
+
# will be parsed as a "lvar" node (which can possibly be nice), not a "send"
|
191
|
+
# node (which can't be nice).
|
192
|
+
|
193
|
+
v = v
|
194
|
+
Ops.add(v, "literal")
|
195
|
+
end
|
196
|
+
EOT
|
197
|
+
|
198
|
+
translated_code = cleanup(<<-EOT)
|
199
|
+
module A
|
200
|
+
v = "literal"
|
201
|
+
end
|
202
|
+
|
203
|
+
module B
|
204
|
+
# The assignment is needed to convince Ruby parser that the "v" reference in
|
205
|
+
# the "Ops.add" call later refers to a variable, not a method. This means it
|
206
|
+
# will be parsed as a "lvar" node (which can possibly be nice), not a "send"
|
207
|
+
# node (which can't be nice).
|
208
|
+
|
209
|
+
v = v
|
210
|
+
Ops.add(v, "literal")
|
211
|
+
end
|
212
|
+
EOT
|
213
|
+
|
214
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "does not confuse variables across `class`s" do
|
218
|
+
original_code = cleanup(<<-EOT)
|
219
|
+
class A
|
220
|
+
v = "literal"
|
221
|
+
end
|
222
|
+
|
223
|
+
class B
|
224
|
+
# The assignment is needed to convince Ruby parser that the "v" reference in
|
225
|
+
# the "Ops.add" call later refers to a variable, not a method. This means it
|
226
|
+
# will be parsed as a "lvar" node (which can possibly be nice), not a "send"
|
227
|
+
# node (which can't be nice).
|
228
|
+
|
229
|
+
v = v
|
230
|
+
Ops.add(v, "literal")
|
231
|
+
end
|
232
|
+
EOT
|
233
|
+
|
234
|
+
translated_code = cleanup(<<-EOT)
|
235
|
+
class A
|
236
|
+
v = "literal"
|
237
|
+
end
|
238
|
+
|
239
|
+
class B
|
240
|
+
# The assignment is needed to convince Ruby parser that the "v" reference in
|
241
|
+
# the "Ops.add" call later refers to a variable, not a method. This means it
|
242
|
+
# will be parsed as a "lvar" node (which can possibly be nice), not a "send"
|
243
|
+
# node (which can't be nice).
|
244
|
+
|
245
|
+
v = v
|
246
|
+
Ops.add(v, "literal")
|
247
|
+
end
|
248
|
+
EOT
|
249
|
+
|
250
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
251
|
+
end
|
252
|
+
|
253
|
+
it "does not confuse variables across singleton `class`s" do
|
254
|
+
original_code = cleanup(<<-EOT)
|
255
|
+
class << self
|
256
|
+
v = "literal"
|
257
|
+
end
|
258
|
+
|
259
|
+
class << self
|
260
|
+
# The assignment is needed to convince Ruby parser that the "v" reference in
|
261
|
+
# the "Ops.add" call later refers to a variable, not a method. This means it
|
262
|
+
# will be parsed as a "lvar" node (which can possibly be nice), not a "send"
|
263
|
+
# node (which can't be nice).
|
264
|
+
|
265
|
+
v = v
|
266
|
+
Ops.add(v, "literal")
|
267
|
+
end
|
268
|
+
EOT
|
269
|
+
|
270
|
+
translated_code = cleanup(<<-EOT)
|
271
|
+
class << self
|
272
|
+
v = "literal"
|
273
|
+
end
|
274
|
+
|
275
|
+
class << self
|
276
|
+
# The assignment is needed to convince Ruby parser that the "v" reference in
|
277
|
+
# the "Ops.add" call later refers to a variable, not a method. This means it
|
278
|
+
# will be parsed as a "lvar" node (which can possibly be nice), not a "send"
|
279
|
+
# node (which can't be nice).
|
280
|
+
|
281
|
+
v = v
|
282
|
+
Ops.add(v, "literal")
|
283
|
+
end
|
284
|
+
EOT
|
285
|
+
|
286
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "assignments:" do
|
291
|
+
describe "and-assignment:" do
|
292
|
+
it "manages niceness correctly in presence of `&&=`" do
|
293
|
+
original_code = cleanup(<<-EOT)
|
294
|
+
nice1 = true
|
295
|
+
nice2 = true
|
296
|
+
ugly1 = nil
|
297
|
+
ugly2 = nil
|
298
|
+
|
299
|
+
nice1 &&= true
|
300
|
+
nice2 &&= nil
|
301
|
+
ugly1 &&= true
|
302
|
+
ugly2 &&= nil
|
303
|
+
|
304
|
+
Ops.add(nice1, 1)
|
305
|
+
Ops.add(nice2, 1)
|
306
|
+
Ops.add(ugly1, 1)
|
307
|
+
Ops.add(ugly2, 1)
|
308
|
+
EOT
|
309
|
+
|
310
|
+
translated_code = cleanup(<<-EOT)
|
311
|
+
nice1 = true
|
312
|
+
nice2 = true
|
313
|
+
ugly1 = nil
|
314
|
+
ugly2 = nil
|
315
|
+
|
316
|
+
nice1 &&= true
|
317
|
+
nice2 &&= nil
|
318
|
+
ugly1 &&= true
|
319
|
+
ugly2 &&= nil
|
320
|
+
|
321
|
+
nice1 + 1
|
322
|
+
Ops.add(nice2, 1)
|
323
|
+
Ops.add(ugly1, 1)
|
324
|
+
Ops.add(ugly2, 1)
|
325
|
+
EOT
|
326
|
+
|
327
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe "or-assignment:" do
|
332
|
+
it "manages niceness correctly in presence of `||=`" do
|
333
|
+
original_code = cleanup(<<-EOT)
|
334
|
+
nice1 = true
|
335
|
+
nice2 = true
|
336
|
+
ugly1 = nil
|
337
|
+
ugly2 = nil
|
338
|
+
|
339
|
+
nice1 ||= true
|
340
|
+
nice2 ||= nil
|
341
|
+
ugly1 ||= true
|
342
|
+
ugly2 ||= nil
|
343
|
+
|
344
|
+
Ops.add(nice1, 1)
|
345
|
+
Ops.add(nice2, 1)
|
346
|
+
Ops.add(ugly1, 1)
|
347
|
+
Ops.add(ugly2, 1)
|
348
|
+
EOT
|
349
|
+
|
350
|
+
translated_code = cleanup(<<-EOT)
|
351
|
+
nice1 = true
|
352
|
+
nice2 = true
|
353
|
+
ugly1 = nil
|
354
|
+
ugly2 = nil
|
355
|
+
|
356
|
+
nice1 ||= true
|
357
|
+
nice2 ||= nil
|
358
|
+
ugly1 ||= true
|
359
|
+
ugly2 ||= nil
|
360
|
+
|
361
|
+
nice1 + 1
|
362
|
+
nice2 + 1
|
363
|
+
ugly1 + 1
|
364
|
+
Ops.add(ugly2, 1)
|
365
|
+
EOT
|
366
|
+
|
367
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
describe "calls preserving niceness:" do
|
373
|
+
it "A localized string literal is nice" do
|
374
|
+
original_code = cleanup(<<-EOT)
|
375
|
+
v = _("Hello")
|
376
|
+
Ops.add(v, "World")
|
377
|
+
EOT
|
378
|
+
|
379
|
+
translated_code = cleanup(<<-EOT)
|
380
|
+
v = _("Hello")
|
381
|
+
v + "World"
|
382
|
+
EOT
|
383
|
+
|
384
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
385
|
+
end
|
386
|
+
|
387
|
+
describe "calls generating niceness:" do
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "translation below top level:" do
|
392
|
+
it "translates a zombie nested in other calls" do
|
393
|
+
original_code = cleanup(<<-EOT)
|
394
|
+
v = 1
|
395
|
+
foo(bar(Ops.add(v, 1), baz))
|
396
|
+
EOT
|
397
|
+
|
398
|
+
translated_code = cleanup(<<-EOT)
|
399
|
+
v = 1
|
400
|
+
foo(bar(v + 1, baz))
|
401
|
+
EOT
|
402
|
+
|
403
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
describe "chained translation:" do
|
408
|
+
it "translates a left-associative chain of nice zombies" do
|
409
|
+
original_code = cleanup(<<-EOT)
|
410
|
+
Ops.add(Ops.add(1, 2), 3)
|
411
|
+
EOT
|
412
|
+
|
413
|
+
translated_code = cleanup(<<-EOT)
|
414
|
+
(1 + 2) + 3
|
415
|
+
EOT
|
416
|
+
|
417
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
418
|
+
end
|
419
|
+
|
420
|
+
it "translates a right-associative chain of nice zombies" do
|
421
|
+
original_code = cleanup(<<-EOT)
|
422
|
+
Ops.add(1, Ops.add(2, 3))
|
423
|
+
EOT
|
424
|
+
|
425
|
+
translated_code = cleanup(<<-EOT)
|
426
|
+
1 + (2 + 3)
|
427
|
+
EOT
|
428
|
+
|
429
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
430
|
+
end
|
431
|
+
|
432
|
+
describe "in case arguments are translated already:" do
|
433
|
+
it "translates `Ops.add` of plus and literal" do
|
434
|
+
original_code = cleanup(<<-EOT)
|
435
|
+
Ops.add("Hello" + " ", "World")
|
436
|
+
EOT
|
437
|
+
|
438
|
+
translated_code = cleanup(<<-EOT)
|
439
|
+
("Hello" + " ") + "World"
|
440
|
+
EOT
|
441
|
+
|
442
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
443
|
+
end
|
444
|
+
|
445
|
+
it "translates `Ops.add` of parenthesized plus and literal" do
|
446
|
+
original_code = cleanup(<<-EOT)
|
447
|
+
Ops.add(("Hello" + " "), "World")
|
448
|
+
EOT
|
449
|
+
|
450
|
+
translated_code = cleanup(<<-EOT)
|
451
|
+
("Hello" + " ") + "World"
|
452
|
+
EOT
|
453
|
+
|
454
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
455
|
+
end
|
456
|
+
|
457
|
+
it "translates `Ops.add` of literal and plus" do
|
458
|
+
original_code = cleanup(<<-EOT)
|
459
|
+
Ops.add("Hello", " " + "World")
|
460
|
+
EOT
|
461
|
+
|
462
|
+
translated_code = cleanup(<<-EOT)
|
463
|
+
"Hello" + (" " + "World")
|
464
|
+
EOT
|
465
|
+
|
466
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
describe "if:" do
|
472
|
+
it "translates the `then` body of an `if` statement" do
|
473
|
+
original_code = cleanup(<<-EOT)
|
474
|
+
if cond
|
475
|
+
Ops.add(1, 1)
|
476
|
+
end
|
477
|
+
EOT
|
478
|
+
|
479
|
+
translated_code = cleanup(<<-EOT)
|
480
|
+
if cond
|
481
|
+
1 + 1
|
482
|
+
end
|
483
|
+
EOT
|
484
|
+
|
485
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
486
|
+
end
|
487
|
+
|
488
|
+
it "translates the `then` body of an `unless` statement" do
|
489
|
+
original_code = cleanup(<<-EOT)
|
490
|
+
unless cond
|
491
|
+
Ops.add(1, 1)
|
492
|
+
end
|
493
|
+
EOT
|
494
|
+
|
495
|
+
translated_code = cleanup(<<-EOT)
|
496
|
+
unless cond
|
497
|
+
1 + 1
|
498
|
+
end
|
499
|
+
EOT
|
500
|
+
|
501
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
502
|
+
end
|
503
|
+
|
504
|
+
it "It translates both branches of an `if` statement, independently of each other" do
|
505
|
+
original_code = cleanup(<<-EOT)
|
506
|
+
v = 1
|
507
|
+
if cond
|
508
|
+
Ops.add(v, 1)
|
509
|
+
v = nil
|
510
|
+
else
|
511
|
+
Ops.add(1, v)
|
512
|
+
v = nil
|
513
|
+
end
|
514
|
+
EOT
|
515
|
+
|
516
|
+
translated_code = cleanup(<<-EOT)
|
517
|
+
v = 1
|
518
|
+
if cond
|
519
|
+
v + 1
|
520
|
+
v = nil
|
521
|
+
else
|
522
|
+
1 + v
|
523
|
+
v = nil
|
524
|
+
end
|
525
|
+
EOT
|
526
|
+
|
527
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
528
|
+
end
|
529
|
+
|
530
|
+
it "The condition also contributes to the data state" do
|
531
|
+
original_code = cleanup(<<-EOT)
|
532
|
+
if cond(v = 1)
|
533
|
+
Ops.add(v, 1)
|
534
|
+
end
|
535
|
+
EOT
|
536
|
+
|
537
|
+
translated_code = cleanup(<<-EOT)
|
538
|
+
if cond(v = 1)
|
539
|
+
v + 1
|
540
|
+
end
|
541
|
+
EOT
|
542
|
+
|
543
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
544
|
+
end
|
545
|
+
|
546
|
+
describe "a variable is not nice after its niceness was invalidated by an `if`:" do
|
547
|
+
it "Plain `if`" do
|
548
|
+
original_code = cleanup(<<-EOT)
|
549
|
+
v = 1
|
550
|
+
if cond
|
551
|
+
v = nil
|
552
|
+
end
|
553
|
+
Ops.add(v, 1)
|
554
|
+
EOT
|
555
|
+
|
556
|
+
translated_code = cleanup(<<-EOT)
|
557
|
+
v = 1
|
558
|
+
if cond
|
559
|
+
v = nil
|
560
|
+
end
|
561
|
+
Ops.add(v, 1)
|
562
|
+
EOT
|
563
|
+
|
564
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
565
|
+
end
|
566
|
+
|
567
|
+
it "Trailing `if`" do
|
568
|
+
original_code = cleanup(<<-EOT)
|
569
|
+
v = 1
|
570
|
+
v = nil if cond
|
571
|
+
Ops.add(v, 1)
|
572
|
+
EOT
|
573
|
+
|
574
|
+
translated_code = cleanup(<<-EOT)
|
575
|
+
v = 1
|
576
|
+
v = nil if cond
|
577
|
+
Ops.add(v, 1)
|
578
|
+
EOT
|
579
|
+
|
580
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
581
|
+
end
|
582
|
+
|
583
|
+
it "Plain `unless`" do
|
584
|
+
original_code = cleanup(<<-EOT)
|
585
|
+
v = 1
|
586
|
+
unless cond
|
587
|
+
v = nil
|
588
|
+
end
|
589
|
+
Ops.add(v, 1)
|
590
|
+
EOT
|
591
|
+
|
592
|
+
translated_code = cleanup(<<-EOT)
|
593
|
+
v = 1
|
594
|
+
unless cond
|
595
|
+
v = nil
|
596
|
+
end
|
597
|
+
Ops.add(v, 1)
|
598
|
+
EOT
|
599
|
+
|
600
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
601
|
+
end
|
602
|
+
|
603
|
+
it "Trailing `unless`" do
|
604
|
+
original_code = cleanup(<<-EOT)
|
605
|
+
v = 1
|
606
|
+
v = nil unless cond
|
607
|
+
Ops.add(v, 1)
|
608
|
+
EOT
|
609
|
+
|
610
|
+
translated_code = cleanup(<<-EOT)
|
611
|
+
v = 1
|
612
|
+
v = nil unless cond
|
613
|
+
Ops.add(v, 1)
|
614
|
+
EOT
|
615
|
+
|
616
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
describe "resuming with a clean slate after an `if`:" do
|
621
|
+
it "It translates zombies whose arguments were found nice after an `if`" do
|
622
|
+
original_code = cleanup(<<-EOT)
|
623
|
+
if cond
|
624
|
+
v = nil
|
625
|
+
end
|
626
|
+
v = 1
|
627
|
+
Ops.add(v, 1)
|
628
|
+
EOT
|
629
|
+
|
630
|
+
translated_code = cleanup(<<-EOT)
|
631
|
+
if cond
|
632
|
+
v = nil
|
633
|
+
end
|
634
|
+
v = 1
|
635
|
+
v + 1
|
636
|
+
EOT
|
637
|
+
|
638
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
describe "case:" do
|
644
|
+
it "translates the `when` body of a `case` statement" do
|
645
|
+
original_code = cleanup(<<-EOT)
|
646
|
+
case expr
|
647
|
+
when 1
|
648
|
+
Ops.add(1, 1)
|
649
|
+
end
|
650
|
+
EOT
|
651
|
+
|
652
|
+
translated_code = cleanup(<<-EOT)
|
653
|
+
case expr
|
654
|
+
when 1
|
655
|
+
1 + 1
|
656
|
+
end
|
657
|
+
EOT
|
658
|
+
|
659
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
660
|
+
end
|
661
|
+
|
662
|
+
it "It translates all branches of a `case` statement, independently of each other" do
|
663
|
+
original_code = cleanup(<<-EOT)
|
664
|
+
v = 1
|
665
|
+
case expr
|
666
|
+
when 1
|
667
|
+
Ops.add(v, 1)
|
668
|
+
v = nil
|
669
|
+
when 2
|
670
|
+
Ops.add(v, 2)
|
671
|
+
v = nil
|
672
|
+
else
|
673
|
+
Ops.add(1, v)
|
674
|
+
v = nil
|
675
|
+
end
|
676
|
+
EOT
|
677
|
+
|
678
|
+
translated_code = cleanup(<<-EOT)
|
679
|
+
v = 1
|
680
|
+
case expr
|
681
|
+
when 1
|
682
|
+
v + 1
|
683
|
+
v = nil
|
684
|
+
when 2
|
685
|
+
v + 2
|
686
|
+
v = nil
|
687
|
+
else
|
688
|
+
1 + v
|
689
|
+
v = nil
|
690
|
+
end
|
691
|
+
EOT
|
692
|
+
|
693
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
694
|
+
end
|
695
|
+
|
696
|
+
it "The expression also contributes to the data state" do
|
697
|
+
original_code = cleanup(<<-EOT)
|
698
|
+
case v = 1
|
699
|
+
when 1
|
700
|
+
Ops.add(v, 1)
|
701
|
+
end
|
702
|
+
EOT
|
703
|
+
|
704
|
+
translated_code = cleanup(<<-EOT)
|
705
|
+
case v = 1
|
706
|
+
when 1
|
707
|
+
v + 1
|
708
|
+
end
|
709
|
+
EOT
|
710
|
+
|
711
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
712
|
+
end
|
713
|
+
|
714
|
+
it "The test also contributes to the data state" do
|
715
|
+
original_code = cleanup(<<-EOT)
|
716
|
+
case expr
|
717
|
+
when v = 1
|
718
|
+
Ops.add(v, 1)
|
719
|
+
end
|
720
|
+
EOT
|
721
|
+
|
722
|
+
translated_code = cleanup(<<-EOT)
|
723
|
+
case expr
|
724
|
+
when v = 1
|
725
|
+
v + 1
|
726
|
+
end
|
727
|
+
EOT
|
728
|
+
|
729
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
730
|
+
end
|
731
|
+
|
732
|
+
describe "a variable is not nice after its niceness was invalidated by a `case`:" do
|
733
|
+
it "The test also contributes to the data state" do
|
734
|
+
original_code = cleanup(<<-EOT)
|
735
|
+
v = 1
|
736
|
+
case expr
|
737
|
+
when 1
|
738
|
+
v = nil
|
739
|
+
end
|
740
|
+
Ops.add(v, 1)
|
741
|
+
EOT
|
742
|
+
|
743
|
+
translated_code = cleanup(<<-EOT)
|
744
|
+
v = 1
|
745
|
+
case expr
|
746
|
+
when 1
|
747
|
+
v = nil
|
748
|
+
end
|
749
|
+
Ops.add(v, 1)
|
750
|
+
EOT
|
751
|
+
|
752
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
describe "resuming with a clean slate after a `case`:" do
|
757
|
+
it "It translates zombies whose arguments were found nice after a `case`" do
|
758
|
+
original_code = cleanup(<<-EOT)
|
759
|
+
case expr
|
760
|
+
when 1
|
761
|
+
v = nil
|
762
|
+
end
|
763
|
+
v = 1
|
764
|
+
Ops.add(v, 1)
|
765
|
+
EOT
|
766
|
+
|
767
|
+
translated_code = cleanup(<<-EOT)
|
768
|
+
case expr
|
769
|
+
when 1
|
770
|
+
v = nil
|
771
|
+
end
|
772
|
+
v = 1
|
773
|
+
v + 1
|
774
|
+
EOT
|
775
|
+
|
776
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
777
|
+
end
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
describe "loops:" do
|
782
|
+
describe "while and until:" do
|
783
|
+
it "does not translate anything in the outer scope that contains a `while`" do
|
784
|
+
original_code = cleanup(<<-EOT)
|
785
|
+
v = 1
|
786
|
+
while Ops.add(v, 1)
|
787
|
+
Ops.add(1, 1)
|
788
|
+
end
|
789
|
+
Ops.add(v, 1)
|
790
|
+
EOT
|
791
|
+
|
792
|
+
translated_code = cleanup(<<-EOT)
|
793
|
+
v = 1
|
794
|
+
while Ops.add(v, 1)
|
795
|
+
Ops.add(1, 1)
|
796
|
+
end
|
797
|
+
Ops.add(v, 1)
|
798
|
+
EOT
|
799
|
+
|
800
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
801
|
+
end
|
802
|
+
|
803
|
+
it "does not translate anything in the outer scope that contains an `until`" do
|
804
|
+
original_code = cleanup(<<-EOT)
|
805
|
+
v = 1
|
806
|
+
until Ops.add(v, 1)
|
807
|
+
Ops.add(1, 1)
|
808
|
+
end
|
809
|
+
Ops.add(v, 1)
|
810
|
+
EOT
|
811
|
+
|
812
|
+
translated_code = cleanup(<<-EOT)
|
813
|
+
v = 1
|
814
|
+
until Ops.add(v, 1)
|
815
|
+
Ops.add(1, 1)
|
816
|
+
end
|
817
|
+
Ops.add(v, 1)
|
818
|
+
EOT
|
819
|
+
|
820
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
821
|
+
end
|
822
|
+
|
823
|
+
it "can continue processing after a `while`" do
|
824
|
+
original_code = cleanup(<<-EOT)
|
825
|
+
while cond
|
826
|
+
foo
|
827
|
+
end
|
828
|
+
v = 1
|
829
|
+
Ops.add(v, 1)
|
830
|
+
EOT
|
831
|
+
|
832
|
+
translated_code = cleanup(<<-EOT)
|
833
|
+
while cond
|
834
|
+
foo
|
835
|
+
end
|
836
|
+
v = 1
|
837
|
+
v + 1
|
838
|
+
EOT
|
839
|
+
|
840
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
841
|
+
end
|
842
|
+
|
843
|
+
it "can continue processing after an `until`" do
|
844
|
+
original_code = cleanup(<<-EOT)
|
845
|
+
until cond
|
846
|
+
foo
|
847
|
+
end
|
848
|
+
v = 1
|
849
|
+
Ops.add(v, 1)
|
850
|
+
EOT
|
851
|
+
|
852
|
+
translated_code = cleanup(<<-EOT)
|
853
|
+
until cond
|
854
|
+
foo
|
855
|
+
end
|
856
|
+
v = 1
|
857
|
+
v + 1
|
858
|
+
EOT
|
859
|
+
|
860
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
861
|
+
end
|
862
|
+
|
863
|
+
it "can parse both the syntactic and semantic post-condition" do
|
864
|
+
original_code = cleanup(<<-EOT)
|
865
|
+
body_runs_after_condition while cond
|
866
|
+
body_runs_after_condition until cond
|
867
|
+
|
868
|
+
begin
|
869
|
+
body_runs_before_condition
|
870
|
+
end while cond
|
871
|
+
|
872
|
+
begin
|
873
|
+
body_runs_before_condition
|
874
|
+
end until cond
|
875
|
+
EOT
|
876
|
+
|
877
|
+
translated_code = cleanup(<<-EOT)
|
878
|
+
body_runs_after_condition while cond
|
879
|
+
body_runs_after_condition until cond
|
880
|
+
|
881
|
+
begin
|
882
|
+
body_runs_before_condition
|
883
|
+
end while cond
|
884
|
+
|
885
|
+
begin
|
886
|
+
body_runs_before_condition
|
887
|
+
end until cond
|
888
|
+
EOT
|
889
|
+
|
890
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
describe "for:" do
|
895
|
+
it "does not translate inside a `for` and resumes with a clean slate" do
|
896
|
+
original_code = cleanup(<<-EOT)
|
897
|
+
v = 1
|
898
|
+
v = Ops.add(v, 1)
|
899
|
+
|
900
|
+
for i in [1, 2, 3]
|
901
|
+
v = Ops.add(v, 1)
|
902
|
+
v = uglify
|
903
|
+
end
|
904
|
+
|
905
|
+
v = Ops.add(v, 1)
|
906
|
+
w = 1
|
907
|
+
w = Ops.add(w, 1)
|
908
|
+
EOT
|
909
|
+
|
910
|
+
translated_code = cleanup(<<-EOT)
|
911
|
+
v = 1
|
912
|
+
v = v + 1
|
913
|
+
|
914
|
+
for i in [1, 2, 3]
|
915
|
+
v = Ops.add(v, 1)
|
916
|
+
v = uglify
|
917
|
+
end
|
918
|
+
|
919
|
+
v = Ops.add(v, 1)
|
920
|
+
w = 1
|
921
|
+
w = w + 1
|
922
|
+
EOT
|
923
|
+
|
924
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
925
|
+
end
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
describe "exceptions:" do
|
930
|
+
it "translates the parts, joining else, rescue separately" do
|
931
|
+
original_code = cleanup(<<-EOT)
|
932
|
+
def foo
|
933
|
+
v = 1
|
934
|
+
Ops.add(v, 1)
|
935
|
+
rescue
|
936
|
+
w = 1
|
937
|
+
Ops.add(w, 1)
|
938
|
+
v = nil
|
939
|
+
rescue
|
940
|
+
Ops.add(w, 1)
|
941
|
+
else
|
942
|
+
Ops.add(v, 1)
|
943
|
+
end
|
944
|
+
EOT
|
945
|
+
|
946
|
+
translated_code = cleanup(<<-EOT)
|
947
|
+
def foo
|
948
|
+
v = 1
|
949
|
+
v + 1
|
950
|
+
rescue
|
951
|
+
w = 1
|
952
|
+
w + 1
|
953
|
+
v = nil
|
954
|
+
rescue
|
955
|
+
Ops.add(w, 1)
|
956
|
+
else
|
957
|
+
v + 1
|
958
|
+
end
|
959
|
+
EOT
|
960
|
+
|
961
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
962
|
+
end
|
963
|
+
|
964
|
+
describe "skipping code:" do
|
965
|
+
it "does not translate code that depends on niceness skipped via an exception" do
|
966
|
+
original_code = cleanup(<<-EOT)
|
967
|
+
def a_problem
|
968
|
+
v = nil
|
969
|
+
w = 1 / 0
|
970
|
+
v = 1
|
971
|
+
rescue
|
972
|
+
puts "Oops", Ops.add(v, 1)
|
973
|
+
end
|
974
|
+
EOT
|
975
|
+
|
976
|
+
translated_code = cleanup(<<-EOT)
|
977
|
+
def a_problem
|
978
|
+
v = nil
|
979
|
+
w = 1 / 0
|
980
|
+
v = 1
|
981
|
+
rescue
|
982
|
+
puts "Oops", Ops.add(v, 1)
|
983
|
+
end
|
984
|
+
EOT
|
985
|
+
|
986
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
987
|
+
end
|
988
|
+
end
|
989
|
+
|
990
|
+
describe "exception syntax:" do
|
991
|
+
it "can parse the syntactic variants of exception handling" do
|
992
|
+
original_code = cleanup(<<-EOT)
|
993
|
+
begin
|
994
|
+
foo
|
995
|
+
raise "LOL"
|
996
|
+
foo
|
997
|
+
rescue Error
|
998
|
+
foo
|
999
|
+
rescue Bug, Blunder => b
|
1000
|
+
foo
|
1001
|
+
rescue => e
|
1002
|
+
foo
|
1003
|
+
rescue
|
1004
|
+
foo
|
1005
|
+
ensure
|
1006
|
+
foo
|
1007
|
+
end
|
1008
|
+
yast rescue nil
|
1009
|
+
EOT
|
1010
|
+
|
1011
|
+
translated_code = cleanup(<<-EOT)
|
1012
|
+
begin
|
1013
|
+
foo
|
1014
|
+
raise "LOL"
|
1015
|
+
foo
|
1016
|
+
rescue Error
|
1017
|
+
foo
|
1018
|
+
rescue Bug, Blunder => b
|
1019
|
+
foo
|
1020
|
+
rescue => e
|
1021
|
+
foo
|
1022
|
+
rescue
|
1023
|
+
foo
|
1024
|
+
ensure
|
1025
|
+
foo
|
1026
|
+
end
|
1027
|
+
yast rescue nil
|
1028
|
+
EOT
|
1029
|
+
|
1030
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
describe "retry:" do
|
1035
|
+
it "does not translate a begin-body when a rescue contains a retry" do
|
1036
|
+
original_code = cleanup(<<-EOT)
|
1037
|
+
def foo
|
1038
|
+
v = 1
|
1039
|
+
begin
|
1040
|
+
Ops.add(v, 1)
|
1041
|
+
maybe_raise
|
1042
|
+
rescue
|
1043
|
+
v = nil
|
1044
|
+
retry
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
EOT
|
1048
|
+
|
1049
|
+
translated_code = cleanup(<<-EOT)
|
1050
|
+
def foo
|
1051
|
+
v = 1
|
1052
|
+
begin
|
1053
|
+
Ops.add(v, 1)
|
1054
|
+
maybe_raise
|
1055
|
+
rescue
|
1056
|
+
v = nil
|
1057
|
+
retry
|
1058
|
+
end
|
1059
|
+
end
|
1060
|
+
EOT
|
1061
|
+
|
1062
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
1063
|
+
end
|
1064
|
+
end
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
describe "blocks:" do
|
1068
|
+
it "does not translate inside a block and resumes with a clean slate" do
|
1069
|
+
original_code = cleanup(<<-EOT)
|
1070
|
+
v = 1
|
1071
|
+
v = Ops.add(v, 1)
|
1072
|
+
|
1073
|
+
2.times do
|
1074
|
+
v = Ops.add(v, 1)
|
1075
|
+
v = uglify
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
v = Ops.add(v, 1)
|
1079
|
+
w = 1
|
1080
|
+
w = Ops.add(w, 1)
|
1081
|
+
EOT
|
1082
|
+
|
1083
|
+
translated_code = cleanup(<<-EOT)
|
1084
|
+
v = 1
|
1085
|
+
v = v + 1
|
1086
|
+
|
1087
|
+
2.times do
|
1088
|
+
v = Ops.add(v, 1)
|
1089
|
+
v = uglify
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
v = Ops.add(v, 1)
|
1093
|
+
w = 1
|
1094
|
+
w = w + 1
|
1095
|
+
EOT
|
1096
|
+
|
1097
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
describe "formatting:" do
|
1102
|
+
it "does not translate `Ops.add` if any argument has a comment" do
|
1103
|
+
original_code = cleanup(<<-EOT)
|
1104
|
+
Ops.add(
|
1105
|
+
"Hello",
|
1106
|
+
# foo
|
1107
|
+
"World"
|
1108
|
+
)
|
1109
|
+
EOT
|
1110
|
+
|
1111
|
+
translated_code = cleanup(<<-EOT)
|
1112
|
+
Ops.add(
|
1113
|
+
"Hello",
|
1114
|
+
# foo
|
1115
|
+
"World"
|
1116
|
+
)
|
1117
|
+
EOT
|
1118
|
+
|
1119
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
1120
|
+
end
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
describe "templates:" do
|
1124
|
+
it "It translates" do
|
1125
|
+
original_code = cleanup(<<-EOT)
|
1126
|
+
|
1127
|
+
EOT
|
1128
|
+
|
1129
|
+
translated_code = cleanup(<<-EOT)
|
1130
|
+
|
1131
|
+
EOT
|
1132
|
+
|
1133
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
it "It does not translate" do
|
1137
|
+
original_code = cleanup(<<-EOT)
|
1138
|
+
|
1139
|
+
EOT
|
1140
|
+
|
1141
|
+
translated_code = cleanup(<<-EOT)
|
1142
|
+
|
1143
|
+
EOT
|
1144
|
+
|
1145
|
+
expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
end
|