vue_compiler_core/
error.rs

1use super::SourceLocation;
2use std::cell::RefCell;
3use std::fmt;
4use std::rc::Rc;
5
6#[derive(PartialEq, Eq, Debug)]
7pub enum CompilationErrorKind {
8    AbruptClosingOfEmptyComment,
9    CDataInHtmlContent,
10    DuplicateAttribute,
11    EndTagWithAttributes,
12    EndTagWithTrailingSolidus,
13    EofBeforeTagName,
14    EofInCdata,
15    EofInComment,
16    EofInScriptHtmlCommentLikeText,
17    EofInTag,
18    IncorrectlyClosedComment,
19    IncorrectlyOpenedComment,
20    InvalidFirstCharacterOfTagName,
21    MissingAttributeValue,
22    MissingEndTagName,
23    MissingWhitespaceBetweenAttributes,
24    NestedComment,
25    UnexpectedEqualsSignBeforeAttributeName,
26    UnexpectedCharacterInAttributeName,
27    UnexpectedCharacterInUnquotedAttributeValue,
28    UnexpectedNullCharacter, // TODO
29    UnexpectedQuestionMarkInsteadOfTagName,
30    UnexpectedSolidusInTag,
31
32    // Vue-specific parse errors
33    InvalidEndTag,
34    MissingEndTag,
35    MissingInterpolationEnd,
36    MissingDynamicDirectiveArgumentEnd,
37    UnexpectedContentAfterDynamicDirective,
38    MissingDirectiveName,
39    MissingDirectiveArg,
40    MissingDirectiveMod,
41    InvalidVSlotModifier,
42
43    // transform errors
44    VIfNoExpression,
45    VIfSameKey,
46    VIfDuplicateDir,
47    VElseNoAdjacentIf,
48    VForNoExpression,
49    VForMalformedExpression,
50    VForTemplateKeyPlacement, // TODO
51    VBindNoExpression,
52    VOnNoExpression,
53    VSlotUnexpectedDirectiveOnSlotOutlet,
54    VSlotMixedSlotUsage,
55    VSlotTemplateMisplaced,
56    VSlotDuplicateSlotNames,
57    VSlotExtraneousDefaultSlotChildren,
58    VSlotMisplaced,
59    // TODO
60    VModelNoExpression,
61    VModelMalformedExpression,
62    VModelOnScopeVariable,
63    InvalidExpression,
64
65    UnexpectedDirExpression,
66    KeepAliveInvalidChildren,
67
68    // generic errors
69    PrefixIdNotSupported,
70    ModuleModeNotSupported,
71    CacheHandlerNotSupported,
72    ScopeIdNotSupported,
73
74    // Special value for higher-order compilers to pick up the last code
75    // to avoid collision of error codes. This should always be kept as the last item.
76    ExtendPoint,
77}
78
79pub struct CompilationError {
80    pub kind: CompilationErrorKind,
81    pub additional_message: Option<String>,
82    pub location: SourceLocation,
83}
84
85impl CompilationError {
86    pub fn new(kind: CompilationErrorKind) -> Self {
87        Self {
88            kind,
89            additional_message: None,
90            location: Default::default(),
91        }
92    }
93    pub fn with_location(mut self, loc: SourceLocation) -> Self {
94        self.location = loc;
95        self
96    }
97    pub fn with_additional_message(mut self, msg: String) -> Self {
98        self.additional_message = Some(msg);
99        self
100    }
101
102    fn msg(&self) -> &'static str {
103        msg(&self.kind)
104    }
105}
106
107#[cold]
108#[inline(never)]
109fn msg(kind: &CompilationErrorKind) -> &'static str {
110    use CompilationErrorKind::*;
111    match *kind {
112        AbruptClosingOfEmptyComment => "Illegal comment.",
113        CDataInHtmlContent => "CDATA section is allowed only in XML context.",
114        DuplicateAttribute => "Duplicate attribute.",
115        EndTagWithAttributes => "End tag cannot have attributes.",
116        EndTagWithTrailingSolidus => r#"Illegal "/" in tags."#,
117        EofBeforeTagName => "Unexpected EOF in tag.",
118        EofInCdata => "Unexpected EOF in CDATA section.",
119        EofInComment => "Unexpected EOF in comment.",
120        EofInScriptHtmlCommentLikeText => "Unexpected EOF in script.",
121        EofInTag => "Unexpected EOF in tag.",
122        IncorrectlyClosedComment => "Incorrectly closed comment.",
123        IncorrectlyOpenedComment => "Incorrectly opened comment.",
124        InvalidFirstCharacterOfTagName => "Illegal tag name. Use '&lt;' to print '<'.",
125        UnexpectedEqualsSignBeforeAttributeName => "Attribute name was expected before '='.",
126        MissingAttributeValue => "Attribute value was expected.",
127        MissingEndTagName => "End tag name was expected.",
128        MissingWhitespaceBetweenAttributes => "Whitespace was expected.",
129        NestedComment => "Unexpected '<!--' in comment.",
130        UnexpectedCharacterInAttributeName =>
131         "Attribute name cannot contain U+0022 (\"), U+0027 ('), and U+003C (<).",
132        UnexpectedCharacterInUnquotedAttributeValue =>
133            "Unquoted attribute value cannot contain U+0022 (\"), U+0027 (\'), U+003C (<), U+003D (=), and U+0060 (`).",
134        UnexpectedQuestionMarkInsteadOfTagName => "'<?' is allowed only in XML context.",
135        UnexpectedNullCharacter => "Unexpected null character.",
136        UnexpectedSolidusInTag => "Illegal '/' in tags.",
137
138        // Vue-specific parse errors
139        InvalidEndTag => "Invalid end tag.",
140        MissingEndTag => "Element is missing end tag.",
141        MissingInterpolationEnd => "Interpolation end sign was not found.",
142        MissingDynamicDirectiveArgumentEnd =>
143            "End bracket for dynamic directive argument was not found. Note that dynamic directive argument cannot contain spaces.",
144        UnexpectedContentAfterDynamicDirective =>
145            "Unexpected content was found after a closed dynamic argument. Add a dot as separator if it is a modifier.",
146        MissingDirectiveName => "Legal directive name was expected.",
147        MissingDirectiveArg => "Directive argument was expected.",
148        MissingDirectiveMod => "Directive modifier was expected.",
149        InvalidVSlotModifier => "v-slot does not take modifier.",
150
151        // transform errors
152        VIfNoExpression => "v-if/v-else-if is missing expression.",
153        VIfSameKey => "v-if/else branches must use unique keys.",
154        VIfDuplicateDir => "Duplicate v-if/else-if/else. Use v-else-if instead.",
155        VElseNoAdjacentIf => "v-else/v-else-if has no adjacent v-if.",
156        VForNoExpression => "v-for is missing expression.",
157        VForMalformedExpression => "v-for has invalid expression.",
158        VForTemplateKeyPlacement => "<template v-for> key should be placed on the <template> tag.",
159        VBindNoExpression => "v-bind is missing expression.",
160        VOnNoExpression => "v-on is missing expression.",
161        VSlotUnexpectedDirectiveOnSlotOutlet => "Unexpected custom directive on <slot> outlet.",
162        VSlotMixedSlotUsage =>
163            "Mixed v-slot usage on both the component and nested <template>. When there are multiple named slots, all slots should use <template> syntax to avoid scope ambiguity.",
164        VSlotDuplicateSlotNames => "Duplicate slot names found. ",
165        VSlotExtraneousDefaultSlotChildren =>
166            r#"Extraneous children found when component already has explicitly named "default slot. These children will be ignored."#,
167        VSlotMisplaced => "v-slot can only be used on components or <template> tags.",
168        VSlotTemplateMisplaced => "<template v-slot> can only be used as a component's direct child.",
169        VModelNoExpression => "v-model is missing expression.",
170        VModelMalformedExpression => "v-model value must be a valid JavaScript member expression.",
171        VModelOnScopeVariable =>
172            "v-model cannot be used on v-for or v-slot scope variables because they are not writable.",
173        InvalidExpression => "Error parsing JavaScript expression: ",
174        UnexpectedDirExpression => "This directive does not accept any epxression.",
175        KeepAliveInvalidChildren => "<KeepAlive> expects exactly one child component.",
176
177        // generic errors
178        PrefixIdNotSupported =>
179            r#""prefixIdentifiers" option is not supported in this build of compiler."#,
180        ModuleModeNotSupported => "ES module mode is not supported in this build of compiler.",
181        CacheHandlerNotSupported =>
182            r#""cacheHandlers" option is only supported when the "prefixIdentifiers" option is enabled."#,
183        ScopeIdNotSupported => r#""scopeId" option is only supported in module mode."#,
184        ExtendPoint => "",
185    }
186}
187
188impl fmt::Display for CompilationError {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        if let Some(additional) = &self.additional_message {
191            write!(f, "{}{}", self.msg(), additional)
192        } else {
193            write!(f, "{}", self.msg())
194        }
195    }
196}
197
198/// This trait handles error occured in the compilation.
199/// NB: clone bound is needed since scan/parse/ir/code gen
200/// all requires ownership of a error report.
201/// Rc/RefCell is a good way to implement ErrorHandler if
202/// collecting errors in compilation pass is desired.
203pub trait ErrorHandler {
204    // cannot use mut ref due to borrow semantics
205    // use RefCell as implementation
206    fn on_error(&self, _: CompilationError) {}
207}
208
209#[derive(Clone)]
210pub struct VecErrorHandler {
211    errors: Rc<RefCell<Vec<CompilationError>>>,
212}
213impl Default for VecErrorHandler {
214    fn default() -> Self {
215        Self {
216            errors: Rc::new(RefCell::new(vec![])),
217        }
218    }
219}
220
221impl ErrorHandler for VecErrorHandler {
222    fn on_error(&self, e: CompilationError) {
223        self.errors.borrow_mut().push(e);
224    }
225}
226
227#[cfg(test)]
228pub mod test {
229    use super::ErrorHandler;
230    #[derive(Clone)]
231    pub struct TestErrorHandler;
232    impl ErrorHandler for TestErrorHandler {}
233}