FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

mruby-regexp: give an inline toggle the alternatives after it by takumin · Pull Request #7366 · mruby/mruby · GitHub

/ mruby Public

mruby-regexp: give an inline toggle the alternatives after it - #7366

Merged
matz merged 3 commits into
mruby:masterfrom
takumin:regexp-inline-toggle-scope
Aug 26, 2026
Merged

mruby-regexp: give an inline toggle the alternatives after it#7366
matz merged 3 commits into
mruby:masterfrom
takumin:regexp-inline-toggle-scope

Conversation

takumin commented Aug 25, 2026
edited by coderabbitai Bot
Loading

Copy link
Copy Markdown
Contributor

Summary

An inline toggle (?imx) encloses the rest of the group it stands in, alternatives and all, and this engine split its alternation one level too high, so an alternative written after a toggle matched on its own. Fixing that gives the parser a second way to recurse, so how deep a pattern may nest is bounded here too.

Changes

File What
mrbgems/mruby-regexp/src/re_compile.c the option-toggle arm of compile_atom() compiles the rest of the group through compile_alt(); compile_alt() becomes a depth-counting wrapper over compile_alt_body()
mrbgems/mruby-regexp/include/re_internal.h adds MRB_REGEXP_PARSE_DEPTH_LIMIT (4096) and its range guard
mrbgems/mruby-regexp/src/regexp.c adds Regexp::PARSE_DEPTH_LIMIT
mrbgems/mruby-regexp/README.md what the third limit guards, and how a build sizes it to its own stack
mrbgems/mruby-regexp/test/regexp_syntax.rb the alternation after a toggle, and the depth refusal
build_config/ci/gcc-clang.rb ascii-ctype sets the limit to 512, so the refusal has a build that can reach it

An alternation after an inline toggle split one level too high

Onigmo wraps everything after the toggle, alternatives included, into one group under the new options, so a(?i)b|c is a(?i:b|c): parse_exp()'s "option only" branch hands the remainder to parse_subexp(), which is the alternation level. compile_atom() set c->flags and returned to compile_seq(), so the | split at the level the toggle was written at, a(?i)b|c being (?:a(?i)b)|c, and the c matched with no a before it:

/a(?i)b|c/ =~ "c"         # CRuby: nil, mruby: 0
/(a(?i)b|c)d/ =~ "acd"    # CRuby: 0,   mruby: 1
/x(a(?i)b|c)d/ =~ "xacd"  # CRuby: 0,   mruby: nil

The options themselves reached the right atoms either way, which is why the divergence showed only in the shape of the alternation. A toggle-off leaked alike, /a(?-i)b|c/i leaving the c case-sensitive, and so did (?x), whose free-spacing side already agreed.

The toggle arm now compiles that remainder with compile_alt() under the new flags and restores them after. compile_alt() consumes every | and stops at the group's ) or the end of the pattern, which is where the arm's caller already expects the parse point, so what follows is unchanged: the enclosing compile_seq() finds its terminator, mrb_re_compile() still refuses a stray ) at top level, (?i)* is still target of repeat operator is not specified, and capture numbering does not move.

The parser had no bound on its own recursion

