1use std::sync::Arc;
2
3use camino::{Utf8Path, Utf8PathBuf};
4use miette::Diagnostic;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use crate::primitives::{SourceId, Span};
9use crate::source::SourceText;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct SourcePosition {
15 pub start: usize,
17 pub end: usize,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct LineColumn {
24 pub line: usize,
26 pub column: usize,
28 pub character: usize,
30}
31
32#[derive(Debug, Clone, Error, Serialize, Deserialize)]
37#[error("{message}")]
38pub struct CompileError {
39 pub code: Arc<str>,
41 pub message: Arc<str>,
43 pub position: Option<Box<SourcePosition>>,
45 pub start: Option<Box<LineColumn>>,
47 pub end: Option<Box<LineColumn>>,
49 pub filename: Option<Arc<Utf8PathBuf>>,
51}
52
53impl CompileError {
54 pub fn unimplemented(feature: &'static str) -> Self {
56 Self {
57 code: Arc::from("unimplemented"),
58 message: Arc::from(format!("{feature} is not implemented yet in rust-port")),
59 position: None,
60 start: None,
61 end: None,
62 filename: None,
63 }
64 }
65
66 pub fn internal(message: impl Into<Arc<str>>) -> Self {
68 Self {
69 code: Arc::from("internal"),
70 message: message.into(),
71 position: None,
72 start: None,
73 end: None,
74 filename: None,
75 }
76 }
77
78 pub fn with_span(mut self, span: Span) -> Self {
80 self.position = Some(Box::new(SourcePosition {
81 start: span.start.as_usize(),
82 end: span.end.as_usize(),
83 }));
84 self
85 }
86
87 pub fn with_filename(mut self, filename: Option<&Utf8Path>) -> Self {
89 if self.filename.is_none() {
90 self.filename = filename.map(|path| Arc::new(path.to_path_buf()));
91 }
92 self
93 }
94
95 pub fn with_source_text(self, source: SourceText<'_>) -> Self {
97 self.with_filename(source.filename)
98 }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Error, Diagnostic)]
107pub enum DiagnosticKind {
108 #[error("Expected attribute value")]
109 #[diagnostic(code(svelte::expected_attribute_value))]
110 ExpectedAttributeValue,
111
112 #[error("Attributes need to be unique")]
113 #[diagnostic(code(svelte::attribute_duplicate))]
114 AttributeDuplicate,
115
116 #[error("Duplicate slot name '{slot}' in <{component}>")]
117 #[diagnostic(code(svelte::slot_attribute_duplicate))]
118 SlotAttributeDuplicate { slot: Arc<str>, component: Arc<str> },
119
120 #[error(
121 "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element"
122 )]
123 #[diagnostic(code(svelte::slot_attribute_invalid_placement))]
124 SlotAttributeInvalidPlacement,
125
126 #[error("Found default slot content alongside an explicit slot=\"default\"")]
127 #[diagnostic(code(svelte::slot_default_duplicate))]
128 SlotDefaultDuplicate,
129
130 #[error("The $ name is reserved, and cannot be used for variables and imports")]
131 #[diagnostic(code(svelte::dollar_binding_invalid))]
132 DollarBindingInvalid,
133
134 #[error(
135 "`{ident}` is an illegal variable name. To reference a global variable called `{ident}`, use `globalThis.{ident}`"
136 )]
137 #[diagnostic(code(svelte::global_reference_invalid))]
138 GlobalReferenceInvalid { ident: Arc<str> },
139
140 #[error(
141 "`$state(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor."
142 )]
143 #[diagnostic(code(svelte::state_invalid_placement))]
144 StateInvalidPlacement,
145
146 #[error("`{directive}:` name cannot be empty")]
147 #[diagnostic(code(svelte::directive_missing_name))]
148 DirectiveMissingName { directive: Arc<str> },
149
150 #[error("Attribute shorthand cannot be empty")]
151 #[diagnostic(code(svelte::attribute_empty_shorthand))]
152 AttributeEmptyShorthand,
153
154 #[error("`bind:value` can only be used with `<input>`, `<textarea>`, `<select>`")]
155 #[diagnostic(code(svelte::bind_invalid_target))]
156 BindInvalidTarget,
157
158 #[error("An `{{#each ...}}` block without an `as` clause cannot have a key")]
159 #[diagnostic(code(svelte::each_key_without_as))]
160 EachKeyWithoutAs,
161
162 #[error("`$effect.active` is now `$effect.tracking`")]
163 #[diagnostic(code(svelte::rune_renamed))]
164 RuneRenamedEffectActive,
165
166 #[error(
167 "Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties"
168 )]
169 #[diagnostic(code(svelte::state_invalid_export))]
170 StateInvalidExport,
171
172 #[error(
173 "Cannot export derived state from a module. To expose the current derived value, export a function returning its value"
174 )]
175 #[diagnostic(code(svelte::derived_invalid_export))]
176 DerivedInvalidExport,
177
178 #[error("`{name}` is not defined")]
179 #[diagnostic(code(svelte::export_undefined))]
180 ExportUndefined { name: Arc<str> },
181
182 #[error("`{name}` is not a valid rune")]
183 #[diagnostic(code(svelte::rune_invalid_name))]
184 RuneInvalidName { name: Arc<str> },
185
186 #[error(
187 "The arguments keyword cannot be used within the template or at the top level of a component"
188 )]
189 #[diagnostic(code(svelte::invalid_arguments_usage))]
190 InvalidArgumentsUsage,
191
192 #[error("Assigning to rvalue")]
193 #[diagnostic(code(svelte::js_parse_error))]
194 JsParseErrorAssigningToRvalue,
195
196 #[error("Cannot assign to constant")]
197 #[diagnostic(code(svelte::constant_assignment))]
198 ConstantAssignment,
199
200 #[error("A component can have a single top-level `<style>` element")]
201 #[diagnostic(code(svelte::style_duplicate))]
202 StyleDuplicate,
203
204 #[error("A component cannot have a default export")]
205 #[diagnostic(code(svelte::module_illegal_default_export))]
206 ModuleIllegalDefaultExport,
207
208 #[error("<svelte:options> cannot have children")]
209 #[diagnostic(code(svelte::svelte_meta_invalid_content))]
210 SvelteMetaInvalidContent,
211
212 #[error("<svelte:window> cannot have children")]
213 #[diagnostic(code(svelte::svelte_meta_invalid_content))]
214 SvelteWindowInvalidContent,
215
216 #[error("Cannot reassign or bind to snippet parameter")]
217 #[diagnostic(code(svelte::snippet_parameter_assignment))]
218 SnippetParameterAssignment,
219
220 #[error("`{name}` has already been declared on this class")]
221 #[diagnostic(code(svelte::state_field_duplicate))]
222 StateFieldDuplicate { name: Arc<str> },
223
224 #[error("Cannot assign to a state field before its declaration")]
225 #[diagnostic(code(svelte::state_field_invalid_assignment))]
226 StateFieldInvalidAssignment,
227
228 #[error("`{name}` has already been declared")]
229 #[diagnostic(code(svelte::duplicate_class_field))]
230 DuplicateClassField { name: Arc<str> },
231
232 #[error("Expected token }}")]
233 #[diagnostic(code(svelte::expected_token))]
234 ExpectedTokenRightBrace,
235
236 #[error("Expected token )")]
237 #[diagnostic(code(svelte::expected_token))]
238 ExpectedTokenRightParen,
239
240 #[error("Calling a snippet function using apply, bind or call is not allowed")]
241 #[diagnostic(code(svelte::render_tag_invalid_call_expression))]
242 RenderTagInvalidCallExpression,
243
244 #[error("`{{@render ...}}` tags can only contain call expressions")]
245 #[diagnostic(code(svelte::render_tag_invalid_expression))]
246 RenderTagInvalidExpression,
247
248 #[error("cannot use spread arguments in `{{@render ...}}` tags")]
249 #[diagnostic(code(svelte::render_tag_invalid_spread_argument))]
250 RenderTagInvalidSpreadArgument,
251
252 #[error("{{@debug ...}} arguments must be identifiers, not arbitrary expressions")]
253 #[diagnostic(code(svelte::debug_tag_invalid_arguments))]
254 DebugTagInvalidArguments,
255
256 #[error("beforeUpdate cannot be used in runes mode")]
257 #[diagnostic(code(svelte::runes_mode_invalid_import))]
258 RunesModeInvalidImportBeforeUpdate,
259
260 #[error("Cannot use rune without parentheses")]
261 #[diagnostic(code(svelte::rune_missing_parentheses))]
262 RuneMissingParentheses,
263
264 #[error("Cannot use `$props()` more than once")]
265 #[diagnostic(code(svelte::props_duplicate))]
266 PropsDuplicate,
267
268 #[error("Cannot use `export let` in runes mode ā use `$props()` instead")]
269 #[diagnostic(code(svelte::legacy_export_invalid))]
270 LegacyExportInvalid,
271
272 #[error(
273 "Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={{array[i]}}` instead of `bind:value={{entry}}`)"
274 )]
275 #[diagnostic(code(svelte::each_item_invalid_assignment))]
276 EachItemInvalidAssignment,
277
278 #[error("The `{{@const foo = ...}}` declaration is not available in this snippet")]
279 #[diagnostic(code(svelte::const_tag_invalid_reference))]
280 ConstTagInvalidReference,
281
282 #[error("Cannot reference store value outside a `.svelte` file")]
283 #[diagnostic(code(svelte::store_invalid_subscription_module))]
284 StoreInvalidSubscriptionModule,
285
286 #[error(
287 "Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals)"
288 )]
289 #[diagnostic(code(svelte::props_illegal_name))]
290 PropsIllegalName,
291
292 #[error("`$bindable` must be called with zero or one arguments")]
293 #[diagnostic(code(svelte::rune_invalid_arguments_length))]
294 RuneInvalidArgumentsLengthBindable,
295
296 #[error("Expected a valid CSS identifier")]
297 #[diagnostic(code(svelte::css_expected_identifier))]
298 CssExpectedIdentifier,
299
300 #[error("Invalid selector")]
301 #[diagnostic(code(svelte::css_selector_invalid))]
302 CssSelectorInvalid,
303
304 #[error("A `:global` selector cannot follow a `>` combinator")]
305 #[diagnostic(code(svelte::css_global_block_invalid_combinator))]
306 CssGlobalBlockInvalidCombinator,
307
308 #[error("A top-level `:global {{...}}` block can only contain rules, not declarations")]
309 #[diagnostic(code(svelte::css_global_block_invalid_declaration))]
310 CssGlobalBlockInvalidDeclaration,
311
312 #[error("A `:global` selector cannot be inside a pseudoclass")]
313 #[diagnostic(code(svelte::css_global_block_invalid_placement))]
314 CssGlobalBlockInvalidPlacement,
315
316 #[error(
317 "A `:global` selector cannot be part of a selector list with entries that don't contain `:global`"
318 )]
319 #[diagnostic(code(svelte::css_global_block_invalid_list))]
320 CssGlobalBlockInvalidList,
321
322 #[error("A `:global` selector cannot modify an existing selector")]
323 #[diagnostic(code(svelte::css_global_block_invalid_modifier))]
324 CssGlobalBlockInvalidModifier,
325
326 #[error("A `:global` selector can only be modified if it is a descendant of other selectors")]
327 #[diagnostic(code(svelte::css_global_block_invalid_modifier_start))]
328 CssGlobalBlockInvalidModifierStart,
329
330 #[error(
331 "`:global(...)` can be at the start or end of a selector sequence, but not in the middle"
332 )]
333 #[diagnostic(code(svelte::css_global_invalid_placement))]
334 CssGlobalInvalidPlacement,
335
336 #[error("`:global(...)` must contain exactly one selector")]
337 #[diagnostic(code(svelte::css_global_invalid_selector))]
338 CssGlobalInvalidSelector,
339
340 #[error(
341 "`:global(...)` must not contain type or universal selectors when used in a compound selector"
342 )]
343 #[diagnostic(code(svelte::css_global_invalid_selector_list))]
344 CssGlobalInvalidSelectorList,
345
346 #[error(
347 "Nesting selectors can only be used inside a rule or as the first selector inside a lone `:global(...)`"
348 )]
349 #[diagnostic(code(svelte::css_nesting_selector_invalid_placement))]
350 CssNestingSelectorInvalidPlacement,
351
352 #[error("`:global(...)` must not be followed by a type selector")]
353 #[diagnostic(code(svelte::css_type_selector_invalid_placement))]
354 CssTypeSelectorInvalidPlacement,
355
356 #[error("`$bindable()` can only be used inside a `$props()` declaration")]
357 #[diagnostic(code(svelte::bindable_invalid_location))]
358 BindableInvalidLocation,
359
360 #[error("`$derived` must be called with exactly one argument")]
361 #[diagnostic(code(svelte::rune_invalid_arguments_length))]
362 RuneInvalidArgumentsLengthDerived,
363
364 #[error("`$effect` must be called with exactly one argument")]
365 #[diagnostic(code(svelte::rune_invalid_arguments_length))]
366 RuneInvalidArgumentsLengthEffect,
367
368 #[error("`$state` must be called with zero or one arguments")]
369 #[diagnostic(code(svelte::rune_invalid_arguments_length))]
370 RuneInvalidArgumentsLengthState,
371
372 #[error("`$state.raw` must be called with zero or one arguments")]
373 #[diagnostic(code(svelte::rune_invalid_arguments_length))]
374 RuneInvalidArgumentsLengthStateRaw,
375
376 #[error("`$state.snapshot` must be called with exactly one argument")]
377 #[diagnostic(code(svelte::rune_invalid_arguments_length))]
378 RuneInvalidArgumentsLengthStateSnapshot,
379
380 #[error("`$props` cannot be called with arguments")]
381 #[diagnostic(code(svelte::rune_invalid_arguments))]
382 RuneInvalidArgumentsProps,
383
384 #[error(
385 "`$props()` can only be used at the top level of components as a variable declaration initializer"
386 )]
387 #[diagnostic(code(svelte::props_invalid_placement))]
388 PropsInvalidPlacement,
389
390 #[error(
391 "`$derived(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor."
392 )]
393 #[diagnostic(code(svelte::state_invalid_placement))]
394 StateInvalidPlacementDerived,
395
396 #[error("`$effect()` can only be used as an expression statement")]
397 #[diagnostic(code(svelte::effect_invalid_placement))]
398 EffectInvalidPlacement,
399
400 #[error("`$host()` can only be used inside custom element component instances")]
401 #[diagnostic(code(svelte::host_invalid_placement))]
402 HostInvalidPlacement,
403
404 #[error("`<script>` was left open")]
405 #[diagnostic(code(svelte::element_unclosed))]
406 ElementUnclosedScript,
407
408 #[error("`<div>` was left open")]
409 #[diagnostic(code(svelte::element_unclosed))]
410 ElementUnclosedDiv,
411
412 #[error(
413 "`<svelte:self>` components can only exist inside `{{#if}}` blocks, `{{#each}}` blocks, `{{#snippet}}` blocks or slots passed to components"
414 )]
415 #[diagnostic(code(svelte::svelte_self_invalid_placement))]
416 SvelteSelfInvalidPlacement,
417
418 #[error(
419 "Cannot use `<slot>` syntax and `{{@render ...}}` tags in the same component. Migrate towards `{{@render ...}}` tags completely"
420 )]
421 #[diagnostic(code(svelte::slot_snippet_conflict))]
422 SlotSnippetConflict,
423
424 #[error(
425 "Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block"
426 )]
427 #[diagnostic(code(svelte::snippet_conflict))]
428 SnippetConflict,
429
430 #[error(
431 "An exported snippet can only reference things declared in a `<script module>`, or other exportable snippets"
432 )]
433 #[diagnostic(code(svelte::snippet_invalid_export))]
434 SnippetInvalidExport,
435
436 #[error("Snippets do not support rest parameters; use an array instead")]
437 #[diagnostic(code(svelte::snippet_invalid_rest_parameter))]
438 SnippetInvalidRestParameter,
439
440 #[error("Cannot reference store value inside `<script module>`")]
441 #[diagnostic(code(svelte::store_invalid_subscription))]
442 StoreInvalidSubscription,
443
444 #[error("Cannot subscribe to stores that are not declared at the top level of the component")]
445 #[diagnostic(code(svelte::store_invalid_scoped_subscription))]
446 StoreInvalidScopedSubscription,
447
448 #[error("The $ prefix is reserved, and cannot be used for variables and imports")]
449 #[diagnostic(code(svelte::dollar_prefix_invalid))]
450 DollarPrefixInvalid,
451
452 #[error(
453 "Expected a valid element or component name. Components must have a valid variable name or dot notation expression"
454 )]
455 #[diagnostic(code(svelte::tag_invalid_name))]
456 TagInvalidName,
457
458 #[error("Expected whitespace")]
459 #[diagnostic(code(svelte::expected_whitespace))]
460 ExpectedWhitespace,
461
462 #[error("{{@const ...}} must consist of a single variable declaration")]
463 #[diagnostic(code(svelte::const_tag_invalid_expression))]
464 ConstTagInvalidExpression,
465
466 #[error("Cyclical dependency detected: a ā b ā a")]
467 #[diagnostic(code(svelte::const_tag_cycle))]
468 ConstTagCycle,
469
470 #[error(
471 "Comma-separated expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses"
472 )]
473 #[diagnostic(code(svelte::attribute_invalid_sequence_expression))]
474 AttributeInvalidSequenceExpression,
475
476 #[error(
477 "{{:...}} block is invalid at this position (did you forget to close the preceding element or block?)"
478 )]
479 #[diagnostic(code(svelte::block_invalid_continuation_placement))]
480 BlockInvalidContinuationPlacement,
481
482 #[error("Expected token -->")]
483 #[diagnostic(code(svelte::expected_token))]
484 ExpectedTokenCommentClose,
485
486 #[error("Expected token </style")]
487 #[diagnostic(code(svelte::expected_token))]
488 ExpectedTokenStyleClose,
489
490 #[error("Expected token {{:else}}")]
491 #[diagnostic(code(svelte::expected_token))]
492 ExpectedTokenElse,
493
494 #[error("Expected token {{:then ...}} or {{:catch ...}}")]
495 #[diagnostic(code(svelte::expected_token))]
496 ExpectedTokenAwaitBranch,
497
498 #[error(
499 "Valid `<svelte:...>` tag names are svelte:head, svelte:options, svelte:window, svelte:document, svelte:body, svelte:element, svelte:component, svelte:self, svelte:fragment or svelte:boundary"
500 )]
501 #[diagnostic(code(svelte::svelte_meta_invalid_tag))]
502 SvelteMetaInvalidTag,
503
504 #[error(
505 "Attribute values containing `{{...}}` must be enclosed in quote marks, unless the value only contains the expression"
506 )]
507 #[diagnostic(code(svelte::attribute_unquoted_sequence))]
508 AttributeUnquotedSequence,
509
510 #[error("Block was left open")]
511 #[diagnostic(code(svelte::block_unclosed))]
512 BlockUnclosed,
513
514 #[error("`</div>` attempted to close an element that was not open")]
515 #[diagnostic(code(svelte::element_invalid_closing_tag))]
516 ElementInvalidClosingTag,
517
518 #[error("`</p>` attempted to close an element that was not open")]
519 #[diagnostic(code(svelte::element_invalid_closing_tag))]
520 ElementInvalidClosingTagP,
521
522 #[error(
523 "`</p>` attempted to close element that was already automatically closed by `<pre>` (cannot nest `<pre>` inside `<p>`)"
524 )]
525 #[diagnostic(code(svelte::element_invalid_closing_tag_autoclosed))]
526 ElementInvalidClosingTagAutoclosed,
527
528 #[error("Void elements cannot have children or closing tags")]
529 #[diagnostic(code(svelte::void_element_invalid_content))]
530 VoidElementInvalidContent,
531
532 #[error("A component can only have one `<svelte:window>` element")]
533 #[diagnostic(code(svelte::svelte_meta_duplicate))]
534 SvelteMetaDuplicate,
535
536 #[error("`<svelte:window>` tags cannot be inside elements or blocks")]
537 #[diagnostic(code(svelte::svelte_meta_invalid_placement))]
538 SvelteMetaInvalidPlacement,
539
540 #[error("Unexpected end of input")]
541 #[diagnostic(code(svelte::unexpected_eof))]
542 UnexpectedEof,
543
544 #[error(
545 "Imports of `svelte/internal/*` are forbidden. It contains private runtime code which is subject to change without notice. If you're importing from `svelte/internal/*` to work around a limitation of Svelte, please open an issue at https://github.com/sveltejs/svelte and explain your use case"
546 )]
547 #[diagnostic(code(svelte::import_svelte_internal_forbidden))]
548 ImportSvelteInternalForbidden,
549}
550
551impl DiagnosticKind {
552 pub fn code(&self) -> &'static str {
554 match self {
555 Self::ExpectedAttributeValue => "expected_attribute_value",
556 Self::AttributeDuplicate => "attribute_duplicate",
557 Self::SlotAttributeDuplicate { .. } => "slot_attribute_duplicate",
558 Self::SlotAttributeInvalidPlacement => "slot_attribute_invalid_placement",
559 Self::SlotDefaultDuplicate => "slot_default_duplicate",
560 Self::DollarBindingInvalid => "dollar_binding_invalid",
561 Self::GlobalReferenceInvalid { .. } => "global_reference_invalid",
562 Self::StateInvalidPlacement => "state_invalid_placement",
563 Self::DirectiveMissingName { .. } => "directive_missing_name",
564 Self::AttributeEmptyShorthand => "attribute_empty_shorthand",
565 Self::BindInvalidTarget => "bind_invalid_target",
566 Self::EachKeyWithoutAs => "each_key_without_as",
567 Self::RuneRenamedEffectActive => "rune_renamed",
568 Self::StateInvalidExport => "state_invalid_export",
569 Self::DerivedInvalidExport => "derived_invalid_export",
570 Self::ExportUndefined { .. } => "export_undefined",
571 Self::RuneInvalidName { .. } => "rune_invalid_name",
572 Self::InvalidArgumentsUsage => "invalid_arguments_usage",
573 Self::JsParseErrorAssigningToRvalue => "js_parse_error",
574 Self::ConstantAssignment => "constant_assignment",
575 Self::StyleDuplicate => "style_duplicate",
576 Self::ModuleIllegalDefaultExport => "module_illegal_default_export",
577 Self::SvelteMetaInvalidContent | Self::SvelteWindowInvalidContent => {
578 "svelte_meta_invalid_content"
579 }
580 Self::SnippetParameterAssignment => "snippet_parameter_assignment",
581 Self::StateFieldDuplicate { .. } => "state_field_duplicate",
582 Self::StateFieldInvalidAssignment => "state_field_invalid_assignment",
583 Self::DuplicateClassField { .. } => "duplicate_class_field",
584 Self::ExpectedTokenRightBrace | Self::ExpectedTokenRightParen => "expected_token",
585 Self::RenderTagInvalidCallExpression => "render_tag_invalid_call_expression",
586 Self::RenderTagInvalidExpression => "render_tag_invalid_expression",
587 Self::RenderTagInvalidSpreadArgument => "render_tag_invalid_spread_argument",
588 Self::DebugTagInvalidArguments => "debug_tag_invalid_arguments",
589 Self::RunesModeInvalidImportBeforeUpdate => "runes_mode_invalid_import",
590 Self::RuneMissingParentheses => "rune_missing_parentheses",
591 Self::PropsDuplicate => "props_duplicate",
592 Self::LegacyExportInvalid => "legacy_export_invalid",
593 Self::EachItemInvalidAssignment => "each_item_invalid_assignment",
594 Self::ConstTagInvalidReference => "const_tag_invalid_reference",
595 Self::StoreInvalidSubscriptionModule => "store_invalid_subscription_module",
596 Self::PropsIllegalName => "props_illegal_name",
597 Self::RuneInvalidArgumentsLengthBindable => "rune_invalid_arguments_length",
598 Self::CssExpectedIdentifier => "css_expected_identifier",
599 Self::CssSelectorInvalid => "css_selector_invalid",
600 Self::CssGlobalBlockInvalidCombinator => "css_global_block_invalid_combinator",
601 Self::CssGlobalBlockInvalidDeclaration => "css_global_block_invalid_declaration",
602 Self::CssGlobalBlockInvalidPlacement => "css_global_block_invalid_placement",
603 Self::CssGlobalBlockInvalidList => "css_global_block_invalid_list",
604 Self::CssGlobalBlockInvalidModifier => "css_global_block_invalid_modifier",
605 Self::CssGlobalBlockInvalidModifierStart => "css_global_block_invalid_modifier_start",
606 Self::CssGlobalInvalidPlacement => "css_global_invalid_placement",
607 Self::CssGlobalInvalidSelector => "css_global_invalid_selector",
608 Self::CssGlobalInvalidSelectorList => "css_global_invalid_selector_list",
609 Self::CssNestingSelectorInvalidPlacement => "css_nesting_selector_invalid_placement",
610 Self::CssTypeSelectorInvalidPlacement => "css_type_selector_invalid_placement",
611 Self::BindableInvalidLocation => "bindable_invalid_location",
612 Self::RuneInvalidArgumentsLengthDerived
613 | Self::RuneInvalidArgumentsLengthEffect
614 | Self::RuneInvalidArgumentsLengthState
615 | Self::RuneInvalidArgumentsLengthStateRaw
616 | Self::RuneInvalidArgumentsLengthStateSnapshot => "rune_invalid_arguments_length",
617 Self::RuneInvalidArgumentsProps => "rune_invalid_arguments",
618 Self::PropsInvalidPlacement => "props_invalid_placement",
619 Self::StateInvalidPlacementDerived => "state_invalid_placement",
620 Self::EffectInvalidPlacement => "effect_invalid_placement",
621 Self::HostInvalidPlacement => "host_invalid_placement",
622 Self::ElementUnclosedScript | Self::ElementUnclosedDiv => "element_unclosed",
623 Self::SvelteSelfInvalidPlacement => "svelte_self_invalid_placement",
624 Self::SlotSnippetConflict => "slot_snippet_conflict",
625 Self::SnippetConflict => "snippet_conflict",
626 Self::SnippetInvalidExport => "snippet_invalid_export",
627 Self::SnippetInvalidRestParameter => "snippet_invalid_rest_parameter",
628 Self::StoreInvalidSubscription => "store_invalid_subscription",
629 Self::StoreInvalidScopedSubscription => "store_invalid_scoped_subscription",
630 Self::DollarPrefixInvalid => "dollar_prefix_invalid",
631 Self::TagInvalidName => "tag_invalid_name",
632 Self::ExpectedWhitespace => "expected_whitespace",
633 Self::ConstTagInvalidExpression => "const_tag_invalid_expression",
634 Self::ConstTagCycle => "const_tag_cycle",
635 Self::AttributeInvalidSequenceExpression => "attribute_invalid_sequence_expression",
636 Self::BlockInvalidContinuationPlacement => "block_invalid_continuation_placement",
637 Self::ExpectedTokenCommentClose
638 | Self::ExpectedTokenStyleClose
639 | Self::ExpectedTokenElse
640 | Self::ExpectedTokenAwaitBranch => "expected_token",
641 Self::SvelteMetaInvalidTag => "svelte_meta_invalid_tag",
642 Self::AttributeUnquotedSequence => "attribute_unquoted_sequence",
643 Self::BlockUnclosed => "block_unclosed",
644 Self::ElementInvalidClosingTag | Self::ElementInvalidClosingTagP => {
645 "element_invalid_closing_tag"
646 }
647 Self::ElementInvalidClosingTagAutoclosed => "element_invalid_closing_tag_autoclosed",
648 Self::VoidElementInvalidContent => "void_element_invalid_content",
649 Self::SvelteMetaDuplicate => "svelte_meta_duplicate",
650 Self::SvelteMetaInvalidPlacement => "svelte_meta_invalid_placement",
651 Self::UnexpectedEof => "unexpected_eof",
652 Self::ImportSvelteInternalForbidden => "import_svelte_internal_forbidden",
653 }
654 }
655
656 pub fn to_compile_error(self, source: &str, start: usize, end: usize) -> CompileError {
659 self.to_compile_error_in(SourceText::new(SourceId::new(0), source, None), start, end)
660 }
661
662 pub fn to_compile_error_in(
665 self,
666 source: SourceText<'_>,
667 start: usize,
668 end: usize,
669 ) -> CompileError {
670 let start_location = source.location_at_offset(start);
671 let end_location = source.location_at_offset(end);
672
673 CompileError {
674 code: Arc::from(self.code()),
675 message: Arc::from(self.to_string()),
676 position: Some(Box::new(SourcePosition {
677 start: start_location.character,
678 end: end_location.character,
679 })),
680 start: Some(Box::new(start_location)),
681 end: Some(Box::new(end_location)),
682 filename: source.filename.map(|path| Arc::new(path.to_path_buf())),
683 }
684 }
685}
686
687#[cfg(test)]
688mod tests {
689 use camino::Utf8Path;
690
691 use super::DiagnosticKind;
692 use crate::{SourceId, SourceText};
693
694 #[test]
695 fn compile_error_uses_source_text_locations_and_filename() {
696 let source = SourceText::new(
697 SourceId::new(7),
698 "a\nšb",
699 Some(Utf8Path::new("input.svelte")),
700 );
701 let error = DiagnosticKind::ExpectedWhitespace.to_compile_error_in(
702 source,
703 "a\nš".len(),
704 "a\nšb".len(),
705 );
706
707 assert_eq!(error.start.as_ref().expect("start").line, 2);
708 assert_eq!(error.start.as_ref().expect("start").column, 2);
709 assert_eq!(error.position.as_ref().expect("position").start, 4);
710 assert_eq!(
711 error.filename.as_deref().map(|path| path.as_str()),
712 Some("input.svelte"),
713 );
714 }
715}