swc_html_parser/
error.rs

1use std::borrow::Cow;
2
3use swc_atoms::Atom;
4use swc_common::{
5    errors::{DiagnosticBuilder, Handler},
6    Span,
7};
8
9/// Size is same as a size of a pointer.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Error {
12    inner: Box<(Span, ErrorKind)>,
13}
14
15impl Error {
16    pub fn kind(&self) -> &ErrorKind {
17        &self.inner.1
18    }
19
20    pub fn into_inner(self) -> Box<(Span, ErrorKind)> {
21        self.inner
22    }
23
24    pub fn new(span: Span, kind: ErrorKind) -> Self {
25        Error {
26            inner: Box::new((span, kind)),
27        }
28    }
29
30    pub fn message(&self) -> Cow<'static, str> {
31        match &self.inner.1 {
32            ErrorKind::Eof => "Unexpected end of file".into(),
33
34            // Lexer errors
35            ErrorKind::AbruptClosingOfEmptyComment => "Abrupt closing of empty comment".into(),
36            ErrorKind::AbruptDoctypePublicIdentifier => "Abrupt doctype public identifier".into(),
37            ErrorKind::AbruptDoctypeSystemIdentifier => "Abrupt doctype system identifier".into(),
38            ErrorKind::AbsenceOfDigitsInNumericCharacterReference => {
39                "Absence of digits in numeric character reference".into()
40            }
41            ErrorKind::CdataInHtmlContent => "Cdata in html content".into(),
42            ErrorKind::CharacterReferenceOutsideUnicodeRange => {
43                "Character reference outside unicode range".into()
44            }
45            ErrorKind::ControlCharacterInInputStream => "Control character in input stream".into(),
46            ErrorKind::ControlCharacterReference => "Control character reference".into(),
47            ErrorKind::EndTagWithAttributes => "End tag with attributes".into(),
48            ErrorKind::DuplicateAttribute => "Duplicate attribute".into(),
49            ErrorKind::EndTagWithTrailingSolidus => "End tag with trailing solidus".into(),
50            ErrorKind::EofBeforeTagName => "Eof before tag name".into(),
51            ErrorKind::EofInCdata => "Eof in cdata".into(),
52            ErrorKind::EofInComment => "Eof in comment".into(),
53            ErrorKind::EofInDoctype => "Eof in doctype".into(),
54            ErrorKind::EofInScriptHtmlCommentLikeText => {
55                "Eof in script html comment like text".into()
56            }
57            ErrorKind::EofInTag => "Eof in tag".into(),
58            ErrorKind::IncorrectlyClosedComment => "Incorrectly closed comment".into(),
59            ErrorKind::IncorrectlyOpenedComment => "Incorrectly opened comment".into(),
60            ErrorKind::InvalidCharacterSequenceAfterDoctypeName => {
61                "Invalid character sequence after doctype name".into()
62            }
63            ErrorKind::InvalidFirstCharacterOfTagName => {
64                "Invalid first character of tag name".into()
65            }
66            ErrorKind::MissingAttributeValue => "Missing attribute value".into(),
67            ErrorKind::MissingDoctypeName => "Missing doctype name".into(),
68            ErrorKind::MissingDoctypePublicIdentifier => "Missing doctype public identifier".into(),
69            ErrorKind::MissingDoctypeSystemIdentifier => "Missing doctype system identifier".into(),
70            ErrorKind::MissingEndTagName => "Missing end tag name".into(),
71            ErrorKind::MissingQuoteBeforeDoctypePublicIdentifier => {
72                "Missing quote before doctype public identifier".into()
73            }
74            ErrorKind::MissingQuoteBeforeDoctypeSystemIdentifier => {
75                "Missing quote before doctype system identifier".into()
76            }
77            ErrorKind::MissingSemicolonAfterCharacterReference => {
78                "Missing semicolon after character reference".into()
79            }
80            ErrorKind::MissingWhitespaceAfterDoctypePublicKeyword => {
81                "Missing whitespace after doctype public keyword".into()
82            }
83            ErrorKind::MissingWhitespaceAfterDoctypeSystemKeyword => {
84                "Missing whitespace after doctype system keyword".into()
85            }
86            ErrorKind::MissingWhitespaceBeforeDoctypeName => {
87                "Missing whitespace before doctype name".into()
88            }
89            ErrorKind::MissingWhitespaceBetweenAttributes => {
90                "Missing whitespace between attributes".into()
91            }
92            ErrorKind::MissingWhitespaceBetweenDoctypePublicAndSystemIdentifiers => {
93                "Missing whitespace between doctype public and system identifiers".into()
94            }
95            ErrorKind::NestedComment => "Nested comment".into(),
96            ErrorKind::NoncharacterCharacterReference => "Noncharacter character reference".into(),
97            ErrorKind::NoncharacterInInputStream => "Noncharacter in input stream".into(),
98            ErrorKind::NonVoidHtmlElementStartTagWithTrailingSolidus => {
99                "Non void html element start tag with trailing solidus".into()
100            }
101            ErrorKind::NullCharacterReference => "Null character reference".into(),
102            ErrorKind::SurrogateCharacterReference => "Surrogate character reference".into(),
103            ErrorKind::SurrogateInInputStream => "Surrogate in input stream".into(),
104            ErrorKind::UnexpectedCharacterAfterDoctypeSystemIdentifier => {
105                "Unexpected character after doctype system identifier".into()
106            }
107            ErrorKind::UnexpectedCharacterInAttributeName => {
108                "Unexpected character in attribute name".into()
109            }
110            ErrorKind::UnexpectedCharacterInUnquotedAttributeValue => {
111                "Unexpected character in unquoted attribute value".into()
112            }
113            ErrorKind::UnexpectedEqualsSignBeforeAttributeName => {
114                "Unexpected equals sign before attribute name".into()
115            }
116            ErrorKind::UnexpectedNullCharacter => "Unexpected null character".into(),
117            ErrorKind::UnexpectedQuestionMarkInsteadOfTagName => {
118                "Unexpected question mark instead of tag name".into()
119            }
120            ErrorKind::UnexpectedSolidusInTag => "Unexpected solidus in tag".into(),
121            ErrorKind::UnknownNamedCharacterReference => "Unknown named character reference".into(),
122
123            // Parser errors
124            ErrorKind::StrayStartTag(tag_name) => format!("Stray start tag \"{tag_name}\"").into(),
125            ErrorKind::StrayEndTag(tag_name) => format!("Stray end tag \"{tag_name}\"").into(),
126            ErrorKind::UnclosedElements(tag_name) => {
127                format!("End tag \"{tag_name}\" seen, but there were open elements").into()
128            }
129            ErrorKind::UnclosedElementsImplied(tag_name) => {
130                format!("End tag \"{tag_name}\" implied, but there were open elements").into()
131            }
132            ErrorKind::UnclosedElementsCell => {
133                "A table cell was implicitly closed, but there were open elements".into()
134            }
135            ErrorKind::StrayDoctype => "Stray doctype".into(),
136            ErrorKind::NonConformingDoctype => "Non conforming doctype".into(),
137            ErrorKind::NonSpaceCharacterInTrailer => "Non-space character in page trailer".into(),
138            ErrorKind::NonSpaceCharacterAfterFrameset => {
139                "Non-space character after \"frameset\"".into()
140            }
141            ErrorKind::NonSpaceCharacterInFrameset => "Non-space character in \"frameset\"".into(),
142            ErrorKind::NonSpaceCharacterAfterBody => "Non-space character after body".into(),
143            ErrorKind::NonSpaceCharacterInColumnGroup => {
144                "Non-space character in \"colgroup\" element".into()
145            }
146            ErrorKind::NonSpaceCharacterInNoscriptInHead => {
147                "Non-space character inside \"noscript\" inside \"head\"".into()
148            }
149            ErrorKind::SomethingBetweenHeadAndBody(tag_name) => {
150                format!("\"{tag_name}\" element between \"head\" and \"body\"").into()
151            }
152            ErrorKind::StartTagWithoutDoctype => {
153                "Start tag seen without seeing a doctype first, expected \"<!DOCTYPE html>\"".into()
154            }
155            ErrorKind::StartSelectWhereEndSelectExpected => {
156                "\"select\" start tag where end tag expected".into()
157            }
158            ErrorKind::StartTagWithSelectOpen(tag_name) => {
159                format!("\"{tag_name}\" start tag with \"select\" open").into()
160            }
161            ErrorKind::BadStartTagInNoscriptInHead(tag_name) => {
162                format!("Bad start tag in \"{tag_name}\" in \"noscript\" in \"head\"").into()
163            }
164            ErrorKind::UnexpectedImageStartTag => {
165                "Saw a start tag \"image\", \"img\" element is outdated".into()
166            }
167            ErrorKind::SomethingSeenWhenSomethingOpen(tag_name) => format!(
168                "Start tag \"{tag_name}\" seen but an element of the same type was already open"
169            )
170            .into(),
171            ErrorKind::HeadingWhenHeadingOpen => {
172                "Heading cannot be a child of another heading".into()
173            }
174            ErrorKind::NoCellToClose => "No cell to close".into(),
175            ErrorKind::StartTagInTable(tag_name) => {
176                format!("Start tag \"{tag_name}\" seen in \"table\"").into()
177            }
178            ErrorKind::FormWhenFormOpen => "Saw a \"form\" start tag, but there was already an \
179                                            active \"form\" element, nested forms are not allowed"
180                .into(),
181            ErrorKind::TableSeenWhileTableOpen => {
182                "Start tag for \"table\" seen but the previous \"table\" is still open".into()
183            }
184            ErrorKind::StartTagInTableBody(tag_name) => {
185                format!("Start tag \"{tag_name}\" seen in \"table\" body").into()
186            }
187            ErrorKind::EndTagSeenWithoutDoctype => {
188                "End tag seen without seeing a doctype first, expected \"<!DOCTYPE html>\"".into()
189            }
190            ErrorKind::EndTagAfterBody => "Saw an end tag after \"body\" had been closed".into(),
191            ErrorKind::EndTagSeenWithSelectOpen(tag_name) => {
192                format!("\"{tag_name}\" end tag with \"select\" open").into()
193            }
194            ErrorKind::GarbageInColumnGroup => "Garbage in \"colgroup\" element".into(),
195            ErrorKind::EndTagBr => "End tag \"br\"".into(),
196            ErrorKind::NoElementToCloseButEndTagSeen(tag_name) => {
197                format!("No \"{tag_name}\" element in scope but a \"{tag_name}\" end tag seen")
198                    .into()
199            }
200            ErrorKind::HtmlStartTagInForeignContext(tag_name) => {
201                format!("HTML start tag \"{tag_name}\" in a foreign namespace context").into()
202            }
203            ErrorKind::NoTableRowToClose => "No table row to close".into(),
204            ErrorKind::NonSpaceCharacterInTable => {
205                "Misplaced non-space characters inside a table".into()
206            }
207            ErrorKind::UnclosedChildrenInRuby => "Unclosed children in \"ruby\"".into(),
208            ErrorKind::StartTagSeenWithoutRuby(tag_name) => {
209                format!("Start tag \"{tag_name}\" seen without a \"ruby\" element being open")
210                    .into()
211            }
212            ErrorKind::UnclosedElementsOnStack => "Unclosed elements on stack".into(),
213            ErrorKind::EndTagDidNotMatchCurrentOpenElement(
214                end_tag_name,
215                current_element_tag_name,
216            ) => format!(
217                "End tag \"{end_tag_name}\" did not match the name of the current open element \
218                 (\"{current_element_tag_name}\")"
219            )
220            .into(),
221            ErrorKind::EndTagViolatesNestingRules(tag_name) => {
222                format!("End tag \"{tag_name}\" violates nesting rules").into()
223            }
224            ErrorKind::EofWithUnclosedElements => {
225                "End of file seen and there were open elements".into()
226            }
227            ErrorKind::EndTagWithUnclosedElements(tag_name) => {
228                format!("Unexpected end tag for \"{tag_name}\", but there were unclosed elements")
229                    .into()
230            }
231            ErrorKind::NonSpaceCharacterWithoutDoctype => "Non-space characters found without \
232                                                           seeing a doctype first, expected \
233                                                           \"<!DOCTYPE html>\""
234                .into(),
235            ErrorKind::EofWithoutDoctype => "End of file seen without seeing a doctype first, \
236                                             expected \"<!DOCTYPE html>\""
237                .into(),
238            ErrorKind::EofInText => "End of file seen when expecting text or an end tag".into(),
239        }
240    }
241
242    pub fn to_diagnostics<'a>(&self, handler: &'a Handler) -> DiagnosticBuilder<'a> {
243        handler.struct_span_err(self.inner.0, &self.message())
244    }
245}
246
247#[derive(Debug, Clone, PartialEq, Eq)]
248#[non_exhaustive]
249pub enum ErrorKind {
250    Eof,
251
252    // Lexer errors
253    AbruptClosingOfEmptyComment,
254    AbruptDoctypePublicIdentifier,
255    AbruptDoctypeSystemIdentifier,
256    AbsenceOfDigitsInNumericCharacterReference,
257    CdataInHtmlContent,
258    CharacterReferenceOutsideUnicodeRange,
259    ControlCharacterInInputStream,
260    ControlCharacterReference,
261    EndTagWithAttributes,
262    DuplicateAttribute,
263    EndTagWithTrailingSolidus,
264    EofBeforeTagName,
265    EofInCdata,
266    EofInComment,
267    EofInDoctype,
268    EofInScriptHtmlCommentLikeText,
269    EofInTag,
270    IncorrectlyClosedComment,
271    IncorrectlyOpenedComment,
272    InvalidCharacterSequenceAfterDoctypeName,
273    InvalidFirstCharacterOfTagName,
274    MissingAttributeValue,
275    MissingDoctypeName,
276    MissingDoctypePublicIdentifier,
277    MissingDoctypeSystemIdentifier,
278    MissingEndTagName,
279    MissingQuoteBeforeDoctypePublicIdentifier,
280    MissingQuoteBeforeDoctypeSystemIdentifier,
281    MissingSemicolonAfterCharacterReference,
282    MissingWhitespaceAfterDoctypePublicKeyword,
283    MissingWhitespaceAfterDoctypeSystemKeyword,
284    MissingWhitespaceBeforeDoctypeName,
285    MissingWhitespaceBetweenAttributes,
286    MissingWhitespaceBetweenDoctypePublicAndSystemIdentifiers,
287    NestedComment,
288    NoncharacterCharacterReference,
289    NoncharacterInInputStream,
290    NonVoidHtmlElementStartTagWithTrailingSolidus,
291    NullCharacterReference,
292    SurrogateCharacterReference,
293    SurrogateInInputStream,
294    UnexpectedCharacterAfterDoctypeSystemIdentifier,
295    UnexpectedCharacterInAttributeName,
296    UnexpectedCharacterInUnquotedAttributeValue,
297    UnexpectedEqualsSignBeforeAttributeName,
298    UnexpectedNullCharacter,
299    UnexpectedQuestionMarkInsteadOfTagName,
300    UnexpectedSolidusInTag,
301    UnknownNamedCharacterReference,
302
303    // Parser errors
304    StrayStartTag(Atom),
305    StrayEndTag(Atom),
306    UnclosedElements(Atom),
307    UnclosedElementsImplied(Atom),
308    UnclosedElementsCell,
309    StrayDoctype,
310    NonConformingDoctype,
311    NonSpaceCharacterInTrailer,
312    NonSpaceCharacterAfterFrameset,
313    NonSpaceCharacterInFrameset,
314    NonSpaceCharacterAfterBody,
315    NonSpaceCharacterInColumnGroup,
316    NonSpaceCharacterInNoscriptInHead,
317    SomethingBetweenHeadAndBody(Atom),
318    StartTagWithoutDoctype,
319    StartSelectWhereEndSelectExpected,
320    StartTagWithSelectOpen(Atom),
321    BadStartTagInNoscriptInHead(Atom),
322    UnexpectedImageStartTag,
323    SomethingSeenWhenSomethingOpen(Atom),
324    HeadingWhenHeadingOpen,
325    NoCellToClose,
326    StartTagInTable(Atom),
327    FormWhenFormOpen,
328    TableSeenWhileTableOpen,
329    StartTagInTableBody(Atom),
330    EndTagSeenWithoutDoctype,
331    EndTagAfterBody,
332    EndTagSeenWithSelectOpen(Atom),
333    GarbageInColumnGroup,
334    EndTagBr,
335    NoElementToCloseButEndTagSeen(Atom),
336    HtmlStartTagInForeignContext(Atom),
337    NoTableRowToClose,
338    NonSpaceCharacterInTable,
339    UnclosedChildrenInRuby,
340    StartTagSeenWithoutRuby(Atom),
341    UnclosedElementsOnStack,
342    EndTagDidNotMatchCurrentOpenElement(Atom, Atom),
343    EndTagViolatesNestingRules(Atom),
344    EofWithUnclosedElements,
345    EndTagWithUnclosedElements(Atom),
346    NonSpaceCharacterWithoutDoctype,
347    EofWithoutDoctype,
348    EofInText,
349}