1 # frozen_string_literal: false
5 require_relative '../lib/parser_support'
8 module AbstractSyntaxTree
12 attr_reader :lineno, :column
13 def initialize(lineno, column)
20 when lineno < other.lineno
22 when lineno == other.lineno
23 column <=> other.column
24 when lineno > other.lineno
31 CodePosition.new(first_lineno, first_column)
35 CodePosition.new(last_lineno, last_column)
43 class TestAst < Test::Unit::TestCase
47 def initialize(path, src: nil)
51 @ast = RubyVM::AbstractSyntaxTree.parse(src) if src
61 def validate_not_cared
63 validate_not_cared0(ast)
69 return @ast if defined?(@ast)
70 @ast = RubyVM::AbstractSyntaxTree.parse_file(@path)
75 def validate_range0(node)
76 beg_pos, end_pos = node.beg_pos, node.end_pos
77 children = node.children.grep(RubyVM::AbstractSyntaxTree::Node)
79 return true if children.empty?
80 # These NODE_D* has NODE_LIST as nd_next->nd_next whose last locations
81 # we can not update when item is appended.
82 return true if [:DSTR, :DXSTR, :DREGX, :DSYM].include? node.type
84 min = children.map(&:beg_pos).min
85 max = children.map(&:end_pos).max
88 @errors << { type: :min_validation_error, min: min, beg_pos: beg_pos, node: node }
92 @errors << { type: :max_validation_error, max: max, end_pos: end_pos, node: node }
95 p "#{node} => #{children}" if @debug
97 children.each do |child|
99 validate_range0(child)
103 def validate_not_cared0(node)
104 beg_pos, end_pos = node.beg_pos, node.end_pos
105 children = node.children.grep(RubyVM::AbstractSyntaxTree::Node)
107 @errors << { type: :first_lineno, node: node } if beg_pos.lineno == 0
108 @errors << { type: :first_column, node: node } if beg_pos.column == -1
109 @errors << { type: :last_lineno, node: node } if end_pos.lineno == 0
110 @errors << { type: :last_column, node: node } if end_pos.column == -1
112 children.each {|c| validate_not_cared0(c) }
116 SRCDIR = File.expand_path("../../..", __FILE__)
118 Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
119 define_method("test_ranges:#{path}") do
120 helper = Helper.new("#{SRCDIR}/#{path}")
121 helper.validate_range
123 assert_equal([], helper.errors)
127 Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
128 define_method("test_not_cared:#{path}") do
129 helper = Helper.new("#{SRCDIR}/#{path}")
130 helper.validate_not_cared
132 assert_equal([], helper.errors)
136 Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
137 define_method("test_all_tokens:#{path}") do
138 node = RubyVM::AbstractSyntaxTree.parse_file("#{SRCDIR}/#{path}", keep_tokens: true)
139 tokens = node.all_tokens.sort_by { [_1.last[0], _1.last[1]] }
140 tokens_bytes = tokens.map { _1[2]}.join.bytes
141 source_bytes = File.read("#{SRCDIR}/#{path}").bytes
143 assert_equal(source_bytes, tokens_bytes)
145 (tokens.count - 1).times do |i|
147 token_1 = tokens[i + 1]
148 end_pos = token_0.last[2..3]
149 beg_pos = token_1.last[0..1]
151 if end_pos[0] == beg_pos[0]
152 # When both tokens are same line, column should be consecutives
153 assert_equal(beg_pos[1], end_pos[1], "#{token_0}. #{token_1}")
155 # Line should be next
156 assert_equal(beg_pos[0], end_pos[0] + 1, "#{token_0}. #{token_1}")
157 # It should be on the beginning of the line
158 assert_equal(0, beg_pos[1], "#{token_0}. #{token_1}")
164 private def parse(src)
165 EnvUtil.suppress_warning {
166 RubyVM::AbstractSyntaxTree.parse(src)
171 assert_raise(TypeError) {RubyVM::AbstractSyntaxTree::Node.allocate}
174 def test_parse_argument_error
175 assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(0)}
176 assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(nil)}
177 assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(false)}
178 assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(true)}
179 assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(:foo)}
182 def test_column_with_long_heredoc_identifier
184 ast = parse("<<-#{term}\n""ddddddd\n#{term}\n")
185 node = ast.children[2]
186 assert_equal(:STR, node.type)
187 assert_equal(0, node.first_column)
190 def test_column_of_heredoc
191 node = parse("<<-SRC\nddddddd\nSRC\n").children[2]
192 assert_equal(:STR, node.type)
193 assert_equal(0, node.first_column)
194 assert_equal(6, node.last_column)
196 node = parse("<<SRC\nddddddd\nSRC\n").children[2]
197 assert_equal(:STR, node.type)
198 assert_equal(0, node.first_column)
199 assert_equal(5, node.last_column)
202 def test_parse_raises_syntax_error
203 assert_raise_with_message(SyntaxError, /\bend\b/) do
204 RubyVM::AbstractSyntaxTree.parse("end")
208 def test_parse_file_raises_syntax_error
209 Tempfile.create(%w"test_ast .rb") do |f|
212 assert_raise_with_message(SyntaxError, /\bend\b/) do
213 RubyVM::AbstractSyntaxTree.parse_file(f.path)
218 def assert_parse(code, warning: '')
219 node = assert_warning(warning) {RubyVM::AbstractSyntaxTree.parse(code)}
220 assert_kind_of(RubyVM::AbstractSyntaxTree::Node, node, code)
223 def assert_invalid_parse(msg, code)
224 assert_raise_with_message(SyntaxError, msg, code) do
225 RubyVM::AbstractSyntaxTree.parse(code)
229 def test_invalid_exit
236 ].each do |code, *args|
237 msg = /Invalid #{code[/\A\w+/]}/
238 assert_parse("while false; #{code}; end")
239 assert_parse("until true; #{code}; end")
240 assert_parse("begin #{code}; end while false")
241 assert_parse("begin #{code}; end until true")
242 assert_parse("->{#{code}}")
243 assert_parse("->{class X; #{code}; end}")
244 assert_invalid_parse(msg, "#{code}")
245 assert_invalid_parse(msg, "def m; #{code}; end")
246 assert_invalid_parse(msg, "begin; #{code}; end")
247 assert_parse("END {#{code}}")
249 assert_parse("!defined?(#{code})")
250 assert_parse("def m; defined?(#{code}); end")
251 assert_parse("!begin; defined?(#{code}); end")
253 next if code.include?(" ")
254 assert_parse("!defined? #{code}")
255 assert_parse("def m; defined? #{code}; end")
256 assert_parse("!begin; defined? #{code}; end")
260 def test_invalid_retry
261 msg = /Invalid retry/
262 assert_invalid_parse(msg, "retry")
263 assert_invalid_parse(msg, "def m; retry; end")
264 assert_invalid_parse(msg, "begin retry; end")
265 assert_parse("begin rescue; retry; end")
266 assert_invalid_parse(msg, "begin rescue; else; retry; end")
267 assert_invalid_parse(msg, "begin rescue; ensure; retry; end")
268 assert_parse("nil rescue retry")
269 assert_invalid_parse(msg, "END {retry}")
270 assert_invalid_parse(msg, "begin rescue; END {retry}; end")
272 assert_parse("!defined?(retry)")
273 assert_parse("def m; defined?(retry); end")
274 assert_parse("!begin defined?(retry); end")
275 assert_parse("begin rescue; else; defined?(retry); end")
276 assert_parse("begin rescue; ensure; p defined?(retry); end")
277 assert_parse("END {defined?(retry)}")
278 assert_parse("begin rescue; END {defined?(retry)}; end")
279 assert_parse("!defined? retry")
281 assert_parse("def m; defined? retry; end")
282 assert_parse("!begin defined? retry; end")
283 assert_parse("begin rescue; else; defined? retry; end")
284 assert_parse("begin rescue; ensure; p defined? retry; end")
285 assert_parse("END {defined? retry}")
286 assert_parse("begin rescue; END {defined? retry}; end")
288 assert_parse("#{<<-"begin;"}\n#{<<-'end;'}")
293 rescue StandardError => e
310 def test_invalid_yield
311 msg = /Invalid yield/
312 assert_invalid_parse(msg, "yield")
313 assert_invalid_parse(msg, "class C; yield; end")
314 assert_invalid_parse(msg, "BEGIN {yield}")
315 assert_invalid_parse(msg, "END {yield}")
316 assert_invalid_parse(msg, "-> {yield}")
318 assert_invalid_parse(msg, "yield true")
319 assert_invalid_parse(msg, "class C; yield true; end")
320 assert_invalid_parse(msg, "BEGIN {yield true}")
321 assert_invalid_parse(msg, "END {yield true}")
322 assert_invalid_parse(msg, "-> {yield true}")
324 assert_parse("!defined?(yield)")
325 assert_parse("class C; defined?(yield); end")
326 assert_parse("BEGIN {defined?(yield)}")
327 assert_parse("END {defined?(yield)}")
329 assert_parse("!defined?(yield true)")
330 assert_parse("class C; defined?(yield true); end")
331 assert_parse("BEGIN {defined?(yield true)}")
332 assert_parse("END {defined?(yield true)}")
334 assert_parse("!defined? yield")
335 assert_parse("class C; defined? yield; end")
336 assert_parse("BEGIN {defined? yield}")
337 assert_parse("END {defined? yield}")
340 def test_node_id_for_location
341 omit if ParserSupport.prism_enabled?
348 loc = exception.backtrace_locations.first
349 node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc)
350 node = RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
352 assert_equal node.node_id, node_id
355 def test_node_id_for_backtrace_location_raises_argument_error
356 bug19262 = '[ruby-core:111435]'
358 assert_raise(TypeError, bug19262) { RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(1) }
361 def test_of_proc_and_method
362 omit if ParserSupport.prism_enabled? || ParserSupport.prism_enabled_in_subprocess?
364 proc = Proc.new { 1 + 2 }
365 method = self.method(__method__)
367 node_proc = RubyVM::AbstractSyntaxTree.of(proc)
368 node_method = RubyVM::AbstractSyntaxTree.of(method)
370 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node_proc)
371 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node_method)
373 Tempfile.create(%w"test_of .rb") do |tmp|
374 tmp.print "#{<<-"begin;"}\n#{<<-'end;'}"
377 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(proc {|x| x}))
380 assert_separately(["-", tmp.path], "#{<<~"begin;"}\n#{<<~'end;'}")
383 assert_empty(SCRIPT_LINES__)
388 def sample_backtrace_location
389 [caller_locations(0).first, __LINE__]
392 def test_of_backtrace_location
393 omit if ParserSupport.prism_enabled?
395 backtrace_location, lineno = sample_backtrace_location
396 node = RubyVM::AbstractSyntaxTree.of(backtrace_location)
397 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node)
398 assert_equal(lineno, node.first_lineno)
402 assert_raise(TypeError) { RubyVM::AbstractSyntaxTree.of("1 + 2") }
405 def test_of_proc_and_method_under_eval
406 omit if ParserSupport.prism_enabled?
408 keep_script_lines_back = RubyVM.keep_script_lines
409 RubyVM.keep_script_lines = false
411 method = self.method(eval("def example_method_#{$$}; end"))
412 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
414 method = self.method(eval("def self.example_singleton_method_#{$$}; end"))
415 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
417 method = eval("proc{}")
418 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
420 method = self.method(eval("singleton_class.define_method(:example_define_method_#{$$}){}"))
421 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
423 method = self.method(eval("define_singleton_method(:example_dsm_#{$$}){}"))
424 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
426 method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
427 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
429 method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
430 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
433 RubyVM.keep_script_lines = keep_script_lines_back
436 def test_of_proc_and_method_under_eval_with_keep_script_lines
437 omit if ParserSupport.prism_enabled?
438 pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO
440 keep_script_lines_back = RubyVM.keep_script_lines
441 RubyVM.keep_script_lines = true
443 method = self.method(eval("def example_method_#{$$}_with_keep_script_lines; end"))
444 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
446 method = self.method(eval("def self.example_singleton_method_#{$$}_with_keep_script_lines; end"))
447 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
449 method = eval("proc{}")
450 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
452 method = self.method(eval("singleton_class.define_method(:example_define_method_#{$$}_with_keep_script_lines){}"))
453 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
455 method = self.method(eval("define_singleton_method(:example_dsm_#{$$}_with_keep_script_lines){}"))
456 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
458 method = eval("Class.new{def example_method_with_keep_script_lines; end}.instance_method(:example_method_with_keep_script_lines)")
459 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
461 method = eval("Class.new{def example_method_with_keep_script_lines; end}.instance_method(:example_method_with_keep_script_lines)")
462 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
465 RubyVM.keep_script_lines = keep_script_lines_back
468 def test_of_backtrace_location_under_eval
469 omit if ParserSupport.prism_enabled?
471 keep_script_lines_back = RubyVM.keep_script_lines
472 RubyVM.keep_script_lines = false
475 eval(<<-END, nil, __FILE__, __LINE__)
476 def self.sample_backtrace_location
477 caller_locations(0).first
481 backtrace_location = m.sample_backtrace_location
482 assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(backtrace_location) }
485 RubyVM.keep_script_lines = keep_script_lines_back
488 def test_of_backtrace_location_under_eval_with_keep_script_lines
489 omit if ParserSupport.prism_enabled?
490 pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO
492 keep_script_lines_back = RubyVM.keep_script_lines
493 RubyVM.keep_script_lines = true
496 eval(<<-END, nil, __FILE__, __LINE__)
497 def self.sample_backtrace_location
498 caller_locations(0).first
502 backtrace_location = m.sample_backtrace_location
503 node = RubyVM::AbstractSyntaxTree.of(backtrace_location)
504 assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node)
505 assert_equal(2, node.first_lineno)
508 RubyVM.keep_script_lines = keep_script_lines_back
512 c = Class.new { attr_reader :foo }
513 assert_nil(RubyVM::AbstractSyntaxTree.of(c.instance_method(:foo)))
516 def test_scope_local_variables
517 node = RubyVM::AbstractSyntaxTree.parse("_x = 0")
518 lv, _, body = *node.children
519 assert_equal([:_x], lv)
520 assert_equal(:LASGN, body.type)
524 node = RubyVM::AbstractSyntaxTree.parse("nil.foo")
525 _, _, body = *node.children
526 assert_equal(:CALL, body.type)
527 recv, mid, args = body.children
528 assert_equal(:NIL, recv.type)
529 assert_equal(:foo, mid)
534 node = RubyVM::AbstractSyntaxTree.parse("foo()")
535 _, _, body = *node.children
536 assert_equal(:FCALL, body.type)
537 mid, args = body.children
538 assert_equal(:foo, mid)
543 node = RubyVM::AbstractSyntaxTree.parse("foo")
544 _, _, body = *node.children
545 assert_equal(:VCALL, body.type)
546 mid, args = body.children
547 assert_equal(:foo, mid)
552 node = RubyVM::AbstractSyntaxTree.parse("def a; end")
553 _, _, body = *node.children
554 assert_equal(:DEFN, body.type)
555 mid, defn = body.children
556 assert_equal(:a, mid)
557 assert_equal(:SCOPE, defn.type)
558 _, args, = defn.children
559 assert_equal(:ARGS, args.type)
562 def test_defn_endless
563 node = RubyVM::AbstractSyntaxTree.parse("def a = nil")
564 _, _, body = *node.children
565 assert_equal(:DEFN, body.type)
566 mid, defn = body.children
567 assert_equal(:a, mid)
568 assert_equal(:SCOPE, defn.type)
569 _, args, = defn.children
570 assert_equal(:ARGS, args.type)
574 node = RubyVM::AbstractSyntaxTree.parse("def a.b; end")
575 _, _, body = *node.children
576 assert_equal(:DEFS, body.type)
577 recv, mid, defn = body.children
578 assert_equal(:VCALL, recv.type)
579 assert_equal(:b, mid)
580 assert_equal(:SCOPE, defn.type)
581 _, args, = defn.children
582 assert_equal(:ARGS, args.type)
585 def test_defs_endless
586 node = RubyVM::AbstractSyntaxTree.parse("def a.b = nil")
587 _, _, body = *node.children
588 assert_equal(:DEFS, body.type)
589 recv, mid, defn = body.children
590 assert_equal(:VCALL, recv.type)
591 assert_equal(:b, mid)
592 assert_equal(:SCOPE, defn.type)
593 _, args, = defn.children
594 assert_equal(:ARGS, args.type)
598 node = parse('"foo#{1}bar"')
599 _, _, body = *node.children
600 assert_equal(:DSTR, body.type)
601 head, body = body.children
602 assert_equal("foo", head)
603 assert_equal(:EVSTR, body.type)
604 body, = body.children
605 assert_equal(:INTEGER, body.type)
606 assert_equal([1], body.children)
610 node = RubyVM::AbstractSyntaxTree.parse('1 while qux')
611 _, _, body = *node.children
612 assert_equal(:WHILE, body.type)
613 type1 = body.children[2]
614 node = RubyVM::AbstractSyntaxTree.parse('begin 1 end while qux')
615 _, _, body = *node.children
616 assert_equal(:WHILE, body.type)
617 type2 = body.children[2]
618 assert_not_equal(type1, type2)
622 node = RubyVM::AbstractSyntaxTree.parse('1 until qux')
623 _, _, body = *node.children
624 assert_equal(:UNTIL, body.type)
625 type1 = body.children[2]
626 node = RubyVM::AbstractSyntaxTree.parse('begin 1 end until qux')
627 _, _, body = *node.children
628 assert_equal(:UNTIL, body.type)
629 type2 = body.children[2]
630 assert_not_equal(type1, type2)
634 rest_arg = lambda do |arg_str|
635 node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end")
636 node = node.children.last.children.last.children[1].children[-4]
639 assert_equal(nil, rest_arg.call(''))
640 assert_equal(:r, rest_arg.call('*r'))
641 assert_equal(:r, rest_arg.call('a, *r'))
642 assert_equal(:*, rest_arg.call('*'))
643 assert_equal(:*, rest_arg.call('a, *'))
647 block_arg = lambda do |arg_str|
648 node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end")
649 node = node.children.last.children.last.children[1].children[-1]
652 assert_equal(nil, block_arg.call(''))
653 assert_equal(:block, block_arg.call('&block'))
654 assert_equal(:&, block_arg.call('&'))
657 def test_keyword_rest
658 kwrest = lambda do |arg_str|
659 node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end")
660 node = node.children.last.children.last.children[1].children[-2]
661 node ? node.children : node
664 assert_equal(nil, kwrest.call(''))
665 assert_equal([:**], kwrest.call('**'))
666 assert_equal(false, kwrest.call('**nil'))
667 assert_equal([:a], kwrest.call('**a'))
670 def test_argument_forwarding
671 forwarding = lambda do |arg_str|
672 node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end")
673 node = node.children.last.children.last.children[1]
674 node ? [node.children[-4], node.children[-2]&.children, node.children[-1]] : []
677 assert_equal([:*, [:**], :&], forwarding.call('...'))
680 def test_ranges_numbered_parameter
681 helper = Helper.new(__FILE__, src: "1.times {_1}")
682 helper.validate_range
683 assert_equal([], helper.errors)
687 node = RubyVM::AbstractSyntaxTree.parse("struct.field += foo")
688 _, _, body = *node.children
689 assert_equal(:OP_ASGN2, body.type)
690 recv, _, mid, op, value = body.children
691 assert_equal(:VCALL, recv.type)
692 assert_equal(:field, mid)
694 assert_equal(:VCALL, value.type)
699 node = RubyVM::AbstractSyntaxTree.parse("proc { |a| }")
700 _, args = *node.children.last.children[1].children
701 assert_equal(nil, args.children[rest])
703 node = RubyVM::AbstractSyntaxTree.parse("proc { |a,| }")
704 _, args = *node.children.last.children[1].children
705 assert_equal(:NODE_SPECIAL_EXCESSIVE_COMMA, args.children[rest])
707 node = RubyVM::AbstractSyntaxTree.parse("proc { |*a| }")
708 _, args = *node.children.last.children[1].children
709 assert_equal(:a, args.children[rest])
713 assert_ast_eqaul(<<~STR, <<~EXP)
739 body: (RETURN@2:2-2:10 (LVAR@2:9-2:10 :a)))))
743 def test_keep_script_lines_for_parse
744 node = RubyVM::AbstractSyntaxTree.parse(<<~END, keep_script_lines: true)
760 assert_equal(expected, node.script_lines)
767 assert_equal(expected, node.source)
774 assert_equal(expected, node.children.last.children.last.source)
779 assert_equal(expected, node.children.last.children.last.children.last.source)
782 def test_keep_script_lines_for_of
783 omit if ParserSupport.prism_enabled?
785 proc = Proc.new { 1 + 2 }
786 method = self.method(__method__)
788 node_proc = RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: true)
789 node_method = RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: true)
791 assert_equal("{ 1 + 2 }", node_proc.source)
792 assert_equal("def test_keep_script_lines_for_of\n", node_method.source.lines.first)
795 def test_keep_script_lines_for_of_with_existing_SCRIPT_LINES__that_has__FILE__as_a_key
796 omit if ParserSupport.prism_enabled? || ParserSupport.prism_enabled_in_subprocess?
798 # This test confirms that the bug that previously occurred because of
799 # `AbstractSyntaxTree.of`s unnecessary dependence on SCRIPT_LINES__ does not reproduce.
800 # The bug occurred only if SCRIPT_LINES__ included __FILE__ as a key.
802 "SCRIPT_LINES__ = {__FILE__ => []}",
803 "puts RubyVM::AbstractSyntaxTree.of(->{ 1 + 2 }, keep_script_lines: true).script_lines",
806 test_stdout = lines + ['{"-e"=>[]}']
807 assert_in_out_err(["-e", lines.join("\n")], "", test_stdout, [])
810 def test_source_with_multibyte_characters
811 ast = RubyVM::AbstractSyntaxTree.parse(%{a("\u00a7");b("\u00a9")}, keep_script_lines: true)
812 a_fcall, b_fcall = ast.children[2].children
814 assert_equal(%{a("\u00a7")}, a_fcall.source)
815 assert_equal(%{b("\u00a9")}, b_fcall.source)
818 def test_keep_tokens_for_parse
819 node = RubyVM::AbstractSyntaxTree.parse(<<~END, keep_tokens: true)
829 [:tIDENTIFIER, "times"],
832 [:tIGNORED_NL, "\n"],
833 [:keyword_end, "end"],
836 assert_equal(expected, node.all_tokens.map { [_2, _3]})
839 def test_keep_tokens_unexpected_backslash
840 assert_raise_with_message(SyntaxError, /unexpected backslash/) do
841 RubyVM::AbstractSyntaxTree.parse("\\", keep_tokens: true)
845 def test_encoding_with_keep_script_lines
846 # Stop a warning "possibly useless use of a literal in void context"
847 verbose_bak, $VERBOSE = $VERBOSE, nil
849 enc = Encoding::EUC_JP
850 code = "__ENCODING__".encode(enc)
852 assert_equal(enc, eval(code))
854 node = RubyVM::AbstractSyntaxTree.parse(code, keep_script_lines: false)
855 assert_equal(enc, node.children[2].children[0])
857 node = RubyVM::AbstractSyntaxTree.parse(code, keep_script_lines: true)
858 assert_equal(enc, node.children[2].children[0])
861 $VERBOSE = verbose_bak
865 omit if ParserSupport.prism_enabled? || ParserSupport.prism_enabled_in_subprocess?
867 assert_in_out_err(["-e", "def foo; end; pp RubyVM::AbstractSyntaxTree.of(method(:foo)).type"],
871 def test_error_tolerant
872 verbose_bak, $VERBOSE = $VERBOSE, false
873 node = RubyVM::AbstractSyntaxTree.parse(<<~STR, error_tolerant: true)
883 assert_equal(:SCOPE, node.type)
885 $VERBOSE = verbose_bak
888 def test_error_tolerant_end_is_short_for_method_define
889 assert_error_tolerant(<<~STR, <<~EXP)
914 body: (VCALL@2:2-2:4 :m2))))
918 def test_error_tolerant_end_is_short_for_singleton_method_define
919 assert_error_tolerant(<<~STR, <<~EXP)
927 (DEFS@1:0-2:4 (VCALL@1:4-1:7 :obj) :m
942 body: (VCALL@2:2-2:4 :m2))))
946 def test_error_tolerant_end_is_short_for_begin
947 assert_error_tolerant(<<~STR, <<~EXP)
951 (SCOPE@1:0-2:7 tbl: [:a] args: nil body: (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1)))
955 def test_error_tolerant_end_is_short_for_if
956 assert_error_tolerant(<<~STR, <<~EXP)
964 (IF@1:0-2:7 (VCALL@1:3-1:7 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
968 assert_error_tolerant(<<~STR, <<~EXP)
977 (IF@1:0-3:4 (VCALL@1:3-1:7 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
978 (BEGIN@3:4-3:4 nil)))
982 def test_error_tolerant_end_is_short_for_unless
983 assert_error_tolerant(<<~STR, <<~EXP)
991 (UNLESS@1:0-2:7 (VCALL@1:7-1:11 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
995 assert_error_tolerant(<<~STR, <<~EXP)
1004 (UNLESS@1:0-3:4 (VCALL@1:7-1:11 :cond) (LASGN@2:2-2:7 :a (INTEGER@2:6-2:7 1))
1005 (BEGIN@3:4-3:4 nil)))
1009 def test_error_tolerant_end_is_short_for_while
1010 assert_error_tolerant(<<~STR, <<~EXP)
1017 body: (WHILE@1:0-2:3 (TRUE@1:6-1:10) (VCALL@2:2-2:3 :m) true))
1021 def test_error_tolerant_end_is_short_for_until
1022 assert_error_tolerant(<<~STR, <<~EXP)
1029 body: (UNTIL@1:0-2:3 (TRUE@1:6-1:10) (VCALL@2:2-2:3 :m) true))
1033 def test_error_tolerant_end_is_short_for_case
1034 assert_error_tolerant(<<~STR, <<~EXP)
1042 (CASE@1:0-2:6 (VCALL@1:5-1:6 :a)
1043 (WHEN@2:0-2:6 (LIST@2:5-2:6 (INTEGER@2:5-2:6 1) nil) (BEGIN@2:6-2:6 nil)
1048 assert_error_tolerant(<<~STR, <<~EXP)
1059 (OPCALL@2:5-2:11 (VCALL@2:5-2:6 :a) :==
1060 (LIST@2:10-2:11 (INTEGER@2:10-2:11 1) nil)) nil)
1061 (BEGIN@2:11-2:11 nil) nil)))
1065 assert_error_tolerant(<<~STR, <<~EXP)
1073 (CASE3@1:0-2:14 (VCALL@1:5-1:6 :a)
1079 (LIST@2:4-2:13 (SYM@2:4-2:6 :a) (CONST@2:7-2:13 :String) nil))
1080 kwrest: nil) (BEGIN@2:14-2:14 nil) nil)))
1084 def test_error_tolerant_end_is_short_for_for
1085 assert_error_tolerant(<<~STR, <<~EXP)
1093 (FOR@1:0-2:3 (VCALL@1:9-1:12 :ary)
1099 pre_init: (LASGN@1:4-1:5 :i (DVAR@1:4-1:5 nil))
1108 body: (VCALL@2:2-2:3 :m))))
1112 def test_error_tolerant_end_is_short_for_class
1113 assert_error_tolerant(<<~STR, <<~EXP)
1120 (CLASS@1:0-1:7 (COLON2@1:6-1:7 nil :C) nil
1121 (SCOPE@1:0-1:7 tbl: [] args: nil body: (BEGIN@1:7-1:7 nil))))
1125 def test_error_tolerant_end_is_short_for_module
1126 assert_error_tolerant(<<~STR, <<~EXP)
1133 (MODULE@1:0-1:8 (COLON2@1:7-1:8 nil :M)
1134 (SCOPE@1:0-1:8 tbl: [] args: nil body: (BEGIN@1:8-1:8 nil))))
1138 def test_error_tolerant_end_is_short_for_do
1139 assert_error_tolerant(<<~STR, <<~EXP)
1147 (ITER@1:0-2:3 (FCALL@1:0-1:1 :m nil)
1148 (SCOPE@1:2-2:3 tbl: [] args: nil body: (VCALL@2:2-2:3 :a))))
1152 def test_error_tolerant_end_is_short_for_do_block
1153 assert_error_tolerant(<<~STR, <<~EXP)
1161 (ITER@1:0-2:3 (FCALL@1:0-1:3 :m (LIST@1:2-1:3 (INTEGER@1:2-1:3 1) nil))
1162 (SCOPE@1:4-2:3 tbl: [] args: nil body: (VCALL@2:2-2:3 :a))))
1166 def test_error_tolerant_end_is_short_for_do_LAMBDA
1167 assert_error_tolerant(<<~STR, <<~EXP)
1190 body: (VCALL@2:2-2:3 :a))))
1194 def test_error_tolerant_treat_end_as_keyword_based_on_indent
1195 assert_error_tolerant(<<~STR, <<~EXP)
1209 (MODULE@1:0-8:3 (COLON2@1:7-1:8 nil :Z)
1214 (BLOCK@1:8-7:5 (BEGIN@1:8-1:8 nil)
1215 (CLASS@2:2-4:5 (COLON2@2:8-2:11 nil :Foo) nil
1219 body: (BLOCK@2:11-4:5 (BEGIN@2:11-2:11 nil) (ERROR@3:4-4:5))))
1241 def test_error_tolerant_expr_value_can_be_error
1242 assert_error_tolerant(<<~STR, <<~EXP)
1268 body: (IF@2:2-3:3 (ERROR@3:0-3:3) nil nil))))
1272 def test_error_tolerant_unexpected_backslash
1273 node = assert_error_tolerant("\\", <<~EXP, keep_tokens: true)
1274 (SCOPE@1:0-1:1 tbl: [] args: nil body: (ERROR@1:0-1:1))
1276 assert_equal([[0, :backslash, "\\", [1, 0, 1, 1]]], node.children.last.tokens)
1280 assert_error_tolerant("\u{feff}nil", <<~EXP)
1281 (SCOPE@1:0-1:3 tbl: [] args: nil body: (NIL@1:0-1:3))
1285 def test_unused_block_local_variable
1286 assert_warning('') do
1287 RubyVM::AbstractSyntaxTree.parse(%{->(; foo) {}})
1291 def test_memory_leak
1292 assert_no_memory_leak([], "#{<<~"begin;"}", "\n#{<<~'end;'}", rss: true)
1302 verbose_bak, $VERBOSE = $VERBOSE, false
1303 node = RubyVM::AbstractSyntaxTree.parse("1 + 2")
1305 $VERBOSE = verbose_bak
1307 locations = node.locations
1309 assert_equal(RubyVM::AbstractSyntaxTree::Location, locations[0].class)
1314 def assert_error_tolerant(src, expected, keep_tokens: false)
1315 assert_ast_eqaul(src, expected, error_tolerant: true, keep_tokens: keep_tokens)
1318 def assert_ast_eqaul(src, expected, **options)
1320 verbose_bak, $VERBOSE = $VERBOSE, false
1321 node = RubyVM::AbstractSyntaxTree.parse(src, **options)
1323 $VERBOSE = verbose_bak
1327 PP.pp(node, str, 80)
1328 assert_equal(expected, str)
1332 class TestLocation < Test::Unit::TestCase
1333 def test_lineno_and_column
1334 node = ast_parse("1 + 2")
1335 assert_locations(node.locations, [[1, 0, 1, 5]])
1338 def test_alias_locations
1339 node = ast_parse("alias foo bar")
1340 assert_locations(node.children[-1].locations, [[1, 0, 1, 13], [1, 0, 1, 5]])
1343 def test_and_locations
1344 node = ast_parse("1 and 2")
1345 assert_locations(node.children[-1].locations, [[1, 0, 1, 7], [1, 2, 1, 5]])
1347 node = ast_parse("1 && 2")
1348 assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 2, 1, 4]])
1351 def test_block_pass_locations
1352 node = ast_parse("foo(&bar)")
1353 assert_locations(node.children[-1].children[-1].locations, [[1, 4, 1, 8], [1, 4, 1, 5]])
1355 node = ast_parse("def a(&); b(&) end")
1356 assert_locations(node.children[-1].children[-1].children[-1].children[-1].children[-1].locations, [[1, 12, 1, 13], [1, 12, 1, 13]])
1359 def test_break_locations
1360 node = ast_parse("loop { break 1 }")
1361 assert_locations(node.children[-1].children[-1].children[-1].locations, [[1, 7, 1, 14], [1, 7, 1, 12]])
1364 def test_case_locations
1365 node = ast_parse("case a; when 1; end")
1366 assert_locations(node.children[-1].locations, [[1, 0, 1, 19], [1, 0, 1, 4], [1, 16, 1, 19]])
1369 def test_case2_locations
1370 node = ast_parse("case; when 1; end")
1371 assert_locations(node.children[-1].locations, [[1, 0, 1, 17], [1, 0, 1, 4], [1, 14, 1, 17]])
1374 def test_case3_locations
1375 node = ast_parse("case a; in 1; end")
1376 assert_locations(node.children[-1].locations, [[1, 0, 1, 17], [1, 0, 1, 4], [1, 14, 1, 17]])
1379 def test_next_locations
1380 node = ast_parse("loop { next 1 }")
1381 assert_locations(node.children[-1].children[-1].children[-1].locations, [[1, 7, 1, 13], [1, 7, 1, 11]])
1384 def test_op_asgn1_locations
1385 node = ast_parse("ary[1] += foo")
1386 assert_locations(node.children[-1].locations, [[1, 0, 1, 13], nil, [1, 3, 1, 4], [1, 5, 1, 6], [1, 7, 1, 9]])
1388 node = ast_parse("ary[1, 2] += foo")
1389 assert_locations(node.children[-1].locations, [[1, 0, 1, 16], nil, [1, 3, 1, 4], [1, 8, 1, 9], [1, 10, 1, 12]])
1392 def test_or_locations
1393 node = ast_parse("1 or 2")
1394 assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 2, 1, 4]])
1396 node = ast_parse("1 || 2")
1397 assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 2, 1, 4]])
1400 def test_redo_locations
1401 node = ast_parse("loop { redo }")
1402 assert_locations(node.children[-1].children[-1].children[-1].locations, [[1, 7, 1, 11], [1, 7, 1, 11]])
1405 def test_return_locations
1406 node = ast_parse("return 1")
1407 assert_locations(node.children[-1].locations, [[1, 0, 1, 8], [1, 0, 1, 6]])
1409 node = ast_parse("return")
1410 assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 0, 1, 6]])
1413 def test_unless_locations
1414 node = ast_parse("unless cond then 1 else 2 end")
1415 assert_locations(node.children[-1].locations, [[1, 0, 1, 29], [1, 0, 1, 6], [1, 12, 1, 16], [1, 26, 1, 29]])
1417 node = ast_parse("1 unless 2")
1418 assert_locations(node.children[-1].locations, [[1, 0, 1, 10], [1, 2, 1, 8], nil, nil])
1421 def test_undef_locations
1422 node = ast_parse("undef foo")
1423 assert_locations(node.children[-1].locations, [[1, 0, 1, 9], [1, 0, 1, 5]])
1425 node = ast_parse("undef foo, bar")
1426 assert_locations(node.children[-1].locations, [[1, 0, 1, 14], [1, 0, 1, 5]])
1429 def test_valias_locations
1430 node = ast_parse("alias $foo $bar")
1431 assert_locations(node.children[-1].locations, [[1, 0, 1, 15], [1, 0, 1, 5]])
1433 node = ast_parse("alias $foo $&")
1434 assert_locations(node.children[-1].locations, [[1, 0, 1, 13], [1, 0, 1, 5]])
1437 def test_when_locations
1438 node = ast_parse("case a; when 1 then 2; end")
1439 assert_locations(node.children[-1].children[1].locations, [[1, 8, 1, 22], [1, 8, 1, 12], [1, 15, 1, 19]])
1442 def test_while_locations
1443 node = ast_parse("while cond do 1 end")
1444 assert_locations(node.children[-1].locations, [[1, 0, 1, 19], [1, 0, 1, 5], [1, 16, 1, 19]])
1446 node = ast_parse("1 while 2")
1447 assert_locations(node.children[-1].locations, [[1, 0, 1, 9], [1, 2, 1, 7], nil])
1450 def test_until_locations
1451 node = ast_parse("until cond do 1 end")
1452 assert_locations(node.children[-1].locations, [[1, 0, 1, 19], [1, 0, 1, 5], [1, 16, 1, 19]])
1454 node = ast_parse("1 until 2")
1455 assert_locations(node.children[-1].locations, [[1, 0, 1, 9], [1, 2, 1, 7], nil])
1459 def ast_parse(src, **options)
1461 verbose_bak, $VERBOSE = $VERBOSE, false
1462 RubyVM::AbstractSyntaxTree.parse(src, **options)
1464 $VERBOSE = verbose_bak
1468 def assert_locations(locations, expected)
1469 ary = locations.map {|loc| loc && [loc.first_lineno, loc.first_column, loc.last_lineno, loc.last_column] }
1471 assert_equal(ary, expected)