mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
When formatting error messages, first try to format into a fixed buffer instead of formatting once to get the length then a second time to get the actual format. https://github.com/ruby/prism/commit/557cffe23e
151 lines
4.7 KiB
Ruby
151 lines
4.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
return if RUBY_VERSION < "3.3.0"
|
|
|
|
require_relative "test_helper"
|
|
|
|
module Ruby::Prism
|
|
class ErrorsTest < TestCase
|
|
base = File.expand_path("errors", __dir__)
|
|
filepaths = Dir[ENV.fetch("FOCUS", "**/*.txt"), base: base]
|
|
|
|
PARSE_Y_EXCLUDES = [
|
|
# https://bugs.ruby-lang.org/issues/20409
|
|
"#{base}/4.1/end_block_exit.txt"
|
|
]
|
|
|
|
filepaths.each do |filepath|
|
|
ruby_versions_for(filepath).each do |version|
|
|
define_method(:"test_#{version}_#{File.basename(filepath, ".txt")}") do
|
|
assert_errors(File.join(base, filepath), version)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_newline_preceding_eof
|
|
err = Ruby::Prism.parse("foo(").errors.first
|
|
assert_equal 1, err.location.start_line
|
|
|
|
err = Ruby::Prism.parse("foo(\n").errors.first
|
|
assert_equal 1, err.location.start_line
|
|
|
|
err = Ruby::Prism.parse("foo(\n\n\n\n\n").errors.first
|
|
assert_equal 5, err.location.start_line
|
|
end
|
|
|
|
def test_embdoc_ending
|
|
source = <<~RUBY
|
|
=begin\n=end
|
|
=begin\n=end\0
|
|
=begin\n=end\C-d
|
|
=begin\n=end\C-z
|
|
RUBY
|
|
|
|
source.each_line do |line|
|
|
assert_valid_syntax(source)
|
|
assert_predicate Ruby::Prism.parse(source), :success?
|
|
end
|
|
end
|
|
|
|
def test_unterminated_string_closing
|
|
statement = Ruby::Prism.parse_statement("'hello")
|
|
assert_equal statement.unescaped, "hello"
|
|
assert_nil statement.closing
|
|
end
|
|
|
|
def test_unterminated_interpolated_string_closing
|
|
statement = Ruby::Prism.parse_statement('"hello')
|
|
assert_equal statement.unescaped, "hello"
|
|
assert_nil statement.closing
|
|
end
|
|
|
|
def test_unterminated_empty_string_closing
|
|
statement = Ruby::Prism.parse_statement('"')
|
|
assert_empty statement.unescaped
|
|
assert_nil statement.closing
|
|
end
|
|
|
|
def test_regexp_encoding_option_mismatch_error
|
|
# UTF-8 char with ASCII-8BIT modifier
|
|
result = Ruby::Prism.parse('/Ȃ/n')
|
|
assert_includes result.errors.map(&:type), :regexp_encoding_option_mismatch
|
|
|
|
# UTF-8 char with EUC-JP modifier
|
|
result = Ruby::Prism.parse('/Ȃ/e')
|
|
assert_includes result.errors.map(&:type), :regexp_encoding_option_mismatch
|
|
|
|
# UTF-8 char with Windows-31J modifier
|
|
result = Ruby::Prism.parse('/Ȃ/s')
|
|
assert_includes result.errors.map(&:type), :regexp_encoding_option_mismatch
|
|
|
|
# UTF-8 char with UTF-8 modifier
|
|
result = Ruby::Prism.parse('/Ȃ/u')
|
|
assert_empty result.errors
|
|
end
|
|
|
|
def test_long_formatted_messages
|
|
# Messages are formatted into a fixed-size buffer first, so check lengths
|
|
# on either side of its size to cover both the copy and the reformat.
|
|
template = "unterminated heredoc; can't find string \"%s\" anywhere before EOF"
|
|
|
|
(254..258).each do |length|
|
|
identifier = "E" * (length - template.length + 2)
|
|
expected = format(template, identifier)
|
|
assert_equal length, expected.bytesize
|
|
|
|
messages = Ruby::Prism.parse("<<#{identifier}\n").errors.map(&:message)
|
|
assert_includes messages, expected
|
|
end
|
|
end
|
|
|
|
def test_incomplete_def_closing_loc
|
|
statement = Ruby::Prism.parse_statement("def f; 123")
|
|
assert_nil(statement.end_keyword)
|
|
end
|
|
|
|
def test_unclosed_interpolation
|
|
statement = Ruby::Prism.parse_statement("\"\#{")
|
|
assert_equal('"', statement.opening)
|
|
assert_nil(statement.closing)
|
|
|
|
assert_equal(1, statement.parts.count)
|
|
assert_equal('#{', statement.parts[0].opening)
|
|
assert_equal("", statement.parts[0].closing)
|
|
assert_nil(statement.parts[0].statements)
|
|
end
|
|
|
|
def test_unclosed_heredoc_and_interpolation
|
|
statement = Ruby::Prism.parse_statement("<<D\n\#{")
|
|
assert_equal("<<D", statement.opening)
|
|
assert_nil(statement.closing)
|
|
|
|
assert_equal(1, statement.parts.count)
|
|
assert_equal('#{', statement.parts[0].opening)
|
|
assert_equal("", statement.parts[0].closing)
|
|
assert_nil(statement.parts[0].statements)
|
|
end
|
|
|
|
private
|
|
|
|
def assert_errors(filepath, version)
|
|
expected = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
|
|
|
|
source = expected.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "")
|
|
if CURRENT_MAJOR_MINOR == version && !PARSE_Y_EXCLUDES.include?(filepath)
|
|
refute_valid_syntax(source)
|
|
end
|
|
|
|
result = Ruby::Prism.parse(source, version: version)
|
|
errors = result.errors
|
|
refute_empty errors, "Expected errors in #{filepath}"
|
|
|
|
actual = result.errors_format
|
|
if expected != actual && ENV["UPDATE_SNAPSHOTS"]
|
|
File.write(filepath, actual)
|
|
end
|
|
|
|
assert_equal expected, actual, "Expected errors to match for #{filepath}"
|
|
end
|
|
end
|
|
end
|