1use crate::TypeDatabase;
26use crate::TypeFormatter;
27use crate::def::DefinitionStore;
28use crate::types::{TypeId, Visibility};
29use std::sync::Arc;
30use tsz_binder::SymbolId;
31use tsz_common::interner::Atom;
32
33pub trait SubtypeTracer {
61 fn on_mismatch(&mut self, reason: impl FnOnce() -> SubtypeFailureReason) -> bool;
77}
78
79pub trait DynSubtypeTracer {
84 fn on_mismatch_dyn(&mut self, reason: SubtypeFailureReason) -> bool;
94}
95
96impl<T: SubtypeTracer> DynSubtypeTracer for T {
98 fn on_mismatch_dyn(&mut self, reason: SubtypeFailureReason) -> bool {
99 self.on_mismatch(|| reason)
100 }
101}
102
103#[cfg(test)]
104#[derive(Clone, Copy, Debug)]
120pub struct FastTracer;
121
122#[cfg(test)]
123impl SubtypeTracer for FastTracer {
124 #[inline(always)]
129 fn on_mismatch(&mut self, _reason: impl FnOnce() -> SubtypeFailureReason) -> bool {
130 false
131 }
132}
133
134#[cfg(test)]
135#[derive(Debug)]
150pub struct DiagnosticTracer {
151 failure: Option<SubtypeFailureReason>,
153}
154
155#[cfg(test)]
156impl DiagnosticTracer {
157 pub fn new() -> Self {
159 Self { failure: None }
160 }
161
162 pub fn take_failure(&mut self) -> Option<SubtypeFailureReason> {
164 self.failure.take()
165 }
166
167 pub fn has_failure(&self) -> bool {
170 self.failure.is_some()
171 }
172}
173
174#[cfg(test)]
175impl Default for DiagnosticTracer {
176 fn default() -> Self {
177 Self::new()
178 }
179}
180
181#[cfg(test)]
182impl SubtypeTracer for DiagnosticTracer {
183 #[inline]
191 fn on_mismatch(&mut self, reason: impl FnOnce() -> SubtypeFailureReason) -> bool {
192 if self.failure.is_none() {
194 self.failure = Some(reason());
195 }
196 false
197 }
198}
199
200#[derive(Clone, Debug, PartialEq)]
211pub enum SubtypeFailureReason {
212 MissingProperty {
214 property_name: Atom,
215 source_type: TypeId,
216 target_type: TypeId,
217 },
218 MissingProperties {
220 property_names: Vec<Atom>,
221 source_type: TypeId,
222 target_type: TypeId,
223 },
224 PropertyTypeMismatch {
226 property_name: Atom,
227 source_property_type: TypeId,
228 target_property_type: TypeId,
229 nested_reason: Option<Box<Self>>,
230 },
231 OptionalPropertyRequired { property_name: Atom },
233 ReadonlyPropertyMismatch { property_name: Atom },
235 PropertyVisibilityMismatch {
237 property_name: Atom,
238 source_visibility: Visibility,
239 target_visibility: Visibility,
240 },
241 PropertyNominalMismatch { property_name: Atom },
243 ReturnTypeMismatch {
245 source_return: TypeId,
246 target_return: TypeId,
247 nested_reason: Option<Box<Self>>,
248 },
249 ParameterTypeMismatch {
251 param_index: usize,
252 source_param: TypeId,
253 target_param: TypeId,
254 },
255 TooManyParameters {
257 source_count: usize,
258 target_count: usize,
259 },
260 TupleElementMismatch {
262 source_count: usize,
263 target_count: usize,
264 },
265 TupleElementTypeMismatch {
267 index: usize,
268 source_element: TypeId,
269 target_element: TypeId,
270 },
271 ArrayElementMismatch {
273 source_element: TypeId,
274 target_element: TypeId,
275 },
276 IndexSignatureMismatch {
278 index_kind: &'static str, source_value_type: TypeId,
280 target_value_type: TypeId,
281 },
282 NoUnionMemberMatches {
284 source_type: TypeId,
285 target_union_members: Vec<TypeId>,
286 },
287 NoIntersectionMemberMatches {
289 source_type: TypeId,
290 target_type: TypeId,
291 },
292 NoCommonProperties {
294 source_type: TypeId,
295 target_type: TypeId,
296 },
297 TypeMismatch {
299 source_type: TypeId,
300 target_type: TypeId,
301 },
302 IntrinsicTypeMismatch {
304 source_type: TypeId,
305 target_type: TypeId,
306 },
307 LiteralTypeMismatch {
309 source_type: TypeId,
310 target_type: TypeId,
311 },
312 ErrorType {
314 source_type: TypeId,
315 target_type: TypeId,
316 },
317 RecursionLimitExceeded,
319 ParameterCountMismatch {
321 source_count: usize,
322 target_count: usize,
323 },
324 ExcessProperty {
326 property_name: Atom,
327 target_type: TypeId,
328 },
329}
330
331#[derive(Clone, Copy, Debug, PartialEq, Eq)]
333pub enum DiagnosticSeverity {
334 Error,
335 Warning,
336 Suggestion,
337 Message,
338}
339
340#[derive(Clone, Debug)]
349pub enum DiagnosticArg {
350 Type(TypeId),
352 Symbol(SymbolId),
354 Atom(Atom),
356 String(Arc<str>),
358 Number(usize),
360}
361
362macro_rules! impl_from_diagnostic_arg {
363 ($($source:ty => $variant:ident),* $(,)?) => {
364 $(impl From<$source> for DiagnosticArg {
365 fn from(v: $source) -> Self { Self::$variant(v) }
366 })*
367 };
368}
369
370impl_from_diagnostic_arg! {
371 TypeId => Type,
372 SymbolId => Symbol,
373 Atom => Atom,
374 usize => Number,
375}
376
377impl From<&str> for DiagnosticArg {
378 fn from(s: &str) -> Self {
379 Self::String(s.into())
380 }
381}
382
383impl From<String> for DiagnosticArg {
384 fn from(s: String) -> Self {
385 Self::String(s.into())
386 }
387}
388
389#[derive(Clone, Debug)]
394pub struct PendingDiagnostic {
395 pub code: u32,
397 pub args: Vec<DiagnosticArg>,
399 pub span: Option<SourceSpan>,
401 pub severity: DiagnosticSeverity,
403 pub related: Vec<Self>,
405}
406
407impl PendingDiagnostic {
408 pub const fn error(code: u32, args: Vec<DiagnosticArg>) -> Self {
410 Self {
411 code,
412 args,
413 span: None,
414 severity: DiagnosticSeverity::Error,
415 related: Vec::new(),
416 }
417 }
418
419 pub fn with_span(mut self, span: SourceSpan) -> Self {
421 self.span = Some(span);
422 self
423 }
424
425 pub fn with_related(mut self, related: Self) -> Self {
427 self.related.push(related);
428 self
429 }
430}
431
432#[derive(Clone, Debug, PartialEq, Eq)]
434pub struct SourceSpan {
435 pub start: u32,
437 pub length: u32,
439 pub file: Arc<str>,
441}
442
443impl SourceSpan {
444 pub fn new(file: impl Into<Arc<str>>, start: u32, length: u32) -> Self {
445 Self {
446 start,
447 length,
448 file: file.into(),
449 }
450 }
451}
452
453#[derive(Clone, Debug)]
455pub struct RelatedInformation {
456 pub span: SourceSpan,
457 pub message: String,
458}
459
460#[derive(Clone, Debug)]
462pub struct TypeDiagnostic {
463 pub message: String,
465 pub code: u32,
467 pub severity: DiagnosticSeverity,
469 pub span: Option<SourceSpan>,
471 pub related: Vec<RelatedInformation>,
473}
474
475impl TypeDiagnostic {
476 pub fn error(message: impl Into<String>, code: u32) -> Self {
478 Self {
479 message: message.into(),
480 code,
481 severity: DiagnosticSeverity::Error,
482 span: None,
483 related: Vec::new(),
484 }
485 }
486
487 pub fn with_span(mut self, span: SourceSpan) -> Self {
489 self.span = Some(span);
490 self
491 }
492
493 pub fn with_related(mut self, span: SourceSpan, message: impl Into<String>) -> Self {
495 self.related.push(RelatedInformation {
496 span,
497 message: message.into(),
498 });
499 self
500 }
501}
502
503pub mod codes {
513 use tsz_common::diagnostics::diagnostic_codes as dc;
514
515 pub use dc::ARGUMENT_OF_TYPE_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE as ARG_NOT_ASSIGNABLE;
517 pub use dc::CANNOT_ASSIGN_TO_BECAUSE_IT_IS_A_READ_ONLY_PROPERTY as READONLY_PROPERTY;
518 pub use dc::OBJECT_LITERAL_MAY_ONLY_SPECIFY_KNOWN_PROPERTIES_AND_DOES_NOT_EXIST_IN_TYPE as EXCESS_PROPERTY;
519 pub use dc::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE as PROPERTY_MISSING;
520 pub use dc::PROPERTY_IS_PRIVATE_AND_ONLY_ACCESSIBLE_WITHIN_CLASS as PROPERTY_VISIBILITY_MISMATCH;
521 pub use dc::PROPERTY_IS_PROTECTED_AND_ONLY_ACCESSIBLE_THROUGH_AN_INSTANCE_OF_CLASS_THIS_IS_A as PROPERTY_NOMINAL_MISMATCH;
522 pub use dc::TYPE_HAS_NO_PROPERTIES_IN_COMMON_WITH_TYPE as NO_COMMON_PROPERTIES;
523 pub use dc::TYPE_IS_MISSING_THE_FOLLOWING_PROPERTIES_FROM_TYPE as MISSING_PROPERTIES;
524 pub use dc::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE as TYPE_NOT_ASSIGNABLE;
525
526 pub use dc::TYPES_OF_PROPERTY_ARE_INCOMPATIBLE as PROPERTY_TYPE_MISMATCH;
527
528 pub use dc::CANNOT_FIND_NAME;
530 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB as CANNOT_FIND_NAME_TARGET_LIB;
531 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB_2 as CANNOT_FIND_NAME_DOM;
532 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_A_TEST_RUNNER_TRY_N as CANNOT_FIND_NAME_TEST_RUNNER;
533 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_NODE_TRY_NPM_I_SAVE as CANNOT_FIND_NAME_NODE;
534 pub use dc::EXPECTED_ARGUMENTS_BUT_GOT as ARG_COUNT_MISMATCH;
535 pub use dc::PROPERTY_DOES_NOT_EXIST_ON_TYPE as PROPERTY_NOT_EXIST;
536 pub use dc::PROPERTY_DOES_NOT_EXIST_ON_TYPE_DID_YOU_MEAN as PROPERTY_NOT_EXIST_DID_YOU_MEAN;
537 pub use dc::THE_THIS_CONTEXT_OF_TYPE_IS_NOT_ASSIGNABLE_TO_METHODS_THIS_OF_TYPE as THIS_TYPE_MISMATCH;
538 pub use dc::THIS_EXPRESSION_IS_NOT_CALLABLE as NOT_CALLABLE;
539
540 pub use dc::FUNCTION_EXPRESSION_WHICH_LACKS_RETURN_TYPE_ANNOTATION_IMPLICITLY_HAS_AN_RETURN as IMPLICIT_ANY_RETURN_FUNCTION_EXPRESSION;
544 pub use dc::MEMBER_IMPLICITLY_HAS_AN_TYPE as IMPLICIT_ANY_MEMBER;
545 pub use dc::PARAMETER_IMPLICITLY_HAS_AN_TYPE as IMPLICIT_ANY_PARAMETER;
546 pub use dc::VARIABLE_IMPLICITLY_HAS_AN_TYPE as IMPLICIT_ANY;
547 pub use dc::WHICH_LACKS_RETURN_TYPE_ANNOTATION_IMPLICITLY_HAS_AN_RETURN_TYPE as IMPLICIT_ANY_RETURN;
548}
549
550fn cannot_find_name_code(name: &str) -> u32 {
559 match name {
560 "require" | "exports" | "module" | "process" | "Buffer" | "__filename" | "__dirname" => {
562 codes::CANNOT_FIND_NAME_NODE
563 }
564 "describe" | "suite" | "it" | "test" => codes::CANNOT_FIND_NAME_TEST_RUNNER,
566 "Promise" | "Symbol" | "Map" | "Set" | "Reflect" | "Iterator" | "AsyncIterator"
568 | "SharedArrayBuffer" => codes::CANNOT_FIND_NAME_TARGET_LIB,
569 "document" | "console" => codes::CANNOT_FIND_NAME_DOM,
571 _ => codes::CANNOT_FIND_NAME,
573 }
574}
575
576pub fn get_message_template(code: u32) -> &'static str {
586 tsz_common::diagnostics::get_message_template(code).unwrap_or("Unknown diagnostic")
587}
588
589pub struct DiagnosticBuilder<'a> {
600 formatter: TypeFormatter<'a>,
601}
602
603impl<'a> DiagnosticBuilder<'a> {
604 pub fn new(interner: &'a dyn TypeDatabase) -> Self {
605 DiagnosticBuilder {
606 formatter: TypeFormatter::new(interner),
607 }
608 }
609
610 pub fn with_symbols(
615 interner: &'a dyn TypeDatabase,
616 symbol_arena: &'a tsz_binder::SymbolArena,
617 ) -> Self {
618 DiagnosticBuilder {
619 formatter: TypeFormatter::with_symbols(interner, symbol_arena),
620 }
621 }
622
623 pub fn with_def_store(mut self, def_store: &'a DefinitionStore) -> Self {
628 self.formatter = self.formatter.with_def_store(def_store);
629 self
630 }
631
632 pub fn type_not_assignable(&mut self, source: TypeId, target: TypeId) -> TypeDiagnostic {
634 let source_str = self.formatter.format(source);
635 let target_str = self.formatter.format(target);
636 TypeDiagnostic::error(
637 format!("Type '{source_str}' is not assignable to type '{target_str}'."),
638 codes::TYPE_NOT_ASSIGNABLE,
639 )
640 }
641
642 pub fn property_missing(
644 &mut self,
645 prop_name: &str,
646 source: TypeId,
647 target: TypeId,
648 ) -> TypeDiagnostic {
649 let source_str = self.formatter.format(source);
650 let target_str = self.formatter.format(target);
651 TypeDiagnostic::error(
652 format!(
653 "Property '{prop_name}' is missing in type '{source_str}' but required in type '{target_str}'."
654 ),
655 codes::PROPERTY_MISSING,
656 )
657 }
658
659 pub fn property_not_exist(&mut self, prop_name: &str, type_id: TypeId) -> TypeDiagnostic {
661 let type_str = self.formatter.format(type_id);
662 TypeDiagnostic::error(
663 format!("Property '{prop_name}' does not exist on type '{type_str}'."),
664 codes::PROPERTY_NOT_EXIST,
665 )
666 }
667
668 pub fn property_not_exist_did_you_mean(
670 &mut self,
671 prop_name: &str,
672 type_id: TypeId,
673 suggestion: &str,
674 ) -> TypeDiagnostic {
675 let type_str = self.formatter.format(type_id);
676 TypeDiagnostic::error(
677 format!(
678 "Property '{prop_name}' does not exist on type '{type_str}'. Did you mean '{suggestion}'?"
679 ),
680 codes::PROPERTY_NOT_EXIST_DID_YOU_MEAN,
681 )
682 }
683
684 pub fn argument_not_assignable(
686 &mut self,
687 arg_type: TypeId,
688 param_type: TypeId,
689 ) -> TypeDiagnostic {
690 let arg_str = self.formatter.format(arg_type);
691 let param_str = self.formatter.format(param_type);
692 TypeDiagnostic::error(
693 format!(
694 "Argument of type '{arg_str}' is not assignable to parameter of type '{param_str}'."
695 ),
696 codes::ARG_NOT_ASSIGNABLE,
697 )
698 }
699
700 pub fn cannot_find_name(&mut self, name: &str) -> TypeDiagnostic {
702 let is_obviously_invalid = name.len() == 1
707 && matches!(
708 name.chars().next(),
709 Some(
710 ',' | ';'
711 | ':'
712 | '('
713 | ')'
714 | '['
715 | ']'
716 | '{'
717 | '}'
718 | '+'
719 | '-'
720 | '*'
721 | '/'
722 | '%'
723 | '&'
724 | '|'
725 | '^'
726 | '!'
727 | '~'
728 | '<'
729 | '>'
730 | '='
731 | '.'
732 )
733 );
734
735 if is_obviously_invalid {
736 return TypeDiagnostic::error("", 0);
738 }
739
740 let code = cannot_find_name_code(name);
741 TypeDiagnostic::error(format!("Cannot find name '{name}'."), code)
742 }
743
744 pub fn not_callable(&mut self, type_id: TypeId) -> TypeDiagnostic {
746 let type_str = self.formatter.format(type_id);
747 TypeDiagnostic::error(
748 format!("Type '{type_str}' has no call signatures."),
749 codes::NOT_CALLABLE,
750 )
751 }
752
753 pub fn this_type_mismatch(
754 &mut self,
755 expected_this: TypeId,
756 actual_this: TypeId,
757 ) -> TypeDiagnostic {
758 let expected_str = self.formatter.format(expected_this);
759 let actual_str = self.formatter.format(actual_this);
760 TypeDiagnostic::error(
761 format!(
762 "The 'this' context of type '{actual_str}' is not assignable to method's 'this' of type '{expected_str}'."
763 ),
764 codes::THIS_TYPE_MISMATCH,
765 )
766 }
767
768 pub fn argument_count_mismatch(&mut self, expected: usize, got: usize) -> TypeDiagnostic {
770 TypeDiagnostic::error(
771 format!("Expected {expected} arguments, but got {got}."),
772 codes::ARG_COUNT_MISMATCH,
773 )
774 }
775
776 pub fn readonly_property(&mut self, prop_name: &str) -> TypeDiagnostic {
778 TypeDiagnostic::error(
779 format!("Cannot assign to '{prop_name}' because it is a read-only property."),
780 codes::READONLY_PROPERTY,
781 )
782 }
783
784 pub fn excess_property(&mut self, prop_name: &str, target: TypeId) -> TypeDiagnostic {
786 let target_str = self.formatter.format(target);
787 TypeDiagnostic::error(
788 format!(
789 "Object literal may only specify known properties, and '{prop_name}' does not exist in type '{target_str}'."
790 ),
791 codes::EXCESS_PROPERTY,
792 )
793 }
794
795 pub fn implicit_any_parameter(&mut self, param_name: &str) -> TypeDiagnostic {
804 TypeDiagnostic::error(
805 format!("Parameter '{param_name}' implicitly has an 'any' type."),
806 codes::IMPLICIT_ANY_PARAMETER,
807 )
808 }
809
810 pub fn implicit_any_parameter_with_type(
815 &mut self,
816 param_name: &str,
817 implicit_type: TypeId,
818 ) -> TypeDiagnostic {
819 let type_str = self.formatter.format(implicit_type);
820 TypeDiagnostic::error(
821 format!("Parameter '{param_name}' implicitly has an '{type_str}' type."),
822 codes::IMPLICIT_ANY_PARAMETER,
823 )
824 }
825
826 pub fn implicit_any_member(&mut self, member_name: &str) -> TypeDiagnostic {
831 TypeDiagnostic::error(
832 format!("Member '{member_name}' implicitly has an 'any' type."),
833 codes::IMPLICIT_ANY_MEMBER,
834 )
835 }
836
837 pub fn implicit_any_variable(&mut self, var_name: &str, var_type: TypeId) -> TypeDiagnostic {
842 let type_str = self.formatter.format(var_type);
843 TypeDiagnostic::error(
844 format!("Variable '{var_name}' implicitly has an '{type_str}' type."),
845 codes::IMPLICIT_ANY,
846 )
847 }
848
849 pub fn implicit_any_return(&mut self, func_name: &str, return_type: TypeId) -> TypeDiagnostic {
854 let type_str = self.formatter.format(return_type);
855 TypeDiagnostic::error(
856 format!(
857 "'{func_name}', which lacks return-type annotation, implicitly has an '{type_str}' return type."
858 ),
859 codes::IMPLICIT_ANY_RETURN,
860 )
861 }
862
863 pub fn implicit_any_return_function_expression(
868 &mut self,
869 return_type: TypeId,
870 ) -> TypeDiagnostic {
871 let type_str = self.formatter.format(return_type);
872 TypeDiagnostic::error(
873 format!(
874 "Function expression, which lacks return-type annotation, implicitly has an '{type_str}' return type."
875 ),
876 codes::IMPLICIT_ANY_RETURN_FUNCTION_EXPRESSION,
877 )
878 }
879}
880
881pub struct PendingDiagnosticBuilder;
890
891impl SubtypeFailureReason {
896 pub const fn diagnostic_code(&self) -> u32 {
902 match self {
903 Self::MissingProperty { .. } | Self::OptionalPropertyRequired { .. } => {
904 codes::PROPERTY_MISSING
905 }
906 Self::MissingProperties { .. } => codes::MISSING_PROPERTIES,
907 Self::PropertyTypeMismatch { .. } => codes::PROPERTY_TYPE_MISMATCH,
908 Self::ReadonlyPropertyMismatch { .. } => codes::READONLY_PROPERTY,
909 Self::PropertyVisibilityMismatch { .. } => codes::PROPERTY_VISIBILITY_MISMATCH,
910 Self::PropertyNominalMismatch { .. } => codes::PROPERTY_NOMINAL_MISMATCH,
911 Self::ReturnTypeMismatch { .. }
912 | Self::ParameterTypeMismatch { .. }
913 | Self::TupleElementMismatch { .. }
914 | Self::TupleElementTypeMismatch { .. }
915 | Self::ArrayElementMismatch { .. }
916 | Self::IndexSignatureMismatch { .. }
917 | Self::NoUnionMemberMatches { .. }
918 | Self::NoIntersectionMemberMatches { .. }
919 | Self::TypeMismatch { .. }
920 | Self::IntrinsicTypeMismatch { .. }
921 | Self::LiteralTypeMismatch { .. }
922 | Self::ErrorType { .. }
923 | Self::RecursionLimitExceeded
924 | Self::ParameterCountMismatch { .. } => codes::TYPE_NOT_ASSIGNABLE,
925 Self::TooManyParameters { .. } => codes::ARG_COUNT_MISMATCH,
926 Self::NoCommonProperties { .. } => codes::NO_COMMON_PROPERTIES,
927 Self::ExcessProperty { .. } => codes::EXCESS_PROPERTY,
928 }
929 }
930
931 pub fn to_diagnostic(&self, source: TypeId, target: TypeId) -> PendingDiagnostic {
936 match self {
937 Self::MissingProperty {
938 property_name,
939 source_type,
940 target_type,
941 } => PendingDiagnostic::error(
942 codes::PROPERTY_MISSING,
943 vec![
944 (*property_name).into(),
945 (*source_type).into(),
946 (*target_type).into(),
947 ],
948 ),
949
950 Self::MissingProperties {
951 property_names: _,
952 source_type,
953 target_type,
954 } => PendingDiagnostic::error(
955 codes::MISSING_PROPERTIES,
956 vec![(*source_type).into(), (*target_type).into()],
957 ),
958
959 Self::PropertyTypeMismatch {
960 property_name,
961 source_property_type,
962 target_property_type,
963 nested_reason,
964 } => {
965 let mut diag = PendingDiagnostic::error(
967 codes::TYPE_NOT_ASSIGNABLE,
968 vec![source.into(), target.into()],
969 );
970
971 let elaboration = PendingDiagnostic::error(
973 codes::PROPERTY_TYPE_MISMATCH,
974 vec![(*property_name).into()],
975 );
976 diag = diag.with_related(elaboration);
977
978 if let Some(nested) = nested_reason {
980 let nested_diag =
981 nested.to_diagnostic(*source_property_type, *target_property_type);
982 diag = diag.with_related(nested_diag);
983 }
984
985 diag
986 }
987
988 Self::OptionalPropertyRequired { property_name } => {
989 PendingDiagnostic::error(
991 codes::TYPE_NOT_ASSIGNABLE,
992 vec![source.into(), target.into()],
993 )
994 .with_related(PendingDiagnostic::error(
995 codes::PROPERTY_MISSING, vec![(*property_name).into(), source.into(), target.into()],
997 ))
998 }
999
1000 Self::ReadonlyPropertyMismatch { property_name } => PendingDiagnostic::error(
1001 codes::TYPE_NOT_ASSIGNABLE,
1002 vec![source.into(), target.into()],
1003 )
1004 .with_related(PendingDiagnostic::error(
1005 codes::READONLY_PROPERTY,
1006 vec![(*property_name).into()],
1007 )),
1008
1009 Self::PropertyVisibilityMismatch {
1010 property_name,
1011 source_visibility,
1012 target_visibility,
1013 } => {
1014 PendingDiagnostic::error(
1016 codes::TYPE_NOT_ASSIGNABLE,
1017 vec![source.into(), target.into()],
1018 )
1019 .with_related(PendingDiagnostic::error(
1020 codes::PROPERTY_VISIBILITY_MISMATCH,
1021 vec![
1022 (*property_name).into(),
1023 format!("{source_visibility:?}").into(),
1024 format!("{target_visibility:?}").into(),
1025 ],
1026 ))
1027 }
1028
1029 Self::PropertyNominalMismatch { property_name } => {
1030 PendingDiagnostic::error(
1032 codes::TYPE_NOT_ASSIGNABLE,
1033 vec![source.into(), target.into()],
1034 )
1035 .with_related(PendingDiagnostic::error(
1036 codes::PROPERTY_NOMINAL_MISMATCH,
1037 vec![(*property_name).into()],
1038 ))
1039 }
1040
1041 Self::ReturnTypeMismatch {
1042 source_return,
1043 target_return,
1044 nested_reason,
1045 } => {
1046 let mut diag = PendingDiagnostic::error(
1047 codes::TYPE_NOT_ASSIGNABLE,
1048 vec![source.into(), target.into()],
1049 );
1050
1051 let return_diag = PendingDiagnostic::error(
1053 codes::TYPE_NOT_ASSIGNABLE,
1054 vec![(*source_return).into(), (*target_return).into()],
1055 );
1056 diag = diag.with_related(return_diag);
1057
1058 if let Some(nested) = nested_reason {
1059 let nested_diag = nested.to_diagnostic(*source_return, *target_return);
1060 diag = diag.with_related(nested_diag);
1061 }
1062
1063 diag
1064 }
1065
1066 Self::ParameterTypeMismatch {
1067 param_index: _,
1068 source_param,
1069 target_param,
1070 } => PendingDiagnostic::error(
1071 codes::TYPE_NOT_ASSIGNABLE,
1072 vec![source.into(), target.into()],
1073 )
1074 .with_related(PendingDiagnostic::error(
1075 codes::TYPE_NOT_ASSIGNABLE,
1076 vec![(*source_param).into(), (*target_param).into()],
1077 )),
1078
1079 Self::TooManyParameters {
1080 source_count,
1081 target_count,
1082 } => PendingDiagnostic::error(
1083 codes::ARG_COUNT_MISMATCH,
1084 vec![(*target_count).into(), (*source_count).into()],
1085 ),
1086
1087 Self::TupleElementMismatch {
1088 source_count,
1089 target_count,
1090 } => PendingDiagnostic::error(
1091 codes::TYPE_NOT_ASSIGNABLE,
1092 vec![source.into(), target.into()],
1093 )
1094 .with_related(PendingDiagnostic::error(
1095 codes::ARG_COUNT_MISMATCH,
1096 vec![(*target_count).into(), (*source_count).into()],
1097 )),
1098
1099 Self::TupleElementTypeMismatch {
1100 index: _,
1101 source_element,
1102 target_element,
1103 }
1104 | Self::ArrayElementMismatch {
1105 source_element,
1106 target_element,
1107 } => PendingDiagnostic::error(
1108 codes::TYPE_NOT_ASSIGNABLE,
1109 vec![source.into(), target.into()],
1110 )
1111 .with_related(PendingDiagnostic::error(
1112 codes::TYPE_NOT_ASSIGNABLE,
1113 vec![(*source_element).into(), (*target_element).into()],
1114 )),
1115
1116 Self::IndexSignatureMismatch {
1117 index_kind: _,
1118 source_value_type,
1119 target_value_type,
1120 } => PendingDiagnostic::error(
1121 codes::TYPE_NOT_ASSIGNABLE,
1122 vec![source.into(), target.into()],
1123 )
1124 .with_related(PendingDiagnostic::error(
1125 codes::TYPE_NOT_ASSIGNABLE,
1126 vec![(*source_value_type).into(), (*target_value_type).into()],
1127 )),
1128
1129 Self::NoUnionMemberMatches {
1130 source_type,
1131 target_union_members,
1132 } => {
1133 const UNION_MEMBER_DIAGNOSTIC_LIMIT: usize = 3;
1134 let mut diag = PendingDiagnostic::error(
1135 codes::TYPE_NOT_ASSIGNABLE,
1136 vec![(*source_type).into(), target.into()],
1137 );
1138 for member in target_union_members
1139 .iter()
1140 .take(UNION_MEMBER_DIAGNOSTIC_LIMIT)
1141 {
1142 diag.related.push(PendingDiagnostic::error(
1143 codes::TYPE_NOT_ASSIGNABLE,
1144 vec![(*source_type).into(), (*member).into()],
1145 ));
1146 }
1147 diag
1148 }
1149
1150 Self::NoIntersectionMemberMatches {
1151 source_type,
1152 target_type,
1153 }
1154 | Self::TypeMismatch {
1155 source_type,
1156 target_type,
1157 }
1158 | Self::IntrinsicTypeMismatch {
1159 source_type,
1160 target_type,
1161 }
1162 | Self::LiteralTypeMismatch {
1163 source_type,
1164 target_type,
1165 }
1166 | Self::ErrorType {
1167 source_type,
1168 target_type,
1169 } => PendingDiagnostic::error(
1170 codes::TYPE_NOT_ASSIGNABLE,
1171 vec![(*source_type).into(), (*target_type).into()],
1172 ),
1173
1174 Self::NoCommonProperties {
1175 source_type,
1176 target_type,
1177 } => PendingDiagnostic::error(
1178 codes::NO_COMMON_PROPERTIES,
1179 vec![(*source_type).into(), (*target_type).into()],
1180 ),
1181
1182 Self::RecursionLimitExceeded => {
1183 PendingDiagnostic::error(
1185 codes::TYPE_NOT_ASSIGNABLE,
1186 vec![source.into(), target.into()],
1187 )
1188 }
1189
1190 Self::ParameterCountMismatch {
1191 source_count: _,
1192 target_count: _,
1193 } => {
1194 PendingDiagnostic::error(
1196 codes::TYPE_NOT_ASSIGNABLE,
1197 vec![source.into(), target.into()],
1198 )
1199 }
1200
1201 Self::ExcessProperty {
1202 property_name,
1203 target_type,
1204 } => {
1205 PendingDiagnostic::error(
1207 codes::EXCESS_PROPERTY,
1208 vec![(*property_name).into(), (*target_type).into()],
1209 )
1210 }
1211 }
1212 }
1213}
1214
1215impl PendingDiagnosticBuilder {
1216 pub fn argument_not_assignable(arg_type: TypeId, param_type: TypeId) -> PendingDiagnostic {
1218 PendingDiagnostic::error(
1219 codes::ARG_NOT_ASSIGNABLE,
1220 vec![arg_type.into(), param_type.into()],
1221 )
1222 }
1223
1224 pub fn argument_count_mismatch(expected: usize, got: usize) -> PendingDiagnostic {
1226 PendingDiagnostic::error(codes::ARG_COUNT_MISMATCH, vec![expected.into(), got.into()])
1227 }
1228}
1229
1230#[cfg(test)]
1231impl PendingDiagnosticBuilder {
1232 pub fn type_not_assignable(source: TypeId, target: TypeId) -> PendingDiagnostic {
1234 PendingDiagnostic::error(
1235 codes::TYPE_NOT_ASSIGNABLE,
1236 vec![source.into(), target.into()],
1237 )
1238 }
1239
1240 pub fn property_missing(prop_name: &str, source: TypeId, target: TypeId) -> PendingDiagnostic {
1242 PendingDiagnostic::error(
1243 codes::PROPERTY_MISSING,
1244 vec![prop_name.into(), source.into(), target.into()],
1245 )
1246 }
1247
1248 pub fn property_not_exist(prop_name: &str, type_id: TypeId) -> PendingDiagnostic {
1250 PendingDiagnostic::error(
1251 codes::PROPERTY_NOT_EXIST,
1252 vec![prop_name.into(), type_id.into()],
1253 )
1254 }
1255
1256 pub fn cannot_find_name(name: &str) -> PendingDiagnostic {
1258 let code = cannot_find_name_code(name);
1259 PendingDiagnostic::error(code, vec![name.into()])
1260 }
1261
1262 pub fn not_callable(type_id: TypeId) -> PendingDiagnostic {
1264 PendingDiagnostic::error(codes::NOT_CALLABLE, vec![type_id.into()])
1265 }
1266
1267 pub fn this_type_mismatch(expected_this: TypeId, actual_this: TypeId) -> PendingDiagnostic {
1268 PendingDiagnostic::error(
1269 codes::THIS_TYPE_MISMATCH,
1270 vec![actual_this.into(), expected_this.into()],
1271 )
1272 }
1273
1274 pub fn readonly_property(prop_name: &str) -> PendingDiagnostic {
1276 PendingDiagnostic::error(codes::READONLY_PROPERTY, vec![prop_name.into()])
1277 }
1278
1279 pub fn excess_property(prop_name: &str, target: TypeId) -> PendingDiagnostic {
1281 PendingDiagnostic::error(
1282 codes::EXCESS_PROPERTY,
1283 vec![prop_name.into(), target.into()],
1284 )
1285 }
1286}
1287
1288pub struct SpannedDiagnosticBuilder<'a> {
1297 builder: DiagnosticBuilder<'a>,
1298 file: Arc<str>,
1299}
1300
1301impl<'a> SpannedDiagnosticBuilder<'a> {
1302 pub fn new(interner: &'a dyn TypeDatabase, file: impl Into<Arc<str>>) -> Self {
1303 SpannedDiagnosticBuilder {
1304 builder: DiagnosticBuilder::new(interner),
1305 file: file.into(),
1306 }
1307 }
1308
1309 pub fn with_symbols(
1314 interner: &'a dyn TypeDatabase,
1315 symbol_arena: &'a tsz_binder::SymbolArena,
1316 file: impl Into<Arc<str>>,
1317 ) -> Self {
1318 SpannedDiagnosticBuilder {
1319 builder: DiagnosticBuilder::with_symbols(interner, symbol_arena),
1320 file: file.into(),
1321 }
1322 }
1323
1324 pub fn with_def_store(mut self, def_store: &'a DefinitionStore) -> Self {
1329 self.builder = self.builder.with_def_store(def_store);
1330 self
1331 }
1332
1333 pub fn span(&self, start: u32, length: u32) -> SourceSpan {
1335 SourceSpan::new(std::sync::Arc::clone(&self.file), start, length)
1336 }
1337
1338 pub fn type_not_assignable(
1340 &mut self,
1341 source: TypeId,
1342 target: TypeId,
1343 start: u32,
1344 length: u32,
1345 ) -> TypeDiagnostic {
1346 self.builder
1347 .type_not_assignable(source, target)
1348 .with_span(self.span(start, length))
1349 }
1350
1351 pub fn property_missing(
1353 &mut self,
1354 prop_name: &str,
1355 source: TypeId,
1356 target: TypeId,
1357 start: u32,
1358 length: u32,
1359 ) -> TypeDiagnostic {
1360 self.builder
1361 .property_missing(prop_name, source, target)
1362 .with_span(self.span(start, length))
1363 }
1364
1365 pub fn property_not_exist(
1367 &mut self,
1368 prop_name: &str,
1369 type_id: TypeId,
1370 start: u32,
1371 length: u32,
1372 ) -> TypeDiagnostic {
1373 self.builder
1374 .property_not_exist(prop_name, type_id)
1375 .with_span(self.span(start, length))
1376 }
1377
1378 pub fn property_not_exist_did_you_mean(
1380 &mut self,
1381 prop_name: &str,
1382 type_id: TypeId,
1383 suggestion: &str,
1384 start: u32,
1385 length: u32,
1386 ) -> TypeDiagnostic {
1387 self.builder
1388 .property_not_exist_did_you_mean(prop_name, type_id, suggestion)
1389 .with_span(self.span(start, length))
1390 }
1391
1392 pub fn argument_not_assignable(
1394 &mut self,
1395 arg_type: TypeId,
1396 param_type: TypeId,
1397 start: u32,
1398 length: u32,
1399 ) -> TypeDiagnostic {
1400 self.builder
1401 .argument_not_assignable(arg_type, param_type)
1402 .with_span(self.span(start, length))
1403 }
1404
1405 pub fn cannot_find_name(&mut self, name: &str, start: u32, length: u32) -> TypeDiagnostic {
1407 self.builder
1408 .cannot_find_name(name)
1409 .with_span(self.span(start, length))
1410 }
1411
1412 pub fn argument_count_mismatch(
1414 &mut self,
1415 expected: usize,
1416 got: usize,
1417 start: u32,
1418 length: u32,
1419 ) -> TypeDiagnostic {
1420 self.builder
1421 .argument_count_mismatch(expected, got)
1422 .with_span(self.span(start, length))
1423 }
1424
1425 pub fn not_callable(&mut self, type_id: TypeId, start: u32, length: u32) -> TypeDiagnostic {
1427 self.builder
1428 .not_callable(type_id)
1429 .with_span(self.span(start, length))
1430 }
1431
1432 pub fn this_type_mismatch(
1433 &mut self,
1434 expected_this: TypeId,
1435 actual_this: TypeId,
1436 start: u32,
1437 length: u32,
1438 ) -> TypeDiagnostic {
1439 self.builder
1440 .this_type_mismatch(expected_this, actual_this)
1441 .with_span(self.span(start, length))
1442 }
1443
1444 pub fn excess_property(
1446 &mut self,
1447 prop_name: &str,
1448 target: TypeId,
1449 start: u32,
1450 length: u32,
1451 ) -> TypeDiagnostic {
1452 self.builder
1453 .excess_property(prop_name, target)
1454 .with_span(self.span(start, length))
1455 }
1456
1457 pub fn readonly_property(
1459 &mut self,
1460 prop_name: &str,
1461 start: u32,
1462 length: u32,
1463 ) -> TypeDiagnostic {
1464 self.builder
1465 .readonly_property(prop_name)
1466 .with_span(self.span(start, length))
1467 }
1468
1469 pub fn add_related(
1471 &self,
1472 diag: TypeDiagnostic,
1473 message: impl Into<String>,
1474 start: u32,
1475 length: u32,
1476 ) -> TypeDiagnostic {
1477 diag.with_related(self.span(start, length), message)
1478 }
1479}
1480
1481impl TypeDiagnostic {
1490 pub fn to_checker_diagnostic(&self, default_file: &str) -> tsz_common::diagnostics::Diagnostic {
1494 use tsz_common::diagnostics::{
1495 Diagnostic, DiagnosticCategory, DiagnosticRelatedInformation,
1496 };
1497
1498 let (file, start, length) = if let Some(ref span) = self.span {
1499 (span.file.to_string(), span.start, span.length)
1500 } else {
1501 (default_file.to_string(), 0, 0)
1502 };
1503
1504 let category = match self.severity {
1505 DiagnosticSeverity::Error => DiagnosticCategory::Error,
1506 DiagnosticSeverity::Warning => DiagnosticCategory::Warning,
1507 DiagnosticSeverity::Suggestion => DiagnosticCategory::Suggestion,
1508 DiagnosticSeverity::Message => DiagnosticCategory::Message,
1509 };
1510
1511 let related_information: Vec<DiagnosticRelatedInformation> = self
1512 .related
1513 .iter()
1514 .map(|rel| DiagnosticRelatedInformation {
1515 file: rel.span.file.to_string(),
1516 start: rel.span.start,
1517 length: rel.span.length,
1518 message_text: rel.message.clone(),
1519 category: DiagnosticCategory::Message,
1520 code: 0,
1521 })
1522 .collect();
1523
1524 Diagnostic {
1525 file,
1526 start,
1527 length,
1528 message_text: self.message.clone(),
1529 category,
1530 code: self.code,
1531 related_information,
1532 }
1533 }
1534}
1535
1536#[derive(Clone)]
1545pub struct SourceLocation {
1546 pub file: Arc<str>,
1548 pub start: u32,
1550 pub end: u32,
1552}
1553
1554impl SourceLocation {
1555 pub fn new(file: impl Into<Arc<str>>, start: u32, end: u32) -> Self {
1556 Self {
1557 file: file.into(),
1558 start,
1559 end,
1560 }
1561 }
1562
1563 pub const fn length(&self) -> u32 {
1565 self.end.saturating_sub(self.start)
1566 }
1567
1568 pub fn to_span(&self) -> SourceSpan {
1570 SourceSpan::new(std::sync::Arc::clone(&self.file), self.start, self.length())
1571 }
1572}
1573
1574pub struct DiagnosticCollector<'a> {
1576 interner: &'a dyn TypeDatabase,
1577 file: Arc<str>,
1578 diagnostics: Vec<TypeDiagnostic>,
1579}
1580
1581impl<'a> DiagnosticCollector<'a> {
1582 pub fn new(interner: &'a dyn TypeDatabase, file: impl Into<Arc<str>>) -> Self {
1583 DiagnosticCollector {
1584 interner,
1585 file: file.into(),
1586 diagnostics: Vec::new(),
1587 }
1588 }
1589
1590 pub fn diagnostics(&self) -> &[TypeDiagnostic] {
1592 &self.diagnostics
1593 }
1594
1595 pub fn take_diagnostics(&mut self) -> Vec<TypeDiagnostic> {
1597 std::mem::take(&mut self.diagnostics)
1598 }
1599
1600 pub fn type_not_assignable(&mut self, source: TypeId, target: TypeId, loc: &SourceLocation) {
1602 let mut builder = DiagnosticBuilder::new(self.interner);
1603 let diag = builder
1604 .type_not_assignable(source, target)
1605 .with_span(loc.to_span());
1606 self.diagnostics.push(diag);
1607 }
1608
1609 pub fn property_missing(
1611 &mut self,
1612 prop_name: &str,
1613 source: TypeId,
1614 target: TypeId,
1615 loc: &SourceLocation,
1616 ) {
1617 let mut builder = DiagnosticBuilder::new(self.interner);
1618 let diag = builder
1619 .property_missing(prop_name, source, target)
1620 .with_span(loc.to_span());
1621 self.diagnostics.push(diag);
1622 }
1623
1624 pub fn property_not_exist(&mut self, prop_name: &str, type_id: TypeId, loc: &SourceLocation) {
1626 let mut builder = DiagnosticBuilder::new(self.interner);
1627 let diag = builder
1628 .property_not_exist(prop_name, type_id)
1629 .with_span(loc.to_span());
1630 self.diagnostics.push(diag);
1631 }
1632
1633 pub fn argument_not_assignable(
1635 &mut self,
1636 arg_type: TypeId,
1637 param_type: TypeId,
1638 loc: &SourceLocation,
1639 ) {
1640 let mut builder = DiagnosticBuilder::new(self.interner);
1641 let diag = builder
1642 .argument_not_assignable(arg_type, param_type)
1643 .with_span(loc.to_span());
1644 self.diagnostics.push(diag);
1645 }
1646
1647 pub fn cannot_find_name(&mut self, name: &str, loc: &SourceLocation) {
1649 let mut builder = DiagnosticBuilder::new(self.interner);
1650 let diag = builder.cannot_find_name(name).with_span(loc.to_span());
1651 self.diagnostics.push(diag);
1652 }
1653
1654 pub fn argument_count_mismatch(&mut self, expected: usize, got: usize, loc: &SourceLocation) {
1656 let mut builder = DiagnosticBuilder::new(self.interner);
1657 let diag = builder
1658 .argument_count_mismatch(expected, got)
1659 .with_span(loc.to_span());
1660 self.diagnostics.push(diag);
1661 }
1662
1663 pub fn to_checker_diagnostics(&self) -> Vec<tsz_common::diagnostics::Diagnostic> {
1665 self.diagnostics
1666 .iter()
1667 .map(|d| d.to_checker_diagnostic(&self.file))
1668 .collect()
1669 }
1670}
1671
1672#[cfg(test)]
1673use crate::types::*;
1674
1675#[cfg(test)]
1676#[path = "../tests/diagnostics_tests.rs"]
1677mod tests;