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 +0 -1
- data/README.md +60 -5
- data/Rakefile +2 -0
- data/lib/ripper-plus.rb +1 -0
- data/lib/ripper-plus/scope_stack.rb +11 -1
- data/lib/ripper-plus/transformer.rb +81 -29
- data/lib/ripper-plus/version.rb +10 -0
- data/spec/scope_stack_spec.rb +20 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/transformer_spec.rb +799 -539
- metadata +7 -18
- data/ripper-plus.gemspec +0 -75
data/Gemfile
CHANGED
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
|
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
|
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`
|
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
|
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
|
-
|
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'
|
data/lib/ripper-plus.rb
CHANGED
@@ -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
|
-
|
60
|
-
|
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
|
-
|
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[
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
78
|
-
|
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
|
-
|
91
|
-
|
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
|
-
|
99
|
-
|
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
|
-
|
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|
|
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
|
-
|
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|
|
179
|
+
lhs.each { |var| add_variables_from_node(var, scope_stack) }
|
138
180
|
when :mlhs_paren, :var_field, :rest_param, :blockarg
|
139
|
-
|
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|
|
184
|
+
pre_star.each { |var| add_variables_from_node(var, scope_stack) }
|
143
185
|
if star
|
144
|
-
|
186
|
+
add_variables_from_node(star, scope_stack)
|
145
187
|
end
|
146
188
|
if post_star
|
147
|
-
post_star.each { |var|
|
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|
|
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
|
-
|
224
|
+
add_variables_from_node(var, scope_stack)
|
179
225
|
end
|
180
226
|
end
|
181
227
|
if rest && rest[1]
|
182
|
-
|
228
|
+
add_variables_from_node(rest, scope_stack)
|
183
229
|
end
|
184
230
|
if positional_2
|
185
|
-
positional_2.each { |var|
|
231
|
+
positional_2.each { |var| add_variables_from_node(var, scope_stack) }
|
186
232
|
end
|
187
233
|
if block
|
188
|
-
|
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|
|
data/spec/scope_stack_spec.rb
CHANGED
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -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|
|
data/spec/transformer_spec.rb
CHANGED
@@ -1,565 +1,825 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe RipperPlus::Transformer do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
[
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
[
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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, "
|
208
|
-
[[:@ident, "
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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, "
|
233
|
-
[[:@ident, "
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
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
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
[:
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
[
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
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
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
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
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
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
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
[:
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
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
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
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
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
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
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
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
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
[
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
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
|
-
[[:
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
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
|