summaryrefslogtreecommitdiff
path: root/test/prism/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'test/prism/ruby')
-rw-r--r--test/prism/ruby/dispatcher_test.rb19
-rw-r--r--test/prism/ruby/location_test.rb9
-rw-r--r--test/prism/ruby/parameters_signature_test.rb7
-rw-r--r--test/prism/ruby/parser_test.rb225
-rw-r--r--test/prism/ruby/ripper_test.rb148
-rw-r--r--test/prism/ruby/ruby_parser_test.rb53
-rw-r--r--test/prism/ruby/source_test.rb47
7 files changed, 358 insertions, 150 deletions
diff --git a/test/prism/ruby/dispatcher_test.rb b/test/prism/ruby/dispatcher_test.rb
index 1b6d7f4117..83eb29e1f3 100644
--- a/test/prism/ruby/dispatcher_test.rb
+++ b/test/prism/ruby/dispatcher_test.rb
@@ -25,9 +25,12 @@ module Prism
end
def test_dispatching_events
- listener = TestListener.new
+ listener_manual = TestListener.new
+ listener_public = TestListener.new
+
dispatcher = Dispatcher.new
- dispatcher.register(listener, :on_call_node_enter, :on_call_node_leave, :on_integer_node_enter)
+ dispatcher.register(listener_manual, :on_call_node_enter, :on_call_node_leave, :on_integer_node_enter)
+ dispatcher.register_public_methods(listener_public)
root = Prism.parse(<<~RUBY).value
def foo
@@ -36,11 +39,17 @@ module Prism
RUBY
dispatcher.dispatch(root)
- assert_equal([:on_call_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_call_node_leave], listener.events_received)
- listener.events_received.clear
+ [listener_manual, listener_public].each do |listener|
+ assert_equal([:on_call_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_call_node_leave], listener.events_received)
+ listener.events_received.clear
+ end
+
dispatcher.dispatch_once(root.statements.body.first.body.body.first)
- assert_equal([:on_call_node_enter, :on_call_node_leave], listener.events_received)
+
+ [listener_manual, listener_public].each do |listener|
+ assert_equal([:on_call_node_enter, :on_call_node_leave], listener.events_received)
+ end
end
end
end
diff --git a/test/prism/ruby/location_test.rb b/test/prism/ruby/location_test.rb
index 33f844243c..5e2ab63802 100644
--- a/test/prism/ruby/location_test.rb
+++ b/test/prism/ruby/location_test.rb
@@ -13,19 +13,22 @@ module Prism
assert_equal 0, joined.start_offset
assert_equal 10, joined.length
- assert_raise(RuntimeError, "Incompatible locations") do
+ e = assert_raise(RuntimeError) do
argument.location.join(receiver.location)
end
+ assert_equal "Incompatible locations", e.message
other_argument = Prism.parse_statement("1234 + 567").arguments.arguments.first
- assert_raise(RuntimeError, "Incompatible sources") do
+ e = assert_raise(RuntimeError) do
other_argument.location.join(receiver.location)
end
+ assert_equal "Incompatible sources", e.message
- assert_raise(RuntimeError, "Incompatible sources") do
+ e = assert_raise(RuntimeError) do
receiver.location.join(other_argument.location)
end
+ assert_equal "Incompatible sources", e.message
end
def test_character_offsets
diff --git a/test/prism/ruby/parameters_signature_test.rb b/test/prism/ruby/parameters_signature_test.rb
index 9256bcc070..ea1eea106b 100644
--- a/test/prism/ruby/parameters_signature_test.rb
+++ b/test/prism/ruby/parameters_signature_test.rb
@@ -54,9 +54,10 @@ module Prism
assert_parameters([[:keyrest, :**]], "**")
end
- def test_key_ordering
- omit("TruffleRuby returns keys in order they were declared") if RUBY_ENGINE == "truffleruby"
- assert_parameters([[:keyreq, :a], [:keyreq, :b], [:key, :c], [:key, :d]], "a:, c: 1, b:, d: 2")
+ if RUBY_ENGINE == "ruby"
+ def test_key_ordering
+ assert_parameters([[:keyreq, :a], [:keyreq, :b], [:key, :c], [:key, :d]], "a:, c: 1, b:, d: 2")
+ end
end
def test_block
diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb
index 606a0e54f6..55c12cab6f 100644
--- a/test/prism/ruby/parser_test.rb
+++ b/test/prism/ruby/parser_test.rb
@@ -5,7 +5,6 @@ require_relative "../test_helper"
begin
verbose, $VERBOSE = $VERBOSE, nil
require "parser/ruby33"
- require "prism/translation/parser33"
rescue LoadError
# In CRuby's CI, we're not going to test against the parser gem because we
# don't want to have to install it. So in this case we'll just skip this test.
@@ -16,6 +15,19 @@ end
# First, opt in to every AST feature.
Parser::Builders::Default.modernize
+Prism::Translation::Parser::Builder.modernize
+
+# The parser gem rejects some strings that would most likely lead to errors
+# in consumers due to encoding problems. RuboCop however monkey-patches this
+# method out in order to accept such code.
+# https://github.com/whitequark/parser/blob/v3.3.6.0/lib/parser/builders/default.rb#L2289-L2295
+Parser::Builders::Default.prepend(
+ Module.new {
+ def string_value(token)
+ value(token)
+ end
+ }
+)
# Modify the source map == check so that it doesn't check against the node
# itself so we don't get into a recursive loop.
@@ -42,6 +54,22 @@ Parser::AST::Node.prepend(
module Prism
class ParserTest < TestCase
+ # These files contain code with valid syntax that can't be parsed.
+ skip_syntax_error = [
+ # alias/undef with %s(abc) symbol literal
+ "alias.txt",
+ "seattlerb/bug_215.txt",
+
+ # %Q with newline delimiter and heredoc interpolation
+ "heredoc_percent_q_newline_delimiter.txt",
+
+ # 1.. && 2
+ "ranges.txt",
+
+ # https://bugs.ruby-lang.org/issues/21168#note-5
+ "command_method_call_2.txt",
+ ]
+
# These files contain code that is being parsed incorrectly by the parser
# gem, and therefore we don't want to compare against our translation.
skip_incorrect = [
@@ -53,134 +81,131 @@ module Prism
"seattlerb/heredoc_nested.txt",
# https://github.com/whitequark/parser/issues/1016
- "whitequark/unary_num_pow_precedence.txt"
- ]
+ "whitequark/unary_num_pow_precedence.txt",
- # These files are either failing to parse or failing to translate, so we'll
- # skip them for now.
- skip_all = skip_incorrect | [
- "regex.txt",
- "unescaping.txt",
- "seattlerb/bug190.txt",
+ # https://github.com/whitequark/parser/issues/950
+ "whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt",
+
+ # Contains an escaped multibyte character. This is supposed to drop to backslash
+ "seattlerb/regexp_escape_extended.txt",
+
+ # https://github.com/whitequark/parser/issues/1020
+ # These contain consecutive \r characters, followed by \n. Prism only receives
+ # the already modified source buffer which dropped one \r but must know the
+ # original code to parse it correctly.
"seattlerb/heredoc_with_extra_carriage_returns_windows.txt",
"seattlerb/heredoc_with_only_carriage_returns_windows.txt",
"seattlerb/heredoc_with_only_carriage_returns.txt",
- "seattlerb/parse_line_heredoc_hardnewline.txt",
- "seattlerb/pctW_lineno.txt",
+
+ # https://github.com/whitequark/parser/issues/1026
+ # Regex with \c escape
+ "unescaping.txt",
"seattlerb/regexp_esc_C_slash.txt",
- "unparser/corpus/literal/literal.txt",
- "unparser/corpus/semantic/dstr.txt",
- "whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt",
- "whitequark/parser_slash_slash_n_escaping_in_literals.txt",
- "whitequark/ruby_bug_11989.txt"
- ]
- # Not sure why these files are failing on JRuby, but skipping them for now.
- if RUBY_ENGINE == "jruby"
- skip_all.push("emoji_method_calls.txt", "symbols.txt")
- end
+ # https://github.com/whitequark/parser/issues/1084
+ "unary_method_calls.txt",
+ ]
# These files are failing to translate their lexer output into the lexer
# output expected by the parser gem, so we'll skip them for now.
skip_tokens = [
- "comments.txt",
"dash_heredocs.txt",
- "dos_endings.txt",
"embdoc_no_newline_at_end.txt",
- "heredoc_with_comment.txt",
- "heredocs_with_ignored_newlines.txt",
- "indented_file_end.txt",
"methods.txt",
- "strings.txt",
- "tilde_heredocs.txt",
- "xstring_with_backslash.txt",
- "seattlerb/backticks_interpolation_line.txt",
"seattlerb/bug169.txt",
"seattlerb/case_in.txt",
- "seattlerb/class_comments.txt",
"seattlerb/difficult4__leading_dots2.txt",
"seattlerb/difficult6__7.txt",
"seattlerb/difficult6__8.txt",
- "seattlerb/dsym_esc_to_sym.txt",
- "seattlerb/heredoc__backslash_dos_format.txt",
- "seattlerb/heredoc_backslash_nl.txt",
- "seattlerb/heredoc_comma_arg.txt",
- "seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt",
- "seattlerb/heredoc_squiggly_blank_lines.txt",
- "seattlerb/heredoc_squiggly_interp.txt",
- "seattlerb/heredoc_squiggly_tabs_extra.txt",
- "seattlerb/heredoc_squiggly_tabs.txt",
- "seattlerb/heredoc_squiggly_visually_blank_lines.txt",
- "seattlerb/heredoc_squiggly.txt",
"seattlerb/heredoc_unicode.txt",
- "seattlerb/heredoc_with_carriage_return_escapes_windows.txt",
- "seattlerb/heredoc_with_carriage_return_escapes.txt",
- "seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt",
- "seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt",
- "seattlerb/interpolated_symbol_array_line_breaks.txt",
- "seattlerb/interpolated_word_array_line_breaks.txt",
- "seattlerb/label_vs_string.txt",
- "seattlerb/module_comments.txt",
- "seattlerb/non_interpolated_symbol_array_line_breaks.txt",
- "seattlerb/non_interpolated_word_array_line_breaks.txt",
- "seattlerb/parse_line_block_inline_comment_leading_newlines.txt",
- "seattlerb/parse_line_block_inline_comment.txt",
- "seattlerb/parse_line_block_inline_multiline_comment.txt",
- "seattlerb/parse_line_dstr_escaped_newline.txt",
"seattlerb/parse_line_heredoc.txt",
- "seattlerb/parse_line_multiline_str_literal_n.txt",
- "seattlerb/parse_line_str_with_newline_escape.txt",
"seattlerb/pct_w_heredoc_interp_nested.txt",
- "seattlerb/qsymbols_empty_space.txt",
- "seattlerb/qw_escape_term.txt",
- "seattlerb/qWords_space.txt",
- "seattlerb/read_escape_unicode_curlies.txt",
- "seattlerb/read_escape_unicode_h4.txt",
"seattlerb/required_kwarg_no_value.txt",
- "seattlerb/slashy_newlines_within_string.txt",
- "seattlerb/str_double_escaped_newline.txt",
- "seattlerb/str_double_newline.txt",
- "seattlerb/str_evstr_escape.txt",
- "seattlerb/str_newline_hash_line_number.txt",
- "seattlerb/str_single_newline.txt",
- "seattlerb/symbols_empty_space.txt",
"seattlerb/TestRubyParserShared.txt",
"unparser/corpus/literal/assignment.txt",
- "unparser/corpus/literal/dstr.txt",
- "unparser/corpus/semantic/opasgn.txt",
+ "unparser/corpus/literal/literal.txt",
"whitequark/args.txt",
"whitequark/beginless_erange_after_newline.txt",
"whitequark/beginless_irange_after_newline.txt",
- "whitequark/bug_ascii_8bit_in_literal.txt",
- "whitequark/bug_def_no_paren_eql_begin.txt",
- "whitequark/dedenting_heredoc.txt",
- "whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt",
"whitequark/forward_arg_with_open_args.txt",
- "whitequark/interp_digit_var.txt",
+ "whitequark/kwarg_no_paren.txt",
"whitequark/lbrace_arg_after_command_args.txt",
"whitequark/multiple_pattern_matches.txt",
"whitequark/newline_in_hash_argument.txt",
- "whitequark/parser_bug_640.txt",
- "whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt",
- "whitequark/ruby_bug_11990.txt",
+ "whitequark/pattern_matching_expr_in_paren.txt",
+ "whitequark/pattern_matching_hash.txt",
"whitequark/ruby_bug_14690.txt",
"whitequark/ruby_bug_9669.txt",
- "whitequark/slash_newline_in_heredocs.txt",
"whitequark/space_args_arg_block.txt",
"whitequark/space_args_block.txt"
]
- Fixture.each do |fixture|
+ Fixture.each_for_version(except: skip_syntax_error, version: "3.3") do |fixture|
define_method(fixture.test_name) do
assert_equal_parses(
fixture,
- compare_asts: !skip_all.include?(fixture.path),
+ compare_asts: !skip_incorrect.include?(fixture.path),
compare_tokens: !skip_tokens.include?(fixture.path),
compare_comments: fixture.path != "embdoc_no_newline_at_end.txt"
)
end
end
+ def test_non_prism_builder_class_deprecated
+ warnings = capture_warnings { Prism::Translation::Parser33.new(Parser::Builders::Default.new) }
+
+ assert_include(warnings, "#{__FILE__}:#{__LINE__ - 2}")
+ assert_include(warnings, "is not a `Prism::Translation::Parser::Builder` subclass")
+
+ warnings = capture_warnings { Prism::Translation::Parser33.new }
+ assert_empty(warnings)
+ end
+
+ if RUBY_VERSION >= "3.3"
+ def test_current_parser_for_current_ruby
+ major, minor = CURRENT_MAJOR_MINOR.split(".")
+ # Let's just hope there never is a Ruby 3.10 or similar
+ expected = major.to_i * 10 + minor.to_i
+ assert_equal(expected, Translation::ParserCurrent.new.version)
+ end
+ end
+
+ def test_invalid_syntax
+ code = <<~RUBY
+ foo do
+ case bar
+ when
+ end
+ end
+ RUBY
+ buffer = Parser::Source::Buffer.new("(string)")
+ buffer.source = code
+
+ parser = Prism::Translation::Parser33.new
+ parser.diagnostics.all_errors_are_fatal = true
+ assert_raise(Parser::SyntaxError) { parser.tokenize(buffer) }
+ end
+
+ def test_it_block_parameter_syntax
+ it_fixture_path = Pathname(__dir__).join("../../../test/prism/fixtures/3.4/it.txt")
+
+ buffer = Parser::Source::Buffer.new(it_fixture_path)
+ buffer.source = it_fixture_path.read
+ actual_ast = Prism::Translation::Parser34.new.tokenize(buffer)[0]
+
+ it_block_parameter_sexp = parse_sexp {
+ s(:begin,
+ s(:itblock,
+ s(:send, nil, :x), :it,
+ s(:lvar, :it)),
+ s(:itblock,
+ s(:lambda), :it,
+ s(:lvar, :it)))
+ }
+
+ assert_equal(it_block_parameter_sexp, actual_ast.to_sexp)
+ end
+
private
def assert_equal_parses(fixture, compare_asts: true, compare_tokens: true, compare_comments: true)
@@ -192,17 +217,13 @@ module Prism
parser.diagnostics.all_errors_are_fatal = true
expected_ast, expected_comments, expected_tokens =
- begin
- ignore_warnings { parser.tokenize(buffer) }
- rescue ArgumentError, Parser::SyntaxError
- return
- end
+ ignore_warnings { parser.tokenize(buffer) }
actual_ast, actual_comments, actual_tokens =
ignore_warnings { Prism::Translation::Parser33.new.tokenize(buffer) }
if expected_ast == actual_ast
- if !compare_asts
+ if !compare_asts && !Fixture.custom_base_path?
puts "#{fixture.path} is now passing"
end
@@ -213,7 +234,7 @@ module Prism
rescue Test::Unit::AssertionFailedError
raise if compare_tokens
else
- puts "#{fixture.path} is now passing" if !compare_tokens
+ puts "#{fixture.path} is now passing" if !compare_tokens && !Fixture.custom_base_path?
end
assert_equal_comments(expected_comments, actual_comments) if compare_comments
@@ -248,22 +269,14 @@ module Prism
def assert_equal_tokens(expected_tokens, actual_tokens)
if expected_tokens != actual_tokens
- expected_index = 0
- actual_index = 0
-
- while expected_index < expected_tokens.length
- expected_token = expected_tokens[expected_index]
- actual_token = actual_tokens.fetch(actual_index, [])
+ index = 0
+ max_index = [expected_tokens, actual_tokens].map(&:size).max
- expected_index += 1
- actual_index += 1
+ while index <= max_index
+ expected_token = expected_tokens.fetch(index, [])
+ actual_token = actual_tokens.fetch(index, [])
- # The parser gem always has a space before a string end in list
- # literals, but we don't. So we'll skip over the space.
- if expected_token[0] == :tSPACE && actual_token[0] == :tSTRING_END
- expected_index += 1
- next
- end
+ index += 1
# There are a lot of tokens that have very specific meaning according
# to the context of the parser. We don't expose that information in
@@ -287,5 +300,9 @@ module Prism
"actual: #{actual_comments.inspect}"
}
end
+
+ def parse_sexp(&block)
+ Class.new { extend AST::Sexp }.instance_eval(&block).to_sexp
+ end
end
end
diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb
index 8db47da3d3..174c90cbca 100644
--- a/test/prism/ruby/ripper_test.rb
+++ b/test/prism/ruby/ripper_test.rb
@@ -1,37 +1,48 @@
# frozen_string_literal: true
-return if RUBY_VERSION < "3.3"
+return if RUBY_VERSION < "3.3" || RUBY_ENGINE != "ruby"
require_relative "../test_helper"
+require "ripper"
module Prism
class RipperTest < TestCase
# Skip these tests that Ripper is reporting the wrong results for.
incorrect = [
# Ripper incorrectly attributes the block to the keyword.
- "seattlerb/block_break.txt",
- "seattlerb/block_next.txt",
"seattlerb/block_return.txt",
- "whitequark/break_block.txt",
- "whitequark/next_block.txt",
"whitequark/return_block.txt",
- # Ripper is not accounting for locals created by patterns using the **
- # operator within an `in` clause.
- "seattlerb/parse_pattern_058.txt",
-
# Ripper cannot handle named capture groups in regular expressions.
"regex.txt",
- "regex_char_width.txt",
- "whitequark/lvar_injecting_match.txt",
# Ripper fails to understand some structures that span across heredocs.
- "spanning_heredoc.txt"
+ "spanning_heredoc.txt",
+
+ # Ripper interprets circular keyword arguments as method calls.
+ "3.4/circular_parameters.txt",
+
+ # Ripper doesn't emit `args_add_block` when endless method is prefixed by modifier.
+ "4.0/endless_methods_command_call.txt",
+
+ # https://bugs.ruby-lang.org/issues/21168#note-5
+ "command_method_call_2.txt",
]
+ if RUBY_VERSION.start_with?("3.3.")
+ incorrect += [
+ "whitequark/lvar_injecting_match.txt",
+ "seattlerb/parse_pattern_058.txt",
+ "regex_char_width.txt",
+ ]
+ end
+
# Skip these tests that we haven't implemented yet.
- omitted = [
+ omitted_sexp_raw = [
+ "bom_leading_space.txt",
+ "bom_spaces.txt",
"dos_endings.txt",
+ "heredocs_with_fake_newlines.txt",
"heredocs_with_ignored_newlines.txt",
"seattlerb/block_call_dot_op2_brace_block.txt",
"seattlerb/block_command_operation_colon.txt",
@@ -45,18 +56,123 @@ module Prism
"whitequark/dedenting_heredoc.txt",
"whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt",
"whitequark/parser_slash_slash_n_escaping_in_literals.txt",
+ "whitequark/ruby_bug_18878.txt",
"whitequark/send_block_chain_cmd.txt",
"whitequark/slash_newline_in_heredocs.txt"
]
- Fixture.each(except: incorrect | omitted) do |fixture|
- define_method(fixture.test_name) { assert_ripper(fixture.read) }
+ omitted_lex = [
+ "comments.txt",
+ "heredoc_percent_q_newline_delimiter.txt",
+ "heredoc_with_escaped_newline_at_start.txt",
+ "heredocs_with_fake_newlines.txt",
+ "indented_file_end.txt",
+ "seattlerb/TestRubyParserShared.txt",
+ "seattlerb/class_comments.txt",
+ "seattlerb/module_comments.txt",
+ "seattlerb/parse_line_block_inline_comment_leading_newlines.txt",
+ "seattlerb/parse_line_block_inline_multiline_comment.txt",
+ "spanning_heredoc_newlines.txt",
+ "strings.txt",
+ "whitequark/dedenting_heredoc.txt",
+ "whitequark/procarg0.txt",
+ ]
+
+ Fixture.each_for_current_ruby(except: incorrect | omitted_sexp_raw) do |fixture|
+ define_method("#{fixture.test_name}_sexp_raw") { assert_ripper_sexp_raw(fixture.read) }
+ end
+
+ Fixture.each_for_current_ruby(except: incorrect | omitted_lex) do |fixture|
+ define_method("#{fixture.test_name}_lex") { assert_ripper_lex(fixture.read) }
+ end
+
+ module Events
+ attr_reader :events
+
+ def initialize(...)
+ super
+ @events = []
+ end
+
+ Prism::Translation::Ripper::PARSER_EVENTS.each do |event|
+ define_method(:"on_#{event}") do |*args|
+ @events << [event, *args]
+ super(*args)
+ end
+ end
+ end
+
+ class RipperEvents < Ripper
+ include Events
+ end
+
+ class PrismEvents < Translation::Ripper
+ include Events
+ end
+
+ def test_events
+ source = "1 rescue 2"
+ ripper = RipperEvents.new(source)
+ prism = PrismEvents.new(source)
+ ripper.parse
+ prism.parse
+ # This makes sure that the content is the same. Ordering is not correct for now.
+ assert_equal(ripper.events.sort, prism.events.sort)
+ end
+
+ def test_lexer
+ lexer = Translation::Ripper::Lexer.new("foo")
+ expected = [[1, 0], :on_ident, "foo", Translation::Ripper::EXPR_CMDARG]
+
+ assert_equal([expected], lexer.lex)
+ assert_equal(expected, lexer.parse[0].to_a)
+ assert_equal(lexer.parse[0].to_a, lexer.scan[0].to_a)
+
+ assert_equal(%i[on_int on_sp on_op], Translation::Ripper::Lexer.new("1 +").lex.map(&:event))
+ assert_raise(SyntaxError) { Translation::Ripper::Lexer.new("1 +").lex(raise_errors: true) }
+ end
+
+ def test_tokenize
+ source = "foo;1;BAZ"
+ assert_equal(Ripper.tokenize(source), Translation::Ripper.tokenize(source))
+ end
+
+ # Check that the hardcoded values don't change without us noticing.
+ def test_internals
+ actual = Translation::Ripper.constants.select { |name| name.start_with?("EXPR_") }.sort
+ expected = Ripper.constants.select { |name| name.start_with?("EXPR_") }.sort
+
+ assert_equal(expected, actual)
+ expected.zip(actual).each do |ripper, prism|
+ assert_equal(Ripper.const_get(ripper), Translation::Ripper.const_get(prism))
+ end
end
private
- def assert_ripper(source)
+ def assert_ripper_sexp_raw(source)
assert_equal Ripper.sexp_raw(source), Prism::Translation::Ripper.sexp_raw(source)
end
+
+ def assert_ripper_lex(source)
+ prism = Translation::Ripper.lex(source)
+ ripper = Ripper.lex(source)
+
+ # Prism emits tokens by their order in the code, not in parse order
+ ripper.sort_by! { |elem| elem[0] }
+
+ [prism.size, ripper.size].max.times do |i|
+ expected = ripper[i]
+ actual = prism[i]
+
+ # Since tokens related to heredocs are not emitted in the same order,
+ # the state also doesn't line up.
+ if expected && actual && expected[1] == :on_heredoc_end && actual[1] == :on_heredoc_end
+ expected[3] = actual[3] = nil
+ end
+
+ assert_equal(expected, actual)
+ end
+ end
end
end
diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb
index a13daeeb84..4b7e9c93ed 100644
--- a/test/prism/ruby/ruby_parser_test.rb
+++ b/test/prism/ruby/ruby_parser_test.rb
@@ -13,40 +13,32 @@ rescue LoadError
return
end
-# We want to also compare lines and files to make sure we're setting them
-# correctly.
-Sexp.prepend(
- Module.new do
- def ==(other)
- super && line == other.line && file == other.file # && line_max == other.line_max
- end
- end
-)
-
module Prism
class RubyParserTest < TestCase
todos = [
- "newline_terminated.txt",
+ "character_literal.txt",
+ "encoding_euc_jp.txt",
"regex_char_width.txt",
- "seattlerb/bug169.txt",
"seattlerb/masgn_colon3.txt",
"seattlerb/messy_op_asgn_lineno.txt",
"seattlerb/op_asgn_primary_colon_const_command_call.txt",
"seattlerb/regexp_esc_C_slash.txt",
"seattlerb/str_lit_concat_bad_encodings.txt",
+ "strings.txt",
"unescaping.txt",
- "unparser/corpus/literal/kwbegin.txt",
- "unparser/corpus/literal/send.txt",
"whitequark/masgn_const.txt",
+ "whitequark/pattern_matching_constants.txt",
+ "whitequark/pattern_matching_single_match.txt",
"whitequark/ruby_bug_12402.txt",
- "whitequark/ruby_bug_14690.txt",
- "whitequark/space_args_block.txt"
]
# https://github.com/seattlerb/ruby_parser/issues/344
failures = [
"alias.txt",
+ "dsym_str.txt",
"dos_endings.txt",
+ "heredoc_percent_q_newline_delimiter.txt",
+ "heredocs_with_fake_newlines.txt",
"heredocs_with_ignored_newlines.txt",
"method_calls.txt",
"methods.txt",
@@ -64,7 +56,9 @@ module Prism
"seattlerb/heredoc_with_only_carriage_returns.txt",
"spanning_heredoc_newlines.txt",
"spanning_heredoc.txt",
+ "symbols.txt",
"tilde_heredocs.txt",
+ "unary_method_calls.txt",
"unparser/corpus/literal/literal.txt",
"while.txt",
"whitequark/cond_eflipflop.txt",
@@ -80,7 +74,22 @@ module Prism
"whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt",
"whitequark/pattern_matching_single_line.txt",
"whitequark/ruby_bug_11989.txt",
- "whitequark/slash_newline_in_heredocs.txt"
+ "whitequark/ruby_bug_18878.txt",
+ "whitequark/ruby_bug_19281.txt",
+ "whitequark/slash_newline_in_heredocs.txt",
+
+ "3.3-3.3/block_args_in_array_assignment.txt",
+ "3.3-3.3/it_with_ordinary_parameter.txt",
+ "3.3-3.3/keyword_args_in_array_assignment.txt",
+ "3.3-3.3/return_in_sclass.txt",
+
+ "3.4/circular_parameters.txt",
+
+ "4.0/endless_methods_command_call.txt",
+ "4.0/leading_logical.txt",
+
+ # https://bugs.ruby-lang.org/issues/21168#note-5
+ "command_method_call_2.txt",
]
Fixture.each(except: failures) do |fixture|
@@ -95,10 +104,16 @@ module Prism
source = fixture.read
expected = ignore_warnings { ::RubyParser.new.parse(source, fixture.path) }
actual = Prism::Translation::RubyParser.new.parse(source, fixture.path)
+ on_failure = -> { message(expected, actual) }
if !allowed_failure
- assert_equal(expected, actual, -> { message(expected, actual) })
- elsif expected == actual
+ assert_equal(expected, actual, on_failure)
+
+ unless actual.nil?
+ assert_equal(expected.line, actual.line, on_failure)
+ assert_equal(expected.file, actual.file, on_failure)
+ end
+ elsif expected == actual && expected.line && actual.line && expected.file == actual.file
puts "#{name} now passes"
end
end
diff --git a/test/prism/ruby/source_test.rb b/test/prism/ruby/source_test.rb
new file mode 100644
index 0000000000..afd2825765
--- /dev/null
+++ b/test/prism/ruby/source_test.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class SourceTest < TestCase
+ def test_line_to_byte_offset
+ parse_result = Prism.parse(<<~SRC)
+ abcd
+ efgh
+ ijkl
+ SRC
+ source = parse_result.source
+
+ assert_equal 0, source.line_to_byte_offset(1)
+ assert_equal 5, source.line_to_byte_offset(2)
+ assert_equal 10, source.line_to_byte_offset(3)
+ assert_equal 15, source.line_to_byte_offset(4)
+ e = assert_raise(ArgumentError) { source.line_to_byte_offset(5) }
+ assert_equal "line 5 is out of range", e.message
+ e = assert_raise(ArgumentError) { source.line_to_byte_offset(0) }
+ assert_equal "line 0 is out of range", e.message
+ e = assert_raise(ArgumentError) { source.line_to_byte_offset(-1) }
+ assert_equal "line -1 is out of range", e.message
+ end
+
+ def test_line_to_byte_offset_with_start_line
+ parse_result = Prism.parse(<<~SRC, line: 11)
+ abcd
+ efgh
+ ijkl
+ SRC
+ source = parse_result.source
+
+ assert_equal 0, source.line_to_byte_offset(11)
+ assert_equal 5, source.line_to_byte_offset(12)
+ assert_equal 10, source.line_to_byte_offset(13)
+ assert_equal 15, source.line_to_byte_offset(14)
+ e = assert_raise(ArgumentError) { source.line_to_byte_offset(15) }
+ assert_equal "line 15 is out of range", e.message
+ e = assert_raise(ArgumentError) { source.line_to_byte_offset(10) }
+ assert_equal "line 10 is out of range", e.message
+ e = assert_raise(ArgumentError) { source.line_to_byte_offset(9) }
+ assert_equal "line 9 is out of range", e.message
+ end
+ end
+end