1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-11-23 21:54:45 +02:00

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
This commit is contained in:
Andrew Gallant
2025-10-12 16:29:10 -04:00
parent 916415857f
commit de2567a4c7
3 changed files with 27 additions and 4 deletions

View File

@@ -4,6 +4,8 @@ Unreleased changes. Release notes have not yet been written.
Performance improvements: Performance improvements:
* [PERF #2111](https://github.com/BurntSushi/ripgrep/issues/2111):
Don't resolve helper binaries on Windows when `-z/--search-zip` isn't used.
* [PERF #2865](https://github.com/BurntSushi/ripgrep/pull/2865): * [PERF #2865](https://github.com/BurntSushi/ripgrep/pull/2865):
Avoid using path canonicalization on Windows when emitting hyperlinks. Avoid using path canonicalization on Windows when emitting hyperlinks.
@@ -46,6 +48,8 @@ Bug fixes:
Statically compile PCRE2 into macOS release artifacts on `aarch64`. Statically compile PCRE2 into macOS release artifacts on `aarch64`.
* [BUG #3173](https://github.com/BurntSushi/ripgrep/issues/3173): * [BUG #3173](https://github.com/BurntSushi/ripgrep/issues/3173):
Fix ancestor ignore filter bug when searching whitelisted hidden files. Fix ancestor ignore filter bug when searching whitelisted hidden files.
* [BUG #3180](https://github.com/BurntSushi/ripgrep/issues/3180):
Fix a panicking bug when using `-U/--multiline` and `-r/--replace`.
Feature enhancements: Feature enhancements:

View File

@@ -59,7 +59,8 @@ impl<M: Matcher> Replacer<M> {
// See the giant comment in 'find_iter_at_in_context' below for why we // See the giant comment in 'find_iter_at_in_context' below for why we
// do this dance. // do this dance.
let is_multi_line = searcher.multi_line_with_matcher(&matcher); let is_multi_line = searcher.multi_line_with_matcher(&matcher);
// Get the line_terminator that was removed (if any) so we can add it back // Get the line_terminator that was removed (if any) so we can add it
// back.
let line_terminator = if is_multi_line { let line_terminator = if is_multi_line {
if haystack[range.end..].len() >= MAX_LOOK_AHEAD { if haystack[range.end..].len() >= MAX_LOOK_AHEAD {
haystack = &haystack[..range.end + MAX_LOOK_AHEAD]; haystack = &haystack[..range.end + MAX_LOOK_AHEAD];
@@ -513,7 +514,8 @@ where
// Otherwise, it's possible for the regex (via look-around) to observe // Otherwise, it's possible for the regex (via look-around) to observe
// the line terminator and not match because of it. // the line terminator and not match because of it.
let mut m = Match::new(0, range.end); let mut m = Match::new(0, range.end);
// No need to rember the line terminator as we aren't doing a replace here // No need to rember the line terminator as we aren't doing a replace
// here.
trim_line_terminator(searcher, bytes, &mut m); trim_line_terminator(searcher, bytes, &mut m);
bytes = &bytes[..m.end()]; bytes = &bytes[..m.end()];
} }
@@ -575,9 +577,13 @@ where
last_match = m.end(); last_match = m.end();
append(caps, dst) append(caps, dst)
})?; })?;
let end = std::cmp::min(bytes.len(), range.end); let end = if last_match > range.end {
bytes.len()
} else {
std::cmp::min(bytes.len(), range.end)
};
dst.extend(&bytes[last_match..end]); dst.extend(&bytes[last_match..end]);
// Add back any line terminator // Add back any line terminator.
dst.extend(line_terminator); dst.extend(line_terminator);
Ok(()) Ok(())
} }

View File

@@ -1654,3 +1654,16 @@ rgtest!(r3173_hidden_whitelist_only_dot, |dir: Dir, _: TestCommand| {
eqnice!(cmd().args(&["--files", "."]).stdout(), "./.foo.txt\n"); eqnice!(cmd().args(&["--files", "."]).stdout(), "./.foo.txt\n");
eqnice!(cmd().args(&["--files", "./"]).stdout(), "./.foo.txt\n"); eqnice!(cmd().args(&["--files", "./"]).stdout(), "./.foo.txt\n");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/3180
rgtest!(r3180_look_around_panic, |dir: Dir, mut cmd: TestCommand| {
dir.create("haystack", " b b b b b b b b\nc\n");
let got = cmd
.arg(r#"(^|[^a-z])((([a-z]+)?)\s)?b(\s([a-z]+)?)($|[^a-z])"#)
.arg("haystack")
.arg("-U")
.arg("-rx")
.stdout();
eqnice!("xbxbx\n", got);
});