Project

General

Profile

Actions

Bug #22422

open

MatchData#integer_at returns nil instead of 0 for captures without digits (Possible difference from ticket intention)

Bug #22422: MatchData#integer_at returns nil instead of 0 for captures without digits (Possible difference from ticket intention)

Added by zevblut (Zev Blut) about 11 hours ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 4.1.0dev (2026-10-07T17:29:37Z master 6203c21e58) +PRISM [arm64-darwin25]
[ruby-core:127025]

Description

The implementation for Feature #21932 differs from what the desired spec says from the comments.
I found this while implementing the feature for JRuby. Please confirm if we want to stay with how it is implemented or adjust to the comments.

Here are the details:

MatchData#integer_at returns nil for a capture that matched but contains no digits. Feature #21932 says it should behave like $N&.to_i, returning 0 there and nil only when the group did not match (note 7, with the reasons in note 9). The rdoc also says "$~.integer_at(N) is equivalent to $N&.to_i".

Steps to reproduce

# Compare MatchData#integer_at with $N&.to_i, which Feature #21932 says it is equivalent to.
[
  ["2024", 10], ["12abc", 10],
  ["foo", 10], ["", 10], ["T", 10], ["_1", 10], ["0x", 0],
].each do |capture, base|
  m = /\A(.*)\z/.match(capture)
  printf "%-8p base %-2d integer_at: %-5p $N&.to_i: %p\n", capture, base, m.integer_at(1, base), m[1]&.to_i(base)
end
m = /(a)|b/.match("b")
printf "%-17s integer_at: %-5p $N&.to_i: %p\n", "unmatched group", m.integer_at(1), m[1]&.to_i

Actual result

ruby 4.1.0dev (2026-10-07T17:29:37Z master 6203c21e58) +PRISM [arm64-darwin25]
"2024"   base 10 integer_at: 2024  $N&.to_i: 2024
"12abc"  base 10 integer_at: 12    $N&.to_i: 12
"foo"    base 10 integer_at: nil   $N&.to_i: 0
""       base 10 integer_at: nil   $N&.to_i: 0
"T"      base 10 integer_at: nil   $N&.to_i: 0
"_1"     base 10 integer_at: nil   $N&.to_i: 0
"0x"     base 0  integer_at: nil   $N&.to_i: 0
unmatched group   integer_at: nil   $N&.to_i: nil

Expected result

Both columns match: integer_at returns 0 for "foo", "", "T", "_1" and "0x", and nil only for the unmatched group. That is the behavior described in #21932 note 7 ("foo" → 0, "" → 0). Note 9 gives the reason: nil is kept to tell an unmatched group apart from a matched "0".

Cause

match_integer_at returns the result of rb_int_parse_cstr as is (re.c#L3871-L3873). That result is nil when no digits were parsed. String#to_i uses the same parser, but rb_str_convert_to_inum turns that nil into 0 (bignum.c#L4343-L4350). The implementation was written before note 7.

The current behavior is also recorded in ruby/spec, in core/matchdata/integer_at_spec.rb ("returns nil on non-integer matches", "T" → nil). That example would need to change too. test_match_integer_at only checks an unmatched group, which is nil either way.

If nil is the intended behavior after all, then the rdoc and #21932 should say so instead.

Rdoc example

Separately, one rdoc example has the wrong result:

/\d+/.match("011").integer_at(0)  #=> 10 in the rdoc, but actually 11

The examples after it (base 12 → 13, base 0 → 9) are correct.


integer_at is new in 4.1, so it is not in the latest stable release (4.0.7). I tested on master at 6203c21e58, and re.c is unchanged in today's master (966eaa54c2). JRuby implemented integer_at to match CRuby's current behavior (https://github.com/jruby/jruby/pull/9767) and will follow whichever behavior is chosen here.


Files

integer_at_repro.rb (479 Bytes) integer_at_repro.rb zevblut (Zev Blut), 10/09/2026 05:50 AM

No data to display

Actions

Also available in: PDF Atom