-
Notifications
You must be signed in to change notification settings - Fork 834
Description
I haven't tested this stuff out yet so I'm not sure how feasible any of it is but here are the ideas I think are worth investigating for 6.0:
Move off of nom::error::ParseError
and use std::error::Error
instead
- see if instead of
append
andadd_context
combinators that need to merge errors can instead usestd::ops::Add
(remove ParseError::add_context #1131).- I'm unsure of what the difference between
append
andor
is in practice, so this may instead apply toor
.
- I'm unsure of what the difference between
- use
From
instead offrom_error_kind
for converting from a root cause error to a user provided error type- this could possibly involve introducing a new root cause error type that functionally replaces the
(I, ErrorKind)
tuple.
- this could possibly involve introducing a new root cause error type that functionally replaces the
Inter-operate with foreign error types instead of discarding
- change
map_res
to try to useFrom
into the user Error type from the error type of the closure, specifically make sure to support preserving std ParseIntError and similar errors - change
verify
to work withResult
instead ofbool
to allow custom error messages upon verify failures.
Look into interoperating with error reporting mechanisms that require 'static
Probably with the ToOwned
trait in std, I'm not sure how easy it would be to write the impl for this as a user in practice.
Alternatively: we can look into removing the lifetime bound on errors that keep the input with them, my understanding is that the main reason that these carry around references is to do offsets against the original reference, we could instead carry a ptr and require that the original reference is provided when printing the final error report to get the nicely rendered contexts.
Stop treating context as Errors
The specific location in a program / context of what it was doing is not an error, or indicative of a failure, and I don't think it should be stored in the error chain/tree the same way the various parse failures are. Instead I'd like to see it gathered in a similar way to how I'm proposing we support Error Return Traces
- https://github.com/yaahc/rfcs/blob/master/text/0000-dyn-error-generic-member-access.md#guide-level-explanation
- https://paper.dropbox.com/doc/Collaborative-Summary-3-Fact-Finding-Pre-RFCs-around-Error-Handling-Reporting-dnShKo1SsHtdF4Yheeoco#:uid=314245190890235841293570&h2=Error-Return-Traces
Additional Comments
I'm not positive is now is the best time to apply these changes. I'm currently working on this RFC rust-lang/rfcs#2895 to add a way to get custom context out of dyn Errors, and a few of the changes I'm imagining would probably want to use this functionality as part of the error reporting mechanism, though it gets a little hairy because nom errors like to have references in them, and the context extraction mechanisms would work for any members with lifetimes in them...
The vague idea im throwing around in my head is something like this.
/// Assuming the error has been made owned
///
/// if the input references in leaf errors are stored entirely for the offset, could we instead
/// store ptrs?
///
/// using println here to be lazy, would actually use `std::fmt` machinery
fn example_nom_error_report(input: &I, error: &dyn std::error::Error + 'static) {
let mut cur = Some(error);
let mut ind = 0;
eprintln!("Error:");
while let Some(error) = cur {
eprintln!(" {}: {}", ind, error);
if let Some(causes) = error.context::<[nom::error::Error]>() {
eprintln!(" Various parse failures:");
for (ind, cause) in causes.iter().enumerate() {
eprintln!(" {}: {}", ind, cause.print_with(input));
}
}
if let Some(contexts) = error.context::<[nom::error::Context]>() {
eprintln!(" Various contexts:");
for (ind, context) in contexts.iter().enumerate() {
eprintln!(" {}: {}", ind, context.print_with(input));
}
}
cur = error.source();
ind += 1;
}
}
This would hopefully support printing all these errors
Error:
0: Unable to parse Type from Input
Various parse failures:
0: at line 2:
"c": { 1"hello" : "world"
^
expected '}', found 1
Various contexts:
1: at line 2, in map:
"c": { 1"hello" : "world"
^
2: at line 0, in map:
{ "a" : 42,
Error:
0: Unable to parse Type from Input
1: invalid digit found in string
Error:
0: Unable to parse Type from Input
Various contexts:
1: at line 2, in map:
"c": { 1"hello" : "world"
^
2: at line 0, in map:
{ "a" : 42,
1: failed verify for some specific reason
The specific syntax and error report shape are not really the point here, this would probably need a lot of tweeking, but hopefully this gets the idea across.