ripper_ruby_parser 1.7.0 → 1.9.0

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