1use crate::SourceLocation;
4use thiserror::Error;
5use vize_carton::{CompactString, ToCompactString};
6
7#[derive(Debug, Clone, Error)]
9#[error("{message}")]
10pub struct CompilerError {
11 pub code: ErrorCode,
12 pub message: CompactString,
13 pub loc: Option<SourceLocation>,
14}
15
16impl CompilerError {
17 pub fn new(code: ErrorCode, loc: Option<SourceLocation>) -> Self {
18 Self {
19 message: code.message().to_compact_string(),
20 code,
21 loc,
22 }
23 }
24
25 pub fn with_message(
26 code: ErrorCode,
27 message: impl Into<CompactString>,
28 loc: Option<SourceLocation>,
29 ) -> Self {
30 Self {
31 code,
32 message: message.into(),
33 loc,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40#[repr(u16)]
41pub enum ErrorCode {
42 AbruptClosingOfEmptyComment = 0,
44 CdataInHtmlContent = 1,
45 DuplicateAttribute = 2,
46 EndTagWithAttributes = 3,
47 EndTagWithTrailingSolidus = 4,
48 EofBeforeTagName = 5,
49 EofInCdata = 6,
50 EofInComment = 7,
51 EofInScriptHtmlCommentLikeText = 8,
52 EofInTag = 9,
53 IncorrectlyClosedComment = 10,
54 IncorrectlyOpenedComment = 11,
55 InvalidFirstCharacterOfTagName = 12,
56 MissingAttributeValue = 13,
57 MissingEndTagName = 14,
58 MissingWhitespaceBetweenAttributes = 15,
59 NestedComment = 16,
60 UnexpectedCharacterInAttributeName = 17,
61 UnexpectedCharacterInUnquotedAttributeValue = 18,
62 UnexpectedEqualsSignBeforeAttributeName = 19,
63 UnexpectedNullCharacter = 20,
64 UnexpectedQuestionMarkInsteadOfTagName = 21,
65 UnexpectedSolidusInTag = 22,
66
67 InvalidEndTag = 23,
69 MissingEndTag = 24,
70 MissingInterpolationEnd = 25,
71 MissingDynamicDirectiveArgumentEnd = 26,
72 MissingDirectiveName = 27,
73 MissingDirectiveModifier = 28,
74
75 VIfNoExpression = 29,
77 VIfSameKey = 30,
78 VElseNoAdjacentIf = 31,
79 VForNoExpression = 32,
80 VForMalformedExpression = 33,
81 VForTemplateKeyPlacement = 34,
82 VBindNoExpression = 35,
83 VBindSameNameShorthand = 36,
84 VOnNoExpression = 37,
85 VSlotUnexpectedDirectiveOnSlotOutlet = 38,
86 VSlotMixedSlotUsage = 39,
87 VSlotDuplicateSlotNames = 40,
88 VSlotExtraneousDefaultSlotChildren = 41,
89 VSlotMisplaced = 42,
90 VModelNoExpression = 43,
91 VModelMalformedExpression = 44,
92 VModelOnScope = 45,
93 VModelOnProps = 46,
94 VModelArgOnElement = 47,
95 VShowNoExpression = 48,
96
97 PrefixIdNotSupported = 49,
99 ModuleModeNotSupported = 50,
100 CacheHandlerNotSupported = 51,
101 ScopeIdNotSupported = 52,
102
103 UnhandledCodePath = 100,
105 ExtendPoint = 1000,
106}
107
108impl ErrorCode {
109 pub fn message(&self) -> &'static str {
110 match self {
111 Self::AbruptClosingOfEmptyComment => "Illegal comment.",
112 Self::CdataInHtmlContent => "CDATA section is allowed only in XML context.",
113 Self::DuplicateAttribute => "Duplicate attribute.",
114 Self::EndTagWithAttributes => "End tag cannot have attributes.",
115 Self::EndTagWithTrailingSolidus => "Trailing solidus not allowed in end tags.",
116 Self::EofBeforeTagName => "Unexpected EOF in tag.",
117 Self::EofInCdata => "EOF in CDATA section.",
118 Self::EofInComment => "EOF in comment.",
119 Self::EofInScriptHtmlCommentLikeText => "EOF in script.",
120 Self::EofInTag => "EOF in tag.",
121 Self::IncorrectlyClosedComment => "Incorrectly closed comment.",
122 Self::IncorrectlyOpenedComment => "Incorrectly opened comment.",
123 Self::InvalidFirstCharacterOfTagName => "Invalid first character of tag name.",
124 Self::MissingAttributeValue => "Attribute value expected.",
125 Self::MissingEndTagName => "End tag name expected.",
126 Self::MissingWhitespaceBetweenAttributes => "Whitespace expected between attributes.",
127 Self::NestedComment => "Nested comments are not allowed.",
128 Self::UnexpectedCharacterInAttributeName => "Unexpected character in attribute name.",
129 Self::UnexpectedCharacterInUnquotedAttributeValue => {
130 "Unexpected character in unquoted attribute value."
131 }
132 Self::UnexpectedEqualsSignBeforeAttributeName => {
133 "Unexpected equals sign before attribute name."
134 }
135 Self::UnexpectedNullCharacter => "Unexpected null character.",
136 Self::UnexpectedQuestionMarkInsteadOfTagName => "Invalid tag name.",
137 Self::UnexpectedSolidusInTag => "Unexpected solidus in tag.",
138
139 Self::InvalidEndTag => "Invalid end tag.",
140 Self::MissingEndTag => "Element is missing end tag.",
141 Self::MissingInterpolationEnd => "Interpolation end sign was not found.",
142 Self::MissingDynamicDirectiveArgumentEnd => {
143 "End bracket for dynamic directive argument was not found."
144 }
145 Self::MissingDirectiveName => "Directive name is missing.",
146 Self::MissingDirectiveModifier => "Directive modifier is expected.",
147
148 Self::VIfNoExpression => "v-if/v-else-if is missing expression.",
149 Self::VIfSameKey => "v-if/v-else-if branches must use unique keys.",
150 Self::VElseNoAdjacentIf => "v-else/v-else-if has no adjacent v-if.",
151 Self::VForNoExpression => "v-for is missing expression.",
152 Self::VForMalformedExpression => "v-for has invalid expression.",
153 Self::VForTemplateKeyPlacement => {
154 "<template v-for> key should be placed on the <template> tag."
155 }
156 Self::VBindNoExpression => "v-bind is missing expression.",
157 Self::VBindSameNameShorthand => "v-bind shorthand requires prop name.",
158 Self::VOnNoExpression => "v-on is missing expression.",
159 Self::VSlotUnexpectedDirectiveOnSlotOutlet => {
160 "Unexpected custom directive on <slot> outlet."
161 }
162 Self::VSlotMixedSlotUsage => "Mixed v-slot usage with named slots detected.",
163 Self::VSlotDuplicateSlotNames => "Duplicate slot names detected.",
164 Self::VSlotExtraneousDefaultSlotChildren => {
165 "Extraneous children found when component already has an explicit default slot."
166 }
167 Self::VSlotMisplaced => "v-slot can only be used on components or <template> tags.",
168 Self::VModelNoExpression => "v-model is missing expression.",
169 Self::VModelMalformedExpression => {
170 "v-model value must be a valid JavaScript member expression."
171 }
172 Self::VModelOnScope => "v-model cannot be used on v-for or v-slot scope variables.",
173 Self::VModelOnProps => "v-model cannot be used on props.",
174 Self::VModelArgOnElement => "v-model argument is not supported on plain elements.",
175 Self::VShowNoExpression => "v-show is missing expression.",
176
177 Self::PrefixIdNotSupported => "prefixIdentifiers option is not supported in this mode.",
178 Self::ModuleModeNotSupported => "ES module mode is not supported in this mode.",
179 Self::CacheHandlerNotSupported => "cacheHandlers option is not supported in this mode.",
180 Self::ScopeIdNotSupported => "scopeId option is not supported in this mode.",
181
182 Self::UnhandledCodePath => "Unhandled code path.",
183 Self::ExtendPoint => "Extension point.",
184 }
185 }
186
187 pub fn is_parse_error(&self) -> bool {
188 (*self as u16) < (Self::VIfNoExpression as u16)
189 }
190
191 pub fn is_transform_error(&self) -> bool {
192 let code = *self as u16;
193 code >= (Self::VIfNoExpression as u16) && code < (Self::PrefixIdNotSupported as u16)
194 }
195}
196
197pub type CompilerResult<T> = Result<T, CompilerError>;
199
200#[cfg(test)]
201mod tests {
202 use super::{CompilerError, ErrorCode};
203
204 #[test]
205 fn compiler_error_new() {
206 let err = CompilerError::new(ErrorCode::EofInTag, None);
207 assert_eq!(err.code, ErrorCode::EofInTag);
208 assert_eq!(err.message, "EOF in tag.");
209 assert!(err.loc.is_none());
210 }
211
212 #[test]
213 fn compiler_error_with_message() {
214 let err =
215 CompilerError::with_message(ErrorCode::UnhandledCodePath, "custom error message", None);
216 assert_eq!(err.code, ErrorCode::UnhandledCodePath);
217 assert_eq!(err.message, "custom error message");
218 }
219
220 #[test]
221 fn error_code_messages_not_empty() {
222 let codes = [
223 ErrorCode::AbruptClosingOfEmptyComment,
224 ErrorCode::CdataInHtmlContent,
225 ErrorCode::DuplicateAttribute,
226 ErrorCode::EndTagWithAttributes,
227 ErrorCode::EofInTag,
228 ErrorCode::InvalidEndTag,
229 ErrorCode::MissingEndTag,
230 ErrorCode::MissingInterpolationEnd,
231 ErrorCode::MissingDirectiveName,
232 ErrorCode::MissingDirectiveModifier,
233 ErrorCode::VIfNoExpression,
234 ErrorCode::VForNoExpression,
235 ErrorCode::VBindNoExpression,
236 ErrorCode::VOnNoExpression,
237 ErrorCode::VModelNoExpression,
238 ErrorCode::VShowNoExpression,
239 ErrorCode::PrefixIdNotSupported,
240 ErrorCode::UnhandledCodePath,
241 ErrorCode::ExtendPoint,
242 ];
243 for code in &codes {
244 assert!(!code.message().is_empty(), "{:?} has empty message", code);
245 }
246 }
247
248 #[test]
249 fn is_parse_error_true() {
250 let parse_errors = [
251 ErrorCode::AbruptClosingOfEmptyComment,
252 ErrorCode::CdataInHtmlContent,
253 ErrorCode::DuplicateAttribute,
254 ErrorCode::EofInTag,
255 ErrorCode::InvalidEndTag,
256 ErrorCode::MissingEndTag,
257 ErrorCode::MissingInterpolationEnd,
258 ErrorCode::MissingDirectiveName,
259 ErrorCode::MissingDirectiveModifier,
260 ];
261 for code in &parse_errors {
262 assert!(code.is_parse_error(), "{:?} should be parse error", code);
263 }
264 }
265
266 #[test]
267 fn is_parse_error_false_for_transform() {
268 assert!(!ErrorCode::VIfNoExpression.is_parse_error());
269 assert!(!ErrorCode::VShowNoExpression.is_parse_error());
270 assert!(!ErrorCode::PrefixIdNotSupported.is_parse_error());
271 }
272
273 #[test]
274 fn is_transform_error_true() {
275 let transform_errors = [
276 ErrorCode::VIfNoExpression,
277 ErrorCode::VIfSameKey,
278 ErrorCode::VElseNoAdjacentIf,
279 ErrorCode::VForNoExpression,
280 ErrorCode::VBindNoExpression,
281 ErrorCode::VOnNoExpression,
282 ErrorCode::VModelNoExpression,
283 ErrorCode::VShowNoExpression,
284 ];
285 for code in &transform_errors {
286 assert!(
287 code.is_transform_error(),
288 "{:?} should be transform error",
289 code
290 );
291 }
292 }
293
294 #[test]
295 fn is_transform_error_false() {
296 assert!(!ErrorCode::EofInTag.is_transform_error());
298 assert!(!ErrorCode::MissingDirectiveModifier.is_transform_error());
299 assert!(!ErrorCode::PrefixIdNotSupported.is_transform_error());
301 }
302
303 #[test]
304 fn boundary_error_codes() {
305 assert!(ErrorCode::MissingDirectiveModifier.is_parse_error());
307 assert!(!ErrorCode::MissingDirectiveModifier.is_transform_error());
308
309 assert!(!ErrorCode::VIfNoExpression.is_parse_error());
311 assert!(ErrorCode::VIfNoExpression.is_transform_error());
312
313 assert!(ErrorCode::VShowNoExpression.is_transform_error());
315 assert!(!ErrorCode::VShowNoExpression.is_parse_error());
316
317 assert!(!ErrorCode::PrefixIdNotSupported.is_parse_error());
319 assert!(!ErrorCode::PrefixIdNotSupported.is_transform_error());
320 }
321
322 #[test]
323 fn mutual_exclusion() {
324 let all_codes = [
325 ErrorCode::AbruptClosingOfEmptyComment,
326 ErrorCode::CdataInHtmlContent,
327 ErrorCode::DuplicateAttribute,
328 ErrorCode::EndTagWithAttributes,
329 ErrorCode::EndTagWithTrailingSolidus,
330 ErrorCode::EofBeforeTagName,
331 ErrorCode::EofInCdata,
332 ErrorCode::EofInComment,
333 ErrorCode::EofInScriptHtmlCommentLikeText,
334 ErrorCode::EofInTag,
335 ErrorCode::IncorrectlyClosedComment,
336 ErrorCode::IncorrectlyOpenedComment,
337 ErrorCode::InvalidFirstCharacterOfTagName,
338 ErrorCode::MissingAttributeValue,
339 ErrorCode::MissingEndTagName,
340 ErrorCode::MissingWhitespaceBetweenAttributes,
341 ErrorCode::NestedComment,
342 ErrorCode::UnexpectedCharacterInAttributeName,
343 ErrorCode::UnexpectedCharacterInUnquotedAttributeValue,
344 ErrorCode::UnexpectedEqualsSignBeforeAttributeName,
345 ErrorCode::UnexpectedNullCharacter,
346 ErrorCode::UnexpectedQuestionMarkInsteadOfTagName,
347 ErrorCode::UnexpectedSolidusInTag,
348 ErrorCode::InvalidEndTag,
349 ErrorCode::MissingEndTag,
350 ErrorCode::MissingInterpolationEnd,
351 ErrorCode::MissingDynamicDirectiveArgumentEnd,
352 ErrorCode::MissingDirectiveName,
353 ErrorCode::MissingDirectiveModifier,
354 ErrorCode::VIfNoExpression,
355 ErrorCode::VIfSameKey,
356 ErrorCode::VElseNoAdjacentIf,
357 ErrorCode::VForNoExpression,
358 ErrorCode::VForMalformedExpression,
359 ErrorCode::VForTemplateKeyPlacement,
360 ErrorCode::VBindNoExpression,
361 ErrorCode::VBindSameNameShorthand,
362 ErrorCode::VOnNoExpression,
363 ErrorCode::VSlotUnexpectedDirectiveOnSlotOutlet,
364 ErrorCode::VSlotMixedSlotUsage,
365 ErrorCode::VSlotDuplicateSlotNames,
366 ErrorCode::VSlotExtraneousDefaultSlotChildren,
367 ErrorCode::VSlotMisplaced,
368 ErrorCode::VModelNoExpression,
369 ErrorCode::VModelMalformedExpression,
370 ErrorCode::VModelOnScope,
371 ErrorCode::VModelOnProps,
372 ErrorCode::VModelArgOnElement,
373 ErrorCode::VShowNoExpression,
374 ErrorCode::PrefixIdNotSupported,
375 ErrorCode::ModuleModeNotSupported,
376 ErrorCode::CacheHandlerNotSupported,
377 ErrorCode::ScopeIdNotSupported,
378 ErrorCode::UnhandledCodePath,
379 ErrorCode::ExtendPoint,
380 ];
381 for code in &all_codes {
382 assert!(
383 !(code.is_parse_error() && code.is_transform_error()),
384 "{:?} should not be both parse and transform error",
385 code
386 );
387 }
388 }
389}