compile_alt() calls compile_seq() calls compile_quantified() calls compile_atom(), whose group, lookaround, atomic and inline-option arms call compile_alt() again, so the parser recurses once per nesting level. Nothing counted the levels, and a deep enough pattern ran off the C stack: a pattern of 50000 nested (?: was a SIGSEGV rather than an error. That is pre-existing, but the change above gives the toggle a second way there, since (?i) now opens a level where before it set a flag and returned, so the bound belongs in this PR.

compile_alt(), the one entry the recursion passes through, now counts levels and refuses past MRB_REGEXP_PARSE_DEPTH_LIMIT with parse depth limit over, CRuby's message for the same refusal. Every construct that opens a level counts, the toggle among them, as Onigmo counts them: 4096 nested (?: and 4096 nested (?i) are refused alike there, which is independent confirmation of the tree this parser now builds.

The default is 4096, Onigmo's ONIG_MAX_PARSE_DEPTH, so a pattern is refused exactly where CRuby refuses it, 4095 levels compiling and 4096 raising on both. What that costs is stack. A level takes 560 bytes on a 64-bit gcc -O2 build, 528 at -Os and 592 on the clang -O3 build measured here, which is compile_alt()'s frame plus compile_seq()'s as -fstack-usage reports them, the rest inlining into the two. Measured as the deepest nesting that survives a given ulimit -s, that is:

Stack Deepest that survives A limit that fits
8 MiB 4095 (the limit, not the stack) 4096 (default, CRuby-exact)
1 MiB 1660 512
256 KiB 364 128
64 KiB 42 32

So the deepest pattern the default accepts spends some 2.4 MiB, and so does a deeper one before being refused, since the count is reached at the bottom of the recursion. A build on a smaller stack than that has to lower the limit or keep the crash it exists to prevent; the header and the README both say so, with the -fstack-usage recipe for measuring a build's own per-level figure. The right-hand column is a third of the stack, the compiler not being the only thing standing on it. Nesting this deep is not what a written pattern does, a handful of levels being ordinary and dozens unusual, so a build that sets 128 still takes every pattern anyone writes and gives up only the CRuby-exact refusal point. Regexp::PARSE_DEPTH_LIMIT reads the value back, as Regexp::STACK_LIMIT and Regexp::STEP_LIMIT read theirs.

That trade, a CRuby-exact refusal point against a stack cost a small-stack build must opt out of, is the one choice in this PR a build can be asked to revisit.

The refusal needs a build that can reach it

Reaching a limit costs the stack that limit stands for, so the test skips a build whose limit stands above 512: at the default a pattern descends some 2.4 MiB before being refused, which is more stack than a Windows thread has at all, and no test may spend what the limit exists to keep a pattern from spending.

Nothing else could lift that skip. Which platforms could pay 2.4 MiB is not a question the test can ask: mruby defines no RUBY_PLATFORM, the one signal it has tells Windows from the rest through File::ALT_SEPARATOR, and Cosmopolitan, which runs the suite and whose main stack is smaller than a POSIX host's, is not distinguishable from Linux by it. So a build sets a limit a test may reach instead, which is what the ascii-ctype build now does at 512, some 300 KiB. That build already exists to give an mruby-regexp refusal a home, what /i answers where the build has no folding table, and the depth a pattern may nest is not what ASCII classification is about, so the two do not tread on each other. It rides along there rather than in a build of its own so the matrix runs no extra pass.

Behaviour

Three things an existing pattern can notice.

An alternative after a toggle now wants what precedes the toggle, which is the fix: the three rows above, and a toggle-off and an (?x) alike.

A pattern nested past the limit raises RegexpError where it used to crash or compile. At the default that is 4096 levels or more, where CRuby raises the same error; a build that lowers the limit moves the point.

Three patterns in a 4000-pattern toggle fuzz now reach step limit over (MRB_REGEXP_STEP_LIMIT) where they used to answer. All three are nested quantified alternations carrying a toggle in nearly every sequence; splitting inside the toggle's scope rather than at the level above changes what the search retries, so the same step limit is reached by a different walk. CRuby answers nil for all three, and two of them answered wrongly here before. A control run of 124,000 cases over patterns with no toggle in them answers identically before and after.

Size

.text of bin/mruby, size -A, for the five builds of build_config/ci/gcc-clang.rb, master and this branch built at the same path in an empty build directory:

Build master this PR delta
bintest 1,072,934 1,073,526 +592
ascii-ctype 1,067,926 1,068,406 +480
byte-string 1,047,942 1,048,390 +448
cxx_abi 1,061,029 1,061,621 +592
full-debug (-O0) 1,863,558 1,863,750 +192

Two objects account for it: re_compile.o .text grows by 560, 448, 432, 560 and 112 bytes down that column, and regexp.o by 48, 48, 48, 48 and 80 for the new constant. The ascii-ctype row also carries the MRB_REGEXP_PARSE_DEPTH_LIMIT=512 this PR gives that build, which changes one constant.

For a build that carries the gem on flash, re_compile.o .text at -Os grows by 50 bytes under gcc (13,863 to 13,913) and 56 under clang (15,638 to 15,694), against some 87 KiB for the whole gem. RAM is one uint32_t on re_compiler, which lives on the C stack for the duration of a compile; no heap allocation changes. The C stack per nesting level is what it was under gcc, 560 bytes at -O2 and 528 at -Os, and under clang -Os (480); at clang -O3 it goes from 376 to 592, clang no longer folding compile_seq() into the level. What changed everywhere is that (?i) now opens a level of its own.

Testing

  • rake test on the default config at each of the three commits: 0 KO, 0 crash.
  • MRUBY_CONFIG=build_config/ci/gcc-clang.rb rake test, all five builds: 0 KO, 0 crash. The depth assertions run in ascii-ctype and skip in the other four.
  • The same suite at MRB_REGEXP_PARSE_DEPTH_LIMIT of 48, 512, 1024 and 4096: green at each, the refusal asked at the first two and skipped at the last two, for a plain group, a scoped option group, a lookahead, an atomic group and a toggle. The range guard rejects 0, -1 and 2000000 at compile time.
  • A differential run of 4000 toggle-heavy patterns against 15 subjects each, master and this branch against CRuby 4.0.6. Of the 14,760 answers that parted from CRuby, 14,750 agree now. The ten that still part part the same way when (?i: is written where the toggle stands, so they are not about the toggle's scope: /[BA]*(?i:A)/ =~ "A" is nil in CRuby and 0 here on master too, and /((|b[ab])+[aB])+/ =~ "aB" parts with no option anywhere in it.
  • A control differential run of 124,000 cases over patterns with no toggle: master and this branch answer identically, byte for byte.
  • The crash and its absence, on the binaries built above:
$ master/bin/mruby -e 'Regexp.new("(?:" * 50000 + "a" + ")" * 50000)'
Segmentation fault (core dumped)

$ branch/bin/mruby -e 'Regexp.new("(?:" * 50000 + "a" + ")" * 50000)'
trace (most recent call last):
        [1] -e:1
-e:1:in initialize: parse depth limit over: /(?:(?:(?:...  (RegexpError)

$ (ulimit -s 1024; master/bin/mruby -e 'Regexp.new("(?:" * 5000 + "a" + ")" * 5000)')
Segmentation fault (core dumped)

$ (ulimit -s 1024; ascii-ctype/bin/mruby -e 'Regexp.new("(?:" * 5000 + "a" + ")" * 5000)')
-e:1:in initialize: parse depth limit over: /(?:(?:(?:...  (RegexpError)

(the ascii-ctype build is the one that sets the limit to 512; the error echoes the whole pattern, elided here.)

Environment

Host, toolchain and the compile line of each build
OS Ubuntu 24.04.4 LTS
Kernel Linux 7.0.0-30-generic x86_64
CPU AMD Ryzen 9 5950X (16 cores)
C compiler Homebrew clang 22.1.8 (CC=clang)
Second compiler, for the -fstack-usage and -Os figures gcc 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04.1)
binutils GNU ld / size 2.47.20260726
CRuby, for the differential runs ruby 4.0.6 (2026-07-14) +PRISM

cxx_abi links through clang++; the other four link through clang. The compile line of re_compile.c in each build, with -MMD -c, the -I paths and -o dropped:

# bintest
clang -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -Wzero-length-array -DMRB_GC_FIXED_ARENA -DMRBGEM_MRUBY_REGEXP_VERSION=0.0.0 -DMRB_USE_BIGINT -DMRB_USE_COMPLEX -DHAVE_MRUBY_ENCODING_GEM -DMRB_UTF8_STRING -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DMRB_USE_TASK_SCHEDULER -DMRB_USE_DEBUG_HOOK

# ascii-ctype
clang -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -Wzero-length-array -DMRB_USE_ASCII_CTYPE -DMRB_REGEXP_PARSE_DEPTH_LIMIT=512 -DMRBGEM_MRUBY_REGEXP_VERSION=0.0.0 -DMRB_USE_BIGINT -DMRB_USE_COMPLEX -DHAVE_MRUBY_ENCODING_GEM -DMRB_UTF8_STRING -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DMRB_USE_TASK_SCHEDULER

# byte-string
clang -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -Wzero-length-array -DMRBGEM_MRUBY_REGEXP_VERSION=0.0.0 -DMRB_USE_BIGINT -DMRB_USE_COMPLEX -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DMRB_USE_TASK_SCHEDULER

# cxx_abi
clang -g -O3 -Wall -Wundef -Wwrite-strings -Wzero-length-array -x c++ -std=gnu++03 -DMRB_GC_FIXED_ARENA -DMRBGEM_MRUBY_REGEXP_VERSION=0.0.0 -DMRB_USE_CXX_EXCEPTION -DMRB_USE_CXX_ABI -DMRB_USE_BIGINT -DMRB_USE_COMPLEX -DHAVE_MRUBY_ENCODING_GEM -DMRB_UTF8_STRING -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DMRB_USE_TASK_SCHEDULER

# full-debug
clang -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -Wzero-length-array -g3 -O0 -DMRB_GC_STRESS -DMRB_USE_DEBUG_HOOK -DMRBGEM_MRUBY_REGEXP_VERSION=0.0.0 -DMRB_DEBUG -DMRB_USE_BIGINT -DMRB_USE_COMPLEX -DHAVE_MRUBY_ENCODING_GEM -DMRB_UTF8_STRING -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DMRB_USE_TASK_SCHEDULER

Summary by CodeRabbit

  • New Features

    • Added a configurable regular-expression parser depth limit, defaulting to 4096.
    • Exposed the limit through Regexp::PARSE_DEPTH_LIMIT.
    • Patterns exceeding the configured limit now raise RegexpError.
  • Bug Fixes

    • Improved handling and scoping of inline regular-expression options across alternatives and nested expressions.
  • Documentation

    • Documented configuration, supported values, counted constructs, and stack-sizing guidance.
  • Tests

    • Added coverage for parser-depth limits, inline options, lookarounds, atomic groups, and invalid quantifier usage.

An inline toggle `(?imx)` applies to the rest of the enclosing group in
both engines, but the two disagreed about what "the rest" is when an
alternation follows. Onigmo wraps everything after the toggle,
alternatives included, into one group under the new options, so
`a(?i)b|c` is `a(?i:b|c)`: `parse_exp()`'s "option only" branch hands the
remainder to `parse_subexp()`, which is the alternation level.
`compile_atom()` set `c->flags` and returned to `compile_seq()`, so the
`|` split at the level the toggle was written at, `a(?i)b|c` being
`(?:a(?i)b)|c`, and the `c` matched with no `a` before it:

```ruby
/a(?i)b|c/ =~ "c"         # CRuby: nil, mruby: 0
/(a(?i)b|c)d/ =~ "acd"    # CRuby: 0,   mruby: 1
/x(a(?i)b|c)d/ =~ "xacd"  # CRuby: 0,   mruby: nil
```

The options themselves reached the right atoms either way, which is why
the divergence showed only in the shape of the alternation. A toggle-off
leaked alike, `/a(?-i)b|c/i` leaving the `c` case-sensitive, and so did
`(?x)`, whose free-spacing side already agreed.

Compile that remainder in the toggle arm with `compile_alt()`, under the
new flags, and restore them after it. `compile_alt()` consumes every `|`
and stops at the group's `)` or the end of the pattern, which is where
the arm's caller already expects the parse point, so what follows is
unchanged: the enclosing `compile_seq()` finds its terminator,
`mrb_re_compile()` still refuses a stray `)` at top level, and `(?i)*` is
still `target of repeat operator is not specified`, refused now by the
`compile_seq()` that reads the toggle's scope. Capture numbering does not
move either, groups being counted in source order.

Checked against CRuby 4.0.6: every row above, and a differential run of
4000 toggle-heavy patterns against 15 subjects each. Of the 14760 answers
that parted from CRuby there, 14750 agree now. The ten that still part
part the same way when `(?i:` is written where the toggle stands, so what
they are about is not the toggle's scope: `/[BA]*(?i:A)/ =~ "A"` is `nil`
in CRuby and `0` here, and `/((|b[ab])+[aB])+/ =~ "aB"` parts with no
option anywhere in it.

Three of those 4000 patterns now reach `step limit over
(MRB_REGEXP_STEP_LIMIT)` where they used to answer. All three are nested
quantified alternations carrying a toggle in nearly every sequence, and
splitting inside the toggle's scope rather than at the level above
changes what the search retries, so the same limit is reached by a
different walk. CRuby answers `nil` for all three, and two of them
answered wrongly here before. A control run of 124,000 cases over
patterns with no toggle in them answers identically before and after.
The parser recurses once per nesting level (`compile_alt()` calls
`compile_seq()` calls `compile_quantified()` calls `compile_atom()`, whose
group, lookaround, atomic and inline-option arms call `compile_alt()`
again) and nothing counted the levels, so a deep enough pattern reached
the end of the C stack. A pattern of 50000 nested `(?:` was a SIGSEGV
rather than an error, and the commit before this one gave the toggle a
second way there: `(?i)` now encloses the rest of its group, which is a
level, where before it set a flag and returned.

Count the levels at `compile_alt()`, the one entry the recursion passes
through, and refuse a pattern past `MRB_REGEXP_PARSE_DEPTH_LIMIT` with
`parse depth limit over`, which is CRuby's message for the same refusal.
Every construct that opens a level is counted, the toggle among them, as
Onigmo counts them: a pattern of 4096 nested `(?:` and one of 4096 nested
`(?i)` are refused alike there, which is the tree this parser now builds
too.

The default is 4096, Onigmo's `ONIG_MAX_PARSE_DEPTH`, so a pattern is
refused exactly where CRuby refuses it, 4095 levels compiling and 4096
raising on both. What that costs is stack, and the header says so where a
build will read it: a level takes 560 bytes on a 64-bit gcc `-O2` build,
528 at `-Os`, and 592 on the clang `-O3` build measured here, which is
`compile_alt()`'s frame plus `compile_seq()`'s as `-fstack-usage` reports
them, the rest inlining into the two. On that clang build the deepest
nesting that survives `ulimit -s 1024` is 1660 levels, 364 under 256 and
42 under 64. The gcc figures are what they were before this commit, and so
is clang's at `-Os`; at `-O3` clang stops folding `compile_seq()` into the
level, which takes it from 376 bytes to 592.

So the deepest pattern the default accepts spends some 2.4 MiB, and a
deeper one spends the same before being refused, the count being reached
at the bottom of the recursion. A build whose stack is smaller than that,
the 1 MiB a Windows thread is given among them, has to set the limit to
what it can pay for or keep the crash this limit is here to prevent. A
third of the stack is the share to size it from, the compiler not being
the only thing standing on it: 512 for 1 MiB, 128 for 256 KiB, 32 for 64
KiB. Nesting this deep is not what a written pattern does, so a build that
lowers it still takes every pattern anyone writes and gives up only the
CRuby-exact refusal point.

`Regexp::PARSE_DEPTH_LIMIT` reads the value back, as `Regexp::STACK_LIMIT`
and `Regexp::STEP_LIMIT` read theirs, and the test sizes its patterns from
it so it holds whatever a build sets. A nesting inside the limit compiles
and matches on every build. The refusal is asked only of a build whose
limit is at most 512: reaching a limit costs the stack that limit stands
for, and a test that made a CRuby-exact build descend 4095 levels would
spend the megabytes this limit exists to keep a pattern from spending.
Checked at 48, 512, 1024 and 4096: the suite is green at each, the refusal
asked at the first two and skipped at the last two, for a plain group, a
scoped option group, a lookahead, an atomic group and a toggle. Past the
limit nothing reaches the end of the stack any more where the stack can
hold the limit: at 512, a pattern of 5000 nested `(?:` raises on a 1 MiB
stack where it crashed before.
`parse depth limit over` had no home in CI. Reaching a limit costs the C
stack that limit stands for, about 600 bytes a level, so the test in
mruby-regexp/test/regexp_syntax.rb skips a build whose limit stands above
512: at the CRuby-exact default of 4096 a pattern descends some 2.4 MiB
before being refused, which is more stack than a Windows thread has at
all, and no test may spend what the limit exists to keep a pattern from
spending.

Nothing else could lift that skip. Which platforms could pay 2.4 MiB is
not a question the test can ask: mruby defines no `RUBY_PLATFORM`, the one
signal it has tells Windows from the rest through `File::ALT_SEPARATOR`,
and Cosmopolitan, which runs the suite and whose main stack is smaller
than a POSIX host's, is not distinguishable from Linux by it. Skipping
only the builds a guess calls small would bet the Cosmopolitan and MinGW
jobs on that guess.

So let a build set a limit a test may reach instead, which is what the
`ascii-ctype` build here now does at 512, some 300 KiB. That build already
exists to give an mruby-regexp refusal a home, what `/i` answers where the
build has no folding table, and the depth a pattern may nest is not what
ASCII classification is about, so the two do not tread on each other. It
rides along there rather than in a build of its own so the matrix runs no
extra pass.

What this covers is the refusal, not the constant: the arithmetic is
`++c->depth > MRB_REGEXP_PARSE_DEPTH_LIMIT` and is the same at any limit.
Checked at 48, 512, 1024 and 4096 that the boundary holds either way, one
level inside compiling and one level past raising, and with 512 alongside
`MRB_USE_ASCII_CTYPE`, as this build has it, the suite is green with the
depth assertions running rather than skipped.

coderabbitai Bot commented Aug 25, 2026
edited
Loading

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info ⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3197847f-6c89-4ab0-80e4-2b61465d2e5a

📥 Commits

Reviewing files that changed from the base of the PR and between 44ab336 and 029a7d0.

📒 Files selected for processing (6)
  • build_config/ci/gcc-clang.rb
  • mrbgems/mruby-regexp/README.md
  • mrbgems/mruby-regexp/include/re_internal.h
  • mrbgems/mruby-regexp/src/re_compile.c
  • mrbgems/mruby-regexp/src/regexp.c
  • mrbgems/mruby-regexp/test/regexp_syntax.rb

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The regexp compiler gains a configurable parser nesting limit, validates the setting, exposes it as Regexp::PARSE_DEPTH_LIMIT, changes inline-option scoping, and adds regression coverage. The CI configuration sets a lower limit for overflow testing.

Changes

Regexp parser depth limit

Layer / File(s) Summary
Depth-limit configuration and API
mrbgems/mruby-regexp/include/re_internal.h, mrbgems/mruby-regexp/README.md, mrbgems/mruby-regexp/src/regexp.c, build_config/ci/gcc-clang.rb
The parser limit defaults to 4096, accepts values from 1 through 1,048,576, is documented, exposed as Regexp::PARSE_DEPTH_LIMIT, and set to 512 in the ascii-ctype CI build.
Compiler depth and option-scope handling
mrbgems/mruby-regexp/src/re_compile.c
The compiler tracks alternation-parser depth and raises RegexpError when the configured limit is exceeded. Inline option toggles now compile the enclosing alternation scope and restore flags afterward.
Depth and option-scope regression coverage
mrbgems/mruby-regexp/test/regexp_syntax.rb
Tests cover nesting limits, groups, options, lookaheads, atomic groups, alternative scoping, and quantifiers following inline options.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 029a7

The PR corrects inline-toggle alternation handling and adds bounded parser-depth protection; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: matz

Sequence Diagram(s)

sequenceDiagram
  participant RegexpCompiler
  participant compile_alt
  participant compile_alt_body
  RegexpCompiler->>compile_alt: compile nested alternation
  compile_alt->>compile_alt: track parser depth
  compile_alt->>compile_alt_body: compile alternation scope
  compile_alt_body-->>compile_alt: return compiled body
  compile_alt-->>RegexpCompiler: return or raise RegexpError
Loading 🚥 Pre-merge checks | ✅ 5 ✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the inline-toggle scoping change, which is a primary objective of the pull request. It is concise and specific. The additional parse-depth safeguard does not need to appear…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 5 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title clearly describes the inline-toggle scoping change, which is a primary objective of the pull request. It is concise and specific. The additional parse-depth safeguard does not need to appear in the title.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 5 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

matz merged commit aaa5c40 into mruby:master Aug 26, 2026
21 checks passed
takumin deleted the regexp-inline-toggle-scope branch August 26, 2026 01:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL