diff --git a/globset/src/glob.rs b/globset/src/glob.rs index cbbc7bad..53d44e15 100644 --- a/globset/src/glob.rs +++ b/globset/src/glob.rs @@ -850,27 +850,44 @@ impl<'a> Parser<'a> { } return Ok(()); } - self.pop_token()?; + if !prev.map(is_separator).unwrap_or(false) { if self.stack.len() <= 1 || (prev != Some(',') && prev != Some('{')) { return Err(self.error(ErrorKind::InvalidRecursive)); } } - match self.chars.peek() { - None => { - assert!(self.bump().is_none()); - self.push_token(Token::RecursiveSuffix) + let is_suffix = + match self.chars.peek() { + None => { + assert!(self.bump().is_none()); + true + } + Some(&',') | Some(&'}') if self.stack.len() >= 2 => { + true + } + Some(&c) if is_separator(c) => { + assert!(self.bump().map(is_separator).unwrap_or(false)); + false + } + _ => return Err(self.error(ErrorKind::InvalidRecursive)), + }; + match self.pop_token()? { + Token::RecursivePrefix => { + self.push_token(Token::RecursivePrefix)?; } - Some(&',') | Some(&'}') if self.stack.len() >= 2 => { - self.push_token(Token::RecursiveSuffix) + Token::RecursiveSuffix => { + self.push_token(Token::RecursiveSuffix)?; } - Some(&c) if is_separator(c) => { - assert!(self.bump().map(is_separator).unwrap_or(false)); - self.push_token(Token::RecursiveZeroOrMore) + _ => { + if is_suffix { + self.push_token(Token::RecursiveSuffix)?; + } else { + self.push_token(Token::RecursiveZeroOrMore)?; + } } - _ => Err(self.error(ErrorKind::InvalidRecursive)), } + Ok(()) } fn parse_class(&mut self) -> Result<(), Error> { @@ -1194,8 +1211,23 @@ mod tests { toregex!(re8, "[*]", r"^[\*]$"); toregex!(re9, "[+]", r"^[\+]$"); toregex!(re10, "+", r"^\+$"); - toregex!(re11, "**", r"^.*$"); - toregex!(re12, "☃", r"^\xe2\x98\x83$"); + toregex!(re11, "☃", r"^\xe2\x98\x83$"); + toregex!(re12, "**", r"^.*$"); + toregex!(re13, "**/", r"^.*$"); + toregex!(re14, "**/*", r"^(?:/?|.*/).*$"); + toregex!(re15, "**/**", r"^.*$"); + toregex!(re16, "**/**/*", r"^(?:/?|.*/).*$"); + toregex!(re17, "**/**/**", r"^.*$"); + toregex!(re18, "**/**/**/*", r"^(?:/?|.*/).*$"); + toregex!(re19, "a/**", r"^a(?:/?|/.*)$"); + toregex!(re20, "a/**/**", r"^a(?:/?|/.*)$"); + toregex!(re21, "a/**/**/**", r"^a(?:/?|/.*)$"); + toregex!(re22, "a/**/b", r"^a(?:/|/.*/)b$"); + toregex!(re23, "a/**/**/b", r"^a(?:/|/.*/)b$"); + toregex!(re24, "a/**/**/**/b", r"^a(?:/|/.*/)b$"); + toregex!(re25, "**/b", r"^(?:/?|.*/)b$"); + toregex!(re26, "**/**/b", r"^(?:/?|.*/)b$"); + toregex!(re27, "**/**/**/b", r"^(?:/?|.*/)b$"); matches!(match1, "a", "a"); matches!(match2, "a*b", "a_b"); diff --git a/ignore/src/gitignore.rs b/ignore/src/gitignore.rs index 48d1bb4b..b8db42ba 100644 --- a/ignore/src/gitignore.rs +++ b/ignore/src/gitignore.rs @@ -710,6 +710,7 @@ mod tests { ignored!(ig41, ROOT, "\\a", "a"); ignored!(ig42, ROOT, "s*.rs", "sfoo.rs"); ignored!(ig43, ROOT, "**", "foo.rs"); + ignored!(ig44, ROOT, "**/**/*", "a/foo.rs"); not_ignored!(ignot1, ROOT, "amonths", "months"); not_ignored!(ignot2, ROOT, "monthsa", "months"); diff --git a/tests/regression.rs b/tests/regression.rs index e4da2f1c..8435f174 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -604,3 +604,12 @@ rgtest!(r1173, |dir: Dir, mut cmd: TestCommand| { dir.create("foo", "test"); cmd.arg("test").assert_err(); }); + +// See: https://github.com/BurntSushi/ripgrep/issues/1174 +rgtest!(r1174, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git"); + dir.create(".gitignore", "**/**/*"); + dir.create_dir("a"); + dir.create("a/foo", "test"); + cmd.arg("test").assert_err(); +});