[#122369] [Ruby Bug#21392] Data classes do not allow overriding #inspect — "austin (Austin Ziegler) via ruby-core" <ruby-core@...>

Issue #21392 has been reported by austin (Austin Ziegler).

8 messages 2025/06/01

[#122411] [Ruby Bug#21396] Set#initialize should call Set#add on items passed in — "tenderlovemaking (Aaron Patterson) via ruby-core" <ruby-core@...>

Issue #21396 has been reported by tenderlovemaking (Aaron Patterson).

12 messages 2025/06/04

[#122506] [Ruby Feature#21435] Kernel#optional as a conditional #then — "Alexander.Senko (Alexander Senko) via ruby-core" <ruby-core@...>

SXNzdWUgIzIxNDM1IGhhcyBiZWVuIHJlcG9ydGVkIGJ5IEFsZXhhbmRlci5TZW5rbyAoQWxleGFu

11 messages 2025/06/10

[#122557] [Ruby Bug#21445] [BUG] push_mark_stack() called for broken object raised since cd9f447be247478d2eb3da985295735cce20cb23 — "yahonda (Yasuo Honda) via ruby-core" <ruby-core@...>

Issue #21445 has been reported by yahonda (Yasuo Honda).

10 messages 2025/06/19

[#122615] [Ruby Misc#21458] Test 'make install'? — "MSP-Greg (Greg L) via ruby-core" <ruby-core@...>

Issue #21458 has been reported by MSP-Greg (Greg L).

11 messages 2025/06/28

[ruby-core:122443] [Ruby Feature#21279] Bare "rescue" should not rescue NameError

From: "matz (Yukihiro Matsumoto) via ruby-core" <ruby-core@...>
Date: 2025-06-05 09:11:52 UTC
List: ruby-core #122443
Issue #21279 has been updated by matz (Yukihiro Matsumoto).

Status changed from Open to Closed

Upon considering the historic constraint (and compatibility concern), I choose to keep `NameError` as it is.
Thank you for the proposal.

Matz.


----------------------------------------
Feature #21279: Bare "rescue" should not rescue NameError
https://bugs.ruby-lang.org/issues/21279#change-113624

* Author: AMomchilov (Alexander Momchilov)
* Status: Closed
----------------------------------------
# Abstract

Bare `rescue` keywords (either as a modifier like `foo rescue bar` or as clause of a `begin` block) should _not_ rescue `NameError` or `NoMethodError`.

This behaviour is unexpected and hides bugs.

## Background

Many Rubyists are surprised to learn that [`NameError`](https://docs.ruby-lang.org/en/master/NameError.html) is a subclass of [`StandardError`](https://docs.ruby-lang.org/en/master/StandardError.html), so it's caught whenever you use a "bare" `rescue` block. 

```ruby
begin
  DoesNotExist
rescue => e
  p e # => #<NameError: uninitialized constant DoesNotExist>
end
```

Similarly, [`NoMethodError`](https://docs.ruby-lang.org/en/master/NoMethodError.html) is also rescued, because it's a subclass of `NameError`.

```ruby
begin
  does_not_exist()
rescue => e
  p e # => #<NoMethodError: undefined method `does_not_exist' for main>
end
```

This is almost never expected behaviour. `NameError`/`NoMethodError` is usually the result of a typo in the Ruby source, that cannot be reasonably recovered from at runtime. It's a programming error just like a [`SyntaxError`](https://docs.ruby-lang.org/en/master/SyntaxError.html), which _isn't_ a `StandandError`.

## Proposal

No matter the solution, solving this problem will require a breaking change. Perhaps this could be part of Ruby 4?

The most obvious solution is to change the superclass of `NameError` from `StandardError` to `Exception` (or perhaps [`ScriptError`](https://docs.ruby-lang.org/en/master/ScriptError.html), similar to `SyntaxError`).

### Alternatives considered

If we want to avoid changing the inheritance hierarchy of standard library classes, we could instead change the semantics of bare `rescue` from "rescues any subtype of `StandardError`", to instead be "rescues any subtype of `StandardError` except `NameError` or its subtypes". This is worse in my opinion, as it complicates the semantics for no good reason.

## Use cases

<details><summary>fun example</summary>
The worst case I've seen of this came from a unit tesat like so:

```ruby
test "aborts if create_user returns error" do
  mock_user_action(data: {
    user: { id: 123, ... },
    errors: [{ code: "foo123" }]
  })

  ex = assert_raises(StandardError) do
    CreateUser.perform(123)
  end

  assert_match(/foo123/, ex.message)
end
```

This test passes, but not for the expected reason. It turns out that inside of the business logic of `CreateUser`, the error code data was accessed as a method call like `error.code`, rather than a key like `error[:code]`. This lead to:

```
NoMethodError (undefined method `code' for {:code=>"foo123"}:Hash)
```

The `NoMethodError` is a `StandardError`, and even more insidious, because `foo123` is part of the NoMethodError's default message, the `assert_match(/foo123/, ex.message)` also mathches!

The correct fix here would be to introduce a specific error like `UserCreationError` that can be rescued specifically, with a field like `code` that can be matched instead of the message. Regardless, this illustrates the kind of confusion that comes from `NoMethodError` being a `StandardError`.
</details>

# Discussion

It might be useful to distinguish between `NameError`s made in "static" code like `DoesNotExist` or `does_not_exist()`, versus those encountered dynamically via `Object.const_get(dynamic_value)` or `object.send(dynamic_value)`. In those metaprogramming cases, the error could be a consequence of bad runtime data, which is more recoverable than just some fundamental error with your Ruby code.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/


In This Thread

Prev Next