tamer: xir::parse::ele: Diagnostic output
The only additional information needed was opening spans so that we can provide useful information regarding closing tags. This uses a generic Span in place of {Open,Close}Span because the latter wasn't necessary, but more descriptive types would be nice; it may be beneficial later on to introduce newtypes for each of the span generated by {Open,Close}Span. DEV-7145main
parent
ce765d3b56
commit
c856fd72d9
|
@ -193,13 +193,13 @@ macro_rules! ele_parse {
|
|||
/// may be a child element,
|
||||
/// but it may be text,
|
||||
/// for example.
|
||||
CloseRecoverIgnore_(Depth, crate::span::Span),
|
||||
CloseRecoverIgnore_((crate::span::Span, Depth), crate::span::Span),
|
||||
/// Parsing element attributes.
|
||||
Attrs_(Depth, [<$nt AttrsState_>]),
|
||||
Attrs_((crate::span::Span, Depth), [<$nt AttrsState_>]),
|
||||
$(
|
||||
$ntref(Depth, $ntref),
|
||||
$ntref((crate::span::Span, Depth), $ntref),
|
||||
)*
|
||||
ExpectClose_(Depth, ()),
|
||||
ExpectClose_((crate::span::Span, Depth), ()),
|
||||
/// Closing tag found and parsing of the element is
|
||||
/// complete.
|
||||
Closed_(crate::span::Span),
|
||||
|
@ -238,7 +238,7 @@ macro_rules! ele_parse {
|
|||
given = TtQuote::wrap(name),
|
||||
expected = TtQuote::wrap($qname),
|
||||
),
|
||||
Self::CloseRecoverIgnore_(depth, _) => write!(
|
||||
Self::CloseRecoverIgnore_((_, depth), _) => write!(
|
||||
f,
|
||||
"attempting to recover by ignoring input \
|
||||
until the expected end tag {expected} \
|
||||
|
@ -247,7 +247,7 @@ macro_rules! ele_parse {
|
|||
),
|
||||
|
||||
Self::Attrs_(_, sa) => std::fmt::Display::fmt(sa, f),
|
||||
Self::ExpectClose_(depth, _) => write!(
|
||||
Self::ExpectClose_((_, depth), _) => write!(
|
||||
f,
|
||||
"expecting closing element {} at depth {depth}",
|
||||
TtCloseXmlEle::wrap($qname)
|
||||
|
@ -271,10 +271,15 @@ macro_rules! ele_parse {
|
|||
/// An element was expected,
|
||||
/// but the name of the element was unexpected.
|
||||
UnexpectedEle_(crate::xir::QName, crate::span::Span),
|
||||
|
||||
/// Unexpected input while expecting an end tag for this
|
||||
/// element.
|
||||
CloseExpected_(crate::xir::flat::XirfToken),
|
||||
///
|
||||
/// The span corresponds to the opening tag.
|
||||
CloseExpected_(crate::span::Span, crate::xir::flat::XirfToken),
|
||||
|
||||
Attrs_(crate::xir::parse::AttrParseError<[<$nt AttrsState_>]>),
|
||||
|
||||
$(
|
||||
$ntref([<$ntref Error_>]),
|
||||
)*
|
||||
|
@ -313,10 +318,13 @@ macro_rules! ele_parse {
|
|||
};
|
||||
|
||||
match self {
|
||||
Self::UnexpectedEle_(name, _) => {
|
||||
write!(f, "unexpected {}", TtOpenXmlEle::wrap(name))
|
||||
}
|
||||
Self::CloseExpected_(tok) => write!(
|
||||
Self::UnexpectedEle_(name, _) => write!(
|
||||
f,
|
||||
"unexpected {unexpected} (expecting {expected})",
|
||||
unexpected = TtOpenXmlEle::wrap(name),
|
||||
expected = TtOpenXmlEle::wrap($qname),
|
||||
),
|
||||
Self::CloseExpected_(_, tok) => write!(
|
||||
f,
|
||||
"expected {}, but found {}",
|
||||
TtCloseXmlEle::wrap($qname),
|
||||
|
@ -332,7 +340,35 @@ macro_rules! ele_parse {
|
|||
|
||||
impl crate::diagnose::Diagnostic for [<$nt Error_>] {
|
||||
fn describe(&self) -> Vec<crate::diagnose::AnnotatedSpan> {
|
||||
todo!()
|
||||
use crate::{
|
||||
diagnose::Annotate,
|
||||
fmt::{DisplayWrapper, TtQuote},
|
||||
parse::Token,
|
||||
xir::fmt::{TtCloseXmlEle},
|
||||
};
|
||||
|
||||
match self {
|
||||
Self::UnexpectedEle_(_, ospan) => ospan.error(
|
||||
format!(
|
||||
"expected {ele_name} here",
|
||||
ele_name = TtQuote::wrap($qname)
|
||||
)
|
||||
).into(),
|
||||
|
||||
Self::CloseExpected_(span, tok) => vec![
|
||||
span.note("element starts here"),
|
||||
tok.span().error(format!(
|
||||
"expected {}",
|
||||
TtCloseXmlEle::wrap($qname),
|
||||
)),
|
||||
],
|
||||
|
||||
Self::Attrs_(e) => e.describe(),
|
||||
|
||||
$(
|
||||
Self::$ntref(e) => e.describe(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,16 +402,20 @@ macro_rules! ele_parse {
|
|||
Expecting_,
|
||||
XirfToken::Open(qname, span, depth)
|
||||
) if qname == $qname => {
|
||||
Transition(Attrs_(depth, parse_attrs(qname, span)))
|
||||
.incomplete()
|
||||
Transition(Attrs_(
|
||||
(span.tag_span(), depth),
|
||||
parse_attrs(qname, span)
|
||||
)).incomplete()
|
||||
},
|
||||
|
||||
(
|
||||
Closed_(..),
|
||||
XirfToken::Open(qname, span, depth)
|
||||
) if cfg.repeat && qname == $qname => {
|
||||
Transition(Attrs_(depth, parse_attrs(qname, span)))
|
||||
.incomplete()
|
||||
Transition(Attrs_(
|
||||
(span.tag_span(), depth),
|
||||
parse_attrs(qname, span)
|
||||
)).incomplete()
|
||||
},
|
||||
|
||||
(Expecting_, XirfToken::Open(qname, span, depth)) => {
|
||||
|
@ -391,11 +431,11 @@ macro_rules! ele_parse {
|
|||
Transition(RecoverEleIgnoreClosed_(qname, span)).incomplete()
|
||||
},
|
||||
|
||||
(Attrs_(depth, sa), tok) => {
|
||||
(Attrs_(meta, sa), tok) => {
|
||||
sa.delegate_until_obj(
|
||||
tok,
|
||||
EmptyContext,
|
||||
|sa| Transition(Attrs_(depth, sa)),
|
||||
|sa| Transition(Attrs_(meta, sa)),
|
||||
|| unreachable!("see ParseState::delegate_until_obj dead"),
|
||||
|#[allow(unused_variables)] sa, attrs| {
|
||||
let obj = match attrs {
|
||||
|
@ -415,7 +455,7 @@ macro_rules! ele_parse {
|
|||
},
|
||||
};
|
||||
|
||||
Transition($ntfirst(depth, Default::default()))
|
||||
Transition($ntfirst(meta, Default::default()))
|
||||
.ok(obj)
|
||||
}
|
||||
)
|
||||
|
@ -435,7 +475,8 @@ macro_rules! ele_parse {
|
|||
// XIRF ensures proper nesting,
|
||||
// so we do not need to check the element name.
|
||||
(
|
||||
ExpectClose_(depth, ()) | CloseRecoverIgnore_(depth, _),
|
||||
ExpectClose_((_, depth), ())
|
||||
| CloseRecoverIgnore_((_, depth), _),
|
||||
XirfToken::Close(_, span, tok_depth)
|
||||
) if tok_depth == depth => {
|
||||
$(
|
||||
|
@ -444,11 +485,11 @@ macro_rules! ele_parse {
|
|||
$closemap.transition(Closed_(span.tag_span()))
|
||||
},
|
||||
|
||||
(ExpectClose_(depth, ()), unexpected_tok) => {
|
||||
(ExpectClose_(meta @ (otspan, _), ()), unexpected_tok) => {
|
||||
use crate::parse::Token;
|
||||
Transition(
|
||||
CloseRecoverIgnore_(depth, unexpected_tok.span())
|
||||
).err([<$nt Error_>]::CloseExpected_(unexpected_tok))
|
||||
CloseRecoverIgnore_(meta, unexpected_tok.span())
|
||||
).err([<$nt Error_>]::CloseExpected_(otspan, unexpected_tok))
|
||||
}
|
||||
|
||||
// We're still in recovery,
|
||||
|
@ -584,7 +625,32 @@ macro_rules! ele_parse {
|
|||
|
||||
impl crate::diagnose::Diagnostic for [<$nt Error_>] {
|
||||
fn describe(&self) -> Vec<crate::diagnose::AnnotatedSpan> {
|
||||
todo!()
|
||||
use crate::{
|
||||
diagnose::Annotate,
|
||||
fmt::{DisplayWrapper, ListDisplayWrapper, TtQuote},
|
||||
xir::fmt::OpenEleSumList,
|
||||
};
|
||||
|
||||
let ntrefs = [
|
||||
$(
|
||||
$ntref::qname(),
|
||||
)*
|
||||
];
|
||||
let expected = OpenEleSumList::wrap(&ntrefs);
|
||||
|
||||
match self {
|
||||
Self::UnexpectedEle_(qname, span) => {
|
||||
span.error(format!(
|
||||
"element {name} cannot appear here \
|
||||
(expecting {expected})",
|
||||
name = TtQuote::wrap(qname),
|
||||
)).into()
|
||||
},
|
||||
|
||||
$(
|
||||
Self::$ntref(e) => e.describe(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
use crate::{
|
||||
convert::ExpectInto,
|
||||
diagnose::Diagnostic,
|
||||
parse::{Object, ParseError, ParseState, Parsed},
|
||||
span::{Span, DUMMY_SPAN},
|
||||
sym::SymbolId,
|
||||
|
@ -270,15 +271,20 @@ fn unexpected_element() {
|
|||
// was encountered
|
||||
// (which was expected),
|
||||
// but to the fact that the name was not the one expected.
|
||||
let err = sut.next().unwrap().unwrap_err();
|
||||
assert_eq!(
|
||||
// TODO: This references generated identifiers.
|
||||
Some(Err(ParseError::StateError(SutError_::UnexpectedEle_(
|
||||
ParseError::StateError(SutError_::UnexpectedEle_(
|
||||
unexpected,
|
||||
span.name_span()
|
||||
)))),
|
||||
sut.next(),
|
||||
)),
|
||||
err,
|
||||
);
|
||||
|
||||
// The diagnostic should describe the name of the element as being
|
||||
// invalid.
|
||||
assert_eq!(err.describe()[0].span(), span.name_span());
|
||||
|
||||
// We should have now entered a recovery mode whereby we discard
|
||||
// input until we close the element that introduced the error.
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // Attr
|
||||
|
@ -500,14 +506,18 @@ fn child_error_and_recovery() {
|
|||
// The token should be consumed and returned in the error,
|
||||
// _not_ produced as a token of lookahead,
|
||||
// since we do not want to reprocess bad input.
|
||||
let err = sut.next().unwrap().unwrap_err();
|
||||
assert_eq!(
|
||||
// TODO: This references generated identifiers.
|
||||
Some(Err(ParseError::StateError(SutError_::ChildA(
|
||||
ParseError::StateError(SutError_::ChildA(
|
||||
ChildAError_::UnexpectedEle_(unexpected, span.name_span())
|
||||
)))),
|
||||
sut.next(),
|
||||
)),
|
||||
err,
|
||||
);
|
||||
|
||||
// Diagnostic message should be delegated to the child.
|
||||
assert_eq!(err.describe()[0].span(), span.name_span());
|
||||
|
||||
// The next token is the self-closing `Close` for the unexpected opening
|
||||
// tag.
|
||||
// Since we are in recovery,
|
||||
|
@ -607,14 +617,25 @@ fn child_error_and_recovery_at_close() {
|
|||
// The token should be consumed and returned in the error,
|
||||
// _not_ produced as a token of lookahead,
|
||||
// since we do not want to reprocess bad input.
|
||||
let err = sut.next().unwrap().unwrap_err();
|
||||
assert_eq!(
|
||||
// TODO: This references generated identifiers.
|
||||
Some(Err(ParseError::StateError(SutError_::CloseExpected_(
|
||||
ParseError::StateError(SutError_::CloseExpected_(
|
||||
OpenSpan(S1, N).tag_span(),
|
||||
XirfToken::Open(unexpected_a, span_a, Depth(1)),
|
||||
)))),
|
||||
sut.next(),
|
||||
)),
|
||||
err,
|
||||
);
|
||||
|
||||
// The diagnostic information should include a reference to where the
|
||||
// element was opened
|
||||
// (so that the user understands what needs closing),
|
||||
// followed by the span of the token in error
|
||||
// (which naturally comes after the opening tag).
|
||||
let desc = err.describe();
|
||||
assert_eq!(desc[0].span(), S1); // Span of opening tag we want closed
|
||||
assert_eq!(desc[1].span(), span_a.span()); // Span of error
|
||||
|
||||
// The recovery state must not be in an accepting state,
|
||||
// because we didn't close at the root depth yet.
|
||||
let (mut sut, _) =
|
||||
|
@ -833,15 +854,19 @@ fn sum_nonterminal_error_recovery() {
|
|||
// was encountered
|
||||
// (which was expected),
|
||||
// but to the fact that the name was not the one expected.
|
||||
let err = sut.next().unwrap().unwrap_err();
|
||||
assert_eq!(
|
||||
sut.next(),
|
||||
err,
|
||||
// TODO: This references generated identifiers.
|
||||
Some(Err(ParseError::StateError(SutError_::UnexpectedEle_(
|
||||
ParseError::StateError(SutError_::UnexpectedEle_(
|
||||
unexpected,
|
||||
OpenSpan(S1, N).name_span(),
|
||||
)))),
|
||||
)),
|
||||
);
|
||||
|
||||
// Diagnostic message should describe the name of the element.
|
||||
assert_eq!(err.describe()[0].span(), OpenSpan(S1, N).name_span());
|
||||
|
||||
// We should have now entered a recovery mode whereby we discard
|
||||
// input until we close the element that introduced the error.
|
||||
assert_eq!(sut.next(), Some(Ok(Parsed::Incomplete))); // Open child
|
||||
|
@ -1051,6 +1076,7 @@ fn child_repetition_invalid_tok_dead() {
|
|||
next(),
|
||||
// TODO: This references generated identifiers.
|
||||
Some(Err(ParseError::StateError(SutError_::CloseExpected_(
|
||||
OpenSpan(S1, N).tag_span(),
|
||||
XirfToken::Open(unexpected, OpenSpan(S2, N), Depth(1)),
|
||||
)))),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue