Repository: rust-bakery/nom (Rust)

Issue:
`error::context` should not rewrite `Error` to `Failure`
Summary:
`nom::error::context` rewrites `Err::Error` variant to `Err::Failure` but it causes some parsers to fail.
It seems it should not rewrite error variants.

## Prerequisites

* Rust version: rustc 1.34.1 (fc50f328b 2019-04-24)
* nom version: `=5.0.0-alpha2`
* nom features: (default)

## Test case

I don't precisely understand `nom::Err` and the issue, but it seems that the parser fails when `context`-ed parser is used in `many0_count`, such as `nom::multi::many0_count(nom::error::context("foo", bar))`.

### Expected result

All four tests pass.

### Actual result

One test fails.

* With `nom::error::context()`:
    + Test fails with `many0_count(context(is_a(...)))` style (`nom_many_alpha`).
    + Test passes with `many0_count(is_a(...))` style (`nom_direct`).
* With fixed `context()`:
    + Tests with both styles pass (`intuitive_many_alpha`, `intuitive_direct`).
    
### Example test case

<details>

Cargo.toml:

```toml
[package]
name = "nom-context-error-and-failure"
version = "0.1.0"
edition = "2018"

[dependencies]
nom = "=5.0.0-alpha2"
```

lib.rs:

```rust
use nom::{bytes::complete::is_a, error::ParseError, multi::many0_count, IResult};

macro_rules! def {
    ($context:expr) => {
        fn alpha<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
            $context("alpha", is_a("abcdefghijklmnopqrstuvwxyz"))(i)
        }

        pub fn many_alpha<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
            $context("many_alpha", many0_count(alpha))(i)
                .map(|(rest, _)| (rest, &i[..(i.len() - rest.len())]))
        }

        pub fn many_alpha_direct<'a, E: ParseError<&'a str>>(
            i: &'a str,
        ) -> IResult<&'a str, &'a str, E> {
            $context(
                "many_single",
                many0_count(is_a("abcdefghijklmnopqrstuvwxyz")),
            )(i)
            .map(|(rest, _)| (rest, &i[..(i.len() - rest.len())]))
        }
    };
}

pub mod with_intuitive_context {
    use super::*;

    fn context<I: Clone, E: ParseError<I>, F, O>(
        context: &'static str,
        f: F,
    ) -> impl Fn(I) -> IResult<I, O, E>
    where
        F: Fn(I) -> IResult<I, O, E>,
    {
        use nom::Err;

        move |i: I| match f(i.clone()) {
            Ok(o) => Ok(o),
            Err(Err::Incomplete(i)) => Err(Err::Incomplete(i)),
            Err(Err::Error(e)) => Err(Err::Error(E::add_context(i, context, e))),
            Err(Err::Failure(e)) => Err(Err::Failure(E::add_context(i, context, e))),
        }
    }

    def!(context);
}

pub mod with_nom_context {
    use super::*;
    use nom::error::context;

    def!(context);
}

#[cfg(test)]
mod tests {
    use nom::{error::VerboseError, IResult};

    fn check<'a, F>(context_type: &str, parser_desc: &str, parser: F, input: &'a str)
    where
        F: Fn(&'a str) -> IResult<&'a str, &'a str, VerboseError<&'a str>>,
    {
        if let Err(e) = parser(input) {
            panic!(
                "error: context={:?}, parser={:?}, input={:?}, error={:#?}",
                context_type, parser_desc, input, e
            );
        }
    }

    const INPUT: &str = "hello";

    #[test]
    fn nom_many_alpha() {
        check(
            "nom",
            "many_alpha",
            crate::with_nom_context::many_alpha,
            INPUT,
        );
    }

    #[test]
    fn nom_direct() {
        check(
            "nom",
            "direct",
            crate::with_nom_context::many_alpha_direct,
            INPUT,
        );
    }

    #[test]
    fn intuitive_many_alpha() {
        check(
            "intuitive",
            "many_alpha",
            crate::with_intuitive_context::many_alpha,
            INPUT,
        );
    }

    #[test]
    fn intuitive_direct() {
        check(
            "intuitive",
            "many_alpha",
            crate::with_intuitive_context::many_alpha_direct,
            INPUT,
        );
    }
}
```

