ruby / error_highlight

The gem enhances Exception#message by adding a short explanation where the exception is raised
MIT License
150 stars 23 forks source link

Question: understanding strings appended to NoMethodError message upon raise #20

Closed searls closed 1 year ago

searls commented 2 years ago

Hello @mame and friends! Thank you for this gem and for working to improve the error experience in Ruby 3.1

I have a few gems that instantiate or extend built-in error types other than StandardError, often to imitate default behavior when metaprogramming. Under Ruby 3.1, Mocktail's test suite started failing because of changes introduced by the error_highlight gem. Specifically, I'm working on fixing this test at the moment.

To verify this is related to the error_highlight gem, I confirmed that this passes:

$ RUBYLIB='./lib:./test' ruby --disable-error_highlight test/unit/raises_neato_no_method_error_test.rb

It seems that this affects NoMethodError objects I create but not StandardError (I haven't tested other types), and only after they have been raised. Here is a minimal example of my issue:

begin
  e = StandardError.new("pants")
  puts "StandardError message BEFORE raise: #{e.message}"
  raise e
rescue => e
  puts "StandardError message AFTER raise: #{e.message}\n\n"
end

begin
  e = NoMethodError.new("pants")
  puts "NoMethodError message BEFORE raise: #{e.message}"
  raise e
rescue => e
  puts "NoMethodError message AFTER raise: #{e.message}"
end

This will output:

StandardError message BEFORE raise: pants
StandardError message AFTER raise: pants

NoMethodError message BEFORE raise: pants
NoMethodError message AFTER raise: pants

  raise e
  ^^^^^

Because I am instantiating these errors myself in library code, appending their raise line (which is inside my gem) is confusing and not helpful to the user.

As a result, I am wondering:

searls commented 2 years ago

As usual @al2o3cr came up with a great workaround for me case: the three argument version of raise, rather than instantiating the error myself.

Invoked this way, with raise NoMethodError, "pants", caller[1..], the custom message can be evaded.

Will leave this open for now if anyone wants to respond because I may just be lucky in this case.

dentarg commented 2 years ago

Yeah, adding this type of information to NameError#message was very unexpected.

As an example, it changes the default 404 response in Sinatra (when run in production): https://github.com/sinatra/sinatra/issues/1772 (the master branch in Sinatra has since been updated to not inherit NameError, as a fix for other reported issues, but that change has not been released yet)

voxik commented 2 years ago

Please don't forget this is not just about error_highlight, but also about did_you_mean. error_highlight is just more prominent.

mame commented 1 year ago

Very sorry for my late reply!!!

Ruby 3.2 or later

Ruby 3.2 will provide Exception#detailed_message, which is a new API for error_highlight and did_you_mean to enhance error output. If the new API is available, error_highlight and did_you_mean use it and not mess with Exception#message. So I think your problem is solved.

Ruby 3.1 or earlier

Since Exception#detailed_message is a Ruby 3.2 or later API, Ruby 3.1 (or before) continue to change Exception#message.

For the case, did_you_mean provides an API called NameError#original_message. You can use it to write:

  puts "NoMethodError message AFTER raise: #{e.respond_to?(:original_message) ? e.original_message : e.message}"

Though it is a bit tedious, I think you'll get the result you're looking for.

searls commented 1 year ago

This is fantastic, @mame! Thank you!