mirror of
https://github.com/ruby/ruby.git
synced 2025-08-24 21:44:30 +02:00

This should make it easier on the sync to determine what changed
and hopefully result in fewer merge conflicts that have to be
manually resolved.
17d82afbfc
100 lines
2.8 KiB
Ruby
100 lines
2.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "test_helper"
|
|
|
|
return unless defined?(RubyVM::InstructionSequence)
|
|
|
|
# It is useful to have a diff even if the strings to compare are big
|
|
# However, ruby/ruby does not have a version of Test::Unit with access to
|
|
# max_diff_target_string_size
|
|
if defined?(Test::Unit::Assertions::AssertionMessage)
|
|
Test::Unit::Assertions::AssertionMessage.max_diff_target_string_size = 5000
|
|
end
|
|
|
|
class NewlineTest < Test::Unit::TestCase
|
|
class NewlineVisitor < YARP::Visitor
|
|
attr_reader :source, :newlines
|
|
|
|
def initialize(source)
|
|
@source = source
|
|
@newlines = []
|
|
end
|
|
|
|
def visit(node)
|
|
newlines << source.line(node.location.start_offset) if node&.newline?
|
|
super(node)
|
|
end
|
|
end
|
|
|
|
base = File.dirname(__dir__)
|
|
Dir["{lib,test}/**/*.rb", base: base].each do |relative|
|
|
define_method("test_newline_flags_#{relative}") do
|
|
assert_newlines(base, relative)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def assert_newlines(base, relative)
|
|
filepath = File.join(base, relative)
|
|
source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
|
|
expected = rubyvm_lines(source)
|
|
|
|
result = YARP.parse_file(filepath)
|
|
assert_empty result.errors
|
|
|
|
result.mark_newlines
|
|
visitor = NewlineVisitor.new(result.source)
|
|
|
|
result.value.accept(visitor)
|
|
actual = visitor.newlines
|
|
|
|
source.each_line.with_index(1) do |line, line_number|
|
|
# Lines like `while (foo = bar)` result in two line flags in the bytecode
|
|
# but only one newline flag in the AST. We need to remove the extra line
|
|
# flag from the bytecode to make the test pass.
|
|
if line.match?(/while \(/)
|
|
index = expected.index(line_number)
|
|
expected.delete_at(index) if index
|
|
end
|
|
|
|
# Lines like `foo =` where the value is on the next line result in another
|
|
# line flag in the bytecode but only one newline flag in the AST.
|
|
if line.match?(/^\s+\w+ =$/)
|
|
if source.lines[line_number].match?(/^\s+case/)
|
|
actual[actual.index(line_number)] += 1
|
|
else
|
|
actual.delete_at(actual.index(line_number))
|
|
end
|
|
end
|
|
|
|
if line.match?(/^\s+\w+ = \[$/)
|
|
if !expected.include?(line_number) && !expected.include?(line_number + 2)
|
|
actual[actual.index(line_number)] += 1
|
|
end
|
|
end
|
|
end
|
|
|
|
assert_equal expected, actual
|
|
end
|
|
|
|
def ignore_warnings
|
|
previous_verbosity = $VERBOSE
|
|
$VERBOSE = nil
|
|
yield
|
|
ensure
|
|
$VERBOSE = previous_verbosity
|
|
end
|
|
|
|
def rubyvm_lines(source)
|
|
queue = [ignore_warnings { RubyVM::InstructionSequence.compile(source) }]
|
|
lines = []
|
|
|
|
while iseq = queue.shift
|
|
lines.concat(iseq.trace_points.filter_map { |line, event| line if event == :line })
|
|
iseq.each_child { |insn| queue << insn unless insn.label.start_with?("ensure in ") }
|
|
end
|
|
|
|
lines.sort
|
|
end
|
|
end
|