ripper_ruby_parser 1.7.0 → 1.9.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -0
  3. data/README.md +6 -4
  4. data/lib/ripper_ruby_parser/commenting_ripper_parser.rb +24 -12
  5. data/lib/ripper_ruby_parser/sexp_handlers.rb +2 -0
  6. data/lib/ripper_ruby_parser/sexp_handlers/assignment.rb +9 -4
  7. data/lib/ripper_ruby_parser/sexp_handlers/blocks.rb +40 -52
  8. data/lib/ripper_ruby_parser/sexp_handlers/conditionals.rb +17 -19
  9. data/lib/ripper_ruby_parser/sexp_handlers/helper_methods.rb +35 -2
  10. data/lib/ripper_ruby_parser/sexp_handlers/literals.rb +15 -242
  11. data/lib/ripper_ruby_parser/sexp_handlers/method_calls.rb +9 -5
  12. data/lib/ripper_ruby_parser/sexp_handlers/methods.rb +22 -17
  13. data/lib/ripper_ruby_parser/sexp_handlers/operators.rb +3 -3
  14. data/lib/ripper_ruby_parser/sexp_handlers/string_literals.rb +256 -0
  15. data/lib/ripper_ruby_parser/sexp_processor.rb +12 -56
  16. data/lib/ripper_ruby_parser/unescape.rb +89 -43
  17. data/lib/ripper_ruby_parser/version.rb +1 -1
  18. metadata +125 -76
  19. data/Rakefile +0 -33
  20. data/test/end_to_end/comments_test.rb +0 -59
  21. data/test/end_to_end/comparison_test.rb +0 -104
  22. data/test/end_to_end/lib_comparison_test.rb +0 -29
  23. data/test/end_to_end/line_numbering_test.rb +0 -31
  24. data/test/end_to_end/samples_comparison_test.rb +0 -13
  25. data/test/end_to_end/test_comparison_test.rb +0 -32
  26. data/test/pt_testcase/pt_test.rb +0 -44
  27. data/test/ripper_ruby_parser/commenting_ripper_parser_test.rb +0 -200
  28. data/test/ripper_ruby_parser/parser_test.rb +0 -553
  29. data/test/ripper_ruby_parser/sexp_handlers/assignment_test.rb +0 -613
  30. data/test/ripper_ruby_parser/sexp_handlers/blocks_test.rb +0 -679
  31. data/test/ripper_ruby_parser/sexp_handlers/conditionals_test.rb +0 -536
  32. data/test/ripper_ruby_parser/sexp_handlers/literals_test.rb +0 -1106
  33. data/test/ripper_ruby_parser/sexp_handlers/loops_test.rb +0 -209
  34. data/test/ripper_ruby_parser/sexp_handlers/method_calls_test.rb +0 -267
  35. data/test/ripper_ruby_parser/sexp_handlers/methods_test.rb +0 -421
  36. data/test/ripper_ruby_parser/sexp_handlers/operators_test.rb +0 -399
  37. data/test/ripper_ruby_parser/sexp_processor_test.rb +0 -303
  38. data/test/ripper_ruby_parser/version_test.rb +0 -7
  39. data/test/samples/assignment.rb +0 -17
  40. data/test/samples/comments.rb +0 -13
  41. data/test/samples/conditionals.rb +0 -23
  42. data/test/samples/lambdas.rb +0 -5
  43. data/test/samples/loops.rb +0 -36
  44. data/test/samples/misc.rb +0 -281
  45. data/test/samples/number.rb +0 -7
  46. data/test/samples/operators.rb +0 -18
  47. data/test/samples/strings.rb +0 -147
  48. data/test/test_helper.rb +0 -107
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
- require "ruby_parser"
5
-
6
- describe "Using RipperRubyParser and RubyParser" do
7
- let :newparser do
8
- RipperRubyParser::Parser.new
9
- end
10
-
11
- let :oldparser do
12
- RubyParser.for_current_ruby
13
- end
14
-
15
- Dir.glob("lib/**/*.rb").each do |file|
16
- describe "for #{file}" do
17
- let :program do
18
- File.read file
19
- end
20
-
21
- it "gives the same result" do
22
- original = oldparser.parse program
23
- imitation = newparser.parse program
24
-
25
- _(formatted(imitation)).must_equal formatted(original)
26
- end
27
- end
28
- end
29
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
- require "ruby_parser"
5
-
6
- describe "Using RipperRubyParser and RubyParser" do
7
- describe "for a multi-line program" do
8
- let :program do
9
- <<-RUBY
10
- class Foo
11
- def foo()
12
- bar()
13
- baz(qux)
14
- end
15
- end
16
-
17
- module Bar
18
- @@baz = {}
19
- end
20
- RUBY
21
- end
22
-
23
- it "gives the same result" do
24
- _(program).must_be_parsed_as_before
25
- end
26
-
27
- it "gives the same result with line numbers" do
28
- _(program).must_be_parsed_as_before with_line_numbers: true
29
- end
30
- end
31
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
- require "ruby_parser"
5
-
6
- describe "Using RipperRubyParser and RubyParser" do
7
- Dir.glob(File.expand_path("../samples/*.rb", File.dirname(__FILE__))).each do |file|
8
- it "gives the same result for #{file}" do
9
- program = File.read file
10
- _(program).must_be_parsed_as_before
11
- end
12
- end
13
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
- require "ruby_parser"
5
-
6
- describe "Using RipperRubyParser and RubyParser" do
7
- let :newparser do
8
- RipperRubyParser::Parser.new
9
- end
10
-
11
- let :oldparser do
12
- RubyParser.for_current_ruby
13
- end
14
-
15
- Dir.glob("test/ripper_ruby_parser/**/*.rb").each do |file|
16
- describe "for #{file}" do
17
- let :program do
18
- File.read file
19
- end
20
-
21
- it "gives the same result" do
22
- # Clone string because ruby_parser destroys it when there's a heredoc
23
- # inside.
24
- copy = program.clone
25
- original = oldparser.parse program
26
- imitation = newparser.parse copy
27
-
28
- _(formatted(imitation)).must_equal formatted(original)
29
- end
30
- end
31
- end
32
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
- require "pt_testcase"
5
-
6
- class TestParser < RipperRubyParser::Parser
7
- def process(input)
8
- parse input
9
- end
10
- end
11
-
12
- SKIPPED_TESTS = ["dstr_heredoc_windoze_sucks"].freeze
13
-
14
- class RubyParserTestCase < ParseTreeTestCase
15
- def self.previous(_key)
16
- "Ruby"
17
- end
18
-
19
- def self.generate_test(klass, node, data, input_name, _output_name)
20
- if data["Ruby"].is_a? Array
21
- klass.send :define_method, "test_#{node}" do
22
- skip "Not a parser test"
23
- end
24
- return
25
- end
26
-
27
- if SKIPPED_TESTS.include? node
28
- klass.send :define_method, "test_#{node}" do
29
- skip "Can't or won't fix this difference"
30
- end
31
- return
32
- end
33
-
34
- super klass, node, data, input_name, "ParseTree"
35
- end
36
- end
37
-
38
- class TestRuby19Parser < RubyParserTestCase
39
- def setup
40
- super
41
-
42
- self.processor = TestParser.new
43
- end
44
- end
@@ -1,200 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
-
5
- describe RipperRubyParser::CommentingRipperParser do
6
- def parse_with_builder(str)
7
- builder = RipperRubyParser::CommentingRipperParser.new str
8
- builder.parse
9
- end
10
-
11
- def empty_params_list
12
- @empty_params_list ||= s(:params, *([nil] * 7))
13
- end
14
-
15
- describe "handling comments" do
16
- # Handle different results for dynamic symbol strings. This was changed in
17
- # Ruby 2.6.3 and up. See https://bugs.ruby-lang.org/issues/15670
18
- let(:dsym_string_type) { RUBY_VERSION < "2.6.3" ? :xstring : :string_content }
19
-
20
- it "produces a comment node surrounding a commented def" do
21
- result = parse_with_builder "# Foo\ndef foo; end"
22
- _(result).must_equal s(:program,
23
- s(:stmts,
24
- s(:comment,
25
- "# Foo\n",
26
- s(:def,
27
- s(:@ident, "foo", s(2, 4)),
28
- empty_params_list,
29
- s(:bodystmt,
30
- s(:stmts, s(:void_stmt, s(2, 12))), nil, nil, nil),
31
- s(2, 0)))))
32
- end
33
-
34
- it "produces a blank comment node surrounding a def that has no comment" do
35
- result = parse_with_builder "def foo; end"
36
- _(result).must_equal s(:program,
37
- s(:stmts,
38
- s(:comment,
39
- "",
40
- s(:def,
41
- s(:@ident, "foo", s(1, 4)),
42
- empty_params_list,
43
- s(:bodystmt,
44
- s(:stmts, s(:void_stmt, s(1, 12))), nil, nil, nil),
45
- s(1, 0)))))
46
- end
47
-
48
- it "produces a comment node surrounding a commented class" do
49
- result = parse_with_builder "# Foo\nclass Foo; end"
50
- _(result).must_equal s(:program,
51
- s(:stmts,
52
- s(:comment,
53
- "# Foo\n",
54
- s(:class,
55
- s(:const_ref, s(:@const, "Foo", s(2, 6))),
56
- nil,
57
- s(:bodystmt,
58
- s(:stmts, s(:void_stmt, s(2, 10))), nil, nil, nil),
59
- s(2, 0)))))
60
- end
61
-
62
- it "produce a blank comment node surrounding a class that has no comment" do
63
- result = parse_with_builder "class Foo; end"
64
- _(result).must_equal s(:program,
65
- s(:stmts,
66
- s(:comment,
67
- "",
68
- s(:class,
69
- s(:const_ref, s(:@const, "Foo", s(1, 6))),
70
- nil,
71
- s(:bodystmt,
72
- s(:stmts, s(:void_stmt, s(1, 10))), nil, nil, nil),
73
- s(1, 0)))))
74
- end
75
-
76
- it "produces a comment node surrounding a commented module" do
77
- result = parse_with_builder "# Foo\nmodule Foo; end"
78
- _(result).must_equal s(:program,
79
- s(:stmts,
80
- s(:comment,
81
- "# Foo\n",
82
- s(:module,
83
- s(:const_ref, s(:@const, "Foo", s(2, 7))),
84
- s(:bodystmt,
85
- s(:stmts, s(:void_stmt, s(2, 11))), nil, nil, nil),
86
- s(2, 0)))))
87
- end
88
-
89
- it "produces a blank comment node surrounding a module that has no comment" do
90
- result = parse_with_builder "module Foo; end"
91
- _(result).must_equal s(:program,
92
- s(:stmts,
93
- s(:comment,
94
- "",
95
- s(:module,
96
- s(:const_ref, s(:@const, "Foo", s(1, 7))),
97
- s(:bodystmt,
98
- s(:stmts, s(:void_stmt, s(1, 11))), nil, nil, nil),
99
- s(1, 0)))))
100
- end
101
-
102
- it "is not confused by a symbol containing a keyword" do
103
- result = parse_with_builder ":class; def foo; end"
104
- _(result).must_equal s(:program,
105
- s(:stmts,
106
- s(:symbol_literal, s(:symbol, s(:@kw, "class", s(1, 1)))),
107
- s(:comment,
108
- "",
109
- s(:def,
110
- s(:@ident, "foo", s(1, 12)),
111
- empty_params_list,
112
- s(:bodystmt,
113
- s(:stmts, s(:void_stmt, s(1, 20))), nil, nil, nil),
114
- s(1, 8)))))
115
- end
116
-
117
- it "is not confused by a dynamic symbol" do
118
- result = parse_with_builder ":'foo'; def bar; end"
119
- _(result).must_equal s(:program,
120
- s(:stmts,
121
- s(:dyna_symbol,
122
- s(dsym_string_type,
123
- s(:@tstring_content, "foo", s(1, 2), ":'"))),
124
- s(:comment,
125
- "",
126
- s(:def,
127
- s(:@ident, "bar", s(1, 12)),
128
- empty_params_list,
129
- s(:bodystmt,
130
- s(:stmts, s(:void_stmt, s(1, 20))), nil, nil, nil),
131
- s(1, 8)))))
132
- end
133
-
134
- it "is not confused by a dynamic symbol containing a class definition" do
135
- result = parse_with_builder ":\"foo\#{class Bar;end}\""
136
- _(result).must_equal s(:program,
137
- s(:stmts,
138
- s(:dyna_symbol,
139
- s(dsym_string_type,
140
- s(:@tstring_content, "foo", s(1, 2), ':"'),
141
- s(:string_embexpr,
142
- s(:stmts,
143
- s(:comment,
144
- "",
145
- s(:class,
146
- s(:const_ref, s(:@const, "Bar", s(1, 13))),
147
- nil,
148
- s(:bodystmt,
149
- s(:stmts, s(:void_stmt, s(1, 17))),
150
- nil, nil, nil),
151
- s(1, 7)))))))))
152
- end
153
-
154
- it "turns an embedded document into a comment node" do
155
- result = parse_with_builder "=begin Hello\nthere\n=end\nclass Foo; end"
156
- _(result).must_equal s(:program,
157
- s(:stmts,
158
- s(:comment,
159
- "=begin Hello\nthere\n=end\n",
160
- s(:class,
161
- s(:const_ref, s(:@const, "Foo", s(4, 6))),
162
- nil,
163
- s(:bodystmt,
164
- s(:stmts, s(:void_stmt, s(4, 10))), nil, nil, nil),
165
- s(4, 0)))))
166
- end
167
- end
168
-
169
- describe "handling syntax errors" do
170
- it "raises an error for an incomplete source" do
171
- _(proc { parse_with_builder "def foo" }).must_raise RipperRubyParser::SyntaxError
172
- end
173
-
174
- it "raises an error for an invalid class name" do
175
- _(proc { parse_with_builder "class foo; end" })
176
- .must_raise RipperRubyParser::SyntaxError
177
- end
178
-
179
- it "raises an error aliasing $1 as foo" do
180
- _(proc { parse_with_builder "alias foo $1" }).must_raise RipperRubyParser::SyntaxError
181
- end
182
-
183
- it "raises an error aliasing foo as $1" do
184
- _(proc { parse_with_builder "alias $1 foo" }).must_raise RipperRubyParser::SyntaxError
185
- end
186
-
187
- it "raises an error aliasing $2 as $1" do
188
- _(proc { parse_with_builder "alias $1 $2" }).must_raise RipperRubyParser::SyntaxError
189
- end
190
-
191
- it "raises an error assigning to $1" do
192
- _(proc { parse_with_builder "$1 = foo" }).must_raise RipperRubyParser::SyntaxError
193
- end
194
-
195
- it "raises an error using an invalid parameter name" do
196
- _(proc { parse_with_builder "def foo(BAR); end" })
197
- .must_raise RipperRubyParser::SyntaxError
198
- end
199
- end
200
- end
@@ -1,553 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path("../test_helper.rb", File.dirname(__FILE__))
4
-
5
- describe RipperRubyParser::Parser do
6
- let(:parser) { RipperRubyParser::Parser.new }
7
- describe "#parse" do
8
- it "returns an s-expression" do
9
- result = parser.parse "foo"
10
- _(result).must_be_instance_of Sexp
11
- end
12
-
13
- describe "for an empty program" do
14
- it "returns nil" do
15
- _("").must_be_parsed_as nil
16
- end
17
- end
18
-
19
- describe "for a class declaration" do
20
- it "works with a namespaced class name" do
21
- _("class Foo::Bar; end")
22
- .must_be_parsed_as s(:class,
23
- s(:colon2, s(:const, :Foo), :Bar),
24
- nil)
25
- end
26
-
27
- it "works for singleton classes" do
28
- _("class << self; end").must_be_parsed_as s(:sclass, s(:self))
29
- end
30
- end
31
-
32
- describe "for a module declaration" do
33
- it "works with a simple module name" do
34
- _("module Foo; end")
35
- .must_be_parsed_as s(:module, :Foo)
36
- end
37
-
38
- it "works with a namespaced module name" do
39
- _("module Foo::Bar; end")
40
- .must_be_parsed_as s(:module,
41
- s(:colon2, s(:const, :Foo), :Bar))
42
- end
43
- end
44
-
45
- describe "for empty parentheses" do
46
- it "works with lone ()" do
47
- _("()").must_be_parsed_as s(:nil)
48
- end
49
- end
50
-
51
- describe "for a begin..end block" do
52
- it "works with no statements" do
53
- _("begin; end")
54
- .must_be_parsed_as s(:nil)
55
- end
56
-
57
- it "works with one statement" do
58
- _("begin; foo; end")
59
- .must_be_parsed_as s(:call, nil, :foo)
60
- end
61
-
62
- it "works with multiple statements" do
63
- _("begin; foo; bar; end")
64
- .must_be_parsed_as s(:block,
65
- s(:call, nil, :foo),
66
- s(:call, nil, :bar))
67
- end
68
- end
69
-
70
- describe "for arguments" do
71
- it "works for a simple case with splat" do
72
- _("foo *bar")
73
- .must_be_parsed_as s(:call,
74
- nil,
75
- :foo,
76
- s(:splat, s(:call, nil, :bar)))
77
- end
78
-
79
- it "works for a multi-argument case with splat" do
80
- _("foo bar, *baz")
81
- .must_be_parsed_as s(:call,
82
- nil,
83
- :foo,
84
- s(:call, nil, :bar),
85
- s(:splat, s(:call, nil, :baz)))
86
- end
87
-
88
- it "works for a simple case passing a block" do
89
- _("foo &bar")
90
- .must_be_parsed_as s(:call, nil, :foo,
91
- s(:block_pass,
92
- s(:call, nil, :bar)))
93
- end
94
-
95
- it "works for a bare hash" do
96
- _("foo bar => baz")
97
- .must_be_parsed_as s(:call, nil, :foo,
98
- s(:hash,
99
- s(:call, nil, :bar),
100
- s(:call, nil, :baz)))
101
- end
102
- end
103
-
104
- describe "for the __ENCODING__ keyword" do
105
- it "evaluates to the equivalent of Encoding::UTF_8" do
106
- _("__ENCODING__")
107
- .must_be_parsed_as s(:colon2, s(:const, :Encoding), :UTF_8)
108
- end
109
- end
110
-
111
- describe "for the __FILE__ keyword" do
112
- describe "when not passing a file name" do
113
- it "creates a string sexp with value '(string)'" do
114
- _("__FILE__")
115
- .must_be_parsed_as s(:str, "(string)")
116
- end
117
- end
118
-
119
- describe "when passing a file name" do
120
- it "creates a string sexp with the file name" do
121
- result = parser.parse "__FILE__", "foo"
122
- _(result).must_equal s(:str, "foo")
123
- end
124
- end
125
- end
126
-
127
- describe "for the __LINE__ keyword" do
128
- it "creates a literal sexp with value of the line number" do
129
- _("__LINE__")
130
- .must_be_parsed_as s(:lit, 1)
131
- _("\n__LINE__")
132
- .must_be_parsed_as s(:lit, 2)
133
- end
134
- end
135
-
136
- describe "for the END keyword" do
137
- it "converts to a :postexe iterator" do
138
- _("END { foo }")
139
- .must_be_parsed_as s(:iter, s(:postexe), 0, s(:call, nil, :foo))
140
- end
141
-
142
- it "works with an empty block" do
143
- _("END { }")
144
- .must_be_parsed_as s(:iter, s(:postexe), 0)
145
- end
146
-
147
- it "assigns correct line numbers" do
148
- _("END {\nfoo\n}")
149
- .must_be_parsed_as s(:iter,
150
- s(:postexe).line(1), 0,
151
- s(:call, nil, :foo).line(2)).line(1),
152
- with_line_numbers: true
153
- end
154
-
155
- it "assigns correct line numbers to a embedded begin block" do
156
- _("END {\nbegin\nfoo\nend\n}")
157
- .must_be_parsed_as s(:iter,
158
- s(:postexe).line(1), 0,
159
- s(:call, nil, :foo).line(3)).line(1),
160
- with_line_numbers: true
161
- end
162
- end
163
-
164
- describe "for the BEGIN keyword" do
165
- it "converts to a :preexe iterator" do
166
- _("BEGIN { foo }")
167
- .must_be_parsed_as s(:iter, s(:preexe), 0, s(:call, nil, :foo))
168
- end
169
-
170
- it "works with an empty block" do
171
- _("BEGIN { }")
172
- .must_be_parsed_as s(:iter, s(:preexe), 0)
173
- end
174
-
175
- it "assigns correct line numbers" do
176
- _("BEGIN {\nfoo\n}")
177
- .must_be_parsed_as s(:iter,
178
- s(:preexe).line(1), 0,
179
- s(:call, nil, :foo).line(2)).line(1),
180
- with_line_numbers: true
181
- end
182
-
183
- it "assigns correct line numbers to a embedded begin block" do
184
- _("BEGIN {\nbegin\nfoo\nend\n}")
185
- .must_be_parsed_as s(:iter,
186
- s(:preexe).line(1), 0,
187
- s(:begin,
188
- s(:call, nil, :foo).line(3)).line(2)).line(1),
189
- with_line_numbers: true
190
- end
191
- end
192
-
193
- describe "for constant lookups" do
194
- it "works when explicitely starting from the root namespace" do
195
- _("::Foo")
196
- .must_be_parsed_as s(:colon3, :Foo)
197
- end
198
-
199
- it "works with a three-level constant lookup" do
200
- _("Foo::Bar::Baz")
201
- .must_be_parsed_as s(:colon2,
202
- s(:colon2, s(:const, :Foo), :Bar),
203
- :Baz)
204
- end
205
-
206
- it "works looking up a constant in a non-constant" do
207
- _("foo::Bar").must_be_parsed_as s(:colon2,
208
- s(:call, nil, :foo),
209
- :Bar)
210
- end
211
- end
212
-
213
- describe "for variable references" do
214
- it "works for self" do
215
- _("self")
216
- .must_be_parsed_as s(:self)
217
- end
218
-
219
- it "works for instance variables" do
220
- _("@foo")
221
- .must_be_parsed_as s(:ivar, :@foo)
222
- end
223
-
224
- it "works for global variables" do
225
- _("$foo")
226
- .must_be_parsed_as s(:gvar, :$foo)
227
- end
228
-
229
- it "works for regexp match references" do
230
- _("$1")
231
- .must_be_parsed_as s(:nth_ref, 1)
232
- end
233
-
234
- specify { _("$'").must_be_parsed_as s(:back_ref, :"'") }
235
- specify { _("$&").must_be_parsed_as s(:back_ref, :"&") }
236
-
237
- it "works for class variables" do
238
- _("@@foo")
239
- .must_be_parsed_as s(:cvar, :@@foo)
240
- end
241
- end
242
-
243
- describe "for expressions" do
244
- it "handles assignment inside binary operator expressions" do
245
- _("foo + (bar = baz)")
246
- .must_be_parsed_as s(:call,
247
- s(:call, nil, :foo),
248
- :+,
249
- s(:lasgn,
250
- :bar,
251
- s(:call, nil, :baz)))
252
- end
253
-
254
- it "handles assignment inside unary operator expressions" do
255
- _("+(foo = bar)")
256
- .must_be_parsed_as s(:call,
257
- s(:lasgn, :foo, s(:call, nil, :bar)),
258
- :+@)
259
- end
260
- end
261
-
262
- # Note: differences in the handling of comments are not caught by Sexp's
263
- # implementation of equality.
264
- describe "for comments" do
265
- it "handles method comments" do
266
- result = parser.parse "# Foo\ndef foo; end"
267
- _(result).must_equal s(:defn,
268
- :foo,
269
- s(:args), s(:nil))
270
- _(result.comments).must_equal "# Foo\n"
271
- end
272
-
273
- it "handles comments for methods with explicit receiver" do
274
- result = parser.parse "# Foo\ndef foo.bar; end"
275
- _(result).must_equal s(:defs,
276
- s(:call, nil, :foo),
277
- :bar,
278
- s(:args),
279
- s(:nil))
280
- _(result.comments).must_equal "# Foo\n"
281
- end
282
-
283
- it "matches comments to the correct entity" do
284
- result = parser.parse "# Foo\nclass Foo\n# Bar\ndef bar\nend\nend"
285
- _(result).must_equal s(:class, :Foo, nil,
286
- s(:defn, :bar,
287
- s(:args), s(:nil)))
288
- _(result.comments).must_equal "# Foo\n"
289
- defn = result[3]
290
- _(defn.sexp_type).must_equal :defn
291
- _(defn.comments).must_equal "# Bar\n"
292
- end
293
-
294
- it "combines multi-line comments" do
295
- result = parser.parse "# Foo\n# Bar\ndef foo; end"
296
- _(result).must_equal s(:defn,
297
- :foo,
298
- s(:args), s(:nil))
299
- _(result.comments).must_equal "# Foo\n# Bar\n"
300
- end
301
-
302
- it "drops comments inside method bodies" do
303
- result = parser.parse <<-RUBY
304
- # Foo
305
- class Foo
306
- # foo
307
- def foo
308
- bar # this is dropped
309
- end
310
-
311
- # bar
312
- def bar
313
- baz
314
- end
315
- end
316
- RUBY
317
- _(result).must_equal s(:class,
318
- :Foo,
319
- nil,
320
- s(:defn, :foo, s(:args), s(:call, nil, :bar)),
321
- s(:defn, :bar, s(:args), s(:call, nil, :baz)))
322
- _(result.comments).must_equal "# Foo\n"
323
- _(result[3].comments).must_equal "# foo\n"
324
- _(result[4].comments).must_equal "# bar\n"
325
- end
326
-
327
- it "handles use of singleton class inside methods" do
328
- result = parser.parse "# Foo\ndef bar\nclass << self\nbaz\nend\nend"
329
- _(result).must_equal s(:defn,
330
- :bar,
331
- s(:args),
332
- s(:sclass, s(:self),
333
- s(:call, nil, :baz)))
334
- _(result.comments).must_equal "# Foo\n"
335
- end
336
-
337
- # TODO: Prefer assigning comment to the BEGIN instead
338
- it "assigns comments on BEGIN blocks to the following item" do
339
- result = parser.parse "# Bar\nBEGIN { }\n# Foo\nclass Bar\n# foo\ndef foo; end\nend"
340
- _(result).must_equal s(:block,
341
- s(:iter, s(:preexe), 0),
342
- s(:class, :Bar, nil,
343
- s(:defn, :foo, s(:args), s(:nil))))
344
- _(result[2].comments).must_equal "# Bar\n# Foo\n"
345
- _(result[2][3].comments).must_equal "# foo\n"
346
- end
347
-
348
- it "assigns comments on multiple BEGIN blocks to the following item" do
349
- result = parser.parse "# Bar\nBEGIN { }\n# Baz\nBEGIN { }\n# Foo\ndef foo; end"
350
- _(result).must_equal s(:block,
351
- s(:iter, s(:preexe), 0),
352
- s(:iter, s(:preexe), 0),
353
- s(:defn, :foo, s(:args), s(:nil)))
354
- _(result[3].comments).must_equal "# Bar\n# Baz\n# Foo\n"
355
- end
356
-
357
- it "assigns comments on BEGIN blocks to the first following item" do
358
- result = parser.parse "# Bar\nBEGIN { }\n# Baz\nBEGIN { }\n# Foo\ndef foo; end"
359
- _(result).must_equal s(:block,
360
- s(:iter, s(:preexe), 0),
361
- s(:iter, s(:preexe), 0),
362
- s(:defn, :foo, s(:args), s(:nil)))
363
- _(result[3].comments).must_equal "# Bar\n# Baz\n# Foo\n"
364
- end
365
- end
366
-
367
- # Note: differences in the handling of line numbers are not caught by
368
- # Sexp's implementation of equality.
369
- describe "assigning line numbers" do
370
- it "works for a plain method call" do
371
- result = parser.parse "foo"
372
- _(result.line).must_equal 1
373
- end
374
-
375
- it "works for a method call with parentheses" do
376
- result = parser.parse "foo()"
377
- _(result.line).must_equal 1
378
- end
379
-
380
- it "works for a method call with receiver" do
381
- result = parser.parse "foo.bar"
382
- _(result.line).must_equal 1
383
- end
384
-
385
- it "works for a method call with receiver and arguments" do
386
- result = parser.parse "foo.bar baz"
387
- _(result.line).must_equal 1
388
- end
389
-
390
- it "works for a method call with arguments" do
391
- result = parser.parse "foo bar"
392
- _(result.line).must_equal 1
393
- end
394
-
395
- it "works for a block with two lines" do
396
- result = parser.parse "foo\nbar\n"
397
- _(result.sexp_type).must_equal :block
398
- _(result[1].line).must_equal 1
399
- _(result[2].line).must_equal 2
400
- _(result.line).must_equal 1
401
- end
402
-
403
- it "works for a constant reference" do
404
- result = parser.parse "Foo"
405
- _(result.line).must_equal 1
406
- end
407
-
408
- it "works for an instance variable" do
409
- result = parser.parse "@foo"
410
- _(result.line).must_equal 1
411
- end
412
-
413
- it "works for a global variable" do
414
- result = parser.parse "$foo"
415
- _(result.line).must_equal 1
416
- end
417
-
418
- it "works for a class variable" do
419
- result = parser.parse "@@foo"
420
- _(result.line).must_equal 1
421
- end
422
-
423
- it "works for a local variable" do
424
- _("foo = bar\nfoo\n")
425
- .must_be_parsed_as s(:block,
426
- s(:lasgn, :foo, s(:call, nil, :bar).line(1)).line(1),
427
- s(:lvar, :foo).line(2)).line(1),
428
- with_line_numbers: true
429
- end
430
-
431
- it "works for an integer literal" do
432
- result = parser.parse "42"
433
- _(result.line).must_equal 1
434
- end
435
-
436
- it "works for a float literal" do
437
- result = parser.parse "3.14"
438
- _(result.line).must_equal 1
439
- end
440
-
441
- it "works for a range literal" do
442
- result = parser.parse "0..4"
443
- _(result.line).must_equal 1
444
- end
445
-
446
- it "works for an exclusive range literal" do
447
- result = parser.parse "0...4"
448
- _(result.line).must_equal 1
449
- end
450
-
451
- it "works for a symbol literal" do
452
- result = parser.parse ":foo"
453
- _(result.line).must_equal 1
454
- end
455
-
456
- it "works for a keyword-like symbol literal" do
457
- result = parser.parse ":and"
458
- _(result.line).must_equal 1
459
- end
460
-
461
- it "works for a string literal" do
462
- result = parser.parse '"foo"'
463
- _(result.line).must_equal 1
464
- end
465
-
466
- it "works for a backtick string literal" do
467
- result = parser.parse "`foo`"
468
- _(result.line).must_equal 1
469
- end
470
-
471
- it "works for a plain regexp literal" do
472
- result = parser.parse "/foo/"
473
- _(result.line).must_equal 1
474
- end
475
-
476
- it "works for a regular expression back reference" do
477
- result = parser.parse "$1"
478
- _(result.line).must_equal 1
479
- end
480
-
481
- it "works for self" do
482
- result = parser.parse "self"
483
- _(result.line).must_equal 1
484
- end
485
-
486
- it "works for __FILE__" do
487
- result = parser.parse "__FILE__"
488
- _(result.line).must_equal 1
489
- end
490
-
491
- it "works for nil" do
492
- result = parser.parse "nil"
493
- _(result.line).must_equal 1
494
- end
495
-
496
- it "works for a class definition" do
497
- result = parser.parse "class Foo; end"
498
- _(result.line).must_equal 1
499
- end
500
-
501
- it "works for a module definition" do
502
- result = parser.parse "module Foo; end"
503
- _(result.line).must_equal 1
504
- end
505
-
506
- it "works for a method definition" do
507
- result = parser.parse "def foo; end"
508
- _(result.line).must_equal 1
509
- end
510
-
511
- it "assigns line numbers to nested sexps without their own line numbers" do
512
- _("foo(bar) do\nnext baz\nend\n")
513
- .must_be_parsed_as s(:iter,
514
- s(:call, nil, :foo, s(:call, nil, :bar).line(1)).line(1),
515
- 0,
516
- s(:next, s(:call, nil, :baz).line(2)).line(2)).line(1),
517
- with_line_numbers: true
518
- end
519
-
520
- describe "when a line number is passed" do
521
- it "shifts all line numbers as appropriate" do
522
- result = parser.parse "foo\nbar\n", "(string)", 3
523
- _(result).must_equal s(:block,
524
- s(:call, nil, :foo),
525
- s(:call, nil, :bar))
526
- _(result.line).must_equal 3
527
- _(result[1].line).must_equal 3
528
- _(result[2].line).must_equal 4
529
- end
530
- end
531
- end
532
- end
533
-
534
- describe "#trickle_up_line_numbers" do
535
- it "works through several nested levels" do
536
- inner = s(:foo)
537
- outer = s(:bar, s(:baz, s(:qux, inner)))
538
- outer.line = 42
539
- parser.send :trickle_down_line_numbers, outer
540
- _(inner.line).must_equal 42
541
- end
542
- end
543
-
544
- describe "#trickle_down_line_numbers" do
545
- it "works through several nested levels" do
546
- inner = s(:foo)
547
- inner.line = 42
548
- outer = s(:bar, s(:baz, s(:qux, inner)))
549
- parser.send :trickle_up_line_numbers, outer
550
- _(outer.line).must_equal 42
551
- end
552
- end
553
- end