ripper-plus 1.0.1 → 1.1.0.pre2

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.
data/Gemfile CHANGED
@@ -10,5 +10,4 @@ group :development do
10
10
  gem "yard", "~> 0.6.0"
11
11
  gem "bundler", "~> 1.0.0"
12
12
  gem "jeweler", "~> 1.5.2"
13
- gem "rcov", ">= 0"
14
13
  end
data/README.md CHANGED
@@ -29,11 +29,11 @@ If one takes the intuitive approach, one would assume it prints "Label: hello",
29
29
 
30
30
  `say_what?` prints a blank line! This is because as soon as the `x` on the LHS is parsed, `x` is a local variable with value `nil`. By the time `defined?(x)` is executed, `x` has long since been a local variable!
31
31
 
32
- ## Ripper's Mistake
32
+ ## Ripper's Bareword Behavior
33
33
 
34
- Ripper doesn't carry scope information as it parses, and as such, parses any lone identifier as an AST node of type `:var_ref`. It is up to consumers of the AST to figure out the meaning of the `:var_ref` node. It can be reasonably argued that the semantics of the `:var_ref` node should not be part of the AST, as my thesis adviser pointed out when I complained about this, as the two are syntactically identical. Unfortunately, the meaning of the `var_ref` node comes from the parser itself; any attempt to determine semantics based solely on the AST is simply re-creating the work the parser ordinarily does! Indeed, when Ruby parses the code for execution, it *does* create different internal node types upon seeing a method-call bareword and a local variable bareword!
34
+ Ripper doesn't carry scope information as it parses, and as such, parses any lone identifier as an AST node of type `:var_ref`. It is up to consumers of the AST to figure out the meaning of the `:var_ref` node. It can be reasonably argued that the semantics of the `:var_ref` node should not be part of the AST, as my thesis adviser pointed out when I complained about this, as the two are syntactically identical. Unfortunately, the meaning of the `var_ref` node comes from the parser itself; any attempt to determine semantics based solely on the AST is simply re-creating the work the parser ordinarily does. Indeed, when Ruby parses the code for execution, it *does* create different internal AST node types upon seeing a method-call bareword and a local variable bareword!
35
35
 
36
- I'd like to see this behavior rolled into Ripper proper. Until then, `ripper-plus` is a reasonable replacement.
36
+ I'd like to see this behavior rolled into Ripper proper. Until then, `ripper-plus` does it.
37
37
 
38
38
  ## ripper-plus
39
39
 
@@ -47,9 +47,64 @@ Need to be properly resolved. Did you know that, unlike the `label = label` exam
47
47
  def foo(x, y = y())
48
48
  end
49
49
 
50
- Anyway, ripper-plus turns all method-call `:var_ref` nodes into `zcall` nodes; the node structure is otherwise unchanged.
50
+ Anyway, ripper-plus turns all method-call `:var_ref` nodes into `:zcall` nodes; the node structure is otherwise unchanged. It runs in O(N) time and O(h) space, where h is the height of the tree.
51
51
 
52
- The truth is, everybody who is using Ripper right now *should* be doing *all* of this. Anything short, and you have bugs. [Laser](https://github.com/michaeledgar/laser/) has bugs as a result. It's a pain in the ass to get it all right. `ripper-plus` probably has bugs - I'm not gonna lie. So hopefully, in Ruby 1.9.x, this will be the default. For now, you *should* use ripper-plus.
52
+ ## Syntax Errors
53
+
54
+ Not all syntax errors in Ruby fail to parse as valid Ruby. Using `next`/`redo`/`break`/`retry` when they have no meaning will cause a "compile error (SyntaxError)" exception to be raised during parsing. Attempting to use those expressions in a value context will also fail to compile. Ripper, however, doesn't do any of this validation:
55
+
56
+ pp Ripper.sexp('x = 5; next x')
57
+ #=>
58
+ [:program,
59
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
60
+ [:next, [:args_add_block, [[:var_ref, [:@ident, "x", [1, 12]]]], false]]]]
61
+
62
+ Assigning to set read-only variables is also caught by the Ruby parser, and not at runtime. An easy way to verify this is to put a simple print statement before the offending syntax error:
63
+
64
+ $ ruby -e 'p 5; $1 = "5"'
65
+ -e:1: Can't set variable $1
66
+
67
+ Ripper catches this by wrapping the offending assignment in an `:assign_error` node. But not all such invalid assignments are caught:
68
+
69
+ pp Ripper.sexp('$1 = 5')
70
+ #=>
71
+ [:program,
72
+ [[:assign, [:assign_error, [:@backref, "$1", [1, 0]]], [:@int, "5", [1, 5]]]]]
73
+
74
+ pp Ripper.sexp('nil = self')
75
+ #=>
76
+ [:program,
77
+ [[:assign,
78
+ [:var_field, [:@kw, "nil", [1, 0]]],
79
+ [:var_ref, [:@kw, "self", [1, 6]]]]]]
80
+
81
+ Ripper has at least one other error node type, `:class_name_error`:
82
+
83
+ pp Ripper.sexp('class abc; end')
84
+ #=>
85
+ [:program,
86
+ [[:class,
87
+ [:const_ref, [:class_name_error, [:@ident, "foo", [1, 6]]]],
88
+ nil,
89
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
90
+
91
+ Ruby has a few dozen of these syntax errors that are caught at compile time, and most of them are things you would be hard-pressed to justify: re-using argument names (`def foo(x, x); end`), creating classes or modules in method bodies, and so on. Yet Ripper does not provide an easy way to check if these errors are present in a given AST.
92
+
93
+ I'm not convinced that wrapping offending nodes in different node types is the best way to handle this issue. Firstly, the error nodes are not always high enough in the tree (`:alias_error`, which arises when aliasing `$1, $2, ...`, is a notable exception). The position of the `:class_name_error` node forces every consumption of a `:class` node to check if the name subtree contains a `:class_name_error` node. The whole class is invalid though! If anything, the entire `:class` node should be inside an `:error` node. The same holds true for `:assign` nodes. This isn't really the Ripper designer's faults: Ripper is essentially a separate set of action routines in the same bison grammar used by YARV proper, and as an SAX-style parser, it'd be much harder to push errors upward.
94
+
95
+ It does seem reasonable, however, that while we are transforming `:var_ref` into `:zcall` nodes, we can find these errors (including ones ignored by Ripper), wrap them in `:error` nodes, and provide a list of exceptions corresponding to the semantic errors. `ripper-plus` does just that. For example, the above error will transform into this:
96
+
97
+ [:program,
98
+ [[:error,
99
+ [:class,
100
+ [:const_ref, [:class_name_error, [:@ident, "foo", [1, 6]]]],
101
+ nil,
102
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]],
103
+ 'class/module name must be CONSTANT']]]
104
+
105
+ ## You Should be Using ripper-plus
106
+
107
+ The truth is, everybody who is using Ripper right now *should* be doing *all* of this. Anything short, and you have bugs. [Laser](https://github.com/michaeledgar/laser/) has bugs as a result. It's a pain in the ass to get it all right. `ripper-plus` probably has bugs - I'm not gonna lie, I found one while writing this. But I'm pretty damn sure it's solid. Hopefully, in Ruby 1.9.x, this will be the default. For now, you *should* use ripper-plus.
53
108
 
54
109
  ## Contributing to ripper-plus
55
110
 
data/Rakefile CHANGED
@@ -10,6 +10,7 @@ end
10
10
  require 'rake'
11
11
 
12
12
  require 'jeweler'
13
+ require './lib/ripper-plus/version'
13
14
  Jeweler::Tasks.new do |gem|
14
15
  # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
16
  gem.name = "ripper-plus"
@@ -21,6 +22,7 @@ with Ruby 1.9. While quite complete, it has some quirks that can
21
22
  make use frustrating. This gem intends to correct them.}
22
23
  gem.email = "michael.j.edgar@dartmouth.edu"
23
24
  gem.authors = ["Michael Edgar"]
25
+ gem.version = RipperPlus::Version::STRING
24
26
  # Include your dependencies below. Runtime dependencies are required when using your gem,
25
27
  # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
26
28
  # gem.add_runtime_dependency 'jabber4r', '> 0.1'
@@ -2,6 +2,7 @@ if RUBY_VERSION < "1.9"
2
2
  raise 'ripper-plus requires Ruby 1.9+'
3
3
  end
4
4
  require 'ripper'
5
+ require 'ripper-plus/version'
5
6
  require 'ripper-plus/ripper-plus'
6
7
  require 'ripper-plus/scope_stack'
7
8
  require 'ripper-plus/transformer'
@@ -2,11 +2,18 @@ module RipperPlus
2
2
  # internal class that manages the current scopes.
3
3
  class ScopeStack
4
4
  SCOPE_BLOCKER_9000 = :scope_block
5
+
5
6
  def initialize
6
7
  # simplifies algorithm to have the scope blocker be the stack base
7
8
  @stack = [SCOPE_BLOCKER_9000, Set.new]
9
+ @in_method = false
10
+ end
11
+
12
+ def in_method?
13
+ @in_method
8
14
  end
9
15
 
16
+ # For debugging purposes
10
17
  def inspect
11
18
  middle = @stack.map do |scope|
12
19
  if SCOPE_BLOCKER_9000 == scope
@@ -31,13 +38,16 @@ module RipperPlus
31
38
  end
32
39
 
33
40
  # An open scope denies reference to local variables in enclosing scopes.
34
- def with_closed_scope
41
+ def with_closed_scope(is_method = false)
42
+ old_in_method = @in_method
43
+ @in_method ||= is_method
35
44
  @stack.push(SCOPE_BLOCKER_9000)
36
45
  @stack.push(Set.new)
37
46
  yield
38
47
  ensure
39
48
  @stack.pop # pop closed scope
40
49
  @stack.pop # pop scope blocker
50
+ @in_method = old_in_method
41
51
  end
42
52
 
43
53
  # Checks if the given variable is in scope.
@@ -43,6 +43,11 @@ module RipperPlus
43
43
  module Transformer
44
44
  extend self
45
45
 
46
+ class SyntaxError < StandardError; end
47
+ class LHSError < SyntaxError; end
48
+ class DynamicConstantError < SyntaxError; end
49
+ class InvalidArgumentError < SyntaxError; end
50
+
46
51
  # Transforms the given AST into a RipperPlus AST.
47
52
  def transform(root)
48
53
  new_copy = clone_sexp(root)
@@ -51,31 +56,48 @@ module RipperPlus
51
56
  new_copy
52
57
  end
53
58
 
54
- # Transforms the given tree into a RipperPlus AST.
59
+ # Transforms the given tree into a RipperPlus AST, using a scope stack.
60
+ # This will be recursively called through each level of the tree.
55
61
  def transform_tree(tree, scope_stack)
56
62
  case tree[0]
57
63
  when :assign, :massign
58
64
  lhs, rhs = tree[1..2]
59
- add_variables_from_lhs(lhs, scope_stack)
60
- transform_tree(rhs, scope_stack)
65
+ begin
66
+ add_variables_from_node(lhs, scope_stack)
67
+ rescue SyntaxError => err
68
+ wrap_node_with_error(tree)
69
+ else
70
+ transform_tree(rhs, scope_stack)
71
+ end
61
72
  when :for
62
73
  vars, iterated, body = tree[1..3]
63
- add_variables_from_lhs(vars, scope_stack)
74
+ add_variables_from_node(vars, scope_stack)
64
75
  transform_tree(iterated, scope_stack)
65
76
  transform_tree(body, scope_stack)
66
77
  when :var_ref
78
+ # When we reach a :var_ref, we should know everything we need to know
79
+ # in order to tell if it should be transformed into a :zcall.
67
80
  if tree[1][0] == :@ident && !scope_stack.has_variable?(tree[1][1])
68
81
  tree[0] = :zcall
69
82
  end
70
83
  when :class
71
- superclass, body = tree[2..3]
72
- transform_tree(superclass, scope_stack) if superclass # superclass node
73
- scope_stack.with_closed_scope do
74
- transform_tree(body, scope_stack)
84
+ name, superclass, body = tree[1..3]
85
+ if name[1][0] == :class_name_error || scope_stack.in_method?
86
+ wrap_node_with_error(tree)
87
+ else
88
+ transform_tree(superclass, scope_stack) if superclass # superclass node
89
+ scope_stack.with_closed_scope do
90
+ transform_tree(body, scope_stack)
91
+ end
75
92
  end
76
93
  when :module
77
- scope_stack.with_closed_scope do
78
- transform_tree(tree[2], scope_stack) # body
94
+ name, body = tree[1..2]
95
+ if name[1][0] == :class_name_error || scope_stack.in_method?
96
+ wrap_node_with_error(tree)
97
+ else
98
+ scope_stack.with_closed_scope do
99
+ transform_tree(body, scope_stack) # body
100
+ end
79
101
  end
80
102
  when :sclass
81
103
  singleton, body = tree[1..2]
@@ -84,25 +106,36 @@ module RipperPlus
84
106
  transform_tree(body, scope_stack)
85
107
  end
86
108
  when :def
87
- scope_stack.with_closed_scope do
109
+ scope_stack.with_closed_scope(true) do
88
110
  param_node = tree[2]
89
111
  body = tree[3]
90
- transform_params(param_node, scope_stack)
91
- transform_tree(body, scope_stack)
112
+ begin
113
+ transform_params(param_node, scope_stack)
114
+ rescue SyntaxError
115
+ wrap_node_with_error(tree)
116
+ else
117
+ transform_tree(body, scope_stack)
118
+ end
92
119
  end
93
120
  when :defs
94
121
  transform_tree(tree[1], scope_stack) # singleton could be a method call!
95
- scope_stack.with_closed_scope do
122
+ scope_stack.with_closed_scope(true) do
96
123
  param_node = tree[4]
97
124
  body = tree[5]
98
- transform_params(param_node, scope_stack)
99
- transform_tree(body, scope_stack)
125
+ begin
126
+ transform_params(param_node, scope_stack)
127
+ rescue SyntaxError
128
+ wrap_node_with_error(tree)
129
+ else
130
+ transform_tree(body, scope_stack)
131
+ end
100
132
  end
101
133
  when :rescue
102
134
  list, name, body = tree[1..3]
103
135
  transform_tree(list, scope_stack)
136
+ # Don't forget the rescue argument!
104
137
  if name
105
- add_variables_from_lhs(name, scope_stack)
138
+ add_variables_from_node(name, scope_stack)
106
139
  end
107
140
  transform_tree(body, scope_stack)
108
141
  when :method_add_block
@@ -115,7 +148,7 @@ module RipperPlus
115
148
  if block_args
116
149
  transform_params(block_args[1], scope_stack)
117
150
  if block_args[2]
118
- block_args[2].each { |var| add_variables_from_lhs(var, scope_stack) }
151
+ block_args[2].each { |var| add_variables_from_node(var, scope_stack) }
119
152
  end
120
153
  end
121
154
  transform_tree(block_body, scope_stack)
@@ -124,28 +157,41 @@ module RipperPlus
124
157
  # The AST is the reverse of the parse order for these nodes.
125
158
  transform_tree(tree[2], scope_stack)
126
159
  transform_tree(tree[1], scope_stack)
160
+ when :alias_error, :assign_error # error already top-level! wrap it again.
161
+ wrap_node_with_error(tree)
127
162
  else
128
163
  transform_in_order(tree, scope_stack)
129
164
  end
130
165
  end
131
166
 
132
- def add_variables_from_lhs(lhs, scope_stack)
167
+ # Adds variables to the given scope stack from the given node. Allows
168
+ # nodes from parameter lists, left-hand-sides, block argument lists, and
169
+ # so on.
170
+ def add_variables_from_node(lhs, scope_stack)
133
171
  case lhs[0]
134
172
  when :@ident
135
173
  scope_stack.add_variable(lhs[1])
174
+ when :const_path_field, :@const, :top_const_field
175
+ if scope_stack.in_method?
176
+ raise DynamicConstantError.new
177
+ end
136
178
  when Array
137
- lhs.each { |var| add_variables_from_lhs(var, scope_stack) }
179
+ lhs.each { |var| add_variables_from_node(var, scope_stack) }
138
180
  when :mlhs_paren, :var_field, :rest_param, :blockarg
139
- add_variables_from_lhs(lhs[1], scope_stack)
181
+ add_variables_from_node(lhs[1], scope_stack)
140
182
  when :mlhs_add_star
141
183
  pre_star, star, post_star = lhs[1..3]
142
- pre_star.each { |var| add_variables_from_lhs(var, scope_stack) }
184
+ pre_star.each { |var| add_variables_from_node(var, scope_stack) }
143
185
  if star
144
- add_variables_from_lhs(star, scope_stack)
186
+ add_variables_from_node(star, scope_stack)
145
187
  end
146
188
  if post_star
147
- post_star.each { |var| add_variables_from_lhs(var, scope_stack) }
189
+ post_star.each { |var| add_variables_from_node(var, scope_stack) }
148
190
  end
191
+ when :param_error
192
+ raise InvalidArgumentError.new
193
+ when :assign_error
194
+ raise LHSError.new
149
195
  end
150
196
  end
151
197
 
@@ -169,27 +215,33 @@ module RipperPlus
169
215
  if param_node
170
216
  positional_1, optional, rest, positional_2, block = param_node[1..5]
171
217
  if positional_1
172
- positional_1.each { |var| add_variables_from_lhs(var, scope_stack) }
218
+ positional_1.each { |var| add_variables_from_node(var, scope_stack) }
173
219
  end
174
220
  if optional
175
221
  optional.each do |var, value|
176
222
  # MUST walk value first. (def foo(y=y); end) == (def foo(y=y()); end)
177
223
  transform_tree(value, scope_stack)
178
- add_variables_from_lhs(var, scope_stack)
224
+ add_variables_from_node(var, scope_stack)
179
225
  end
180
226
  end
181
227
  if rest && rest[1]
182
- add_variables_from_lhs(rest, scope_stack)
228
+ add_variables_from_node(rest, scope_stack)
183
229
  end
184
230
  if positional_2
185
- positional_2.each { |var| add_variables_from_lhs(var, scope_stack) }
231
+ positional_2.each { |var| add_variables_from_node(var, scope_stack) }
186
232
  end
187
233
  if block
188
- add_variables_from_lhs(block, scope_stack)
234
+ add_variables_from_node(block, scope_stack)
189
235
  end
190
236
  end
191
237
  end
192
238
 
239
+ # Wraps the given node as an error node with minimal space overhead.
240
+ def wrap_node_with_error(tree)
241
+ new_tree = [:error, tree.dup]
242
+ tree.replace(new_tree)
243
+ end
244
+
193
245
  # Deep-copies the sexp. I wish Array#clone did deep copies...
194
246
  def clone_sexp(node)
195
247
  node.map do |part|
@@ -0,0 +1,10 @@
1
+ module RipperPlus
2
+ module Version
3
+ MAJOR = 1
4
+ MINOR = 1
5
+ PATCH = 0
6
+ BUILD = 'pre2'
7
+
8
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
9
+ end
10
+ end
@@ -26,4 +26,24 @@ describe RipperPlus::ScopeStack do
26
26
  end
27
27
  @stack.should have_variable(:hello)
28
28
  end
29
+
30
+ it 'tracks entry and exit of methods' do
31
+ @stack.should_not be_in_method
32
+ @stack.with_open_scope do
33
+ @stack.should_not be_in_method
34
+ @stack.with_closed_scope do
35
+ @stack.should_not be_in_method
36
+ @stack.with_closed_scope(true) do
37
+ @stack.should be_in_method
38
+ @stack.with_closed_scope do
39
+ @stack.should be_in_method
40
+ end
41
+ @stack.should be_in_method
42
+ end
43
+ @stack.should_not be_in_method
44
+ end
45
+ @stack.should_not be_in_method
46
+ end
47
+ @stack.should_not be_in_method
48
+ end
29
49
  end
@@ -11,6 +11,20 @@ RSpec::Matchers.define :transform_to do |output|
11
11
  match do |input|
12
12
  RipperPlus::Transformer.transform(input) == output
13
13
  end
14
+
15
+ diffable
16
+ end
17
+
18
+ def dfs_for_node_type(tree, type)
19
+ if tree[0] == type
20
+ return tree
21
+ else
22
+ tree.select { |child| Array === child }.each do |child|
23
+ result = dfs_for_node_type(child, type)
24
+ return result if result
25
+ end
26
+ end
27
+ nil
14
28
  end
15
29
 
16
30
  RSpec.configure do |config|
@@ -1,565 +1,825 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe RipperPlus::Transformer do
4
- it 'should transform a simple zcall in a method' do
5
- input_tree =
6
- [:program,
7
- [[:def,
8
- [:@ident, "foo", [1, 4]],
9
- [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
10
- [:bodystmt,
11
- [[:void_stmt],
12
- [:var_ref, [:@ident, "y", [1, 12]]],
13
- [:assign,
14
- [:var_field, [:@ident, "y", [1, 15]]],
15
- [:var_ref, [:@ident, "x", [1, 19]]]]],
16
- nil, nil, nil]]]]
17
- output_tree =
18
- [:program,
19
- [[:def,
20
- [:@ident, "foo", [1, 4]],
21
- [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
22
- [:bodystmt,
23
- [[:void_stmt],
24
- [:zcall, [:@ident, "y", [1, 12]]],
25
- [:assign,
26
- [:var_field, [:@ident, "y", [1, 15]]],
27
- [:var_ref, [:@ident, "x", [1, 19]]]]],
28
- nil, nil, nil]]]]
29
- input_tree.should transform_to(output_tree)
30
- end
4
+ describe 'zcall detection' do
5
+ it 'should transform a simple zcall in a method' do
6
+ input_tree =
7
+ [:program,
8
+ [[:def,
9
+ [:@ident, "foo", [1, 4]],
10
+ [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
11
+ [:bodystmt,
12
+ [[:void_stmt],
13
+ [:var_ref, [:@ident, "y", [1, 12]]],
14
+ [:assign,
15
+ [:var_field, [:@ident, "y", [1, 15]]],
16
+ [:var_ref, [:@ident, "x", [1, 19]]]]],
17
+ nil, nil, nil]]]]
18
+ output_tree =
19
+ [:program,
20
+ [[:def,
21
+ [:@ident, "foo", [1, 4]],
22
+ [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
23
+ [:bodystmt,
24
+ [[:void_stmt],
25
+ [:zcall, [:@ident, "y", [1, 12]]],
26
+ [:assign,
27
+ [:var_field, [:@ident, "y", [1, 15]]],
28
+ [:var_ref, [:@ident, "x", [1, 19]]]]],
29
+ nil, nil, nil]]]]
30
+ input_tree.should transform_to(output_tree)
31
+ end
31
32
 
32
- it 'should respect argument order in method definitions' do
33
- input_tree =
34
- [:program,
35
- [[:def,
36
- [:@ident, "foo", [1, 4]],
37
- [:paren,
38
- [:params,
39
- [[:@ident, "x", [1, 8]]],
40
- [[[:@ident, "y", [1, 11]], [:var_ref, [:@ident, "z", [1, 13]]]],
41
- [[:@ident, "z", [1, 16]], [:var_ref, [:@ident, "y", [1, 18]]]]],
42
- nil,
43
- nil,
44
- nil]],
45
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
46
- output_tree =
47
- [:program,
48
- [[:def,
49
- [:@ident, "foo", [1, 4]],
50
- [:paren,
51
- [:params,
52
- [[:@ident, "x", [1, 8]]],
53
- [[[:@ident, "y", [1, 11]], [:zcall, [:@ident, "z", [1, 13]]]],
54
- [[:@ident, "z", [1, 16]], [:var_ref, [:@ident, "y", [1, 18]]]]],
55
- nil,
56
- nil,
57
- nil]],
58
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
59
- input_tree.should transform_to(output_tree)
60
- end
33
+ it 'should respect argument order in method definitions' do
34
+ input_tree =
35
+ [:program,
36
+ [[:def,
37
+ [:@ident, "foo", [1, 4]],
38
+ [:paren,
39
+ [:params,
40
+ [[:@ident, "x", [1, 8]]],
41
+ [[[:@ident, "y", [1, 11]], [:var_ref, [:@ident, "z", [1, 13]]]],
42
+ [[:@ident, "z", [1, 16]], [:var_ref, [:@ident, "y", [1, 18]]]]],
43
+ nil,
44
+ nil,
45
+ nil]],
46
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
47
+ output_tree =
48
+ [:program,
49
+ [[:def,
50
+ [:@ident, "foo", [1, 4]],
51
+ [:paren,
52
+ [:params,
53
+ [[:@ident, "x", [1, 8]]],
54
+ [[[:@ident, "y", [1, 11]], [:zcall, [:@ident, "z", [1, 13]]]],
55
+ [[:@ident, "z", [1, 16]], [:var_ref, [:@ident, "y", [1, 18]]]]],
56
+ nil,
57
+ nil,
58
+ nil]],
59
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
60
+ input_tree.should transform_to(output_tree)
61
+ end
61
62
 
62
- it 'should respect argument order in block argument definitions' do
63
- input_tree =
64
- [:program,
65
- [[:method_add_block,
66
- [:zsuper],
67
- [:do_block,
68
- [:block_var,
69
- [:params,
70
- [[:@ident, "x", [1, 10]]],
71
- [[[:@ident, "y", [1, 13]], [:var_ref, [:@ident, "x", [1, 15]]]],
72
- [[:@ident, "z", [1, 18]], [:var_ref, [:@ident, "a", [1, 20]]]]],
73
- [:rest_param, [:@ident, "a", [1, 24]]],
74
- nil,
75
- nil],
76
- nil],
77
- [[:void_stmt],
78
- [:command,
79
- [:@ident, "p", [1, 28]],
80
- [:args_add_block,
81
- [[:var_ref, [:@ident, "x", [1, 30]]],
82
- [:var_ref, [:@ident, "y", [1, 33]]],
83
- [:var_ref, [:@ident, "z", [1, 36]]],
84
- [:var_ref, [:@ident, "a", [1, 39]]]],
85
- false]]]]]]]
86
- output_tree =
87
- [:program,
88
- [[:method_add_block,
89
- [:zsuper],
90
- [:do_block,
91
- [:block_var,
92
- [:params,
93
- [[:@ident, "x", [1, 10]]],
94
- [[[:@ident, "y", [1, 13]], [:var_ref, [:@ident, "x", [1, 15]]]],
95
- [[:@ident, "z", [1, 18]], [:zcall, [:@ident, "a", [1, 20]]]]],
96
- [:rest_param, [:@ident, "a", [1, 24]]],
97
- nil,
98
- nil],
99
- nil],
100
- [[:void_stmt],
101
- [:command,
102
- [:@ident, "p", [1, 28]],
103
- [:args_add_block,
104
- [[:var_ref, [:@ident, "x", [1, 30]]],
105
- [:var_ref, [:@ident, "y", [1, 33]]],
106
- [:var_ref, [:@ident, "z", [1, 36]]],
107
- [:var_ref, [:@ident, "a", [1, 39]]]],
108
- false]]]]]]]
109
- input_tree.should transform_to(output_tree)
110
- end
63
+ it 'should respect argument order in block argument definitions' do
64
+ input_tree =
65
+ [:program,
66
+ [[:method_add_block,
67
+ [:zsuper],
68
+ [:do_block,
69
+ [:block_var,
70
+ [:params,
71
+ [[:@ident, "x", [1, 10]]],
72
+ [[[:@ident, "y", [1, 13]], [:var_ref, [:@ident, "x", [1, 15]]]],
73
+ [[:@ident, "z", [1, 18]], [:var_ref, [:@ident, "a", [1, 20]]]]],
74
+ [:rest_param, [:@ident, "a", [1, 24]]],
75
+ nil,
76
+ nil],
77
+ nil],
78
+ [[:void_stmt],
79
+ [:command,
80
+ [:@ident, "p", [1, 28]],
81
+ [:args_add_block,
82
+ [[:var_ref, [:@ident, "x", [1, 30]]],
83
+ [:var_ref, [:@ident, "y", [1, 33]]],
84
+ [:var_ref, [:@ident, "z", [1, 36]]],
85
+ [:var_ref, [:@ident, "a", [1, 39]]]],
86
+ false]]]]]]]
87
+ output_tree =
88
+ [:program,
89
+ [[:method_add_block,
90
+ [:zsuper],
91
+ [:do_block,
92
+ [:block_var,
93
+ [:params,
94
+ [[:@ident, "x", [1, 10]]],
95
+ [[[:@ident, "y", [1, 13]], [:var_ref, [:@ident, "x", [1, 15]]]],
96
+ [[:@ident, "z", [1, 18]], [:zcall, [:@ident, "a", [1, 20]]]]],
97
+ [:rest_param, [:@ident, "a", [1, 24]]],
98
+ nil,
99
+ nil],
100
+ nil],
101
+ [[:void_stmt],
102
+ [:command,
103
+ [:@ident, "p", [1, 28]],
104
+ [:args_add_block,
105
+ [[:var_ref, [:@ident, "x", [1, 30]]],
106
+ [:var_ref, [:@ident, "y", [1, 33]]],
107
+ [:var_ref, [:@ident, "z", [1, 36]]],
108
+ [:var_ref, [:@ident, "a", [1, 39]]]],
109
+ false]]]]]]]
110
+ input_tree.should transform_to(output_tree)
111
+ end
111
112
 
112
- it 'should transform singleton names in singleton method definitions' do
113
- input_tree =
114
- [:program,
115
- [[:defs,
116
- [:var_ref, [:@ident, "foo", [1, 4]]],
117
- [:@period, ".", [1, 7]],
118
- [:@ident, "silly", [1, 8]],
119
- [:paren, [:params, nil, nil, nil, nil, nil]],
120
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
121
- output_tree =
122
- [:program,
123
- [[:defs,
124
- [:zcall, [:@ident, "foo", [1, 4]]],
125
- [:@period, ".", [1, 7]],
126
- [:@ident, "silly", [1, 8]],
127
- [:paren, [:params, nil, nil, nil, nil, nil]],
128
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
129
- input_tree.should transform_to(output_tree)
130
- end
113
+ it 'should transform singleton names in singleton method definitions' do
114
+ input_tree =
115
+ [:program,
116
+ [[:defs,
117
+ [:var_ref, [:@ident, "foo", [1, 4]]],
118
+ [:@period, ".", [1, 7]],
119
+ [:@ident, "silly", [1, 8]],
120
+ [:paren, [:params, nil, nil, nil, nil, nil]],
121
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
122
+ output_tree =
123
+ [:program,
124
+ [[:defs,
125
+ [:zcall, [:@ident, "foo", [1, 4]]],
126
+ [:@period, ".", [1, 7]],
127
+ [:@ident, "silly", [1, 8]],
128
+ [:paren, [:params, nil, nil, nil, nil, nil]],
129
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
130
+ input_tree.should transform_to(output_tree)
131
+ end
131
132
 
132
- it 'should not transform singleton names when an appropriate local variable exists' do
133
- input_tree =
134
- [:program,
135
- [[:assign,
136
- [:var_field, [:@ident, "foo", [1, 0]]],
137
- [:var_ref, [:@kw, "self", [1, 6]]]],
138
- [:defs,
139
- [:var_ref, [:@ident, "foo", [1, 16]]],
140
- [:@period, ".", [1, 19]],
141
- [:@ident, "silly", [1, 20]],
142
- [:params, nil, nil, nil, nil, nil],
143
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
144
- input_tree.should transform_to(input_tree)
145
- end
133
+ it 'should not transform singleton names when an appropriate local variable exists' do
134
+ input_tree =
135
+ [:program,
136
+ [[:assign,
137
+ [:var_field, [:@ident, "foo", [1, 0]]],
138
+ [:var_ref, [:@kw, "self", [1, 6]]]],
139
+ [:defs,
140
+ [:var_ref, [:@ident, "foo", [1, 16]]],
141
+ [:@period, ".", [1, 19]],
142
+ [:@ident, "silly", [1, 20]],
143
+ [:params, nil, nil, nil, nil, nil],
144
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
145
+ input_tree.should transform_to(input_tree)
146
+ end
146
147
 
147
- it 'does not transform the tricky x = 5 unless defined?(x) case' do
148
- input_tree =
149
- [:program,
150
- [[:unless_mod,
151
- [:defined, [:var_ref, [:@ident, "x", [1, 22]]]],
152
- [:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]]]]]
153
- input_tree.should transform_to(input_tree)
154
- end
148
+ it 'does not transform the tricky x = 5 unless defined?(x) case' do
149
+ input_tree =
150
+ [:program,
151
+ [[:unless_mod,
152
+ [:defined, [:var_ref, [:@ident, "x", [1, 22]]]],
153
+ [:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]]]]]
154
+ input_tree.should transform_to(input_tree)
155
+ end
155
156
 
156
- it 'finds all LHS vars before transforming an MLHS' do
157
- input_tree =
158
- [:program,
159
- [[:massign,
160
- [[:@ident, "a", [1, 0]],
161
- [:@const, "B", [1, 3]],
162
- [:mlhs_paren,
163
- [:mlhs_add_star,
164
- [[:@gvar, "$c", [1, 7]]],
165
- [:@ident, "rest", [1, 12]],
166
- [[:@ident, "d", [1, 18]], [:@ident, "e", [1, 21]]]]]],
167
- [:method_add_arg,
168
- [:fcall, [:@ident, "foo", [1, 26]]],
169
- [:arg_paren,
170
- [:args_add_block,
171
- [[:var_ref, [:@ident, "a", [1, 30]]],
172
- [:var_ref, [:@ident, "c", [1, 33]]],
173
- [:var_ref, [:@ident, "d", [1, 36]]]],
174
- false]]]]]]
175
- output_tree =
176
- [:program,
177
- [[:massign,
178
- [[:@ident, "a", [1, 0]],
179
- [:@const, "B", [1, 3]],
180
- [:mlhs_paren,
181
- [:mlhs_add_star,
182
- [[:@gvar, "$c", [1, 7]]],
183
- [:@ident, "rest", [1, 12]],
184
- [[:@ident, "d", [1, 18]], [:@ident, "e", [1, 21]]]]]],
185
- [:method_add_arg,
186
- [:fcall, [:@ident, "foo", [1, 26]]],
187
- [:arg_paren,
188
- [:args_add_block,
189
- [[:var_ref, [:@ident, "a", [1, 30]]],
190
- [:zcall, [:@ident, "c", [1, 33]]],
191
- [:var_ref, [:@ident, "d", [1, 36]]]],
192
- false]]]]]]
193
- input_tree.should transform_to(output_tree)
194
- end
195
-
196
- it 'creates for-loop MLHS vars before transforming the iteratee' do
197
- input_tree =
198
- [:program,
199
- [[:for,
200
- [[:@ident, "a", [1, 4]],
201
- [:@ident, "b", [1, 7]],
202
- [:mlhs_paren,
203
- [[:@ident, "c", [1, 11]],
157
+ it 'finds all LHS vars before transforming an MLHS' do
158
+ input_tree =
159
+ [:program,
160
+ [[:massign,
161
+ [[:@ident, "a", [1, 0]],
162
+ [:@const, "B", [1, 3]],
204
163
  [:mlhs_paren,
205
164
  [:mlhs_add_star,
206
- [],
207
- [:@ident, "d", [1, 16]],
208
- [[:@ident, "e", [1, 19]]]]]]]],
209
- [:method_add_arg,
210
- [:fcall, [:@ident, "foo", [1, 26]]],
211
- [:arg_paren,
212
- [:args_add_block,
213
- [[:var_ref, [:@ident, "a", [1, 30]]],
214
- [:var_ref, [:@ident, "b", [1, 33]]],
215
- [:var_ref, [:@ident, "c", [1, 36]]],
216
- [:var_ref, [:@ident, "d", [1, 39]]],
217
- [:var_ref, [:@ident, "e", [1, 42]]],
218
- [:var_ref, [:@ident, "f", [1, 45]]],
219
- [:var_ref, [:@ident, "g", [1, 48]]]],
220
- false]]],
221
- [[:void_stmt]]]]]
222
- output_tree =
223
- [:program,
224
- [[:for,
225
- [[:@ident, "a", [1, 4]],
226
- [:@ident, "b", [1, 7]],
227
- [:mlhs_paren,
228
- [[:@ident, "c", [1, 11]],
165
+ [[:@gvar, "$c", [1, 7]]],
166
+ [:@ident, "rest", [1, 12]],
167
+ [[:@ident, "d", [1, 18]], [:@ident, "e", [1, 21]]]]]],
168
+ [:method_add_arg,
169
+ [:fcall, [:@ident, "foo", [1, 26]]],
170
+ [:arg_paren,
171
+ [:args_add_block,
172
+ [[:var_ref, [:@ident, "a", [1, 30]]],
173
+ [:var_ref, [:@ident, "c", [1, 33]]],
174
+ [:var_ref, [:@ident, "d", [1, 36]]]],
175
+ false]]]]]]
176
+ output_tree =
177
+ [:program,
178
+ [[:massign,
179
+ [[:@ident, "a", [1, 0]],
180
+ [:@const, "B", [1, 3]],
229
181
  [:mlhs_paren,
230
182
  [:mlhs_add_star,
231
- [],
232
- [:@ident, "d", [1, 16]],
233
- [[:@ident, "e", [1, 19]]]]]]]],
234
- [:method_add_arg,
235
- [:fcall, [:@ident, "foo", [1, 26]]],
236
- [:arg_paren,
237
- [:args_add_block,
238
- [[:var_ref, [:@ident, "a", [1, 30]]],
239
- [:var_ref, [:@ident, "b", [1, 33]]],
240
- [:var_ref, [:@ident, "c", [1, 36]]],
241
- [:var_ref, [:@ident, "d", [1, 39]]],
242
- [:var_ref, [:@ident, "e", [1, 42]]],
243
- [:zcall, [:@ident, "f", [1, 45]]],
244
- [:zcall, [:@ident, "g", [1, 48]]]],
245
- false]]],
246
- [[:void_stmt]]]]]
247
- input_tree.should transform_to(output_tree)
248
- end
183
+ [[:@gvar, "$c", [1, 7]]],
184
+ [:@ident, "rest", [1, 12]],
185
+ [[:@ident, "d", [1, 18]], [:@ident, "e", [1, 21]]]]]],
186
+ [:method_add_arg,
187
+ [:fcall, [:@ident, "foo", [1, 26]]],
188
+ [:arg_paren,
189
+ [:args_add_block,
190
+ [[:var_ref, [:@ident, "a", [1, 30]]],
191
+ [:zcall, [:@ident, "c", [1, 33]]],
192
+ [:var_ref, [:@ident, "d", [1, 36]]]],
193
+ false]]]]]]
194
+ input_tree.should transform_to(output_tree)
195
+ end
249
196
 
250
- it 'should transform respecting subassignments in block arguments' do
251
- input_tree =
252
- [:program,
253
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "10", [1, 4]]],
254
- [:method_add_block,
255
- [:call,
256
- [:top_const_ref, [:@const, "ARR", [1, 10]]],
257
- :".",
258
- [:@ident, "each", [1, 14]]],
259
- [:brace_block,
260
- [:block_var,
261
- [:params,
262
- [[:@ident, "y", [1, 22]],
263
- [:mlhs_paren,
264
- [[:mlhs_paren, [:@ident, "z", [1, 26]]],
265
- [:mlhs_paren, [:@ident, "a", [1, 29]]]]]],
266
- [[[:@ident, "k", [1, 33]], [:var_ref, [:@ident, "z", [1, 37]]]],
267
- [[:@ident, "j", [1, 40]], [:var_ref, [:@ident, "d", [1, 44]]]]],
268
- [:rest_param, [:@ident, "r", [1, 48]]],
269
- nil,
270
- [:blockarg, [:@ident, "blk", [1, 52]]]],
271
- [[:@ident, "local", [1, 57]]]],
272
- [[:command,
273
- [:@ident, "p", [1, 64]],
274
- [:args_add_block,
275
- [[:var_ref, [:@ident, "x", [1, 66]]],
276
- [:var_ref, [:@ident, "y", [1, 69]]],
277
- [:var_ref, [:@ident, "z", [1, 72]]],
278
- [:var_ref, [:@ident, "a", [1, 75]]],
279
- [:var_ref, [:@ident, "k", [1, 78]]],
280
- [:var_ref, [:@ident, "j", [1, 81]]],
281
- [:var_ref, [:@ident, "r", [1, 84]]],
282
- [:var_ref, [:@ident, "blk", [1, 87]]],
283
- [:var_ref, [:@ident, "local", [1, 92]]],
284
- [:var_ref, [:@ident, "foo", [1, 99]]]],
285
- false]]]]]]]
286
- output_tree =
287
- [:program,
288
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "10", [1, 4]]],
289
- [:method_add_block,
290
- [:call,
291
- [:top_const_ref, [:@const, "ARR", [1, 10]]],
292
- :".",
293
- [:@ident, "each", [1, 14]]],
294
- [:brace_block,
295
- [:block_var,
296
- [:params,
297
- [[:@ident, "y", [1, 22]],
298
- [:mlhs_paren,
299
- [[:mlhs_paren, [:@ident, "z", [1, 26]]],
300
- [:mlhs_paren, [:@ident, "a", [1, 29]]]]]],
301
- [[[:@ident, "k", [1, 33]], [:var_ref, [:@ident, "z", [1, 37]]]],
302
- [[:@ident, "j", [1, 40]], [:zcall, [:@ident, "d", [1, 44]]]]],
303
- [:rest_param, [:@ident, "r", [1, 48]]],
304
- nil,
305
- [:blockarg, [:@ident, "blk", [1, 52]]]],
306
- [[:@ident, "local", [1, 57]]]],
307
- [[:command,
308
- [:@ident, "p", [1, 64]],
309
- [:args_add_block,
310
- [[:var_ref, [:@ident, "x", [1, 66]]],
311
- [:var_ref, [:@ident, "y", [1, 69]]],
312
- [:var_ref, [:@ident, "z", [1, 72]]],
313
- [:var_ref, [:@ident, "a", [1, 75]]],
314
- [:var_ref, [:@ident, "k", [1, 78]]],
315
- [:var_ref, [:@ident, "j", [1, 81]]],
316
- [:var_ref, [:@ident, "r", [1, 84]]],
317
- [:var_ref, [:@ident, "blk", [1, 87]]],
318
- [:var_ref, [:@ident, "local", [1, 92]]],
319
- [:zcall, [:@ident, "foo", [1, 99]]]],
320
- false]]]]]]]
321
- input_tree.should transform_to(output_tree)
322
- end
197
+ it 'creates for-loop MLHS vars before transforming the iteratee' do
198
+ input_tree =
199
+ [:program,
200
+ [[:for,
201
+ [[:@ident, "a", [1, 4]],
202
+ [:@ident, "b", [1, 7]],
203
+ [:mlhs_paren,
204
+ [[:@ident, "c", [1, 11]],
205
+ [:mlhs_paren,
206
+ [:mlhs_add_star,
207
+ [],
208
+ [:@ident, "d", [1, 16]],
209
+ [[:@ident, "e", [1, 19]]]]]]]],
210
+ [:method_add_arg,
211
+ [:fcall, [:@ident, "foo", [1, 26]]],
212
+ [:arg_paren,
213
+ [:args_add_block,
214
+ [[:var_ref, [:@ident, "a", [1, 30]]],
215
+ [:var_ref, [:@ident, "b", [1, 33]]],
216
+ [:var_ref, [:@ident, "c", [1, 36]]],
217
+ [:var_ref, [:@ident, "d", [1, 39]]],
218
+ [:var_ref, [:@ident, "e", [1, 42]]],
219
+ [:var_ref, [:@ident, "f", [1, 45]]],
220
+ [:var_ref, [:@ident, "g", [1, 48]]]],
221
+ false]]],
222
+ [[:void_stmt]]]]]
223
+ output_tree =
224
+ [:program,
225
+ [[:for,
226
+ [[:@ident, "a", [1, 4]],
227
+ [:@ident, "b", [1, 7]],
228
+ [:mlhs_paren,
229
+ [[:@ident, "c", [1, 11]],
230
+ [:mlhs_paren,
231
+ [:mlhs_add_star,
232
+ [],
233
+ [:@ident, "d", [1, 16]],
234
+ [[:@ident, "e", [1, 19]]]]]]]],
235
+ [:method_add_arg,
236
+ [:fcall, [:@ident, "foo", [1, 26]]],
237
+ [:arg_paren,
238
+ [:args_add_block,
239
+ [[:var_ref, [:@ident, "a", [1, 30]]],
240
+ [:var_ref, [:@ident, "b", [1, 33]]],
241
+ [:var_ref, [:@ident, "c", [1, 36]]],
242
+ [:var_ref, [:@ident, "d", [1, 39]]],
243
+ [:var_ref, [:@ident, "e", [1, 42]]],
244
+ [:zcall, [:@ident, "f", [1, 45]]],
245
+ [:zcall, [:@ident, "g", [1, 48]]]],
246
+ false]]],
247
+ [[:void_stmt]]]]]
248
+ input_tree.should transform_to(output_tree)
249
+ end
323
250
 
324
- it 'does not let block variables escape into enclosing scopes' do
325
- input_tree =
326
- [:program,
327
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
328
- [:method_add_block,
329
- [:call, [:array, nil], :".", [:@ident, "each", [1, 10]]],
330
- [:brace_block,
331
- [:block_var,
332
- [:params, [[:@ident, "y", [1, 18]]], nil, nil, nil, nil],
333
- [[:@ident, "z", [1, 20]], [:@ident, "x", [1, 23]]]],
334
- [[:var_ref, [:@ident, "x", [1, 26]]],
335
- [:var_ref, [:@ident, "y", [1, 28]]],
336
- [:var_ref, [:@ident, "z", [1, 30]]]]]],
337
- [:var_ref, [:@ident, "x", [1, 36]]],
338
- [:var_ref, [:@ident, "y", [1, 39]]],
339
- [:var_ref, [:@ident, "z", [1, 42]]]]]
340
- output_tree =
341
- [:program,
342
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
343
- [:method_add_block,
344
- [:call, [:array, nil], :".", [:@ident, "each", [1, 10]]],
345
- [:brace_block,
346
- [:block_var,
347
- [:params, [[:@ident, "y", [1, 18]]], nil, nil, nil, nil],
348
- [[:@ident, "z", [1, 20]], [:@ident, "x", [1, 23]]]],
349
- [[:var_ref, [:@ident, "x", [1, 26]]],
350
- [:var_ref, [:@ident, "y", [1, 28]]],
351
- [:var_ref, [:@ident, "z", [1, 30]]]]]],
352
- [:var_ref, [:@ident, "x", [1, 36]]],
353
- [:zcall, [:@ident, "y", [1, 39]]],
354
- [:zcall, [:@ident, "z", [1, 42]]]]]
355
- input_tree.should transform_to(output_tree)
356
- end
251
+ it 'should transform respecting subassignments in block arguments' do
252
+ input_tree =
253
+ [:program,
254
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "10", [1, 4]]],
255
+ [:method_add_block,
256
+ [:call,
257
+ [:top_const_ref, [:@const, "ARR", [1, 10]]],
258
+ :".",
259
+ [:@ident, "each", [1, 14]]],
260
+ [:brace_block,
261
+ [:block_var,
262
+ [:params,
263
+ [[:@ident, "y", [1, 22]],
264
+ [:mlhs_paren,
265
+ [[:mlhs_paren, [:@ident, "z", [1, 26]]],
266
+ [:mlhs_paren, [:@ident, "a", [1, 29]]]]]],
267
+ [[[:@ident, "k", [1, 33]], [:var_ref, [:@ident, "z", [1, 37]]]],
268
+ [[:@ident, "j", [1, 40]], [:var_ref, [:@ident, "d", [1, 44]]]]],
269
+ [:rest_param, [:@ident, "r", [1, 48]]],
270
+ nil,
271
+ [:blockarg, [:@ident, "blk", [1, 52]]]],
272
+ [[:@ident, "local", [1, 57]]]],
273
+ [[:command,
274
+ [:@ident, "p", [1, 64]],
275
+ [:args_add_block,
276
+ [[:var_ref, [:@ident, "x", [1, 66]]],
277
+ [:var_ref, [:@ident, "y", [1, 69]]],
278
+ [:var_ref, [:@ident, "z", [1, 72]]],
279
+ [:var_ref, [:@ident, "a", [1, 75]]],
280
+ [:var_ref, [:@ident, "k", [1, 78]]],
281
+ [:var_ref, [:@ident, "j", [1, 81]]],
282
+ [:var_ref, [:@ident, "r", [1, 84]]],
283
+ [:var_ref, [:@ident, "blk", [1, 87]]],
284
+ [:var_ref, [:@ident, "local", [1, 92]]],
285
+ [:var_ref, [:@ident, "foo", [1, 99]]]],
286
+ false]]]]]]]
287
+ output_tree =
288
+ [:program,
289
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "10", [1, 4]]],
290
+ [:method_add_block,
291
+ [:call,
292
+ [:top_const_ref, [:@const, "ARR", [1, 10]]],
293
+ :".",
294
+ [:@ident, "each", [1, 14]]],
295
+ [:brace_block,
296
+ [:block_var,
297
+ [:params,
298
+ [[:@ident, "y", [1, 22]],
299
+ [:mlhs_paren,
300
+ [[:mlhs_paren, [:@ident, "z", [1, 26]]],
301
+ [:mlhs_paren, [:@ident, "a", [1, 29]]]]]],
302
+ [[[:@ident, "k", [1, 33]], [:var_ref, [:@ident, "z", [1, 37]]]],
303
+ [[:@ident, "j", [1, 40]], [:zcall, [:@ident, "d", [1, 44]]]]],
304
+ [:rest_param, [:@ident, "r", [1, 48]]],
305
+ nil,
306
+ [:blockarg, [:@ident, "blk", [1, 52]]]],
307
+ [[:@ident, "local", [1, 57]]]],
308
+ [[:command,
309
+ [:@ident, "p", [1, 64]],
310
+ [:args_add_block,
311
+ [[:var_ref, [:@ident, "x", [1, 66]]],
312
+ [:var_ref, [:@ident, "y", [1, 69]]],
313
+ [:var_ref, [:@ident, "z", [1, 72]]],
314
+ [:var_ref, [:@ident, "a", [1, 75]]],
315
+ [:var_ref, [:@ident, "k", [1, 78]]],
316
+ [:var_ref, [:@ident, "j", [1, 81]]],
317
+ [:var_ref, [:@ident, "r", [1, 84]]],
318
+ [:var_ref, [:@ident, "blk", [1, 87]]],
319
+ [:var_ref, [:@ident, "local", [1, 92]]],
320
+ [:zcall, [:@ident, "foo", [1, 99]]]],
321
+ false]]]]]]]
322
+ input_tree.should transform_to(output_tree)
323
+ end
357
324
 
358
- it 'creates closed scopes when classes are created' do
359
- input_tree =
360
- [:program,
361
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
362
- [:class,
363
- [:const_ref, [:@const, "Foo", [1, 13]]],
364
- nil,
365
- [:bodystmt, [[:var_ref, [:@ident, "x", [1, 18]]]], nil, nil, nil]],
366
- [:var_ref, [:@ident, "x", [1, 26]]]]]
367
- output_tree =
368
- [:program,
369
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
370
- [:class,
371
- [:const_ref, [:@const, "Foo", [1, 13]]],
372
- nil,
373
- [:bodystmt, [[:zcall, [:@ident, "x", [1, 18]]]], nil, nil, nil]],
374
- [:var_ref, [:@ident, "x", [1, 26]]]]]
375
- input_tree.should transform_to(output_tree)
376
- end
325
+ it 'does not let block variables escape into enclosing scopes' do
326
+ input_tree =
327
+ [:program,
328
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
329
+ [:method_add_block,
330
+ [:call, [:array, nil], :".", [:@ident, "each", [1, 10]]],
331
+ [:brace_block,
332
+ [:block_var,
333
+ [:params, [[:@ident, "y", [1, 18]]], nil, nil, nil, nil],
334
+ [[:@ident, "z", [1, 20]], [:@ident, "x", [1, 23]]]],
335
+ [[:var_ref, [:@ident, "x", [1, 26]]],
336
+ [:var_ref, [:@ident, "y", [1, 28]]],
337
+ [:var_ref, [:@ident, "z", [1, 30]]]]]],
338
+ [:var_ref, [:@ident, "x", [1, 36]]],
339
+ [:var_ref, [:@ident, "y", [1, 39]]],
340
+ [:var_ref, [:@ident, "z", [1, 42]]]]]
341
+ output_tree =
342
+ [:program,
343
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
344
+ [:method_add_block,
345
+ [:call, [:array, nil], :".", [:@ident, "each", [1, 10]]],
346
+ [:brace_block,
347
+ [:block_var,
348
+ [:params, [[:@ident, "y", [1, 18]]], nil, nil, nil, nil],
349
+ [[:@ident, "z", [1, 20]], [:@ident, "x", [1, 23]]]],
350
+ [[:var_ref, [:@ident, "x", [1, 26]]],
351
+ [:var_ref, [:@ident, "y", [1, 28]]],
352
+ [:var_ref, [:@ident, "z", [1, 30]]]]]],
353
+ [:var_ref, [:@ident, "x", [1, 36]]],
354
+ [:zcall, [:@ident, "y", [1, 39]]],
355
+ [:zcall, [:@ident, "z", [1, 42]]]]]
356
+ input_tree.should transform_to(output_tree)
357
+ end
377
358
 
378
- it 'uses the previous scope for superclass specification' do
379
- input_tree =
380
- [:program,
381
- [[:assign,
382
- [:var_field, [:@ident, "x", [1, 0]]],
383
- [:var_ref, [:@const, "String", [1, 4]]]],
384
- [:class,
385
- [:const_ref, [:@const, "A", [1, 18]]],
386
- [:var_ref, [:@ident, "x", [1, 22]]],
387
- [:bodystmt, [[:var_ref, [:@ident, "x", [1, 25]]]], nil, nil, nil]]]]
388
- output_tree =
389
- [:program,
390
- [[:assign,
391
- [:var_field, [:@ident, "x", [1, 0]]],
392
- [:var_ref, [:@const, "String", [1, 4]]]],
393
- [:class,
394
- [:const_ref, [:@const, "A", [1, 18]]],
395
- [:var_ref, [:@ident, "x", [1, 22]]],
396
- [:bodystmt, [[:zcall, [:@ident, "x", [1, 25]]]], nil, nil, nil]]]]
397
- input_tree.should transform_to output_tree
398
- end
359
+ it 'creates closed scopes when classes are created' do
360
+ input_tree =
361
+ [:program,
362
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
363
+ [:class,
364
+ [:const_ref, [:@const, "Foo", [1, 13]]],
365
+ nil,
366
+ [:bodystmt, [[:var_ref, [:@ident, "x", [1, 18]]]], nil, nil, nil]],
367
+ [:var_ref, [:@ident, "x", [1, 26]]]]]
368
+ output_tree =
369
+ [:program,
370
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
371
+ [:class,
372
+ [:const_ref, [:@const, "Foo", [1, 13]]],
373
+ nil,
374
+ [:bodystmt, [[:zcall, [:@ident, "x", [1, 18]]]], nil, nil, nil]],
375
+ [:var_ref, [:@ident, "x", [1, 26]]]]]
376
+ input_tree.should transform_to(output_tree)
377
+ end
399
378
 
400
- it 'creates closed scopes when modules are created' do
401
- input_tree =
402
- [:program,
403
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
404
- [:module,
405
- [:const_ref, [:@const, "Foo", [1, 14]]],
406
- [:bodystmt,
407
- [[:void_stmt], [:var_ref, [:@ident, "x", [1, 19]]]],
408
- nil,
409
- nil,
410
- nil]],
411
- [:var_ref, [:@ident, "x", [1, 27]]]]]
412
- output_tree =
413
- [:program,
414
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
415
- [:module,
416
- [:const_ref, [:@const, "Foo", [1, 14]]],
417
- [:bodystmt,
418
- [[:void_stmt], [:zcall, [:@ident, "x", [1, 19]]]],
419
- nil,
420
- nil,
421
- nil]],
422
- [:var_ref, [:@ident, "x", [1, 27]]]]]
423
- input_tree.should transform_to output_tree
424
- end
379
+ it 'uses the previous scope for superclass specification' do
380
+ input_tree =
381
+ [:program,
382
+ [[:assign,
383
+ [:var_field, [:@ident, "x", [1, 0]]],
384
+ [:var_ref, [:@const, "String", [1, 4]]]],
385
+ [:class,
386
+ [:const_ref, [:@const, "A", [1, 18]]],
387
+ [:var_ref, [:@ident, "x", [1, 22]]],
388
+ [:bodystmt, [[:var_ref, [:@ident, "x", [1, 25]]]], nil, nil, nil]]]]
389
+ output_tree =
390
+ [:program,
391
+ [[:assign,
392
+ [:var_field, [:@ident, "x", [1, 0]]],
393
+ [:var_ref, [:@const, "String", [1, 4]]]],
394
+ [:class,
395
+ [:const_ref, [:@const, "A", [1, 18]]],
396
+ [:var_ref, [:@ident, "x", [1, 22]]],
397
+ [:bodystmt, [[:zcall, [:@ident, "x", [1, 25]]]], nil, nil, nil]]]]
398
+ input_tree.should transform_to output_tree
399
+ end
425
400
 
426
- it 'creates closed scopes when singleton classes are opened' do
427
- input_tree =
428
- [:program,
429
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
430
- [:sclass,
431
- [:var_ref, [:@const, "String", [1, 16]]],
432
- [:bodystmt, [[:var_ref, [:@ident, "x", [1, 24]]]], nil, nil, nil]]]]
433
- output_tree =
434
- [:program,
435
- [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
436
- [:sclass,
437
- [:var_ref, [:@const, "String", [1, 16]]],
438
- [:bodystmt, [[:zcall, [:@ident, "x", [1, 24]]]], nil, nil, nil]]]]
439
- input_tree.should transform_to output_tree
440
- end
401
+ it 'creates closed scopes when modules are created' do
402
+ input_tree =
403
+ [:program,
404
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
405
+ [:module,
406
+ [:const_ref, [:@const, "Foo", [1, 14]]],
407
+ [:bodystmt,
408
+ [[:void_stmt], [:var_ref, [:@ident, "x", [1, 19]]]],
409
+ nil,
410
+ nil,
411
+ nil]],
412
+ [:var_ref, [:@ident, "x", [1, 27]]]]]
413
+ output_tree =
414
+ [:program,
415
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
416
+ [:module,
417
+ [:const_ref, [:@const, "Foo", [1, 14]]],
418
+ [:bodystmt,
419
+ [[:void_stmt], [:zcall, [:@ident, "x", [1, 19]]]],
420
+ nil,
421
+ nil,
422
+ nil]],
423
+ [:var_ref, [:@ident, "x", [1, 27]]]]]
424
+ input_tree.should transform_to output_tree
425
+ end
441
426
 
442
- it 'refers to the enclosing environment to determine the singleton to open' do
443
- input_tree =
444
- [:program,
445
- [[:assign,
446
- [:var_field, [:@ident, "x", [1, 0]]],
447
- [:string_literal, [:string_content, [:@tstring_content, "hi", [1, 5]]]]],
448
- [:sclass,
449
- [:var_ref, [:@ident, "x", [1, 19]]],
450
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
451
- input_tree.should transform_to input_tree
452
- end
427
+ it 'creates closed scopes when singleton classes are opened' do
428
+ input_tree =
429
+ [:program,
430
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
431
+ [:sclass,
432
+ [:var_ref, [:@const, "String", [1, 16]]],
433
+ [:bodystmt, [[:var_ref, [:@ident, "x", [1, 24]]]], nil, nil, nil]]]]
434
+ output_tree =
435
+ [:program,
436
+ [[:assign, [:var_field, [:@ident, "x", [1, 0]]], [:@int, "5", [1, 4]]],
437
+ [:sclass,
438
+ [:var_ref, [:@const, "String", [1, 16]]],
439
+ [:bodystmt, [[:zcall, [:@ident, "x", [1, 24]]]], nil, nil, nil]]]]
440
+ input_tree.should transform_to output_tree
441
+ end
453
442
 
454
- it 'refers to the enclosing environment to determine the singleton to open - resulting in zcall' do
455
- input_tree =
456
- [:program,
457
- [[:sclass,
458
- [:var_ref, [:@ident, "x", [1, 19]]],
459
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
460
- output_tree =
461
- [:program,
462
- [[:sclass,
463
- [:zcall, [:@ident, "x", [1, 19]]],
464
- [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
465
- input_tree.should transform_to output_tree
466
- end
443
+ it 'refers to the enclosing environment to determine the singleton to open' do
444
+ input_tree =
445
+ [:program,
446
+ [[:assign,
447
+ [:var_field, [:@ident, "x", [1, 0]]],
448
+ [:string_literal, [:string_content, [:@tstring_content, "hi", [1, 5]]]]],
449
+ [:sclass,
450
+ [:var_ref, [:@ident, "x", [1, 19]]],
451
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
452
+ input_tree.should transform_to input_tree
453
+ end
467
454
 
468
- it 'observes the creation of local variables by rescue clauses' do
469
- input_tree =
470
- [:program,
471
- [[:def,
472
- [:@ident, "foo", [1, 4]],
473
- [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
474
- [:bodystmt,
475
- [[:void_stmt],
476
- [:assign,
477
- [:var_field, [:@ident, "y", [1, 12]]],
478
- [:binary,
479
- [:@int, "1", [1, 16]],
480
- :/,
481
- [:var_ref, [:@ident, "x", [1, 20]]]]]],
482
- [:rescue,
483
- [[:var_ref, [:@const, "Exception", [1, 30]]]],
484
- [:var_field, [:@ident, "err", [1, 43]]],
485
- [[:command,
486
- [:@ident, "p", [1, 48]],
487
- [:args_add_block, [[:var_ref, [:@ident, "err", [1, 50]]]], false]]],
488
- nil],
489
- [:else,
490
- [[:void_stmt],
491
- [:command,
492
- [:@ident, "p", [1, 61]],
493
- [:args_add_block, [[:var_ref, [:@ident, "err", [1, 63]]]], false]]]],
494
- [:ensure,
495
- [[:void_stmt],
496
- [:command,
497
- [:@ident, "p", [1, 76]],
498
- [:args_add_block, [[:var_ref, [:@ident, "err", [1, 78]]]], false]]]]]]]]
499
- input_tree.should transform_to input_tree
500
- end
455
+ it 'refers to the enclosing environment to determine the singleton to open - resulting in zcall' do
456
+ input_tree =
457
+ [:program,
458
+ [[:sclass,
459
+ [:var_ref, [:@ident, "x", [1, 19]]],
460
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
461
+ output_tree =
462
+ [:program,
463
+ [[:sclass,
464
+ [:zcall, [:@ident, "x", [1, 19]]],
465
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
466
+ input_tree.should transform_to output_tree
467
+ end
501
468
 
502
- it 'handles rescue clauses normally otherwise' do
503
- input_tree =
504
- [:program,
505
- [[:def,
506
- [:@ident, "foo", [1, 4]],
507
- [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
508
- [:bodystmt,
509
- [[:void_stmt],
510
- [:assign,
511
- [:var_field, [:@ident, "y", [1, 12]]],
512
- [:binary,
513
- [:@int, "1", [1, 16]],
514
- :/,
515
- [:var_ref, [:@ident, "x", [1, 20]]]]]],
516
- [:rescue,
517
- [[:var_ref, [:@const, "Exception", [1, 30]]]],
518
- nil,
519
- [[:command,
520
- [:@ident, "p", [1, 48]],
521
- [:args_add_block, [[:var_ref, [:@ident, "err", [1, 50]]]], false]]],
522
- nil],
523
- [:else,
524
- [[:void_stmt],
525
- [:command,
526
- [:@ident, "p", [1, 61]],
527
- [:args_add_block, [[:var_ref, [:@ident, "err", [1, 63]]]], false]]]],
528
- [:ensure,
529
- [[:void_stmt],
530
- [:command,
531
- [:@ident, "p", [1, 76]],
532
- [:args_add_block, [[:var_ref, [:@ident, "err", [1, 78]]]], false]]]]]]]]
533
- output_tree =
534
- [:program,
535
- [[:def,
536
- [:@ident, "foo", [1, 4]],
537
- [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
538
- [:bodystmt,
539
- [[:void_stmt],
540
- [:assign,
541
- [:var_field, [:@ident, "y", [1, 12]]],
542
- [:binary,
543
- [:@int, "1", [1, 16]],
544
- :/,
545
- [:var_ref, [:@ident, "x", [1, 20]]]]]],
546
- [:rescue,
547
- [[:var_ref, [:@const, "Exception", [1, 30]]]],
469
+ it 'observes the creation of local variables by rescue clauses' do
470
+ input_tree =
471
+ [:program,
472
+ [[:def,
473
+ [:@ident, "foo", [1, 4]],
474
+ [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
475
+ [:bodystmt,
476
+ [[:void_stmt],
477
+ [:assign,
478
+ [:var_field, [:@ident, "y", [1, 12]]],
479
+ [:binary,
480
+ [:@int, "1", [1, 16]],
481
+ :/,
482
+ [:var_ref, [:@ident, "x", [1, 20]]]]]],
483
+ [:rescue,
484
+ [[:var_ref, [:@const, "Exception", [1, 30]]]],
485
+ [:var_field, [:@ident, "err", [1, 43]]],
486
+ [[:command,
487
+ [:@ident, "p", [1, 48]],
488
+ [:args_add_block, [[:var_ref, [:@ident, "err", [1, 50]]]], false]]],
489
+ nil],
490
+ [:else,
491
+ [[:void_stmt],
492
+ [:command,
493
+ [:@ident, "p", [1, 61]],
494
+ [:args_add_block, [[:var_ref, [:@ident, "err", [1, 63]]]], false]]]],
495
+ [:ensure,
496
+ [[:void_stmt],
497
+ [:command,
498
+ [:@ident, "p", [1, 76]],
499
+ [:args_add_block, [[:var_ref, [:@ident, "err", [1, 78]]]], false]]]]]]]]
500
+ input_tree.should transform_to input_tree
501
+ end
502
+
503
+ it 'handles rescue clauses normally otherwise' do
504
+ input_tree =
505
+ [:program,
506
+ [[:def,
507
+ [:@ident, "foo", [1, 4]],
508
+ [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
509
+ [:bodystmt,
510
+ [[:void_stmt],
511
+ [:assign,
512
+ [:var_field, [:@ident, "y", [1, 12]]],
513
+ [:binary,
514
+ [:@int, "1", [1, 16]],
515
+ :/,
516
+ [:var_ref, [:@ident, "x", [1, 20]]]]]],
517
+ [:rescue,
518
+ [[:var_ref, [:@const, "Exception", [1, 30]]]],
519
+ nil,
520
+ [[:command,
521
+ [:@ident, "p", [1, 48]],
522
+ [:args_add_block, [[:var_ref, [:@ident, "err", [1, 50]]]], false]]],
523
+ nil],
524
+ [:else,
525
+ [[:void_stmt],
526
+ [:command,
527
+ [:@ident, "p", [1, 61]],
528
+ [:args_add_block, [[:var_ref, [:@ident, "err", [1, 63]]]], false]]]],
529
+ [:ensure,
530
+ [[:void_stmt],
531
+ [:command,
532
+ [:@ident, "p", [1, 76]],
533
+ [:args_add_block, [[:var_ref, [:@ident, "err", [1, 78]]]], false]]]]]]]]
534
+ output_tree =
535
+ [:program,
536
+ [[:def,
537
+ [:@ident, "foo", [1, 4]],
538
+ [:paren, [:params, [[:@ident, "x", [1, 8]]], nil, nil, nil, nil]],
539
+ [:bodystmt,
540
+ [[:void_stmt],
541
+ [:assign,
542
+ [:var_field, [:@ident, "y", [1, 12]]],
543
+ [:binary,
544
+ [:@int, "1", [1, 16]],
545
+ :/,
546
+ [:var_ref, [:@ident, "x", [1, 20]]]]]],
547
+ [:rescue,
548
+ [[:var_ref, [:@const, "Exception", [1, 30]]]],
549
+ nil,
550
+ [[:command,
551
+ [:@ident, "p", [1, 48]],
552
+ [:args_add_block, [[:zcall, [:@ident, "err", [1, 50]]]], false]]],
553
+ nil],
554
+ [:else,
555
+ [[:void_stmt],
556
+ [:command,
557
+ [:@ident, "p", [1, 61]],
558
+ [:args_add_block, [[:zcall, [:@ident, "err", [1, 63]]]], false]]]],
559
+ [:ensure,
560
+ [[:void_stmt],
561
+ [:command,
562
+ [:@ident, "p", [1, 76]],
563
+ [:args_add_block, [[:zcall, [:@ident, "err", [1, 78]]]], false]]]]]]]]
564
+ input_tree.should transform_to output_tree
565
+ end
566
+ end
567
+ describe 'error transformation' do
568
+ it 'should push up module name errors' do
569
+ input_tree =
570
+ [:program,
571
+ [[:module,
572
+ [:const_ref, [:class_name_error, [:@ident, "a", [1, 7]]]],
573
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
574
+ output_tree =
575
+ [:program,
576
+ [[:error,
577
+ [:module,
578
+ [:const_ref, [:class_name_error, [:@ident, "a", [1, 7]]]],
579
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
580
+ input_tree.should transform_to output_tree
581
+ end
582
+
583
+ it 'should push up class name errors' do
584
+ input_tree =
585
+ [:program,
586
+ [[:class,
587
+ [:const_ref, [:class_name_error, [:@ident, "a", [1, 6]]]],
548
588
  nil,
549
- [[:command,
550
- [:@ident, "p", [1, 48]],
551
- [:args_add_block, [[:zcall, [:@ident, "err", [1, 50]]]], false]]],
552
- nil],
553
- [:else,
554
- [[:void_stmt],
555
- [:command,
556
- [:@ident, "p", [1, 61]],
557
- [:args_add_block, [[:zcall, [:@ident, "err", [1, 63]]]], false]]]],
558
- [:ensure,
559
- [[:void_stmt],
560
- [:command,
561
- [:@ident, "p", [1, 76]],
562
- [:args_add_block, [[:zcall, [:@ident, "err", [1, 78]]]], false]]]]]]]]
563
- input_tree.should transform_to output_tree
589
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
590
+ output_tree =
591
+ [:program,
592
+ [[:error,
593
+ [:class,
594
+ [:const_ref, [:class_name_error, [:@ident, "a", [1, 6]]]],
595
+ nil,
596
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]]
597
+ input_tree.should transform_to output_tree
598
+ end
599
+
600
+ it 'wraps single assignments in :error if they assign read-only gvars' do
601
+ input_tree =
602
+ [:program,
603
+ [[:assign, [:assign_error, [:@backref, "$`", [1, 0]]], [:@int, "3", [1, 5]]]]]
604
+ output_tree =
605
+ [:program,
606
+ [[:error, [:assign, [:assign_error, [:@backref, "$`", [1, 0]]], [:@int, "3", [1, 5]]]]]]
607
+ input_tree.should transform_to output_tree
608
+ end
609
+
610
+ it 'wraps multiple assignments in :error if they assign read-only gvars' do
611
+ input_tree =
612
+ [:program,
613
+ [[:massign,
614
+ [[:@ident, "a", [1, 0]],
615
+ [:@ident, "b", [1, 3]],
616
+ [:assign_error, [:var_field, [:@backref, "$&", [1, 6]]]]],
617
+ [:mrhs_add_star,
618
+ [],
619
+ [:paren, [[:dot2, [:@int, "1", [1, 13]], [:@int, "3", [1, 16]]]]]]]]]
620
+ output_tree =
621
+ [:program,
622
+ [[:error,
623
+ [:massign,
624
+ [[:@ident, "a", [1, 0]],
625
+ [:@ident, "b", [1, 3]],
626
+ [:assign_error, [:var_field, [:@backref, "$&", [1, 6]]]]],
627
+ [:mrhs_add_star,
628
+ [],
629
+ [:paren, [[:dot2, [:@int, "1", [1, 13]], [:@int, "3", [1, 16]]]]]]]]]]
630
+ input_tree.should transform_to output_tree
631
+ end
632
+
633
+ it 'wraps assignments in :error if they assign read-only vars deep in subassignments' do
634
+ input_tree =
635
+ [:program,
636
+ [[:massign,
637
+ [[:@ident, "a", [1, 0]],
638
+ [:mlhs_paren,
639
+ [:mlhs_add_star,
640
+ [[:@ident, "b", [1, 4]]],
641
+ [:@ident, "c", [1, 8]],
642
+ [[:mlhs_paren,
643
+ [[:assign_error, [:var_field, [:@backref, "$1", [1, 12]]]],
644
+ [:@ident, "d", [1, 16]]]]]]]],
645
+ [:mrhs_add_star,
646
+ [],
647
+ [:method_add_arg,
648
+ [:fcall, [:@ident, "foo", [1, 23]]],
649
+ [:arg_paren, nil]]]]]]
650
+ output_tree =
651
+ [:program,
652
+ [[:error,
653
+ [:massign,
654
+ [[:@ident, "a", [1, 0]],
655
+ [:mlhs_paren,
656
+ [:mlhs_add_star,
657
+ [[:@ident, "b", [1, 4]]],
658
+ [:@ident, "c", [1, 8]],
659
+ [[:mlhs_paren,
660
+ [[:assign_error, [:var_field, [:@backref, "$1", [1, 12]]]],
661
+ [:@ident, "d", [1, 16]]]]]]]],
662
+ [:mrhs_add_star,
663
+ [],
664
+ [:method_add_arg,
665
+ [:fcall, [:@ident, "foo", [1, 23]]],
666
+ [:arg_paren, nil]]]]]]]
667
+ input_tree.should transform_to output_tree
668
+ end
669
+
670
+ it 'wraps alias errors with a standard :error node' do
671
+ input_tree =
672
+ [:program,
673
+ [[:alias_error,
674
+ [:var_alias, [:@gvar, "$foo", [1, 6]], [:@backref, "$1", [1, 11]]]]]]
675
+ output_tree =
676
+ [:program,
677
+ [[:error, [:alias_error,
678
+ [:var_alias, [:@gvar, "$foo", [1, 6]], [:@backref, "$1", [1, 11]]]]]]]
679
+ input_tree.should transform_to output_tree
680
+ end
681
+
682
+ it 'wraps dynamic assignments to constants in :error nodes' do
683
+ input_tree =
684
+ [:program,
685
+ [[:def,
686
+ [:@ident, "foo", [1, 4]],
687
+ [:params, nil, nil, nil, nil, nil],
688
+ [:bodystmt,
689
+ [[:assign, [:var_field, [:@const, "C", [1, 9]]], [:@int, "5", [1, 13]]]],
690
+ nil,
691
+ nil,
692
+ nil]]]]
693
+ output_tree =
694
+ [:program,
695
+ [[:def,
696
+ [:@ident, "foo", [1, 4]],
697
+ [:params, nil, nil, nil, nil, nil],
698
+ [:bodystmt,
699
+ [[:error, [:assign, [:var_field, [:@const, "C", [1, 9]]], [:@int, "5", [1, 13]]]]],
700
+ nil,
701
+ nil,
702
+ nil]]]]
703
+ input_tree.should transform_to output_tree
704
+ end
705
+
706
+ it 'wraps dynamic assignments in massigns in :error nodes' do
707
+ input_tree =
708
+ [:program,
709
+ [[:defs,
710
+ [:var_ref, [:@kw, "self", [1, 4]]],
711
+ [:@period, ".", [1, 8]],
712
+ [:@ident, "foo", [1, 9]],
713
+ [:params, nil, nil, nil, nil, nil],
714
+ [:bodystmt,
715
+ [[:massign,
716
+ [[:@ident, "a", [1, 14]],
717
+ [:mlhs_paren,
718
+ [[:@ident, "b", [1, 18]],
719
+ [:mlhs_paren,
720
+ [[:@gvar, "$c", [1, 22]],
721
+ [:const_path_field,
722
+ [:var_ref, [:@kw, "self", [1, 26]]],
723
+ [:@const, "D", [1, 32]]]]]]]],
724
+ [:mrhs_add_star, [], [:var_ref, [:@const, "CONST", [1, 39]]]]]],
725
+ nil, nil, nil]]]]
726
+ output_tree =
727
+ [:program,
728
+ [[:defs,
729
+ [:var_ref, [:@kw, "self", [1, 4]]],
730
+ [:@period, ".", [1, 8]],
731
+ [:@ident, "foo", [1, 9]],
732
+ [:params, nil, nil, nil, nil, nil],
733
+ [:bodystmt,
734
+ [[:error, [:massign,
735
+ [[:@ident, "a", [1, 14]],
736
+ [:mlhs_paren,
737
+ [[:@ident, "b", [1, 18]],
738
+ [:mlhs_paren,
739
+ [[:@gvar, "$c", [1, 22]],
740
+ [:const_path_field,
741
+ [:var_ref, [:@kw, "self", [1, 26]]],
742
+ [:@const, "D", [1, 32]]]]]]]],
743
+ [:mrhs_add_star, [], [:var_ref, [:@const, "CONST", [1, 39]]]]]]],
744
+ nil, nil, nil]]]]
745
+ input_tree.should transform_to output_tree
746
+ end
747
+
748
+ it 'wraps classes in method bodies with error nodes' do
749
+ input_tree =
750
+ [:program,
751
+ [[:def,
752
+ [:@ident, "foo", [1, 4]],
753
+ [:params, nil, nil, nil, nil, nil],
754
+ [:bodystmt,
755
+ [[:class,
756
+ [:const_ref, [:@const, "A", [1, 15]]],
757
+ nil,
758
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
759
+ nil,
760
+ nil,
761
+ nil]]]]
762
+ output_tree =
763
+ [:program,
764
+ [[:def,
765
+ [:@ident, "foo", [1, 4]],
766
+ [:params, nil, nil, nil, nil, nil],
767
+ [:bodystmt,
768
+ [[:error, [:class,
769
+ [:const_ref, [:@const, "A", [1, 15]]],
770
+ nil,
771
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]],
772
+ nil,
773
+ nil,
774
+ nil]]]]
775
+ input_tree.should transform_to output_tree
776
+ end
777
+
778
+ it 'wraps modules in method bodies with error nodes' do
779
+ input_tree =
780
+ [:program,
781
+ [[:def,
782
+ [:@ident, "foo", [1, 4]],
783
+ [:params, nil, nil, nil, nil, nil],
784
+ [:bodystmt,
785
+ [[:module,
786
+ [:const_ref, [:@const, "A", [1, 15]]],
787
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
788
+ nil,
789
+ nil,
790
+ nil]]]]
791
+ output_tree =
792
+ [:program,
793
+ [[:def,
794
+ [:@ident, "foo", [1, 4]],
795
+ [:params, nil, nil, nil, nil, nil],
796
+ [:bodystmt,
797
+ [[:error, [:module,
798
+ [:const_ref, [:@const, "A", [1, 15]]],
799
+ [:bodystmt, [[:void_stmt]], nil, nil, nil]]]],
800
+ nil,
801
+ nil,
802
+ nil]]]]
803
+ input_tree.should transform_to output_tree
804
+ end
805
+
806
+ %w(FOO @foo @@foo $foo).each do |bad_var|
807
+ [['singleton', 'String.', :defs], ['normal', '', :def]].each do |kind, prefix, fail_node|
808
+ it "wraps #{kind} definitions with #{bad_var} as a positional argument name with :error" do
809
+ input_code = "def #{prefix}foo(a, #{bad_var}, *rest); end"
810
+ tree = RipperPlus.sexp(input_code)
811
+ error = dfs_for_node_type(tree, :error)
812
+ error[1][0].should be fail_node
813
+ end
814
+
815
+ it "wraps #{kind} definitions with #{bad_var} as a second-section positional argument name with :error" do
816
+ input_code = "def #{prefix}foo(a, b=2, #{bad_var}); end"
817
+ tree = RipperPlus.sexp(input_code)
818
+ error = dfs_for_node_type(tree, :error)
819
+ error[1][0].should be fail_node
820
+ end
821
+ end
822
+ end
823
+
564
824
  end
565
825
  end