ripper-plus 1.0.1 → 1.1.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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