diff options
Diffstat (limited to 'test/ripper')
| -rw-r--r-- | test/ripper/assert_parse_files.rb | 1 | ||||
| -rw-r--r-- | test/ripper/test_lexer.rb | 329 | ||||
| -rw-r--r-- | test/ripper/test_parser_events.rb | 120 | ||||
| -rw-r--r-- | test/ripper/test_ripper.rb | 37 | ||||
| -rw-r--r-- | test/ripper/test_scanner_events.rb | 6 | ||||
| -rw-r--r-- | test/ripper/test_sexp.rb | 15 |
6 files changed, 490 insertions, 18 deletions
diff --git a/test/ripper/assert_parse_files.rb b/test/ripper/assert_parse_files.rb index 0d583a99e3..4f08589e41 100644 --- a/test/ripper/assert_parse_files.rb +++ b/test/ripper/assert_parse_files.rb @@ -40,6 +40,7 @@ class TestRipper::Generic < Test::Unit::TestCase end } end + assert(true) if scripts.empty? end; end end diff --git a/test/ripper/test_lexer.rb b/test/ripper/test_lexer.rb index 7d62a7ee28..4bc6fd7ced 100644 --- a/test/ripper/test_lexer.rb +++ b/test/ripper/test_lexer.rb @@ -253,18 +253,31 @@ world" assert_equal(code, Ripper.tokenize(code).join(""), bug) end + InvalidHeredocInsideBlockParam = <<~CODE + a do |b + <<-C + C + | + end + CODE + def test_heredoc_inside_block_param bug = '[Bug #19399]' - code = <<~CODE - a do |b - <<-C - C - | - end - CODE + code = InvalidHeredocInsideBlockParam assert_equal(code, Ripper.tokenize(code).join(""), bug) end + def test_heredoc_no_memory_leak + assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}", rss: true) + require "ripper" + source = "" #{InvalidHeredocInsideBlockParam.dump} + begin; + 400_000.times do + Ripper.new(source).parse + end + end; + end + def test_heredoc_unterminated_interpolation code = <<~'HEREDOC' <<A+1 @@ -302,9 +315,8 @@ world" [[6, 2], :on_tstring_content, "3\n", state(:EXPR_BEG)], [[7, 0], :on_heredoc_end, "H1\n", state(:EXPR_BEG)], ] - assert_equal(code, Ripper.tokenize(code).join("")) - assert_equal(expected, result = Ripper.lex(code), - proc {expected.zip(result) {|e, r| break diff(e, r) unless e == r}}) + + assert_lexer(expected, code) code = <<~'HEREDOC' <<-H1 @@ -330,6 +342,303 @@ world" [[6, 0], :on_tstring_content, " 3\n", state(:EXPR_BEG)], [[7, 0], :on_heredoc_end, "H1\n", state(:EXPR_BEG)], ] + + assert_lexer(expected, code) + + code = <<~'HEREDOC' + <<H1 + #{<<H2}a + H2 + b + HEREDOC + + expected = [ + [[1, 0], :on_heredoc_beg, "<<H1", state(:EXPR_BEG)], + [[1, 4], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_embexpr_beg, "\#{", state(:EXPR_BEG)], + [[2, 2], :on_heredoc_beg, "<<H2", state(:EXPR_BEG)], + [[2, 6], :on_embexpr_end, "}", state(:EXPR_END)], + [[2, 7], :on_tstring_content, "a\n", state(:EXPR_BEG)], + [[3, 0], :on_heredoc_end, "H2\n", state(:EXPR_BEG)], + [[4, 0], :on_tstring_content, "b\n", state(:EXPR_BEG)] + ] + + assert_lexer(expected, code) + + code = <<~'HEREDOC' + <<H1 + #{<<H2}a + H2 + b + c + HEREDOC + + expected = [ + [[1, 0], :on_heredoc_beg, "<<H1", state(:EXPR_BEG)], + [[1, 4], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_embexpr_beg, "\#{", state(:EXPR_BEG)], + [[2, 2], :on_heredoc_beg, "<<H2", state(:EXPR_BEG)], + [[2, 6], :on_embexpr_end, "}", state(:EXPR_END)], + [[2, 7], :on_tstring_content, "a\n", state(:EXPR_BEG)], + [[3, 0], :on_heredoc_end, "H2\n", state(:EXPR_BEG)], + [[4, 0], :on_tstring_content, "b\nc\n", state(:EXPR_BEG)] + ] + + assert_lexer(expected, code) + end + + def test_invalid_escape_ctrl_mbchar + code = %["\\C-\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\C-\u{3042}", state(:EXPR_BEG)], + [[1, 7], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + + code = %["\\C-\\\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\C-\\\u{3042}", state(:EXPR_BEG)], + [[1, 8], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + end + + def test_invalid_escape_meta_mbchar + code = %["\\M-\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\M-\u{3042}", state(:EXPR_BEG)], + [[1, 7], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + + code = %["\\M-\\\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\M-\\\u{3042}", state(:EXPR_BEG)], + [[1, 8], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + end + + def test_invalid_escape_meta_ctrl_mbchar + code = %["\\M-\\C-\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\M-\\C-\u{3042}", state(:EXPR_BEG)], + [[1, 10], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + + code = %["\\M-\\C-\\\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\M-\\C-\\\u{3042}", state(:EXPR_BEG)], + [[1, 11], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + end + + def test_invalid_escape_ctrl_meta_mbchar + code = %["\\C-\\M-\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\C-\\M-\u{3042}", state(:EXPR_BEG)], + [[1, 10], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + + code = %["\\C-\\M-\\\u{3042}"] + expected = [ + [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\\C-\\M-\\\u{3042}", state(:EXPR_BEG)], + [[1, 11], :on_tstring_end, '"', state(:EXPR_END)], + ] + + assert_lexer(expected, code) + end + + def test_invalid_escape_string + code = "\"hello\\x world" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "hello", state(:EXPR_BEG)], + [[1, 5], :on_tstring_content, "\\x", state(:EXPR_BEG)], + [[1, 7], :on_tstring_content, " world", state(:EXPR_BEG)], + ] + + code = "\"\nhello\\x world" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\n" "hello", state(:EXPR_BEG)], + [[2, 5], :on_tstring_content, "\\x", state(:EXPR_BEG)], + [[2, 7], :on_tstring_content, " world", state(:EXPR_BEG)], + ] + assert_lexer(expected, code) + + code = "\"\n\\Cxx\"" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\Cx", state(:EXPR_BEG)], + [[2, 3], :on_tstring_content, "x", state(:EXPR_BEG)], + [[2, 4], :on_tstring_end, "\"", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = "\"\n\\Mxx\"" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\Mx", state(:EXPR_BEG)], + [[2, 3], :on_tstring_content, "x", state(:EXPR_BEG)], + [[2, 4], :on_tstring_end, "\"", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = "\"\n\\c\\cx\"" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\c\\c", state(:EXPR_BEG)], + [[2, 4], :on_tstring_content, "x", state(:EXPR_BEG)], + [[2, 5], :on_tstring_end, "\"", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = "\"\n\\ux\"" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\u", state(:EXPR_BEG)], + [[2, 2], :on_tstring_content, "x", state(:EXPR_BEG)], + [[2, 3], :on_tstring_end, "\"", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = "\"\n\\xx\"" + expected = [ + [[1, 0], :on_tstring_beg, "\"", state(:EXPR_BEG)], + [[1, 1], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\x", state(:EXPR_BEG)], + [[2, 2], :on_tstring_content, "x", state(:EXPR_BEG)], + [[2, 3], :on_tstring_end, "\"", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = "<<A\n\n\\xyz" + expected = [ + [[1, 0], :on_heredoc_beg, "<<A", state(:EXPR_BEG)], + [[1, 3], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[3, 0], :on_tstring_content, "\\x", state(:EXPR_BEG)], + [[3, 2], :on_tstring_content, "yz", state(:EXPR_BEG)], + ] + assert_lexer(expected, code) + + code = "%(\n\\xyz)" + expected = [ + [[1, 0], :on_tstring_beg, "%(", state(:EXPR_BEG)], + [[1, 2], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\x", state(:EXPR_BEG)], + [[2, 2], :on_tstring_content, "yz", state(:EXPR_BEG)], + [[2, 4], :on_tstring_end, ")", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = "%Q(\n\\xyz)" + expected = [ + [[1, 0], :on_tstring_beg, "%Q(", state(:EXPR_BEG)], + [[1, 3], :on_tstring_content, "\n", state(:EXPR_BEG)], + [[2, 0], :on_tstring_content, "\\x", state(:EXPR_BEG)], + [[2, 2], :on_tstring_content, "yz", state(:EXPR_BEG)], + [[2, 4], :on_tstring_end, ")", state(:EXPR_END)], + ] + assert_lexer(expected, code) + + code = ":\"\n\\xyz\"" + expected = [ + [[1, 0], :on_symbeg, ":\"", state(:EXPR_FNAME)], + [[1, 2], :on_tstring_content, "\n", state(:EXPR_FNAME)], + [[2, 0], :on_tstring_content, "\\x", state(:EXPR_FNAME)], + [[2, 2], :on_tstring_content, "yz", state(:EXPR_FNAME)], + [[2, 4], :on_tstring_end, "\"", state(:EXPR_END)], + ] + assert_lexer(expected, code) + end + + def test_spaces_at_eof + code = "1\n\t \t" + expected = [ + [[1, 0], :on_int, "1", state(:EXPR_END)], + [[1, 1], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_sp, "\t \t", state(:EXPR_END)], + ] + assert_lexer(expected, code) + end + + def test_fluent_and + code = "foo\n" "and" + expected = [ + [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)], + [[1, 3], :on_ignored_nl, "\n", state(:EXPR_CMDARG)], + [[2, 0], :on_kw, "and", state(:EXPR_BEG)], + ] + assert_lexer(expected, code) + + code = "foo\n" "and?" + expected = [ + [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)], + [[1, 3], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_ident, "and?", state(:EXPR_CMDARG)], + ] + assert_lexer(expected, code) + + code = "foo\n" "and!" + expected = [ + [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)], + [[1, 3], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_ident, "and!", state(:EXPR_CMDARG)], + ] + assert_lexer(expected, code) + end + + def test_fluent_or + code = "foo\n" "or" + expected = [ + [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)], + [[1, 3], :on_ignored_nl, "\n", state(:EXPR_CMDARG)], + [[2, 0], :on_kw, "or", state(:EXPR_BEG)], + ] + assert_lexer(expected, code) + + code = "foo\n" "or?" + expected = [ + [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)], + [[1, 3], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_ident, "or?", state(:EXPR_CMDARG)], + ] + assert_lexer(expected, code) + + code = "foo\n" "or!" + expected = [ + [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)], + [[1, 3], :on_nl, "\n", state(:EXPR_BEG)], + [[2, 0], :on_ident, "or!", state(:EXPR_CMDARG)], + ] + assert_lexer(expected, code) + end + + def assert_lexer(expected, code) assert_equal(code, Ripper.tokenize(code).join("")) assert_equal(expected, result = Ripper.lex(code), proc {expected.zip(result) {|e, r| break diff(e, r) unless e == r}}) diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index 9283a4d343..3e72c7a331 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -267,17 +267,29 @@ class TestRipper::ParserEvents < Test::Unit::TestCase end def test_assign_error_backref - thru_assign_error = false + errors = [] result = - parse('$` = 1', :on_assign_error) {thru_assign_error = true} - assert_equal true, thru_assign_error - assert_equal '[assign(assign_error(var_field($`)),1)]', result + parse('$& = 1', %i[on_assign_error compile_error]) {|e, *| errors << e} + assert_equal %i[on_assign_error], errors + assert_equal '[assign(assign_error(var_field($&)),1)]', result - thru_assign_error = false + errors = [] result = - parse('$`, _ = 1', :on_assign_error) {thru_assign_error = true} - assert_equal true, thru_assign_error - assert_equal '[massign([assign_error(var_field($`)),var_field(_)],1)]', result + parse('$&, _ = 1', %i[on_assign_error compile_error]) {|e, *| errors << e} + assert_equal %i[on_assign_error], errors + assert_equal '[massign([assign_error(var_field($&)),var_field(_)],1)]', result + + errors = [] + result = + parse('$& += 1', %i[on_assign_error compile_error]) {|e, *| errors << e} + assert_equal %i[on_assign_error], errors + assert_equal '[assign_error(opassign(var_field($&),+=,1))]', result + + errors = [] + result = + parse('$& += cmd 1, 2', %i[on_assign_error compile_error]) {|e, *| errors << e} + assert_equal %i[on_assign_error], errors + assert_equal '[assign_error(opassign(var_field($&),+=,command(cmd,[1,2])))]', result end def test_assign_error_const_qualified @@ -470,6 +482,13 @@ class TestRipper::ParserEvents < Test::Unit::TestCase thru_call = false assert_nothing_raised { + tree = parse("a b do end.()", :on_call) {thru_call = true} + } + assert_equal true, thru_call + assert_equal "[call(command(a,[vcall(b)],&do_block(,bodystmt([void()]))),.,call,[])]", tree + + thru_call = false + assert_nothing_raised { tree = parse("self::foo", :on_call) {thru_call = true} } assert_equal true, thru_call @@ -1672,6 +1691,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase assert_equal(%w"frozen_string_literal nottrue", args) end + def test_warning_duplicated_when_clause + fmt, *args = warning(<<~STR) + a = 1 + case a + when 1 + when 1 + when 2 + else + end + STR + assert_match(/duplicates 'when' clause/, fmt) + assert_equal([4, 3], args) + end + + def test_warn_duplicated_hash_keys + fmt, *args = warn("{ a: 1, a: 2 }") + assert_match(/is duplicated and overwritten on line/, fmt) + assert_equal([:a, 1], args) + end + def test_warn_cr_in_middle fmt = nil assert_warn("") {fmt, = warn("\r;")} @@ -1711,4 +1750,69 @@ class TestRipper::ParserEvents < Test::Unit::TestCase parse('case 0; in {a:}; end', :on_hshptn) {thru_hshptn = true} assert_equal true, thru_hshptn end + + def test_return_out_of_compile_error_no_memory_leak + assert_no_memory_leak(%w(-rripper), "#{<<~'begin;'}", "#{<<~'end;'}", rss: true) + class MyRipper < Ripper + def initialize(src, &blk) + super(src) + @blk = blk + end + + def compile_error(msg) = @blk.call(msg) + end + + def call_parse = MyRipper.new("/") { |msg| return msg }.parse + + # Check that call_parse does return a syntax error + raise "call_parse should return a syntax error" unless call_parse + begin; + 100_000.times do + call_parse + end + end; + end + + def test_return_out_of_warn_no_memory_leak + assert_no_memory_leak(%w(-rripper), "#{<<~'begin;'}", "#{<<~'end;'}", rss: true) + class MyRipper < Ripper + def initialize(src, &blk) + super(src) + @blk = blk + end + + def warn(msg, *args) = @blk.call(msg) + end + + def call_parse = MyRipper.new("{ a: 1, a: 2 }") { |msg| return msg }.parse + + # Check that call_parse does warn + raise "call_parse should warn" unless call_parse + begin; + 500_000.times do + call_parse + end + end; + + assert_no_memory_leak(%w(-rripper), "#{<<~'begin;'}", "#{<<~'end;'}", rss: true) + class MyRipper < Ripper + def initialize(src, &blk) + super(src) + @blk = blk + end + + def warn(msg, *args) = @blk.call(msg) + end + + $VERBOSE = true + def call_parse = MyRipper.new("if true\n end\n") { |msg| return msg }.parse + + # Check that call_parse does warn + raise "call_parse should warn" unless call_parse + begin; + 1_000_000.times do + call_parse + end + end; + end end if ripper_test diff --git a/test/ripper/test_ripper.rb b/test/ripper/test_ripper.rb index 6061496d9c..2b3421d827 100644 --- a/test/ripper/test_ripper.rb +++ b/test/ripper/test_ripper.rb @@ -148,6 +148,25 @@ end assert_nothing_raised { Ripper.lex src } end + def test_assignable_in_regexp + assert_separately(%w(-rripper), "", "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + assert_nil(Ripper.parse('/(?<_1>)/ =~ s')) + end; + end + + def test_invalid_multibyte_character_in_regexp + lex = Ripper::Lexer.new(%q[/#{"\xcd"}/]).scan.map(&:to_a) + assert_equal([[1, 0], :on_regexp_beg, "/", state(:EXPR_BEG)], lex.shift) + assert_equal([[1, 1], :on_embexpr_beg, "\#{", state(:EXPR_BEG)], lex.shift) + assert_equal([[1, 3], :on_tstring_beg, "\"", state(:EXPR_BEG)], lex.shift) + assert_equal([[1, 4], :on_tstring_content, "\\xcd", state(:EXPR_BEG)], lex.shift) + assert_equal([[1, 8], :on_tstring_end, "\"", state(:EXPR_END)], lex.shift) + assert_equal([[1, 9], :on_embexpr_end, "}", state(:EXPR_END)], lex.shift) + assert_equal([[1, 10], :on_regexp_end, "/", state(:EXPR_BEG)], lex.shift) + assert_empty(lex) + end + def test_no_memory_leak assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true) 2_000_000.times do @@ -168,6 +187,21 @@ end Ripper.parse("-> {") end end; + + # [Bug #21004] + assert_no_memory_leak(%w(-rripper), "", <<~RUBY, rss: true) + 1_000_000.times do + Ripper.parse("-> do it end") + end + RUBY + end + + def test_sexp_no_memory_leak + assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true) + 1_000_000.times do + Ripper.sexp("") + end + end; end class TestInput < self @@ -187,4 +221,7 @@ end end end + def state(name) + Ripper::Lexer::State.new(Ripper.const_get(name)) + end end if ripper_test diff --git a/test/ripper/test_scanner_events.rb b/test/ripper/test_scanner_events.rb index 792f19ef1a..ed291c4384 100644 --- a/test/ripper/test_scanner_events.rb +++ b/test/ripper/test_scanner_events.rb @@ -53,6 +53,8 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase Ripper.tokenize("1 .foo\n") assert_equal ["1", "\n", " ", ".", "foo", "\n"], Ripper.tokenize("1\n .foo\n") + assert_equal ["def", " ", "f", ";", " ", "(", "x", ")", "::", "A", " ", "="], + Ripper.tokenize("def f; (x)::A =") end def test_lex @@ -950,6 +952,10 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase scan('CHAR', "?\\u{41}") err = nil + assert_equal [], scan('CHAR', '?\\') {|*e| err = e} + assert_equal([:on_parse_error, "Invalid escape character syntax", "?\\"], err) + + err = nil assert_equal [], scan('CHAR', '?\\M ') {|*e| err = e} assert_equal([:on_parse_error, "Invalid escape character syntax", "?\\M "], err) diff --git a/test/ripper/test_sexp.rb b/test/ripper/test_sexp.rb index 32285be9ab..3ebcf3062e 100644 --- a/test/ripper/test_sexp.rb +++ b/test/ripper/test_sexp.rb @@ -123,6 +123,21 @@ eot assert_equal(exp, named) end + def test_command + sexp = Ripper.sexp("a::C {}") + assert_equal( + [:program, + [ + [:method_add_block, + [:command_call, + [:vcall, [:@ident, "a", [1, 0]]], + [:@op, "::", [1, 1]], + [:@const, "C", [1, 3]], + nil], + [:brace_block, nil, [[:void_stmt]]]]]], + sexp) + end + def search_sexp(sym, sexp) return sexp if !sexp or sexp[0] == sym sexp.find do |e| |
