Skip to main content

rustidy_parse/
error.rs

1//! Parser error
2
3// Exports
4pub use rustidy_macros::ParseError;
5
6// Imports
7use {
8	super::{AstPos, AstRange, Parse, Parser},
9	app_error::AppError,
10	core::{error::Error as StdError, fmt},
11};
12
13/// Parse error
14pub trait ParseError {
15	/// Returns whether this error is fatal
16	fn is_fatal(&self) -> bool;
17
18	/// Returns the position at which this error occurred.
19	fn pos(&self) -> Option<AstPos>;
20
21	/// Converts this error type to an `AppError`
22	fn to_app_error(&self, parser: &Parser) -> AppError;
23}
24
25impl ParseError for ! {
26	fn is_fatal(&self) -> bool {
27		*self
28	}
29
30	fn pos(&self) -> Option<AstPos> {
31		*self
32	}
33
34	fn to_app_error(&self, _parser: &Parser) -> AppError {
35		*self
36	}
37}
38
39impl<E: ParseError> ParseError for Box<E> {
40	fn is_fatal(&self) -> bool {
41		(**self).is_fatal()
42	}
43
44	fn pos(&self) -> Option<AstPos> {
45		(**self).pos()
46	}
47
48	fn to_app_error(&self, parser: &Parser) -> AppError {
49		(**self).to_app_error(parser)
50	}
51}
52
53impl ParseError for () {
54	fn is_fatal(&self) -> bool {
55		false
56	}
57
58	fn pos(&self) -> Option<AstPos> {
59		None
60	}
61
62	fn to_app_error(&self, _parser: &Parser) -> AppError {
63		AppError::from_multiple([])
64	}
65}
66
67
68/// Parser error
69pub struct ParserError<T: Parse> {
70	// Note: This is behind an indirection to avoid overflowing the stack when
71	//       we parse large enums.
72	// TODO: Make this an `ArenaIdx` once either `#[fundamental]` can be applied
73	//       to enums, or generic statics become available.
74	source: Box<T::Error>,
75	range:  AstRange,
76}
77
78impl<T: Parse> ParserError<T> {
79	pub(super) fn new(source: T::Error, range: AstRange) -> Self {
80		Self { source: Box::new(source), range, }
81	}
82}
83
84impl<T: Parse<Error: fmt::Debug>> fmt::Debug for ParserError<T> {
85	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86		f
87			.debug_struct("ParserError")
88			.field("source", &self.source)
89			.field("span", &self.range).finish()
90	}
91}
92
93impl<T: Parse<Error: StdError + 'static>> StdError for ParserError<T> {
94	fn source(&self) -> Option<&( dyn StdError + 'static )> {
95		match self::name_of::<T>().is_some() {
96			true => Some(&self.source),
97			false => self.source.source(),
98		}
99	}
100}
101
102impl<T: Parse<Error: fmt::Display>> fmt::Display for ParserError<T> {
103	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104		match self::name_of::<T>() {
105			Some(name) => write!(f, "Expected {name}"),
106			None => self.source.fmt(f),
107		}
108	}
109}
110
111impl<T: Parse> ParseError for ParserError<T> {
112	fn is_fatal(&self) -> bool {
113		self.source.is_fatal()
114	}
115
116	fn pos(&self) -> Option<AstPos> {
117		// Note: We prefer deeper positions since they contain the
118		//       nearest position to the error.
119		let pos = match self.source.pos() {
120			Some(pos) => AstPos::max(pos, self.range.end),
121			None => self.range.end,
122		};
123
124		Some(pos)
125	}
126
127	fn to_app_error(&self, parser: &Parser) -> AppError {
128		let err = self.source.to_app_error(parser).flatten();
129		match self::name_of::<T>() {
130			Some(name) => err.with_context(
131				|| format!("Expected {name} at {}", parser.loc(self.range.start))
132			),
133			None => err,
134		}
135	}
136}
137
138/// Gets the name of a parsable type
139fn name_of<T: Parse>() -> Option<String> {
140	let name = T::name().map(|s| s.to_string());
141
142	#[cfg(feature = "parse-debug-name")]
143	let name = Some(
144		name
145			.unwrap_or_else(|| std::any::type_name::<T>().to_owned())
146	);
147
148	name
149}