Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions ext/prism/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ VALUE rb_cPrismLexResult;
VALUE rb_cPrismParseLexResult;
VALUE rb_cPrismStringQuery;
VALUE rb_cPrismScope;
VALUE rb_cPrismCurrentVersionError;

VALUE rb_cPrismDebugEncoding;

Expand Down Expand Up @@ -199,7 +200,13 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
if (!NIL_P(value)) {
const char *version = check_string(value);

if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
VALUE current_ruby_value = rb_const_get(rb_cObject, rb_intern("RUBY_VERSION"));
const char *current_version = RSTRING_PTR(current_ruby_value);
if (!pm_options_version_set(options, current_version, 3)) {
rb_exc_raise(rb_exc_new_str(rb_cPrismCurrentVersionError, current_ruby_value));
}
} else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
}
}
Expand Down Expand Up @@ -888,7 +895,7 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
* version of Ruby syntax (which you can trigger with `nil` or
* `"latest"`). You may also restrict the syntax to a specific version of
* Ruby, e.g., with `"3.3.0"`. To parse with the same syntax version that
* the current Ruby is running use `version: RUBY_VERSION`. Raises
* the current Ruby is running use `version: "current"`. Raises
* ArgumentError if the version is not currently supported by Prism.
*/
static VALUE
Expand Down Expand Up @@ -1364,6 +1371,8 @@ Init_prism(void) {
rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);

rb_cPrismCurrentVersionError = rb_const_get(rb_cPrism, rb_intern("CurrentVersionError"));

// Intern all of the IDs eagerly that we support so that we don't have to do
// it every time we parse.
rb_id_option_command_line = rb_intern_const("command_line");
Expand Down
21 changes: 21 additions & 0 deletions lib/prism.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ module Prism
private_constant :LexCompat
private_constant :LexRipper

# Raised when requested to parse as the currently running Ruby version but Prism has no support for it.
class CurrentVersionError < ArgumentError
# Initialize a new exception for the given ruby version string.
def initialize(version)
message = +"invalid version: Requested to parse as `version: 'current'`; "
gem_version =
begin
Gem::Version.new(version)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ruby/ruby CI is failing here because Gem is not loaded: https://github.com/ruby/ruby/actions/runs/18561653353/job/52911907413

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh of course. I'll fix it. Shouldn't have cowboy'd.

Copy link
Collaborator Author

@Earlopain Earlopain Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to have messed something up as well https://github.com/ruby/ruby/actions/runs/18562695205/job/52915425235
Can't look into that right now though

(This one doesn't crash: https://github.com/ruby/ruby/actions/runs/18562695142/job/52915424818)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird. I think RUBY_VERSION is just not consistent inside hte CRuby test suite. I reduced a lot of the tests and just ended up with some bare minimum stuff, and it appears to have resolved.

rescue ArgumentError
end

if gem_version && gem_version < Gem::Version.new("3.3.0")
message << " #{version} is below the minimum supported syntax."
else
message << " #{version} is unknown. Please update the `prism` gem."
end

super(message)
end
end

# :call-seq:
# Prism::lex_compat(source, **options) -> LexCompat::Result
#
Expand Down
10 changes: 8 additions & 2 deletions lib/prism/ffi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,9 @@ def dump_options_command_line(options)

# Return the value that should be dumped for the version option.
def dump_options_version(version)
case version
current = version == "current"

case current ? RUBY_VERSION : version
when nil, "latest"
0 # Handled in pm_parser_init
when /\A3\.3(\.\d+)?\z/
Expand All @@ -433,7 +435,11 @@ def dump_options_version(version)
when /\A3\.5(\.\d+)?\z/
3
else
raise ArgumentError, "invalid version: #{version}"
if current
raise CurrentVersionError, RUBY_VERSION
else
raise ArgumentError, "invalid version: #{version}"
end
end
end

Expand Down
4 changes: 4 additions & 0 deletions templates/sig/prism.rbs.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ module Prism
BACKEND: :CEXT | :FFI
VERSION: String

class CurrentVersionError < ArgumentError
def initialize: (String version) -> void
end

# Methods taking a Ruby source code string:
<%-
{
Expand Down
30 changes: 30 additions & 0 deletions test/prism/api/parse_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@ def test_version
end
end

def test_version_current
if RUBY_VERSION >= "3.3"
assert Prism.parse_success?("1 + 1", version: "current")
else
assert_raise(CurrentVersionError) { Prism.parse_success?("1 + 1", version: "current") }
end

version = RUBY_VERSION.split(".").tap { |segments| segments[0] = segments[0].succ }.join(".")
stub_ruby_version(version) do
error = assert_raise(CurrentVersionError) { Prism.parse("1 + 1", version: "current") }
assert_includes error.message, "unknown"
end

stub_ruby_version("2.7.0") do
error = assert_raise(CurrentVersionError) { Prism.parse("1 + 1", version: "current") }
assert_includes error.message, "minimum"
end
end

def test_scopes
assert_kind_of Prism::CallNode, Prism.parse_statement("foo")
assert_kind_of Prism::LocalVariableReadNode, Prism.parse_statement("foo", scopes: [[:foo]])
Expand Down Expand Up @@ -167,5 +186,16 @@ def find_source_file_node(program)
queue.concat(node.compact_child_nodes)
end
end

def stub_ruby_version(version)
old_version = RUBY_VERSION

Object.send(:remove_const, :RUBY_VERSION)
Object.const_set(:RUBY_VERSION, version)
yield
ensure
Object.send(:remove_const, :RUBY_VERSION)
Object.const_set(:RUBY_VERSION, old_version)
end
end
end