[ruby/prism] Check using Prism nodes if a command call has any arguments in Ripper translator

* We don't know what `on_*` events might return so we cannot assume it's an Array.
* See https://github.com/ruby/prism/issues/3838#issuecomment-3774702396

https://github.com/ruby/prism/commit/bed4271ce2
This commit is contained in:
Benoit Daloze 2026-01-20 21:20:06 +01:00 committed by git
parent 519a4bdbc1
commit 913ffcd1dd
2 changed files with 25 additions and 5 deletions

View File

@ -1112,7 +1112,7 @@ module Prism
else
arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
call =
if node.opening_loc.nil? && arguments&.any?
if node.opening_loc.nil? && get_arguments_and_block(node.arguments, node.block).first.any?
bounds(node.location)
on_command(message, arguments)
elsif !node.opening_loc.nil?
@ -1179,17 +1179,25 @@ module Prism
end
end
# Visit the arguments and block of a call node and return the arguments
# and block as they should be used.
private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
# Extract the arguments and block Ripper-style, which means if the block
# is like `&b` then it's moved to arguments.
private def get_arguments_and_block(arguments_node, block_node)
arguments = arguments_node&.arguments || []
block = block_node
if block.is_a?(BlockArgumentNode)
arguments << block
arguments += [block]
block = nil
end
[arguments, block]
end
# Visit the arguments and block of a call node and return the arguments
# and block as they should be used.
private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
arguments, block = get_arguments_and_block(arguments_node, block_node)
[
if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
visit(arguments.first)

View File

@ -110,6 +110,14 @@ module Prism
include Events
end
class ObjectEvents < Translation::Ripper
Prism::Translation::Ripper::PARSER_EVENTS.each do |event|
define_method(:"on_#{event}") do |*args|
Object.new
end
end
end
def test_events
source = "1 rescue 2"
ripper = RipperEvents.new(source)
@ -152,6 +160,10 @@ module Prism
def assert_ripper_sexp_raw(source)
assert_equal Ripper.sexp_raw(source), Prism::Translation::Ripper.sexp_raw(source)
# Similar to test/ripper/assert_parse_files.rb in CRuby
object_events = ObjectEvents.new(source)
assert_nothing_raised { object_events.parse }
end
def assert_ripper_lex(source)