ezno_parser/
errors.rs

1/// Contains lexing and parser errors
2use std::fmt::{self, Display};
3
4use crate::TSXToken;
5use source_map::Span;
6use tokenizer_lib::{sized_tokens::TokenStart, Token};
7
8#[allow(missing_docs)]
9pub enum ParseErrors<'a> {
10	UnexpectedToken { expected: &'a [TSXToken], found: TSXToken },
11	UnexpectedSymbol(derive_finite_automaton::InvalidCharacter),
12	ClosingTagDoesNotMatch { expected: &'a str, found: &'a str },
13	ExpectedStringLiteral { found: TSXToken },
14	TypeArgumentsNotValidOnReference,
15	UnmatchedBrackets,
16	FunctionParameterOptionalAndDefaultValue,
17	ExpectedIdent { found: TSXToken, at_location: &'a str },
18	ParameterCannotHaveDefaultValueHere,
19	InvalidLHSAssignment,
20	LexingFailed,
21	ExpectedCatchOrFinally,
22	InvalidDeclareItem(&'static str),
23	DestructuringRequiresValue,
24	CannotAccessObjectLiteralDirectly,
25	TrailingCommaNotAllowedHere,
26	InvalidNumberLiteral,
27	ReservedIdentifier,
28	AwaitRequiresForOf,
29	CannotUseLeadingParameterHere,
30	ExpectedIdentifier,
31	ExpectedNumberLiteral,
32	NonStandardSyntaxUsedWithoutEnabled,
33	ExpectRule,
34	InvalidRegexFlag,
35	ExpectedDeclaration,
36	CannotHaveRegularMemberAfterSpread,
37	InvalidLHSOfIs,
38}
39
40impl<'a> Display for ParseErrors<'a> {
41	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42		match self {
43			ParseErrors::UnexpectedToken { expected, found } => {
44				f.write_str("Expected ")?;
45				match expected {
46					[] => unreachable!("no expected tokens given"),
47					[a] => f.write_fmt(format_args!("{a:?}")),
48					[a, b] => f.write_fmt(format_args!("{a:?} or {b:?}")),
49					[head @ .., end] => {
50						let start = head
51							.iter()
52							.map(|token| format!("{token:?}"))
53							.reduce(|mut acc, token| {
54								acc.push_str(", ");
55								acc.push_str(&token);
56								acc
57							})
58							.unwrap();
59						f.write_fmt(format_args!("{start} or {end:?}"))
60					}
61				}?;
62				write!(f, " found {found:?}")
63			}
64			ParseErrors::UnexpectedSymbol(invalid_character) => Display::fmt(invalid_character, f),
65			ParseErrors::ClosingTagDoesNotMatch { expected, found } => {
66				write!(f, "Expected </{expected}> found </{found}>")
67			}
68			ParseErrors::NonStandardSyntaxUsedWithoutEnabled => {
69				write!(f, "Cannot use this syntax without flag enabled")
70			}
71			ParseErrors::ExpectedStringLiteral { found } => {
72				write!(f, "Expected string literal, found {found:?}")
73			}
74			ParseErrors::TypeArgumentsNotValidOnReference => {
75				write!(f, "Type arguments not valid on reference",)
76			}
77			ParseErrors::UnmatchedBrackets => f.write_str("Unmatched brackets"),
78			ParseErrors::FunctionParameterOptionalAndDefaultValue => {
79				f.write_str("Function parameter cannot be optional *and* have default expression")
80			}
81			ParseErrors::ExpectedIdent { found, at_location } => {
82				write!(f, "Expected identifier at {at_location}, found {found:?}")
83			}
84			ParseErrors::ParameterCannotHaveDefaultValueHere => {
85				f.write_str("Function parameter cannot be have default value here")
86			}
87			ParseErrors::InvalidLHSAssignment => f.write_str("Invalid syntax on LHS of assignment"),
88			ParseErrors::ExpectedCatchOrFinally => {
89				f.write_str("Expected 'catch' or 'finally' to follow 'try'")
90			}
91			ParseErrors::LexingFailed => {
92				// unreachable!("This should never be written"),
93				f.write_str("Lexing issue")
94			}
95			ParseErrors::InvalidDeclareItem(item) => {
96				write!(f, "Declare item '{item}' must be in .d.ts file")
97			}
98			ParseErrors::DestructuringRequiresValue => {
99				write!(f, "RHS of destructured declaration requires expression")
100			}
101			ParseErrors::CannotAccessObjectLiteralDirectly => {
102				write!(f, "Cannot get property on object literal directly")
103			}
104			ParseErrors::TrailingCommaNotAllowedHere => {
105				write!(f, "Trailing comma not allowed here")
106			}
107			ParseErrors::InvalidNumberLiteral => {
108				write!(f, "Invalid number literal")
109			}
110			ParseErrors::ReservedIdentifier => {
111				write!(f, "Found reserved identifier")
112			}
113			ParseErrors::AwaitRequiresForOf => {
114				write!(f, "Can only use await on for (.. of ..)")
115			}
116			ParseErrors::CannotUseLeadingParameterHere => {
117				write!(f, "Cannot write this constraint in this kind of function")
118			}
119			ParseErrors::ExpectedIdentifier => {
120				write!(f, "Expected variable identifier")
121			}
122			ParseErrors::ExpectedNumberLiteral => {
123				write!(f, "Expected number literal")
124			}
125			ParseErrors::ExpectRule => {
126				write!(f, "'-' must be followed by a readonly rule")
127			}
128			ParseErrors::InvalidRegexFlag => {
129				write!(f, "Regexp flags must be 'd', 'g', 'i', 'm', 's', 'u' or 'y'")
130			}
131			ParseErrors::ExpectedDeclaration => {
132				write!(f, "Expected identifier after variable declaration keyword")
133			}
134			ParseErrors::CannotHaveRegularMemberAfterSpread => {
135				write!(f, "Cannot have regular member after spread")
136			}
137			ParseErrors::InvalidLHSOfIs => {
138				write!(f, "LHS must be variable reference")
139			}
140		}
141	}
142}
143
144#[allow(missing_docs)]
145#[derive(Debug)]
146pub enum LexingErrors {
147	SecondDecimalPoint,
148	NumberLiteralCannotHaveDecimalPoint,
149	NumberLiteralBaseSpecifierMustPrecededWithZero,
150	InvalidCharacterInJSXTag(char),
151	UnbalancedJSXClosingTags,
152	ExpectedClosingChevronAtEndOfSelfClosingTag,
153	InvalidCharacterInAttributeKey(char),
154	UnexpectedCharacter(derive_finite_automaton::InvalidCharacter),
155	EmptyAttributeName,
156	ExpectedJSXEndTag,
157	NewLineInStringLiteral,
158	ExpectedEndToMultilineComment,
159	ExpectedEndToStringLiteral,
160	UnexpectedEndToNumberLiteral,
161	InvalidNumeralItemBecauseOfLiteralKind,
162	ExpectedEndToRegexLiteral,
163	ExpectedEndToJSXLiteral,
164	ExpectedEndToTemplateLiteral,
165	InvalidExponentUsage,
166	InvalidUnderscore,
167	CannotLoadLargeFile(usize),
168	ExpectedDashInComment,
169	ExpectedOpenChevron,
170}
171
172impl Display for LexingErrors {
173	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174		match self {
175			LexingErrors::SecondDecimalPoint => {
176				f.write_str("Second decimal point found in number literal")
177			}
178			LexingErrors::NumberLiteralCannotHaveDecimalPoint => {
179				f.write_str("Number literal with specified base cannot have decimal point")
180			}
181			LexingErrors::NumberLiteralBaseSpecifierMustPrecededWithZero => {
182				f.write_str("Number literal base character must be proceeded with a zero")
183			}
184			LexingErrors::InvalidCharacterInJSXTag(chr) => {
185				write!(f, "Invalid character {chr:?} in JSX tag")
186			}
187			LexingErrors::ExpectedClosingChevronAtEndOfSelfClosingTag => {
188				f.write_str("Expected closing angle at end of self closing JSX tag")
189			}
190			LexingErrors::InvalidCharacterInAttributeKey(chr) => {
191				write!(f, "Invalid character {chr:?} in JSX attribute name")
192			}
193			LexingErrors::EmptyAttributeName => f.write_str("Empty JSX attribute name"),
194			LexingErrors::ExpectedJSXEndTag => f.write_str("Expected JSX end tag"),
195			LexingErrors::NewLineInStringLiteral => {
196				f.write_str("String literals cannot contain new lines")
197			}
198			LexingErrors::ExpectedEndToMultilineComment => {
199				f.write_str("Unclosed multiline comment")
200			}
201			LexingErrors::ExpectedEndToStringLiteral => f.write_str("Unclosed string literal"),
202			LexingErrors::UnexpectedEndToNumberLiteral => f.write_str("Unclosed number literal"),
203			LexingErrors::ExpectedEndToRegexLiteral => f.write_str("Unclosed regex literal"),
204			LexingErrors::ExpectedEndToJSXLiteral => f.write_str("Unclosed JSX literal"),
205			LexingErrors::ExpectedEndToTemplateLiteral => f.write_str("Unclosed template literal"),
206			LexingErrors::UnexpectedCharacter(err) => Display::fmt(err, f),
207			LexingErrors::UnbalancedJSXClosingTags => f.write_str("Too many closing JSX tags"),
208			LexingErrors::InvalidExponentUsage => f.write_str("Two e in number literal"),
209			LexingErrors::InvalidUnderscore => f.write_str("Numeric separator in invalid place"),
210			LexingErrors::InvalidNumeralItemBecauseOfLiteralKind => {
211				f.write_str("Invalid item in binary, hex or octal literal")
212			}
213			LexingErrors::CannotLoadLargeFile(size) => {
214				write!(f, "Cannot parse {size:?} byte file (4GB maximum)")
215			}
216			LexingErrors::ExpectedDashInComment => {
217				f.write_str("JSX comments must have two dashes after `<!` start")
218			}
219			LexingErrors::ExpectedOpenChevron => {
220				f.write_str("Unexpected token in HTML. Expected '<'")
221			}
222		}
223	}
224}
225
226// For TokenReader::expect_next
227impl From<Option<(TSXToken, Token<TSXToken, TokenStart>)>> for ParseError {
228	fn from(opt: Option<(TSXToken, Token<TSXToken, TokenStart>)>) -> Self {
229		if let Some((expected_type, token)) = opt {
230			let position = token.get_span();
231			let reason =
232				ParseErrors::UnexpectedToken { expected: &[expected_type], found: token.0 };
233			Self::new(reason, position)
234		} else {
235			parse_lexing_error()
236		}
237	}
238}
239
240// For TokenReader::next which only
241pub(crate) fn parse_lexing_error() -> ParseError {
242	ParseError::new(ParseErrors::LexingFailed, source_map::Nullable::NULL)
243}
244
245pub trait ParserErrorReason: Display {}
246
247impl<'a> ParserErrorReason for ParseErrors<'a> {}
248impl ParserErrorReason for LexingErrors {}
249
250/// A error for not parsing
251#[derive(Debug)]
252pub struct ParseError {
253	pub reason: String,
254	pub position: Span,
255}
256
257impl ParseError {
258	#[allow(clippy::needless_pass_by_value)]
259	pub fn new(reason: impl ParserErrorReason, position: Span) -> Self {
260		Self { reason: reason.to_string(), position }
261	}
262}
263
264impl std::error::Error for ParseError {}
265impl std::fmt::Display for ParseError {
266	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267		f.write_fmt(format_args!("ParseError: {} @ byte indices {:?}", self.reason, self.position))
268	}
269}
270
271pub type ParseResult<T> = Result<T, ParseError>;