Skip to content

Prism::RipperCompat differences with Ripper #1972

@schneems

Description

@schneems

I found that Ripper and Prism::RipperCompat produced different results on this invalid ruby code %Q* lol. This is using %Q to define a string and then declaring it will use the * character to end the string. It's invalid because it is missing a second * like %Q* lol*.

Reproduction

Define subclasses:

require 'prism'
require 'ripper'

class RipperErrors < Ripper
  attr_reader :errors

  # Comes from ripper, called
  # on every parse error, msg
  # is a string
  def on_parse_error(msg)
    @errors ||= []
    @errors << msg
  end

  alias_method :on_alias_error, :on_parse_error
  alias_method :on_assign_error, :on_parse_error
  alias_method :on_class_name_error, :on_parse_error
  alias_method :on_param_error, :on_parse_error
  alias_method :compile_error, :on_parse_error

  def call
    @run_once ||= begin
      @errors = []
      parse
      true
    end
    self
  end
end

class PrismErrors < Prism::RipperCompat
  attr_reader :errors

  # Comes from ripper, called
  # on every parse error, msg
  # is a string
  def on_parse_error(msg)
    @errors ||= []
    @errors << msg
  end

  alias_method :on_alias_error, :on_parse_error
  alias_method :on_assign_error, :on_parse_error
  alias_method :on_class_name_error, :on_parse_error
  alias_method :on_param_error, :on_parse_error
  alias_method :compile_error, :on_parse_error

  def call
    @run_once ||= begin
      @errors = []
      parse
      true
    end
    self
  end
end

Expected

I would expect that the output of these two are the same

Actual

Ripper reports an error but prism does not:

puts RipperErrors.new('%Q* lol').call.errors.inspect
# => ["unterminated string meets end of file"]
puts PrismErrors.new('%Q* lol').call.errors.inspect
# => []

It's also worth noting that Ripper can parse the valid code but prism cannot:

puts RipperErrors.new('%Q* lol*').call.errors.inspect
# => []
puts PrismErrors.new('%Q* lol*').call.errors.inspect
/Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/node.rb:15270:in `accept': undefined method `visit_string_node' for #<PrismErrors:0x00000001065e67d8 @source="%Q* lol*", @result=#<Prism::ParseResult:0x00000001065e62d8 @value=@ ProgramNode (location: (1,0)-(1,8)) (NoMethodError)
├── locals: []
└── statements:
    @ StatementsNode (location: (1,0)-(1,8))
    └── body: (length: 1)
        └── @ StringNode (location: (1,0)-(1,8))
            ├── flags: ∅
            ├── opening_loc: (1,0)-(1,3) = "%Q*"
            ├── content_loc: (1,3)-(1,7) = " lol"
            ├── closing_loc: (1,7)-(1,8) = "*"
            └── unescaped: " lol"
, @comments=[], @magic_comments=[], @errors=[], @warnings=[], @source=#<Prism::Source:0x00000001065e6710 @source="%Q* lol*", @start_line=1, @offsets=[0]>>, @lineno=1, @column=0, @errors=[]>

      visitor.visit_string_node(self)
             ^^^^^^^^^^^^^^^^^^
Did you mean?  visit_integer_node
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:95:in `visit'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:123:in `block in visit_statements_node'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:122:in `each'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:122:in `inject'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:122:in `visit_statements_node'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/node.rb:15161:in `accept'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:95:in `visit'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:144:in `visit_program_node'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/node.rb:13305:in `accept'
	from /Users/rschneeman/.gem/ruby/3.1.4/gems/prism-0.18.0/lib/prism/ripper_compat.rb:85:in `parse'
	from (irb):104:in `call'
	from (irb):123:in `<main>'
	from /Users/rschneeman/.rubies/ruby-3.1.4/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
	from /Users/rschneeman/.rubies/ruby-3.1.4/bin/irb:25:in `load'
	from /Users/rschneeman/.rubies/ruby-3.1.4/bin/irb:25:in `<main>'

This is with prism 0.18.0 and Ruby 3.1.4 (also tested with 3.2.2, same result).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions