diff --git a/CHANGELOG.md b/CHANGELOG.md index 7553cf77..605e9ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ Bug fixes: [BUG #2836](https://github.com/BurntSushi/ripgrep/issues/2836), [BUG #2933](https://github.com/BurntSushi/ripgrep/pull/2933): Fix bug related to gitignores from parent directories. +* [BUG #1332](https://github.com/BurntSushi/ripgrep/issues/1332), + [BUG #3001](https://github.com/BurntSushi/ripgrep/issues/3001): + Make `rg -vf file` where `file` is empty match everything. * [BUG #2177](https://github.com/BurntSushi/ripgrep/issues/2177): Ignore a UTF-8 BOM marker at the start of `.gitignore` (and similar files). diff --git a/crates/core/flags/hiargs.rs b/crates/core/flags/hiargs.rs index 1fd93708..53490a6d 100644 --- a/crates/core/flags/hiargs.rs +++ b/crates/core/flags/hiargs.rs @@ -517,7 +517,7 @@ impl HiArgs { /// When this returns false, it is impossible for ripgrep to ever report /// a match. pub(crate) fn matches_possible(&self) -> bool { - if self.patterns.patterns.is_empty() { + if self.patterns.patterns.is_empty() && !self.invert_match { return false; } if self.max_count == Some(0) { diff --git a/crates/pcre2/src/matcher.rs b/crates/pcre2/src/matcher.rs index 56c9356d..9a6710c0 100644 --- a/crates/pcre2/src/matcher.rs +++ b/crates/pcre2/src/matcher.rs @@ -55,7 +55,12 @@ impl RegexMatcherBuilder { format!("(?:{})", p.as_ref()) }); } - let mut singlepat = pats.join("|"); + let mut singlepat = if patterns.is_empty() { + // A way to spell a pattern that can never match anything. + r"[^\S\s]".to_string() + } else { + pats.join("|") + }; if self.case_smart && !has_uppercase_literal(&singlepat) { builder.caseless(true); } diff --git a/tests/regression.rs b/tests/regression.rs index 4c087844..bd845905 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -955,6 +955,43 @@ rgtest!(r1319, |dir: Dir, mut cmd: TestCommand| { ); }); +// See: https://github.com/BurntSushi/ripgrep/issues/1332 +rgtest!(r1334_invert_empty_patterns, |dir: Dir, _cmd: TestCommand| { + dir.create("zero-patterns", ""); + dir.create("one-pattern", "\n"); + dir.create("haystack", "one\ntwo\nthree\n"); + + // zero patterns matches nothing + { + let mut cmd = dir.command(); + cmd.arg("-f").arg("zero-patterns").arg("haystack").assert_err(); + } + // one pattern that matches empty string matches everything + { + let mut cmd = dir.command(); + eqnice!( + "one\ntwo\nthree\n", + cmd.arg("-f").arg("one-pattern").arg("haystack").stdout() + ); + } + + // inverting zero patterns matches everything + // (This is the regression. ripgrep used to match nothing because of an + // incorrect optimization.) + { + let mut cmd = dir.command(); + eqnice!( + "one\ntwo\nthree\n", + cmd.arg("-vf").arg("zero-patterns").arg("haystack").stdout() + ); + } + // inverting one pattern that matches empty string matches nothing + { + let mut cmd = dir.command(); + cmd.arg("-vf").arg("one-pattern").arg("haystack").assert_err(); + } +}); + // See: https://github.com/BurntSushi/ripgrep/issues/1334 rgtest!(r1334_crazy_literals, |dir: Dir, mut cmd: TestCommand| { dir.create("patterns", &"1.208.0.0/12\n".repeat(40));