</details>

### Test result:
```
     Running target/debug/deps/nom_context_error_and_failure-3635aa3404b46f26

running 4 tests
test tests::intuitive_direct ... ok
test tests::intuitive_many_alpha ... ok
test tests::nom_direct ... ok
test tests::nom_many_alpha ... FAILED

failures:

---- tests::nom_many_alpha stdout ----
thread 'tests::nom_many_alpha' panicked at 'error: context="nom", parser="many_alpha", input="hello", error=Failure(
    VerboseError {
        errors: [
            (
                "",
                Nom(
                    IsA
                )
            ),
            (
                "",
                Context(
                    "alpha"
                )
            ),
            (
                "hello",
                Context(
                    "many_alpha"
                )
            )
        ]
    }
)', src/lib.rs:65:13
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    tests::nom_many_alpha

test result: FAILED. 3 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--lib'
```

### Description

It seems that this part:

https://github.com/Geal/nom/blob/17b08f2921a5b1a96e34df3e5b8a14c6c6b9f780/src/error.rs#L101-L103

should be like this:

```rust
        Err(Err::Error(e)) => Err(Err::Error(E::add_context(i, context, e))),
        Err(Err::Failure(e)) => Err(Err::Failure(E::add_context(i, context, e))),
```

Is there any reason that `context()` rewrites `Error` to `Failure`?


Discussion:
I made a Pull Request.
In that process, I made very small test case (which currently fails, but users will expect success).

```rust
#[test]
fn issue_942() {
  use nom::error::ParseError;
  pub fn parser<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, usize, E> {
    use nom::{character::complete::char, error::context, multi::many0_count};
    many0_count(context("char_a", char('a')))(i)
  }
  assert_eq!(parser::<()>("aaa"), Ok(("", 3)));
}
```
I originally wrote `context` to act like `return_error!`. The idea was to have a way to indicate that if we got to that part of the parser, any error should be considered a failure (ie do not try other branches in `alt`, fail in `many*`, etc).
It would still be useful to have that kind of behaviour, but maybe not in `context`.
I'll think a bit about it and get back to you
thank you for the report, it is merged :)



Patch:
diff --git a/src/error.rs b/src/error.rs
index 60d1d6eaa..fc88c31c1 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -98,9 +98,8 @@ where
       match f(i.clone()) {
         Ok(o) => Ok(o),
         Err(Err::Incomplete(i)) => Err(Err::Incomplete(i)),
-        Err(Err::Error(e)) | Err(Err::Failure(e)) => {
-          Err(Err::Failure(E::add_context(i, context, e)))
-        }
+        Err(Err::Error(e)) => Err(Err::Error(E::add_context(i, context, e))),
+        Err(Err::Failure(e)) => Err(Err::Failure(E::add_context(i, context, e))),
       }
     }
 


Test patch:
diff --git a/tests/issues.rs b/tests/issues.rs
index 31ec205ee..c5f1bd0b6 100644
--- a/tests/issues.rs
+++ b/tests/issues.rs
@@ -279,3 +279,13 @@ fn issue_848_overflow_incomplete_bits_to_bytes() {
   named!(parser<&[u8], &[u8]>, bits!(bytes!(take!(0x2000000000000000))));
   assert_eq!(parser(&b""[..]), Err(Err::Failure(error_position!(&b""[..], ErrorKind::TooLarge))));
 }
+
+#[test]
+fn issue_942() {
+  use nom::error::ParseError;
+  pub fn parser<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, usize, E> {
+    use nom::{character::complete::char, error::context, multi::many0_count};
+    many0_count(context("char_a", char('a')))(i)
+  }
+  assert_eq!(parser::<()>("aaa"), Ok(("", 3)));
+}