Skip to content

Commit afc820c

Browse files
committed
cli: make rg -vf file behave sensibly
Previously, when `file` is empty (literally empty, as in, zero byte), `rg -f file` and `rg -vf file` would behave identically. This is odd and also doesn't match how GNU grep behaves. It's also not logically correct. An empty file means _zero_ patterns which is an empty set. An empty set matches nothing. Inverting the empty set should result in matching everything. This was because of an errant optimization that lets ripgrep quit early if it can statically detect that no matches are possible. Moreover, there was *also* a bug in how we constructed the PCRE2 pattern when there are zero patterns. PCRE2 doesn't have a concept of sets of patterns (unlike the `regex` crate), so we need to fake it with an empty character class. Fixes #1332, Fixes #3001, Closes #3041
1 parent b9f2da2 commit afc820c

File tree

4 files changed

+47
-2
lines changed

4 files changed

+47
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Bug fixes:
1616
[BUG #2836](https://github.com/BurntSushi/ripgrep/issues/2836),
1717
[BUG #2933](https://github.com/BurntSushi/ripgrep/pull/2933):
1818
Fix bug related to gitignores from parent directories.
19+
* [BUG #1332](https://github.com/BurntSushi/ripgrep/issues/1332),
20+
[BUG #3001](https://github.com/BurntSushi/ripgrep/issues/3001):
21+
Make `rg -vf file` where `file` is empty match everything.
1922
* [BUG #2177](https://github.com/BurntSushi/ripgrep/issues/2177):
2023
Ignore a UTF-8 BOM marker at the start of `.gitignore` (and similar files).
2124

crates/core/flags/hiargs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ impl HiArgs {
517517
/// When this returns false, it is impossible for ripgrep to ever report
518518
/// a match.
519519
pub(crate) fn matches_possible(&self) -> bool {
520-
if self.patterns.patterns.is_empty() {
520+
if self.patterns.patterns.is_empty() && !self.invert_match {
521521
return false;
522522
}
523523
if self.max_count == Some(0) {

crates/pcre2/src/matcher.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ impl RegexMatcherBuilder {
5555
format!("(?:{})", p.as_ref())
5656
});
5757
}
58-
let mut singlepat = pats.join("|");
58+
let mut singlepat = if patterns.is_empty() {
59+
// A way to spell a pattern that can never match anything.
60+
r"[^\S\s]".to_string()
61+
} else {
62+
pats.join("|")
63+
};
5964
if self.case_smart && !has_uppercase_literal(&singlepat) {
6065
builder.caseless(true);
6166
}

tests/regression.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,43 @@ rgtest!(r1319, |dir: Dir, mut cmd: TestCommand| {
955955
);
956956
});
957957

958+
// See: https://github.com/BurntSushi/ripgrep/issues/1332
959+
rgtest!(r1334_invert_empty_patterns, |dir: Dir, _cmd: TestCommand| {
960+
dir.create("zero-patterns", "");
961+
dir.create("one-pattern", "\n");
962+
dir.create("haystack", "one\ntwo\nthree\n");
963+
964+
// zero patterns matches nothing
965+
{
966+
let mut cmd = dir.command();
967+
cmd.arg("-f").arg("zero-patterns").arg("haystack").assert_err();
968+
}
969+
// one pattern that matches empty string matches everything
970+
{
971+
let mut cmd = dir.command();
972+
eqnice!(
973+
"one\ntwo\nthree\n",
974+
cmd.arg("-f").arg("one-pattern").arg("haystack").stdout()
975+
);
976+
}
977+
978+
// inverting zero patterns matches everything
979+
// (This is the regression. ripgrep used to match nothing because of an
980+
// incorrect optimization.)
981+
{
982+
let mut cmd = dir.command();
983+
eqnice!(
984+
"one\ntwo\nthree\n",
985+
cmd.arg("-vf").arg("zero-patterns").arg("haystack").stdout()
986+
);
987+
}
988+
// inverting one pattern that matches empty string matches nothing
989+
{
990+
let mut cmd = dir.command();
991+
cmd.arg("-vf").arg("one-pattern").arg("haystack").assert_err();
992+
}
993+
});
994+
958995
// See: https://github.com/BurntSushi/ripgrep/issues/1334
959996
rgtest!(r1334_crazy_literals, |dir: Dir, mut cmd: TestCommand| {
960997
dir.create("patterns", &"1.208.0.0/12\n".repeat(40));

0 commit comments

Comments
 (0)