mirror of
https://github.com/ruby/ruby.git
synced 2025-08-25 05:55:46 +02:00

The problem was that we were treating heredoc bodies as part of the %W
list because we didn't push the scanning cursor past the heredoc after
lexing out the here doc. To fix this, we changed the whitespace
scanning function to quit scanning when it reaches a newline but only in
the case that a heredoc is present.
Additionally, we need to prevent double counting newlines in the case of
a heredoc. For example:
```ruby
%W(<<foo 123)
foo
```
The newline after the `)` is counted as part of scanning the heredoc, so
we added logic to prevent double counting the newline when scanning the
rest of the %W list.
eb090d8126
Co-authored-by: Jemma Issroff <jemmaissroff@gmail.com>
132 lines
4.4 KiB
Ruby
132 lines
4.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "yarp_test_helper"
|
|
|
|
class ParseTest < Test::Unit::TestCase
|
|
# When we pretty-print the trees to compare against the snapshots, we want to
|
|
# be certain that we print with the same external encoding. This is because
|
|
# methods like Symbol#inspect take into account external encoding and it could
|
|
# change how the snapshot is generated. On machines with certain settings
|
|
# (like LANG=C or -Eascii-8bit) this could have been changed. So here we're
|
|
# going to force it to be UTF-8 to keep the snapshots consistent.
|
|
def setup
|
|
@previous_default_external = Encoding.default_external
|
|
ignore_warnings { Encoding.default_external = Encoding::UTF_8 }
|
|
end
|
|
|
|
def teardown
|
|
ignore_warnings { Encoding.default_external = @previous_default_external }
|
|
end
|
|
|
|
def test_Ruby_3_2_plus
|
|
assert_operator RUBY_VERSION, :>=, "3.2.0", "ParseTest requires Ruby 3.2+"
|
|
end
|
|
|
|
def test_empty_string
|
|
YARP.parse("") => YARP::ParseResult[value: YARP::ProgramNode[statements: YARP::StatementsNode[body: []]]]
|
|
end
|
|
|
|
known_failures = %w[
|
|
seattlerb/heredoc_nested.txt
|
|
]
|
|
|
|
def find_source_file_node(node)
|
|
if node.is_a?(YARP::SourceFileNode)
|
|
node
|
|
else
|
|
node && node.child_nodes.each do |child_node|
|
|
source_file_node = find_source_file_node(child_node)
|
|
return source_file_node if source_file_node
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_parse_dollar0
|
|
parsed_result = YARP.parse("$0", "-e")
|
|
assert_equal 2, parsed_result.value.location.length
|
|
end
|
|
|
|
def test_parse_takes_file_path
|
|
filepath = "filepath.rb"
|
|
parsed_result = YARP.parse("def foo; __FILE__; end", filepath)
|
|
|
|
assert_equal filepath, find_source_file_node(parsed_result.value).filepath
|
|
end
|
|
|
|
base = File.join(__dir__, "fixtures")
|
|
Dir["**/*.txt", base: base].each do |relative|
|
|
next if known_failures.include?(relative)
|
|
|
|
filepath = File.join(base, relative)
|
|
snapshot = File.expand_path(File.join("snapshots", relative), __dir__)
|
|
directory = File.dirname(snapshot)
|
|
FileUtils.mkdir_p(directory) unless File.directory?(directory)
|
|
|
|
define_method "test_filepath_#{relative}" do
|
|
# First, read the source from the filepath. Use binmode to avoid converting CRLF on Windows,
|
|
# and explicitly set the external encoding to UTF-8 to override the binmode default.
|
|
source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
|
|
|
|
# Make sure that it can be correctly parsed by Ripper. If it can't, then we have a fixture
|
|
# that is invalid Ruby.
|
|
refute_nil Ripper.sexp_raw(source)
|
|
|
|
# Next, assert that there were no errors during parsing.
|
|
result = YARP.parse(source, relative)
|
|
assert_empty result.errors
|
|
|
|
# Next, pretty print the source.
|
|
printed = PP.pp(result.value, +"", 79)
|
|
|
|
if File.exist?(snapshot)
|
|
saved = File.read(snapshot)
|
|
|
|
# If the snapshot file exists, but the printed value does not match the
|
|
# snapshot, then update the snapshot file.
|
|
if printed != saved
|
|
File.write(snapshot, printed)
|
|
warn("Updated snapshot at #{snapshot}.")
|
|
end
|
|
|
|
# If the snapshot file exists, then assert that the printed value
|
|
# matches the snapshot.
|
|
assert_equal(saved, printed)
|
|
else
|
|
# If the snapshot file does not yet exist, then write it out now.
|
|
File.write(snapshot, printed)
|
|
warn("Created snapshot at #{snapshot}.")
|
|
end
|
|
|
|
# Next, assert that the value can be serialized and deserialized without
|
|
# changing the shape of the tree.
|
|
assert_equal_nodes(result.value, YARP.load(source, YARP.dump(source, relative)))
|
|
|
|
# Next, assert that the newlines are in the expected places.
|
|
expected_newlines = [0]
|
|
source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
|
|
assert_equal expected_newlines, YARP.newlines(source)
|
|
|
|
# Finally, assert that we can lex the source and get the same tokens as
|
|
# Ripper.
|
|
YARP.lex_compat(source) => { errors: [], value: tokens }
|
|
|
|
begin
|
|
YARP.lex_ripper(source).zip(tokens).each do |(ripper, yarp)|
|
|
assert_equal ripper, yarp
|
|
end
|
|
rescue SyntaxError
|
|
raise ArgumentError, "Test file has invalid syntax #{filepath}"
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def ignore_warnings
|
|
previous_verbosity = $VERBOSE
|
|
$VERBOSE = nil
|
|
yield
|
|
ensure
|
|
$VERBOSE = previous_verbosity
|
|
end
|
|
end
|