1use crate::{
3 context::{environment::Label, AssignmentError, InformationChain},
4 diagnostics,
5 features::{modules::CouldNotOpenFile, CannotDeleteFromError},
6 types::{
7 calling::FunctionCallingError,
8 printing::print_type_with_type_arguments,
9 properties::{assignment::SetPropertyError, PropertyKey},
10 GenericChain, GenericChainLink,
11 },
12};
13use source_map::{SourceId, SpanWithSource};
14use std::{
15 fmt::{self, Debug, Display},
16 iter,
17 path::PathBuf,
18};
19
20#[derive(Debug, Clone, Copy)]
21#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize), serde(rename_all = "lowercase"))]
22#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
23pub enum DiagnosticKind {
24 Error,
25 Warning,
26 Info,
27}
28
29#[derive(Debug)]
31#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize), serde(untagged))]
32#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
33pub enum Diagnostic {
34 Global {
36 reason: String,
37 kind: DiagnosticKind,
38 },
39 Position {
40 reason: String,
41 position: SpanWithSource,
42 kind: DiagnosticKind,
43 },
44 PositionWithAdditionalLabels {
45 reason: String,
46 position: SpanWithSource,
47 labels: Vec<(String, SpanWithSource)>,
48 kind: DiagnosticKind,
49 },
50}
51
52#[allow(clippy::upper_case_acronyms)]
54pub struct VariableUsedInTDZ {
55 pub variable_name: String,
56 pub position: SpanWithSource,
57}
58
59pub struct InvalidRegExp {
60 pub error: String,
61 pub position: SpanWithSource,
62}
63
64pub struct NotInLoopOrCouldNotFindLabel {
65 pub label: Label,
66 pub position: SpanWithSource,
67}
68
69impl Diagnostic {
70 pub fn sources(&self) -> impl Iterator<Item = SourceId> + '_ {
71 use either::{Left, Right};
72 match self {
73 Diagnostic::Global { .. } => Left(Left(iter::empty())),
74 Diagnostic::Position { position: span, .. } => Left(Right(iter::once(span.source))),
75 Diagnostic::PositionWithAdditionalLabels { position: pos, labels, .. } => {
76 Right(iter::once(pos.source).chain(labels.iter().map(|(_, span)| span.source)))
77 }
78 }
79 }
80
81 #[must_use]
82 pub fn reason(&self) -> &str {
83 match self {
84 Diagnostic::Global { reason, .. }
85 | Diagnostic::Position { reason, .. }
86 | Diagnostic::PositionWithAdditionalLabels { reason, .. } => reason,
87 }
88 }
89
90 #[must_use]
91 pub fn reason_and_position(self) -> (String, Option<SpanWithSource>) {
92 match self {
93 Diagnostic::Global { reason, .. } => (reason, None),
94 Diagnostic::Position { reason, position, .. }
95 | Diagnostic::PositionWithAdditionalLabels { reason, position, .. } => (reason, Some(position)),
96 }
97 }
98
99 #[must_use]
100 pub fn kind(&self) -> DiagnosticKind {
101 match self {
102 Diagnostic::Global { kind, .. }
103 | Diagnostic::Position { kind, .. }
104 | Diagnostic::PositionWithAdditionalLabels { kind, .. } => *kind,
105 }
106 }
107}
108
109#[derive(Default)]
111#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize), serde(transparent))]
112#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))]
113pub struct DiagnosticsContainer {
114 diagnostics: Vec<Diagnostic>,
115 #[cfg_attr(feature = "serde-serialize", serde(skip_serializing))]
117 contains_error: bool,
118}
119
120impl DiagnosticsContainer {
122 #[must_use]
123 pub fn new() -> Self {
124 Self { diagnostics: Default::default(), contains_error: false }
125 }
126
127 pub fn add_error<T: Into<Diagnostic>>(&mut self, error: T) {
128 self.contains_error = true;
129 self.diagnostics.push(error.into());
130 }
131
132 pub fn add_warning<T: Into<Diagnostic>>(&mut self, warning: T) {
133 self.diagnostics.push(warning.into());
134 }
135
136 pub fn add_info<T: Into<Diagnostic>>(&mut self, info: T) {
137 self.diagnostics.push(info.into());
138 }
139
140 #[must_use]
141 pub fn contains_error(&self) -> bool {
142 self.contains_error
143 }
144
145 pub fn sources(&self) -> impl Iterator<Item = SourceId> + '_ {
146 self.diagnostics.iter().flat_map(diagnostics::Diagnostic::sources)
147 }
148
149 #[doc(hidden)]
150 #[must_use]
151 pub fn get_diagnostics(self) -> Vec<Diagnostic> {
152 self.diagnostics
153 }
154
155 pub fn into_result(self) -> Result<Self, Self> {
156 if self.contains_error {
157 Err(self)
158 } else {
159 Ok(self)
160 }
161 }
162
163 #[must_use]
164 pub fn count(&self) -> usize {
165 self.diagnostics.len()
166 }
167}
168
169impl IntoIterator for DiagnosticsContainer {
170 type Item = Diagnostic;
171
172 type IntoIter = std::vec::IntoIter<Diagnostic>;
173
174 fn into_iter(self) -> Self::IntoIter {
175 self.diagnostics.into_iter()
176 }
177}
178
179use crate::types::{printing::print_type, TypeId, TypeStore};
180
181pub struct TypeStringRepresentation(String);
183
184pub enum PropertyKeyRepresentation {
185 Type(String),
186 StringKey(String),
187}
188
189impl PropertyKeyRepresentation {
190 pub fn new(
191 under: &PropertyKey,
192 environment: &impl InformationChain,
193 types: &TypeStore,
194 ) -> PropertyKeyRepresentation {
195 match under.clone() {
196 PropertyKey::String(s) => PropertyKeyRepresentation::StringKey(s.to_string()),
197 PropertyKey::Type(t) => {
198 PropertyKeyRepresentation::Type(print_type(t, types, environment, false))
199 }
200 }
201 }
202}
203
204impl TypeStringRepresentation {
205 #[must_use]
206 pub fn from_type_id(
207 id: TypeId,
208 ctx: &impl InformationChain,
209 types: &TypeStore,
210 debug_mode: bool,
211 ) -> Self {
212 let value = print_type(id, types, ctx, debug_mode);
213 Self(value)
214 }
215
216 #[must_use]
217 pub fn from_type_id_with_generics(
218 id: TypeId,
219 type_arguments: GenericChain,
220 ctx: &impl InformationChain,
221 types: &TypeStore,
222 debug_mode: bool,
223 ) -> Self {
224 let value = print_type_with_type_arguments(id, type_arguments, types, ctx, debug_mode);
225 Self(value)
226 }
227
228 pub(crate) fn from_property_constraint(
230 property_constraint: crate::types::logical::Logical<crate::PropertyValue>,
231 generics: GenericChain,
232 ctx: &impl InformationChain,
233 types: &TypeStore,
234 debug_mode: bool,
235 ) -> TypeStringRepresentation {
236 match property_constraint {
237 crate::types::logical::Logical::Pure(constraint) => match constraint.inner_simple() {
238 crate::PropertyValue::Value(v) => {
239 let value =
240 print_type_with_type_arguments(*v, generics, types, ctx, debug_mode);
241 Self(value)
242 }
243 crate::PropertyValue::GetterAndSetter { .. }
244 | crate::PropertyValue::Getter(_)
245 | crate::PropertyValue::Setter(_) => Self("getter/setter".to_owned()),
246 crate::PropertyValue::Deleted => Self("never".to_owned()),
247 crate::PropertyValue::ConditionallyExists { .. }
248 | crate::PropertyValue::Configured { .. } => unreachable!(),
249 },
250 crate::types::logical::Logical::Or { condition, left, right } => {
251 let left_right = (*left, *right);
252 crate::utilities::notify!("Printing {:?} base on {:?}", left_right, condition);
263 Self("TODO or".to_owned())
264 }
266 crate::types::logical::Logical::Implies { on, antecedent } => {
267 if generics.is_some() {
268 crate::utilities::notify!("TODO chaining");
269 }
270 let generics = Some(GenericChainLink::PartiallyAppliedGenericArgumentsLink {
271 parent_link: None,
272 value: &antecedent,
273 from: TypeId::UNIMPLEMENTED_ERROR_TYPE,
274 });
275 Self::from_property_constraint(*on, generics, ctx, types, debug_mode)
276 }
277 crate::types::logical::Logical::BasedOnKey { .. } => {
278 Self("TODO based on key?".to_owned())
279 }
280 }
281 }
282}
283
284impl Display for TypeStringRepresentation {
285 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286 f.write_str(&self.0)
287 }
288}
289
290pub(crate) struct NoEnvironmentSpecified;
291
292impl From<NoEnvironmentSpecified> for Diagnostic {
293 fn from(_error: NoEnvironmentSpecified) -> Self {
294 Diagnostic::Global { reason: "No environment".to_owned(), kind: DiagnosticKind::Error }
295 }
296}
297
298pub(crate) enum TypeCheckError<'a> {
301 FunctionCallingError(FunctionCallingError),
302 JSXCallingError(FunctionCallingError),
303 TemplateLiteralCallingError(FunctionCallingError),
304 GetterCallingError(FunctionCallingError),
305 SetterCallingError(FunctionCallingError),
306 SuperCallError(FunctionCallingError),
308 PropertyDoesNotExist {
310 on: TypeStringRepresentation,
311 property: PropertyKeyRepresentation,
312 position: SpanWithSource,
313 possibles: Vec<&'a str>,
314 },
315 CyclicTypeAlias {
317 position: SpanWithSource,
318 },
319 #[allow(dead_code)]
320 NotInLoopOrCouldNotFindLabel(NotInLoopOrCouldNotFindLabel),
321 #[allow(dead_code)]
322 RestParameterAnnotationShouldBeArrayType(SpanWithSource),
323 CouldNotFindVariable {
324 variable: &'a str,
325 possibles: Vec<&'a str>,
326 position: SpanWithSource,
327 },
328 CouldNotFindType(&'a str, Vec<&'a str>, SpanWithSource),
329 AssignmentError(AssignmentError),
331 SetPropertyError(SetPropertyError),
332 ReturnedTypeDoesNotMatch {
333 expected_return_type: TypeStringRepresentation,
334 returned_type: TypeStringRepresentation,
335 annotation_position: SpanWithSource,
337 returned_position: SpanWithSource,
338 },
339 NonTopLevelExport(SpanWithSource),
341 FieldNotExported {
342 file: &'a str,
343 importing: &'a str,
344 position: SpanWithSource,
345 possibles: Vec<&'a str>,
346 },
347 NotSatisfied {
349 at: SpanWithSource,
350 expected: TypeStringRepresentation,
351 found: TypeStringRepresentation,
352 },
353 CatchTypeDoesNotMatch {
355 at: SpanWithSource,
356 expected: TypeStringRepresentation,
357 found: TypeStringRepresentation,
358 },
359 Unsupported {
361 thing: &'static str,
362 at: SpanWithSource,
363 },
364 InvalidDefaultParameter {
365 at: SpanWithSource,
366 expected: TypeStringRepresentation,
367 found: TypeStringRepresentation,
368 },
369 #[allow(dead_code)]
371 FunctionDoesNotMeetConstraint {
372 function_constraint: TypeStringRepresentation,
373 function_type: TypeStringRepresentation,
374 position: SpanWithSource,
375 },
376 CannotRedeclareVariable {
377 name: String,
378 position: SpanWithSource,
379 },
380 GenericArgumentDoesNotMeetRestriction {
382 parameter_restriction: TypeStringRepresentation,
383 argument: TypeStringRepresentation,
384 position: SpanWithSource,
385 },
386 GenericArgumentCountMismatch {
388 expected_count: usize,
389 count: usize,
390 position: SpanWithSource,
391 },
392 #[allow(dead_code)]
393 NotTopLevelImport(SpanWithSource),
394 DuplicateImportName {
395 import_position: SpanWithSource,
396 existing_position: SpanWithSource,
397 },
398 #[allow(dead_code)]
399 DoubleDefaultExport(SpanWithSource),
400 NoDefaultExport {
401 position: SpanWithSource,
402 partial_import_path: &'a str,
403 },
404 CannotOpenFile {
405 file: CouldNotOpenFile,
406 import_position: Option<SpanWithSource>,
407 possibles: Vec<&'a str>,
408 partial_import_path: &'a str,
409 },
410 #[allow(dead_code)]
412 VariableNotDefinedInContext {
413 variable: &'a str,
414 expected_context: &'a str,
415 current_context: String,
416 position: SpanWithSource,
417 },
418 TypeNeedsTypeArguments(&'a str, SpanWithSource),
419 TypeAlreadyDeclared {
420 name: String,
421 position: SpanWithSource,
422 },
423 #[allow(clippy::upper_case_acronyms)]
424 VariableUsedInTDZ(VariableUsedInTDZ),
425 InvalidMathematicalOrBitwiseOperation {
426 operator: crate::features::operations::MathematicalOrBitwiseOperation,
427 lhs: TypeStringRepresentation,
428 rhs: TypeStringRepresentation,
429 position: SpanWithSource,
430 },
431 InvalidEqualityOperation {
433 operator: crate::features::operations::EqualityAndInequality,
434 lhs: TypeStringRepresentation,
435 rhs: TypeStringRepresentation,
436 position: SpanWithSource,
437 },
438 InvalidUnaryOperation {
439 operator: crate::features::operations::UnaryOperation,
440 operand: TypeStringRepresentation,
441 position: SpanWithSource,
442 },
443 #[allow(dead_code)]
444 InvalidCast {
445 position: SpanWithSource,
446 from: TypeStringRepresentation,
447 to: TypeStringRepresentation,
448 },
449 #[allow(dead_code)]
452 UnreachableVariableClosedOver(String, SpanWithSource),
453 IncompatibleOverloadParameter {
454 parameter_position: SpanWithSource,
455 overloaded_parameter_position: SpanWithSource,
456 parameter: TypeStringRepresentation,
457 overloaded_parameter: TypeStringRepresentation,
458 },
459 IncompatibleOverloadReturnType {
460 base_position: SpanWithSource,
461 overload_position: SpanWithSource,
462 base: TypeStringRepresentation,
463 overload: TypeStringRepresentation,
464 },
465 FunctionWithoutBodyNotAllowedHere {
466 position: SpanWithSource,
467 },
468 CannotDeleteProperty(CannotDeleteFromError),
469 InvalidRegExp(InvalidRegExp),
470}
471
472#[allow(clippy::useless_format)]
473#[must_use]
474pub fn get_possibles_message(possibles: &[&str]) -> String {
475 match possibles {
476 [] => format!(""),
477 [a] => format!("Did you mean '{a}'?"),
478 [a @ .., b] => {
479 let mut iter = a.iter();
480 let first = format!("'{first}'", first = iter.next().unwrap());
481 format!(
482 "Did you mean {items} or '{b}'?",
483 items = iter.fold(first, |acc, item| format!("{acc}, '{item}'"))
484 )
485 }
486 }
487}
488
489fn map_error_empty<U, T: Default>(n: Vec<U>, cb: impl FnOnce(Vec<U>) -> T) -> T {
490 if n.is_empty() {
491 <T as Default>::default()
492 } else {
493 cb(n)
494 }
495}
496
497impl From<TypeCheckError<'_>> for Diagnostic {
498 fn from(error: TypeCheckError<'_>) -> Self {
499 let kind = super::DiagnosticKind::Error;
500 match error {
501 TypeCheckError::CouldNotFindVariable { variable, possibles, position } => {
502 Diagnostic::PositionWithAdditionalLabels {
503 reason: format!("Could not find variable '{variable}' in scope"),
504 labels: map_error_empty(possibles, |possibles| vec![(
505 get_possibles_message(&possibles),
506 position,
507 )]),
508 position,
509 kind,
510 }
511 }
512 TypeCheckError::CouldNotFindType(reference, possibles, position) => Diagnostic::PositionWithAdditionalLabels {
513 reason: format!("Could not find type '{reference}'"),
514 position,
515 labels: map_error_empty(possibles, |possibles| vec![(
516 get_possibles_message(&possibles),
517 position,
518 )]),
519 kind,
520 },
521 TypeCheckError::PropertyDoesNotExist { property, on, position, possibles } => {
522 Diagnostic::PositionWithAdditionalLabels {
523 reason: match property {
524 PropertyKeyRepresentation::Type(ty) => format!("No property of type {ty} on {on}"),
525 PropertyKeyRepresentation::StringKey(property) => format!("No property '{property}' on {on}"),
526 },
527 position,
528 labels: map_error_empty(possibles, |possibles| vec![(
529 get_possibles_message(&possibles),
530 position,
531 )]),
532 kind,
533 }
534 }
535 TypeCheckError::FunctionCallingError(error) => function_calling_error_diagnostic(error, kind, ""),
536 TypeCheckError::JSXCallingError(error) => function_calling_error_diagnostic(error, kind, " (in JSX)"),
537 TypeCheckError::GetterCallingError(error) => function_calling_error_diagnostic(error, kind, " (in getter)"),
538 TypeCheckError::SetterCallingError(error) => function_calling_error_diagnostic(error, kind, " (in setter)"),
539 TypeCheckError::TemplateLiteralCallingError(error) => {
540 function_calling_error_diagnostic(error, kind, " (in template literal)")
541 },
542 TypeCheckError::SuperCallError(error) => function_calling_error_diagnostic(error, kind, " (in super call)"),
543 TypeCheckError::AssignmentError(error) => match error {
544 AssignmentError::DoesNotMeetConstraint {
545 variable_type,
546 variable_position,
547 value_type,
548 value_position,
549 } => Diagnostic::PositionWithAdditionalLabels {
550 reason: format!(
551 "Type {value_type} is not assignable to type {variable_type}",
552 ),
553 position: value_position,
554 labels: vec![(
555 format!("Variable declared with type {variable_type}"),
556 variable_position,
557 )],
558 kind,
559 },
560 AssignmentError::PropertyConstraint {
561 property_constraint: property_type,
562 value_type,
563 assignment_position,
564 } => Diagnostic::Position {
565 reason: format!(
566 "Type {value_type} does not meet property constraint {property_type}"
567 ),
568 position: assignment_position,
569 kind,
570 },
571 AssignmentError::Constant(position) => Diagnostic::Position {
572 reason: "Cannot assign to constant".into(),
573 position,
574 kind,
575 },
576 AssignmentError::VariableNotFound { variable, assignment_position } => {
577 Diagnostic::Position {
578 reason: format!("Cannot assign to unknown variable '{variable}'"),
579 position: assignment_position,
580 kind,
581 }
582 }
583 AssignmentError::VariableUsedInTDZ(VariableUsedInTDZ { variable_name, position }) => {
584 Diagnostic::Position {
585 reason: format!("Cannot assign to '{variable_name}' before declaration"),
586 position,
587 kind,
588 }
589 }
590 },
591 TypeCheckError::ReturnedTypeDoesNotMatch {
592 annotation_position,
593 returned_position,
594 expected_return_type,
595 returned_type,
596 } => Diagnostic::PositionWithAdditionalLabels {
597 reason: format!(
598 "Cannot return {returned_type} because the function is expected to return {expected_return_type}"
599 ),
600 labels: vec![(
601 format!("Function annotated to return {expected_return_type} here"),
602 annotation_position,
603 )],
604 position: returned_position,
605 kind,
606 },
607 TypeCheckError::InvalidDefaultParameter {
608 expected,
609 found,
610 at,
611 } => Diagnostic::Position {
612 reason: format!( "Cannot use a default value of type {found} for parameter of type {expected}"),
613 position: at,
614 kind,
615 },
616 TypeCheckError::CatchTypeDoesNotMatch {
617 expected,
618 found,
619 at,
620 } => Diagnostic::Position {
621 reason: format!( "Cannot catch type {found} because the try block throws {expected}" ),
622 position: at,
623 kind,
624 },
625 TypeCheckError::NonTopLevelExport(position) => Diagnostic::Position {
626 reason: "Cannot export at not top level".to_owned(),
627 position,
628 kind,
629 },
630 TypeCheckError::FieldNotExported { file, importing, position, possibles } => {
631 Diagnostic::PositionWithAdditionalLabels {
632 reason: format!("{importing} not exported from {file}"),
633 position,
634 kind,
635 labels: map_error_empty(possibles, |possibles| vec![(
636 get_possibles_message(&possibles),
637 position,
638 )]),
639 }
640 }
641 TypeCheckError::RestParameterAnnotationShouldBeArrayType(pos) => {
642 Diagnostic::Position {
643 reason: "Rest parameter annotation should be array type".to_owned(),
644 position: pos,
645 kind,
646 }
647 }
648 TypeCheckError::Unsupported { thing, at } => Diagnostic::Position {
649 reason: format!("Unsupported: {thing}"),
650 position: at,
651 kind,
652 },
653 TypeCheckError::FunctionDoesNotMeetConstraint {
654 function_constraint,
655 function_type,
656 position,
657 } => Diagnostic::Position {
658 reason: format!(
659 "{function_constraint} constraint on function does not match synthesised form {function_type}",
660 ),
661 position,
662 kind,
663 },
664 TypeCheckError::NotSatisfied { at, expected, found } => Diagnostic::Position {
665 reason: format!("Expected {expected}, found {found}"),
666 position: at,
667 kind,
668 },
669 TypeCheckError::CannotRedeclareVariable { name, position } => {
670 Diagnostic::Position {
671 reason: format!("Cannot redeclare variable '{name}'"),
672 position,
673 kind,
674 }
675 }
676 TypeCheckError::GenericArgumentDoesNotMeetRestriction {
677 argument,
678 parameter_restriction,
679 position,
680 } => Diagnostic::Position {
681 reason: format!(
682 "Generic argument {argument} does not match {parameter_restriction}"
683 ),
684 position,
685 kind,
686 },
687 TypeCheckError::GenericArgumentCountMismatch {
688 count,
689 expected_count,
690 position,
691 } => {
692 let reason = if expected_count == 0 {
693 "Cannot pass a type argument to a non-generic type".to_owned()
694 } else if expected_count == 1 {
695 format!("Expected 1 type argument, but got {count}")
696 } else {
697 format!("Expected {expected_count} type arguments, but got {count}")
698 };
699 Diagnostic::Position {
700 position,
701 kind,
702 reason
703 }
704 },
705 TypeCheckError::NotTopLevelImport(position) => Diagnostic::Position {
706 reason: "Import must be in the top of the scope".to_owned(),
707 position,
708 kind,
709 },
710 TypeCheckError::DoubleDefaultExport(position) => Diagnostic::Position {
711 reason: "Cannot have more than one default export".to_owned(),
712 position,
713 kind,
714 },
715 TypeCheckError::DuplicateImportName { import_position: position, existing_position, ..} => Diagnostic::PositionWithAdditionalLabels {
716 reason: "Cannot import using conflicting name".to_string(),
717 position,
718 kind,
719 labels: vec![("Existing import with same name".to_string(), existing_position)],
720 },
721 TypeCheckError::NoDefaultExport { partial_import_path, position, ..} => Diagnostic::Position {
722 reason: format!("Cannot find default export from module '{partial_import_path}'"),
723 position,
724 kind
725 },
726 TypeCheckError::CannotOpenFile { file, import_position, possibles, partial_import_path } => if let Some(import_position) = import_position {
727 Diagnostic::PositionWithAdditionalLabels {
728 reason: format!("Cannot find {partial_import_path}"),
729 position: import_position,
730 kind,
731 labels: map_error_empty(possibles, |possibles| vec![(
732 get_possibles_message(&possibles),
733 import_position,
734 )])
735 }
736 } else {
737 Diagnostic::Global { reason: format!("Cannot find file {}", file.0.display()), kind }
738 },
739 TypeCheckError::VariableNotDefinedInContext {
740 variable,
741 expected_context,
742 current_context,
743 position,
744 } => Diagnostic::Position {
745 reason: format!("'{variable}' is only available on the {expected_context}, currently in {current_context}"),
746 position,
747 kind,
748 },
749 TypeCheckError::TypeNeedsTypeArguments(ty, position) => Diagnostic::Position {
750 reason: format!("Type {ty} requires type arguments"),
751 position,
752 kind,
753 },
754 TypeCheckError::TypeAlreadyDeclared { name, position } => Diagnostic::Position {
755 reason: format!("Type named '{name}' already declared"),
756 position,
757 kind,
758 },
759 TypeCheckError::VariableUsedInTDZ(VariableUsedInTDZ { position, variable_name }) => Diagnostic::Position {
760 reason: format!("Variable '{variable_name}' used before declaration"),
761 position,
762 kind,
763 },
764 TypeCheckError::InvalidMathematicalOrBitwiseOperation { operator, lhs, rhs, position } => Diagnostic::Position {
765 reason: format!("Cannot {lhs} {operator:?} {rhs}"),
767 position,
768 kind,
769 },
770 TypeCheckError::InvalidUnaryOperation {
771 operator,
772 operand,
773 position,
774 } => {
775 Diagnostic::Position {
776 reason: format!("Cannot {operator:?} {operand}"),
778 position,
779 kind,
780 }
781 },
782 TypeCheckError::InvalidEqualityOperation { operator, lhs, rhs, position } => Diagnostic::Position {
783 reason: format!("Cannot {lhs} {operator:?} {rhs}"),
785 position,
786 kind,
787 },
788 TypeCheckError::NotInLoopOrCouldNotFindLabel(NotInLoopOrCouldNotFindLabel {
789 label: _,
790 position,
791 }) => {
792 Diagnostic::Position {
793 reason: "Cannot use `break` or `continue` here or could not find label".to_owned(),
795 position,
796 kind,
797 }
798 }
799 TypeCheckError::InvalidCast { position, from, to } => {
800 Diagnostic::Position {
801 reason: format!("Cannot cast {from} to {to}"),
802 position,
803 kind,
804 }
805 },
806 TypeCheckError::UnreachableVariableClosedOver(name, function_position) => {
807 Diagnostic::Position {
808 reason: format!("Function contains unreachable closed over variable '{name}'"),
809 position: function_position,
810 kind,
811 }
812 },
813 TypeCheckError::IncompatibleOverloadParameter { parameter_position, overloaded_parameter_position, parameter, overloaded_parameter } => Diagnostic::PositionWithAdditionalLabels {
814 reason: format!(
815 "Overload with parameter of {overloaded_parameter} does not meet base parameter {parameter}"
816 ),
817 labels: vec![(
818 format!("Function has base type {parameter} here"),
819 parameter_position,
820 )],
821 position: overloaded_parameter_position,
822 kind,
823 },
824 TypeCheckError::IncompatibleOverloadReturnType { base_position, overload_position, base, overload } => Diagnostic::PositionWithAdditionalLabels {
825 reason: format!(
826 "Cannot return {overload} in overload because base function is expected to return {base}"
827 ),
828 labels: vec![(
829 format!("Function annotated to return {base} here"),
830 base_position,
831 )],
832 position: overload_position,
833 kind,
834 },
835 TypeCheckError::FunctionWithoutBodyNotAllowedHere { position } => {
836 Diagnostic::Position {
837 reason: "Function without body not allowed here".to_owned(),
838 position,
839 kind,
840 }
841 }
842 TypeCheckError::CyclicTypeAlias { position } => {
843 Diagnostic::Position {
844 reason: "Circular type reference".to_owned(),
845 position,
846 kind,
847 }
848 }
849 TypeCheckError::CannotDeleteProperty(CannotDeleteFromError::Constraint { constraint, position }) => {
850 Diagnostic::Position {
851 reason: format!("Cannot delete from object constrained to {constraint}"),
852 position,
853 kind,
854 }
855 }
856 TypeCheckError::CannotDeleteProperty(CannotDeleteFromError::NonConfigurable {
857 position,
858 }) => {
859 Diagnostic::Position {
860 reason: "Cannot delete from non-configurable property".to_owned(),
861 position,
862 kind,
863 }
864 }
865 TypeCheckError::SetPropertyError(error) => match error {
866 SetPropertyError::NotWriteable {
867 property,
868 position,
869 } => Diagnostic::Position {
870 reason: match property {
871 PropertyKeyRepresentation::Type(ty) => format!("Cannot write to property of type {ty}"),
872 PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}'")
873 },
874 position,
875 kind,
876 },
877 SetPropertyError::DoesNotMeetConstraint {
878 property_constraint,
879 value_type,
880 reason: _,
881 position,
882 } => Diagnostic::Position {
883 reason: format!(
884 "Type {value_type} does not meet property constraint {property_constraint}"
885 ),
886 position,
887 kind,
888 },
889 SetPropertyError::AssigningToGetter {
890 property,
891 position,
892 } => Diagnostic::Position {
893 reason: match property {
894 PropertyKeyRepresentation::Type(ty) => format!("Cannot write to property of type {ty} as it is a getter"),
895 PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}' as it is a getter")
896 },
897 position,
898 kind,
899 },
900 SetPropertyError::AssigningToNonExistent {
901 property,
902 position,
903 } => Diagnostic::Position {
904 reason: match property {
905 PropertyKeyRepresentation::Type(ty) => format!("Cannot write to non-existent property of type {ty}"),
906 PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to non-existent property '{property}'")
907 },
908 position,
909 kind,
910 }
911 },
912 TypeCheckError::InvalidRegExp(InvalidRegExp { error, position }) => Diagnostic::Position {
913 reason: format!("Invalid regular expression: {error}"),
914 position,
915 kind,
916 },
917 }
918 }
919}
920
921pub enum TypeCheckWarning {
922 AwaitUsedOnNonPromise(SpanWithSource),
923 DeadBranch {
925 expression_span: SpanWithSource,
926 expression_value: bool,
927 },
928 ExcessProperty {
929 position: SpanWithSource,
930 expected_type: TypeStringRepresentation,
931 excess_property_name: String,
932 },
933 IgnoringAsExpression(SpanWithSource),
934 Unimplemented {
935 item: &'static str,
936 position: SpanWithSource,
937 },
938 UselessExpression {
939 expression_span: SpanWithSource,
940 },
942 MergingInterfaceInSameContext {
943 position: SpanWithSource,
944 },
945 TypesDoNotIntersect {
946 left: TypeStringRepresentation,
947 right: TypeStringRepresentation,
948 position: SpanWithSource,
949 },
950 InvalidOrUnimplementedDefinitionFileItem(SpanWithSource),
951 ConditionalExceptionInvoked {
953 value: TypeStringRepresentation,
954 call_site: SpanWithSource,
956 },
957 Unreachable(SpanWithSource),
958 DisjointEquality {
959 lhs: TypeStringRepresentation,
960 rhs: TypeStringRepresentation,
961 result: bool,
962 position: SpanWithSource,
963 },
964 ItemMustBeUsedWithFlag {
965 item: &'static str,
966 position: SpanWithSource,
967 },
968}
969
970impl From<TypeCheckWarning> for Diagnostic {
971 fn from(warning: TypeCheckWarning) -> Self {
972 let kind = super::DiagnosticKind::Warning;
973
974 match warning {
975 TypeCheckWarning::AwaitUsedOnNonPromise(position) => Diagnostic::Position {
976 reason: "Unnecessary await expression / type is not promise".to_owned(),
977 position,
978 kind,
979 },
980 TypeCheckWarning::DeadBranch { expression_span, expression_value } => {
981 Diagnostic::Position {
982 reason: format!("Expression is always {expression_value:?}"),
983 position: expression_span,
984 kind,
985 }
986 }
987 TypeCheckWarning::ExcessProperty { position, expected_type, excess_property_name } => {
988 Diagnostic::Position {
989 reason: format!(
990 "'{excess_property_name}' is not a property of {expected_type}"
991 ),
992 position,
993 kind,
994 }
995 }
996 TypeCheckWarning::IgnoringAsExpression(position) => Diagnostic::Position {
997 reason: "'as' expressions are ignore by the checker".to_owned(),
998 position,
999 kind,
1000 },
1001 TypeCheckWarning::Unimplemented { item, position } => {
1002 Diagnostic::Position { reason: format!("Unsupported: {item}"), position, kind }
1003 }
1004 TypeCheckWarning::ItemMustBeUsedWithFlag { item, position } => Diagnostic::Position {
1005 reason: format!("{item} must be used with 'extras' option"),
1006 position,
1007 kind,
1008 },
1009 TypeCheckWarning::UselessExpression { expression_span } => Diagnostic::Position {
1010 reason: "Expression is always true".to_owned(),
1011 position: expression_span,
1012 kind,
1013 },
1014 TypeCheckWarning::MergingInterfaceInSameContext { position } => Diagnostic::Position {
1015 reason: "Merging interfaces in the same context".to_owned(),
1016 position,
1017 kind,
1018 },
1019 TypeCheckWarning::TypesDoNotIntersect { left, right, position } => {
1020 Diagnostic::Position {
1021 reason: format!("No intersection between types {left} and {right}"),
1022 position,
1023 kind,
1024 }
1025 }
1026 TypeCheckWarning::InvalidOrUnimplementedDefinitionFileItem(position) => {
1027 Diagnostic::Position {
1028 reason: "Invalid (or unimplemented) item in definition file skipped".to_owned(),
1029 position,
1030 kind,
1031 }
1032 }
1033 TypeCheckWarning::Unreachable(position) => {
1034 Diagnostic::Position { reason: "Unreachable statement".to_owned(), position, kind }
1035 }
1036 TypeCheckWarning::ConditionalExceptionInvoked { value, call_site } => {
1037 Diagnostic::Position {
1038 reason: format!("Conditional '{value}' was thrown in function"),
1039 position: call_site,
1040 kind,
1041 }
1042 }
1043 TypeCheckWarning::DisjointEquality { lhs, rhs, position, result } => {
1044 Diagnostic::Position {
1045 reason: format!(
1046 "This equality is always {result} as {lhs} and {rhs} have no overlap"
1047 ),
1048 position,
1049 kind,
1050 }
1051 }
1052 }
1053 }
1054}
1055
1056pub struct InfoDiagnostic(pub String, pub SpanWithSource);
1060
1061#[derive(Debug)]
1062pub struct CannotFindTypeError<'a>(pub &'a str);
1063
1064#[derive(Debug)]
1065pub struct TypeIsNotIndexable<'a>(pub &'a TypeId);
1066
1067pub(crate) struct TypeDefinitionModuleNotFound(pub PathBuf);
1068
1069impl From<TypeDefinitionModuleNotFound> for Diagnostic {
1070 fn from(val: TypeDefinitionModuleNotFound) -> Self {
1071 Diagnostic::Global {
1072 reason: format!(
1073 "Could not find type definition module at '{}'",
1074 val.0.as_path().display()
1075 ),
1076 kind: DiagnosticKind::Error,
1077 }
1078 }
1079}
1080
1081pub(crate) struct EntryPointNotFound(pub PathBuf);
1082
1083impl From<EntryPointNotFound> for Diagnostic {
1084 fn from(val: EntryPointNotFound) -> Self {
1085 Diagnostic::Global {
1086 reason: format!("Could not entry point module at '{}'", val.0.as_path().display()),
1087 kind: DiagnosticKind::Error,
1088 }
1089 }
1090}
1091
1092#[derive(Debug)]
1093pub struct CannotRedeclareVariable<'a> {
1094 pub name: &'a str,
1095}
1096
1097fn function_calling_error_diagnostic(
1099 error: FunctionCallingError,
1100 kind: crate::DiagnosticKind,
1101 context: &str,
1102) -> Diagnostic {
1103 match error {
1104 FunctionCallingError::InvalidArgumentType {
1105 parameter_type,
1106 argument_type,
1107 argument_position,
1108 parameter_position,
1109 restriction,
1110 } => {
1111 if let Some((restriction_pos, restriction)) = restriction {
1112 Diagnostic::PositionWithAdditionalLabels {
1113 reason: format!("Argument of type {argument_type} is not assignable to parameter of type {restriction}{context}"),
1114 position: argument_position,
1115 labels: vec![(
1116 format!("{parameter_type} was specialised with type {restriction}"),
1117 restriction_pos,
1118 )],
1119 kind,
1120 }
1121 } else {
1122 Diagnostic::PositionWithAdditionalLabels {
1123 reason: format!( "Argument of type {argument_type} is not assignable to parameter of type {parameter_type}{context}"),
1124 position: argument_position,
1125 labels: vec![(
1126 format!("Parameter has type {parameter_type}"),
1127 parameter_position,
1128 )],
1129 kind,
1130 }
1131 }
1132 }
1133 FunctionCallingError::MissingArgument { parameter_position, call_site } => {
1134 Diagnostic::PositionWithAdditionalLabels {
1135 reason: format!("Missing argument{context}"),
1136 position: call_site,
1137 kind,
1138 labels: vec![(
1139 "(non-optional) Parameter declared here".into(),
1140 parameter_position,
1141 )],
1142 }
1143 }
1144 FunctionCallingError::ExcessArguments { count: _, position } => {
1145 Diagnostic::Position { reason: format!("Excess argument{context}"), position, kind }
1146 }
1147 FunctionCallingError::ExcessTypeArguments { expected_count, count, position } => {
1148 let reason = if expected_count == 0 {
1149 format!("Cannot pass a type argument to a non-generic function{context}")
1150 } else if expected_count == 1 {
1151 format!("Expected 1 type argument, but got {count}{context}")
1152 } else {
1153 format!("Expected {expected_count} type arguments, but got {count}{context}")
1154 };
1155 Diagnostic::Position {
1156 position,
1157 kind,
1158 reason
1159 }
1160 }
1161 FunctionCallingError::NotCallable { calling, call_site } => Diagnostic::Position {
1162 reason: format!("Cannot call type {calling}{context}"),
1163 position: call_site,
1164 kind,
1165 },
1166 FunctionCallingError::ReferenceRestrictionDoesNotMatch {
1167 reference: _,
1168 requirement: _,
1169 found: _,
1170 } => todo!(),
1171 FunctionCallingError::CyclicRecursion(_, call_site) => Diagnostic::Position {
1180 reason: format!("Encountered recursion{context}"),
1181 position: call_site,
1182 kind,
1183 },
1184 FunctionCallingError::NoLogicForIdentifier(name, position) => Diagnostic::Position {
1185 reason: format!("No logic for constant function {name}{context}"),
1186 kind,
1187 position,
1188 },
1189 FunctionCallingError::NeedsToBeCalledWithNewKeyword(position) => Diagnostic::Position {
1190 reason: "Class constructor must be called with new".to_owned(),
1191 kind,
1192 position,
1193 },
1194 FunctionCallingError::VariableUsedInTDZ { error: VariableUsedInTDZ { position, variable_name }, call_site } => {
1195 Diagnostic::PositionWithAdditionalLabels {
1196 reason: format!("Variable '{variable_name}' used before declaration{context}"),
1197 position: call_site,
1198 kind,
1199 labels: vec![("Variable referenced here".to_owned(), position)],
1200 }
1201 }
1202 FunctionCallingError::SetPropertyConstraint {
1203 property_type,
1204 value_type,
1205 assignment_position,
1206 call_site,
1207 } => Diagnostic::PositionWithAdditionalLabels {
1208 reason: format!("Invalid assignment through parameter{context}"),
1209 position: call_site,
1210 kind,
1211 labels: vec![(
1212 format!("Type {value_type} does not meet property constraint {property_type}"),
1213 assignment_position,
1214 )],
1215 },
1216 FunctionCallingError::MismatchedThis { call_site, expected, found } => {
1217 Diagnostic::Position {
1218 reason: format!("The 'this' context of the function is expected to be {expected}, found {found}{context}"),
1219 position: call_site,
1220 kind,
1221 }
1222 }
1223 FunctionCallingError::CannotCatch { catch, thrown, thrown_position } => {
1224 Diagnostic::Position {
1225 reason: format!("Cannot throw {thrown} in block that expects {catch}{context}"),
1226 position: thrown_position,
1227 kind,
1228 }
1229 }
1230 FunctionCallingError::DeleteConstraint { constraint, delete_position, call_site: _ } => {
1231 Diagnostic::Position {
1232 reason: format!("Cannot delete from object constrained to {constraint}"),
1233 position: delete_position,
1234 kind,
1235 }
1236 }
1237 FunctionCallingError::NotConfigurable {
1238 property,
1239 call_site,
1240 } => {
1241 Diagnostic::Position {
1242 reason: match property {
1243 PropertyKeyRepresentation::Type(ty) => format!("Property of type '{ty}' not configurable"),
1244 PropertyKeyRepresentation::StringKey(property) => format!("Property '{property}' not configurable"),
1245 },
1246 position: call_site,
1247 kind,
1248 }
1249 }
1250 FunctionCallingError::InvalidRegExp(InvalidRegExp { error, position }) => Diagnostic::Position {
1251 reason: format!("Invalid regular expression: {error}"),
1252 position,
1253 kind,
1254 }
1255 }
1256}