Skip to content

Commit de2567a

Browse files
committed
printer: fix panic in replacements in look-around corner case
The abstraction boundary fuck up is the gift that keeps on giving. It turns out that the invariant that the match would never exceed the range given is not always true. So we kludge around it. Also, update the CHANGELOG to include the fix for #2111. Fixes #3180
1 parent 9164158 commit de2567a

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Unreleased changes. Release notes have not yet been written.
44

55
Performance improvements:
66

7+
* [PERF #2111](https://github.com/BurntSushi/ripgrep/issues/2111):
8+
Don't resolve helper binaries on Windows when `-z/--search-zip` isn't used.
79
* [PERF #2865](https://github.com/BurntSushi/ripgrep/pull/2865):
810
Avoid using path canonicalization on Windows when emitting hyperlinks.
911

@@ -46,6 +48,8 @@ Bug fixes:
4648
Statically compile PCRE2 into macOS release artifacts on `aarch64`.
4749
* [BUG #3173](https://github.com/BurntSushi/ripgrep/issues/3173):
4850
Fix ancestor ignore filter bug when searching whitelisted hidden files.
51+
* [BUG #3180](https://github.com/BurntSushi/ripgrep/issues/3180):
52+
Fix a panicking bug when using `-U/--multiline` and `-r/--replace`.
4953

5054
Feature enhancements:
5155

crates/printer/src/util.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ impl<M: Matcher> Replacer<M> {
5959
// See the giant comment in 'find_iter_at_in_context' below for why we
6060
// do this dance.
6161
let is_multi_line = searcher.multi_line_with_matcher(&matcher);
62-
// Get the line_terminator that was removed (if any) so we can add it back
62+
// Get the line_terminator that was removed (if any) so we can add it
63+
// back.
6364
let line_terminator = if is_multi_line {
6465
if haystack[range.end..].len() >= MAX_LOOK_AHEAD {
6566
haystack = &haystack[..range.end + MAX_LOOK_AHEAD];
@@ -513,7 +514,8 @@ where
513514
// Otherwise, it's possible for the regex (via look-around) to observe
514515
// the line terminator and not match because of it.
515516
let mut m = Match::new(0, range.end);
516-
// No need to rember the line terminator as we aren't doing a replace here
517+
// No need to rember the line terminator as we aren't doing a replace
518+
// here.
517519
trim_line_terminator(searcher, bytes, &mut m);
518520
bytes = &bytes[..m.end()];
519521
}
@@ -575,9 +577,13 @@ where
575577
last_match = m.end();
576578
append(caps, dst)
577579
})?;
578-
let end = std::cmp::min(bytes.len(), range.end);
580+
let end = if last_match > range.end {
581+
bytes.len()
582+
} else {
583+
std::cmp::min(bytes.len(), range.end)
584+
};
579585
dst.extend(&bytes[last_match..end]);
580-
// Add back any line terminator
586+
// Add back any line terminator.
581587
dst.extend(line_terminator);
582588
Ok(())
583589
}

tests/regression.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,3 +1654,16 @@ rgtest!(r3173_hidden_whitelist_only_dot, |dir: Dir, _: TestCommand| {
16541654
eqnice!(cmd().args(&["--files", "."]).stdout(), "./.foo.txt\n");
16551655
eqnice!(cmd().args(&["--files", "./"]).stdout(), "./.foo.txt\n");
16561656
});
1657+
1658+
// See: https://github.com/BurntSushi/ripgrep/issues/3180
1659+
rgtest!(r3180_look_around_panic, |dir: Dir, mut cmd: TestCommand| {
1660+
dir.create("haystack", " b b b b b b b b\nc\n");
1661+
1662+
let got = cmd
1663+
.arg(r#"(^|[^a-z])((([a-z]+)?)\s)?b(\s([a-z]+)?)($|[^a-z])"#)
1664+
.arg("haystack")
1665+
.arg("-U")
1666+
.arg("-rx")
1667+
.stdout();
1668+
eqnice!("xbxbx\n", got);
1669+
});

0 commit comments

Comments
 (0)