[ruby/irb] Rescue errors from main.to_s/inspect when formatting

prompt
(https://github.com/ruby/irb/pull/791)

Currently, IRB just terminates if `main.to_s` raises while IRB
constructs the prompt string. This can easily happen if the user wants
to start an IRB session in the instance scope of an uninitialized
object, for example:

```
class C
  def initialize
    binding.irb
    @values = []
  end

  def to_s = @values.join(',')  # raises if uninitialized
end

C.new
```

This patch makes IRB rescue from such an exception and displays the
class name of the exception instead of `main.to_s` to indicate some
error has occurred.

We may display more detailed information about the exception, but this
patch chooses not to do so because 1) the prompt has limited space,
2) users can evaluate `to_s` in IRB to examine the error if they want,
and 3) obtaining the details can also raise, which requires nested
exception handling and can be complicated.

https://github.com/ruby/irb/commit/412ab26067
This commit is contained in:
Kasumi Hanazuki 2023-11-28 23:05:26 +09:00 коммит произвёл git
Родитель 9365b78d49
Коммит 5fc71feb6c
2 изменённых файлов: 13 добавлений и 2 удалений

Просмотреть файл

@ -930,9 +930,11 @@ module IRB
when "N"
@context.irb_name
when "m"
truncate_prompt_main(@context.main.to_s)
main_str = @context.main.to_s rescue "!#{$!.class}"
truncate_prompt_main(main_str)
when "M"
truncate_prompt_main(@context.main.inspect)
main_str = @context.main.inspect rescue "!#{$!.class}"
truncate_prompt_main(main_str)
when "l"
ltype
when "i"

Просмотреть файл

@ -631,6 +631,15 @@ module TestIRB
assert_equal('irb("aaaaaaaaaaaaaaaaaaaaaaaaaaaa...)>', irb.send(:format_prompt, 'irb(%M)>', nil, 1, 1))
end
def test_prompt_main_raise
main = Object.new
def main.to_s; raise TypeError; end
def main.inspect; raise ArgumentError; end
irb = IRB::Irb.new(IRB::WorkSpace.new(main), TestInputMethod.new)
assert_equal("irb(!TypeError)>", irb.send(:format_prompt, 'irb(%m)>', nil, 1, 1))
assert_equal("irb(!ArgumentError)>", irb.send(:format_prompt, 'irb(%M)>', nil, 1, 1))
end
def test_lineno
input = TestInputMethod.new([
"\n",