Skip to content

Commit 9e200dd

Browse files
committed
Check for syntax errors using RubyVM
1 parent 5f86742 commit 9e200dd

File tree

3 files changed

+66
-56
lines changed

3 files changed

+66
-56
lines changed

test/prism/errors_test.rb

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,32 +2199,6 @@ def test_duplicate_pattern_hash_key
21992199

22002200
private
22012201

2202-
if RUBY_ENGINE == "ruby"
2203-
def check_syntax(source)
2204-
$VERBOSE, previous = nil, $VERBOSE
2205-
2206-
begin
2207-
RubyVM::InstructionSequence.compile(source)
2208-
ensure
2209-
$VERBOSE = previous
2210-
end
2211-
end
2212-
2213-
def assert_valid_syntax(source)
2214-
check_syntax(source)
2215-
end
2216-
2217-
def refute_valid_syntax(source)
2218-
assert_raise(SyntaxError) { check_syntax(source) }
2219-
end
2220-
else
2221-
def assert_valid_syntax(source)
2222-
end
2223-
2224-
def refute_valid_syntax(source)
2225-
end
2226-
end
2227-
22282202
def assert_errors(expected, source, errors, check_valid_syntax: true)
22292203
refute_valid_syntax(source) if check_valid_syntax
22302204

test/prism/parse_test.rb

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -188,39 +188,43 @@ def test_parse_file_comments
188188
directory = File.dirname(snapshot)
189189
FileUtils.mkdir_p(directory) unless File.directory?(directory)
190190

191-
ripper_should_parse = ripper_should_match = ripper_enabled
192-
193-
# This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
194-
# we're on an earlier version.
195-
ripper_should_match = false if relative == "seattlerb/pct_w_heredoc_interp_nested.txt" && RUBY_VERSION < "3.3.0"
196-
197-
# It seems like there are some oddities with nested heredocs and ripper.
198-
# Waiting for feedback on https://bugs.ruby-lang.org/issues/19838.
199-
ripper_should_match = false if relative == "seattlerb/heredoc_nested.txt"
200-
ripper_should_match = false if relative == "whitequark/dedenting_heredoc.txt"
201-
202-
# Ripper seems to have a bug that the regex portions before and after the heredoc are combined
203-
# into a single token. See https://bugs.ruby-lang.org/issues/19838.
204-
#
205-
# Additionally, Ripper cannot parse the %w[] fixture in this file, so set ripper_should_parse to false.
206-
ripper_should_parse = false if relative == "spanning_heredoc.txt"
207-
ripper_should_match = false if relative == "spanning_heredoc_newlines.txt"
208-
209-
# Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace characters in the heredoc start.
210-
# Example: <<~' EOF' or <<-' EOF'
211-
# https://bugs.ruby-lang.org/issues/19539
212-
ripper_should_parse = false if relative == "heredocs_leading_whitespace.txt" && RUBY_VERSION < "3.3.0"
191+
ripper_should_match = ripper_enabled
192+
check_valid_syntax = true
193+
194+
case relative
195+
when "seattlerb/pct_w_heredoc_interp_nested.txt"
196+
# This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
197+
# we're on an earlier version.
198+
ripper_should_match = false if RUBY_VERSION < "3.3.0"
199+
when "seattlerb/heredoc_nested.txt", "whitequark/dedenting_heredoc.txt"
200+
# It seems like there are some oddities with nested heredocs and ripper.
201+
# Waiting for feedback on https://bugs.ruby-lang.org/issues/19838.
202+
ripper_should_match = false
203+
when "spanning_heredoc.txt", "spanning_heredoc_newlines.txt"
204+
# Ripper seems to have a bug that the regex portions before and after
205+
# the heredoc are combined into a single token. See
206+
# https://bugs.ruby-lang.org/issues/19838.
207+
ripper_should_match = false
208+
when "heredocs_leading_whitespace.txt"
209+
# Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace
210+
# characters in the heredoc start.
211+
# Example: <<~' EOF' or <<-' EOF'
212+
# https://bugs.ruby-lang.org/issues/19539
213+
if RUBY_VERSION < "3.3.0"
214+
ripper_should_match = false
215+
check_valid_syntax = false
216+
end
217+
end
213218

214219
define_method "test_filepath_#{relative}" do
215-
# First, read the source from the filepath. Use binmode to avoid converting CRLF on Windows,
216-
# and explicitly set the external encoding to UTF-8 to override the binmode default.
220+
# First, read the source from the filepath. Use binmode to avoid
221+
# converting CRLF on Windows, and explicitly set the external encoding
222+
# to UTF-8 to override the binmode default.
217223
source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
218224

219-
if ripper_should_parse
220-
# Make sure that it can be correctly parsed by Ripper. If it can't,
221-
# then we have a fixture that is invalid Ruby.
222-
refute_nil(Ripper.sexp_raw(source), "Ripper failed to parse")
223-
end
225+
# Make sure that the given source is valid syntax, otherwise we have an
226+
# invalid fixture.
227+
assert_valid_syntax(source) if check_valid_syntax
224228

225229
# Next, assert that there were no errors during parsing.
226230
result = Prism.parse(source, filepath: relative)
@@ -263,7 +267,7 @@ def test_parse_file_comments
263267
source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
264268
assert_equal expected_newlines, Debug.newlines(source)
265269

266-
if ripper_should_parse && ripper_should_match
270+
if ripper_should_match
267271
# Finally, assert that we can lex the source and get the same tokens as
268272
# Ripper.
269273
lex_result = Prism.lex_compat(source)

test/prism/test_helper.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,38 @@ module Prism
1919
class TestCase < ::Test::Unit::TestCase
2020
private
2121

22+
if RUBY_ENGINE == "ruby"
23+
# Check that the given source is valid syntax by compiling it with RubyVM.
24+
def check_syntax(source)
25+
$VERBOSE, previous = nil, $VERBOSE
26+
27+
begin
28+
RubyVM::InstructionSequence.compile(source)
29+
ensure
30+
$VERBOSE = previous
31+
end
32+
end
33+
34+
# Assert that the given source is valid Ruby syntax by attempting to
35+
# compile it, and then implicitly checking that it does not raise an
36+
# syntax errors.
37+
def assert_valid_syntax(source)
38+
check_syntax(source)
39+
end
40+
41+
# Refute that the given source is invalid Ruby syntax by attempting to
42+
# compile it and asserting that it raises a SyntaxError.
43+
def refute_valid_syntax(source)
44+
assert_raise(SyntaxError) { check_syntax(source) }
45+
end
46+
else
47+
def assert_valid_syntax(source)
48+
end
49+
50+
def refute_valid_syntax(source)
51+
end
52+
end
53+
2254
def assert_raises(*args, &block)
2355
raise "Use assert_raise instead"
2456
end

0 commit comments

Comments
 (0)