1use 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 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
226impl 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
240pub(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#[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>;