Skip to content

Fix matched_size when the stored string is shrunk - #217

Merged
kou merged 2 commits into
ruby:masterfrom
youdie006:fix-matched-size-when-shrunk
Sep 14, 2026
Merged

kou merged 2 commits into
ruby:masterfrom
youdie006:fix-matched-size-when-shrunk

Conversation

@youdie006

@youdie006 youdie006 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

matched_size returns the raw register width without clamping it to the current length of the stored string, so shrinking the string leaves it reporting the old match:

s = StringScanner.new("abcdef")
s.scan(/abcdef/)
s.string = "abc"
s.matched_size   # => 6, should be 3
s.matched        # => "abc"   (this one is already correct)

extract_range (ext/strscan/strscan.c:168-169) already clamps, which is why matched answers correctly. #212 added the same two lines to integer_at, charpos got them in 7b77f30, and bol? carries its own guard — matched_size was missed.

Fixed in all three backends (CRuby, JRuby, TruffleRuby) with a shared regression test.

Copilot AI lite review requested due to automatic review settings September 10, 2026 03:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes are covered by regression tests and have no unresolved blocking issues.

Pull request overview

Updates matched_size across CRuby, JRuby, and TruffleRuby to respect shortened strings.

Changes:

  • Clamp matched ranges to the current string length.
  • Return nil when the match begins beyond the string.
  • Add shared regression tests.
File summaries
File Summary
test/strscan/test_stringscanner.rb Adds shrinking-string regression tests.
lib/strscan/truffleruby.rb Corrects TruffleRuby matched-size handling.
ext/strscan/strscan.c Corrects CRuby matched-size handling.
ext/jruby/org/jruby/ext/strscan/RubyStringScanner.java Corrects JRuby matched-size handling.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@kou kou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you simplify the PR description?

Comment thread test/strscan/test_stringscanner.rb Outdated
@youdie006

Copy link
Copy Markdown
Contributor Author

Simplified — thanks, and sorry for the wall of text.

matched_size returned the raw register width, so after the stored
string was shrunk under the scanner it disagreed with matched:

  s = StringScanner.new(+"before 29 after")
  s.skip_until(" ")
  s.scan(/\d+/)          #=> "29"
  s.string.replace("before ")
  s.matched              #=> ""
  s.matched_size         #=> 2, should be 0

matched answers correctly because extract_range clamps to the current
length of the string. integer_at was given the same two lines in ruby#212,
charpos in 7b77f30, and bol? carries its own guard. matched_size was
missed in all three implementations.

Clamp it the same way, and return nil once the match start is past the
end of the string, which is what extract_range does.
Copilot AI review requested due to automatic review settings September 14, 2026 05:57
@youdie006
youdie006 force-pushed the fix-matched-size-when-shrunk branch from 7f947b3 to d1b0afd Compare September 14, 2026 05:57
@youdie006

Copy link
Copy Markdown
Contributor Author

The truffleruby rows were red, and the cause turned out to be worth widening the PR slightly.

lib/strscan/truffleruby.rb had matched = @last_match&.to_s, which re-reads nothing and so returns the original match text after the string shrinks. Fixing only matched_size there made the two disagree with each other — matched_size would report 1 while matched still returned "29". So matched now clamps the same way, mirroring extract_range:

beg = Primitive.match_data_byte_begin(@last_match, 0)
return if beg > @string.bytesize
fin = [Primitive.match_data_byte_end(@last_match, 0), @string.bytesize].min
@string.byteslice(beg, fin - beg)

All four backends now use the same shape as extract_range (ext/strscan/strscan.c:168-169): nil when the match begins past the end, otherwise the end clamped to the current length.

CRuby is green locally — 167 tests, 1114 assertions, 0 failures.

One thing I could not verify locally, so I would rather say it than imply otherwise: lib/strscan.rb:12 only loads truffleruby.rb when RUBY_ENGINE_VERSION.to_i >= 34, and the TruffleRuby image I have is 25.0.0, so it takes the stdlib branch. I confirmed from the failing job logs that CI runs 34.0.1 and 40.0.0 on those rows, so they do exercise this file — but the truffleruby rows here are verified by CI, not by me.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The fixes are covered by regression tests and consistently address the stale match-size behavior.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 14, 2026 08:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@kou kou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

We may want to change [], pre_match and so on in the TruffleRuby implementation for shrunk case as a separated tasks.

@kou
kou merged commit 0906399 into ruby:master Sep 14, 2026
43 checks passed
matzbot pushed a commit to ruby/ruby that referenced this pull request Sep 14, 2026
(ruby/strscan#217)

`matched_size` returns the raw register width without clamping it to the
current length of the stored string, so shrinking the string leaves it
reporting the old match:

```ruby
s = StringScanner.new("abcdef")
s.scan(/abcdef/)
s.string = "abc"
s.matched_size   # => 6, should be 3
s.matched        # => "abc"   (this one is already correct)
```

`extract_range` (`ext/strscan/strscan.c:168-169`) already clamps, which
is why `matched` answers correctly. #212 added the same two lines to
`integer_at`, `charpos` got them in `7b77f30`, and `bol?` carries its
own guard — `matched_size` was missed.

Fixed in all three backends (CRuby, JRuby, TruffleRuby) with a shared
regression test.

---------

ruby/strscan@0906399858

Co-authored-by: Sutou Kouhei <kou@cozmixng.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants