Skip to content

Commit cf290f1

Browse files
committed
automata: fix a bug in the reverse suffix/inner search
1 parent 436e80a commit cf290f1

2 files changed

Lines changed: 42 additions & 9 deletions

File tree

regex-automata/src/meta/strategy.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,9 +1215,10 @@ impl ReverseSuffix {
12151215
) -> Result<Option<HalfMatch>, RetryError> {
12161216
let mut span = input.get_span();
12171217
let mut min_start = 0;
1218+
let mut first: Option<HalfMatch> = None;
12181219
loop {
12191220
let litmatch = match self.pre.find(input.haystack(), span) {
1220-
None => return Ok(None),
1221+
None => break,
12211222
Some(span) => span,
12221223
};
12231224
trace!("reverse suffix scan found suffix match at {litmatch:?}");
@@ -1228,7 +1229,13 @@ impl ReverseSuffix {
12281229
if let Some(hm) =
12291230
self.try_search_half_rev_limited(cache, &revinput, min_start)?
12301231
{
1231-
return Ok(Some(hm));
1232+
// We only track the first half-match. We can never find a
1233+
// half-match with lower offset than the first half-match, it
1234+
// would mean quadratic behavior. We can only confirm this
1235+
// half-match or return a quadratic error.
1236+
if first.is_none() {
1237+
first = Some(hm);
1238+
}
12321239
}
12331240

12341241
if span.start >= span.end {
@@ -1237,7 +1244,7 @@ impl ReverseSuffix {
12371244
span.start = litmatch.start.checked_add(1).unwrap();
12381245
min_start = litmatch.end;
12391246
}
1240-
Ok(None)
1247+
Ok(first)
12411248
}
12421249

12431250
#[cfg_attr(feature = "perf-inline", inline(always))]
@@ -1623,9 +1630,10 @@ impl ReverseInner {
16231630
let mut span = input.get_span();
16241631
let mut min_match_start = 0;
16251632
let mut min_pre_start = 0;
1633+
let mut first: Option<Match> = None;
16261634
loop {
16271635
let litmatch = match self.preinner.find(input.haystack(), span) {
1628-
None => return Ok(None),
1636+
None => break,
16291637
Some(span) => span,
16301638
};
16311639
if litmatch.start < min_pre_start {
@@ -1662,10 +1670,12 @@ impl ReverseInner {
16621670
span.start = litmatch.start.checked_add(1).unwrap();
16631671
}
16641672
Ok(hm_end) => {
1665-
return Ok(Some(Match::new(
1666-
hm_start.pattern(),
1667-
hm_start.offset()..hm_end.offset(),
1668-
)))
1673+
if first.is_none() {
1674+
first = Some(Match::new(
1675+
hm_start.pattern(),
1676+
hm_start.offset()..hm_end.offset(),
1677+
));
1678+
}
16691679
}
16701680
}
16711681
}
@@ -1676,7 +1686,7 @@ impl ReverseInner {
16761686
span.start = litmatch.start.checked_add(1).unwrap();
16771687
min_match_start = litmatch.end;
16781688
}
1679-
Ok(None)
1689+
Ok(first)
16801690
}
16811691

16821692
#[cfg_attr(feature = "perf-inline", inline(always))]

testdata/regression.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,21 @@ matches = []
409409
unicode = false
410410
utf8 = false
411411

412+
# See: https://github.com/rust-lang/regex/issues/1354
413+
[[test]]
414+
name = 'reverse-suffix-start-of-match-failure-010'
415+
regex = '.abb|b'
416+
haystack = 'zabb'
417+
matches = [[0, 4]]
418+
419+
# See: https://github.com/rust-lang/regex/issues/1354
420+
# See: https://github.com/rust-lang/regex/pull/1355
421+
[[test]]
422+
name = 'reverse-suffix-start-of-match-failure-011'
423+
regex = '.abb|b'
424+
haystack = 'zzzabbz'
425+
matches = [[2, 6]]
426+
412427
# See: https://github.com/BurntSushi/ripgrep/issues/1247
413428
[[test]]
414429
name = "stops"
@@ -757,6 +772,14 @@ regex = '(?:([0-9][0-9][0-9]):)?([0-9][0-9]):([0-9][0-9])'
757772
haystack = '102:12:39'
758773
matches = [[[0, 9], [0, 3], [4, 6], [7, 9]]]
759774

775+
776+
# See: https://github.com/rust-lang/regex/issues/1354
777+
[[test]]
778+
name = 'reverse-inner-start-of-match-failure-010'
779+
regex = '(?:..acbb|b)a(?:c|d)'
780+
haystack = 'xzbacbbac'
781+
matches = [[1, 9]]
782+
760783
# This regression test was found via the RegexSet APIs. It triggered a
761784
# particular code path where a regex was compiled with 'All' match semantics
762785
# (to support overlapping search), but got funneled down into a standard

0 commit comments

Comments
 (0)