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 MissingIndexSignature { index_kind: &'static str },
284 NoUnionMemberMatches {
286 source_type: TypeId,
287 target_union_members: Vec<TypeId>,
288 },
289 NoIntersectionMemberMatches {
291 source_type: TypeId,
292 target_type: TypeId,
293 },
294 NoCommonProperties {
296 source_type: TypeId,
297 target_type: TypeId,
298 },
299 TypeMismatch {
301 source_type: TypeId,
302 target_type: TypeId,
303 },
304 IntrinsicTypeMismatch {
306 source_type: TypeId,
307 target_type: TypeId,
308 },
309 LiteralTypeMismatch {
311 source_type: TypeId,
312 target_type: TypeId,
313 },
314 ErrorType {
316 source_type: TypeId,
317 target_type: TypeId,
318 },
319 RecursionLimitExceeded,
321 ParameterCountMismatch {
323 source_count: usize,
324 target_count: usize,
325 },
326 ExcessProperty {
328 property_name: Atom,
329 target_type: TypeId,
330 },
331}
332
333#[derive(Clone, Copy, Debug, PartialEq, Eq)]
335pub enum DiagnosticSeverity {
336 Error,
337 Warning,
338 Suggestion,
339 Message,
340}
341
342#[derive(Clone, Debug)]
351pub enum DiagnosticArg {
352 Type(TypeId),
354 Symbol(SymbolId),
356 Atom(Atom),
358 String(Arc<str>),
360 Number(usize),
362}
363
364macro_rules! impl_from_diagnostic_arg {
365 ($($source:ty => $variant:ident),* $(,)?) => {
366 $(impl From<$source> for DiagnosticArg {
367 fn from(v: $source) -> Self { Self::$variant(v) }
368 })*
369 };
370}
371
372impl_from_diagnostic_arg! {
373 TypeId => Type,
374 SymbolId => Symbol,
375 Atom => Atom,
376 usize => Number,
377}
378
379impl From<&str> for DiagnosticArg {
380 fn from(s: &str) -> Self {
381 Self::String(s.into())
382 }
383}
384
385impl From<String> for DiagnosticArg {
386 fn from(s: String) -> Self {
387 Self::String(s.into())
388 }
389}
390
391#[derive(Clone, Debug)]
396pub struct PendingDiagnostic {
397 pub code: u32,
399 pub args: Vec<DiagnosticArg>,
401 pub span: Option<SourceSpan>,
403 pub severity: DiagnosticSeverity,
405 pub related: Vec<Self>,
407}
408
409impl PendingDiagnostic {
410 pub const fn error(code: u32, args: Vec<DiagnosticArg>) -> Self {
412 Self {
413 code,
414 args,
415 span: None,
416 severity: DiagnosticSeverity::Error,
417 related: Vec::new(),
418 }
419 }
420
421 pub fn with_span(mut self, span: SourceSpan) -> Self {
423 self.span = Some(span);
424 self
425 }
426
427 pub fn with_related(mut self, related: Self) -> Self {
429 self.related.push(related);
430 self
431 }
432}
433
434#[derive(Clone, Debug, PartialEq, Eq)]
436pub struct SourceSpan {
437 pub start: u32,
439 pub length: u32,
441 pub file: Arc<str>,
443}
444
445impl SourceSpan {
446 pub fn new(file: impl Into<Arc<str>>, start: u32, length: u32) -> Self {
447 Self {
448 start,
449 length,
450 file: file.into(),
451 }
452 }
453}
454
455#[derive(Clone, Debug)]
457pub struct RelatedInformation {
458 pub span: SourceSpan,
459 pub message: String,
460}
461
462#[derive(Clone, Debug)]
464pub struct TypeDiagnostic {
465 pub message: String,
467 pub code: u32,
469 pub severity: DiagnosticSeverity,
471 pub span: Option<SourceSpan>,
473 pub related: Vec<RelatedInformation>,
475}
476
477impl TypeDiagnostic {
478 pub fn error(message: impl Into<String>, code: u32) -> Self {
480 Self {
481 message: message.into(),
482 code,
483 severity: DiagnosticSeverity::Error,
484 span: None,
485 related: Vec::new(),
486 }
487 }
488
489 pub fn with_span(mut self, span: SourceSpan) -> Self {
491 self.span = Some(span);
492 self
493 }
494
495 pub fn with_related(mut self, span: SourceSpan, message: impl Into<String>) -> Self {
497 self.related.push(RelatedInformation {
498 span,
499 message: message.into(),
500 });
501 self
502 }
503}
504
505pub mod codes {
515 use tsz_common::diagnostics::diagnostic_codes as dc;
516
517 pub use dc::ARGUMENT_OF_TYPE_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE as ARG_NOT_ASSIGNABLE;
519 pub use dc::CANNOT_ASSIGN_TO_BECAUSE_IT_IS_A_READ_ONLY_PROPERTY as READONLY_PROPERTY;
520 pub use dc::OBJECT_LITERAL_MAY_ONLY_SPECIFY_KNOWN_PROPERTIES_AND_DOES_NOT_EXIST_IN_TYPE as EXCESS_PROPERTY;
521 pub use dc::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE as PROPERTY_MISSING;
522 pub use dc::PROPERTY_IS_PRIVATE_AND_ONLY_ACCESSIBLE_WITHIN_CLASS as PROPERTY_VISIBILITY_MISMATCH;
523 pub use dc::PROPERTY_IS_PROTECTED_AND_ONLY_ACCESSIBLE_THROUGH_AN_INSTANCE_OF_CLASS_THIS_IS_A as PROPERTY_NOMINAL_MISMATCH;
524 pub use dc::TYPE_HAS_NO_PROPERTIES_IN_COMMON_WITH_TYPE as NO_COMMON_PROPERTIES;
525 pub use dc::TYPE_IS_MISSING_THE_FOLLOWING_PROPERTIES_FROM_TYPE as MISSING_PROPERTIES;
526 pub use dc::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE as TYPE_NOT_ASSIGNABLE;
527
528 pub use dc::INDEX_SIGNATURE_FOR_TYPE_IS_MISSING_IN_TYPE as MISSING_INDEX_SIGNATURE;
529 pub use dc::TYPES_OF_PROPERTY_ARE_INCOMPATIBLE as PROPERTY_TYPE_MISMATCH;
530
531 pub use dc::CANNOT_FIND_NAME;
533 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB as CANNOT_FIND_NAME_TARGET_LIB;
534 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB_2 as CANNOT_FIND_NAME_DOM;
535 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;
536 pub use dc::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_NODE_TRY_NPM_I_SAVE as CANNOT_FIND_NAME_NODE;
537 pub use dc::EXPECTED_ARGUMENTS_BUT_GOT as ARG_COUNT_MISMATCH;
538 pub use dc::PROPERTY_DOES_NOT_EXIST_ON_TYPE as PROPERTY_NOT_EXIST;
539 pub use dc::PROPERTY_DOES_NOT_EXIST_ON_TYPE_DID_YOU_MEAN as PROPERTY_NOT_EXIST_DID_YOU_MEAN;
540 pub use dc::THE_THIS_CONTEXT_OF_TYPE_IS_NOT_ASSIGNABLE_TO_METHODS_THIS_OF_TYPE as THIS_TYPE_MISMATCH;
541 pub use dc::THIS_EXPRESSION_IS_NOT_CALLABLE as NOT_CALLABLE;
542
543 pub use dc::FUNCTION_EXPRESSION_WHICH_LACKS_RETURN_TYPE_ANNOTATION_IMPLICITLY_HAS_AN_RETURN as IMPLICIT_ANY_RETURN_FUNCTION_EXPRESSION;
547 pub use dc::MEMBER_IMPLICITLY_HAS_AN_TYPE as IMPLICIT_ANY_MEMBER;
548 pub use dc::PARAMETER_IMPLICITLY_HAS_AN_TYPE as IMPLICIT_ANY_PARAMETER;
549 pub use dc::VARIABLE_IMPLICITLY_HAS_AN_TYPE as IMPLICIT_ANY;
550 pub use dc::WHICH_LACKS_RETURN_TYPE_ANNOTATION_IMPLICITLY_HAS_AN_RETURN_TYPE as IMPLICIT_ANY_RETURN;
551}
552
553fn cannot_find_name_code(name: &str) -> u32 {
562 match name {
563 "require" | "exports" | "module" | "process" | "Buffer" | "__filename" | "__dirname" => {
565 codes::CANNOT_FIND_NAME_NODE
566 }
567 "describe" | "suite" | "it" | "test" => codes::CANNOT_FIND_NAME_TEST_RUNNER,
569 "Promise" | "Symbol" | "Map" | "Set" | "Reflect" | "Iterator" | "AsyncIterator"
571 | "SharedArrayBuffer" => codes::CANNOT_FIND_NAME_TARGET_LIB,
572 "document" | "console" => codes::CANNOT_FIND_NAME_DOM,
574 _ => codes::CANNOT_FIND_NAME,
576 }
577}
578
579pub fn get_message_template(code: u32) -> &'static str {
589 tsz_common::diagnostics::get_message_template(code).unwrap_or("Unknown diagnostic")
590}
591
592pub struct DiagnosticBuilder<'a> {
603 formatter: TypeFormatter<'a>,
604}
605
606impl<'a> DiagnosticBuilder<'a> {
607 pub fn new(interner: &'a dyn TypeDatabase) -> Self {
608 DiagnosticBuilder {
609 formatter: TypeFormatter::new(interner),
610 }
611 }
612
613 pub fn with_symbols(
618 interner: &'a dyn TypeDatabase,
619 symbol_arena: &'a tsz_binder::SymbolArena,
620 ) -> Self {
621 DiagnosticBuilder {
622 formatter: TypeFormatter::with_symbols(interner, symbol_arena),
623 }
624 }
625
626 pub fn with_def_store(mut self, def_store: &'a DefinitionStore) -> Self {
631 self.formatter = self.formatter.with_def_store(def_store);
632 self
633 }
634
635 pub fn type_not_assignable(&mut self, source: TypeId, target: TypeId) -> TypeDiagnostic {
637 let source_str = self.formatter.format(source);
638 let target_str = self.formatter.format(target);
639 TypeDiagnostic::error(
640 format!("Type '{source_str}' is not assignable to type '{target_str}'."),
641 codes::TYPE_NOT_ASSIGNABLE,
642 )
643 }
644
645 pub fn property_missing(
647 &mut self,
648 prop_name: &str,
649 source: TypeId,
650 target: TypeId,
651 ) -> TypeDiagnostic {
652 let source_str = self.formatter.format(source);
653 let target_str = self.formatter.format(target);
654 TypeDiagnostic::error(
655 format!(
656 "Property '{prop_name}' is missing in type '{source_str}' but required in type '{target_str}'."
657 ),
658 codes::PROPERTY_MISSING,
659 )
660 }
661
662 pub fn property_not_exist(&mut self, prop_name: &str, type_id: TypeId) -> TypeDiagnostic {
664 let type_str = self.formatter.format(type_id);
665 TypeDiagnostic::error(
666 format!("Property '{prop_name}' does not exist on type '{type_str}'."),
667 codes::PROPERTY_NOT_EXIST,
668 )
669 }
670
671 pub fn property_not_exist_did_you_mean(
673 &mut self,
674 prop_name: &str,
675 type_id: TypeId,
676 suggestion: &str,
677 ) -> TypeDiagnostic {
678 let type_str = self.formatter.format(type_id);
679 TypeDiagnostic::error(
680 format!(
681 "Property '{prop_name}' does not exist on type '{type_str}'. Did you mean '{suggestion}'?"
682 ),
683 codes::PROPERTY_NOT_EXIST_DID_YOU_MEAN,
684 )
685 }
686
687 pub fn argument_not_assignable(
689 &mut self,
690 arg_type: TypeId,
691 param_type: TypeId,
692 ) -> TypeDiagnostic {
693 let arg_str = self.formatter.format(arg_type);
694 let param_str = self.formatter.format(param_type);
695 TypeDiagnostic::error(
696 format!(
697 "Argument of type '{arg_str}' is not assignable to parameter of type '{param_str}'."
698 ),
699 codes::ARG_NOT_ASSIGNABLE,
700 )
701 }
702
703 pub fn cannot_find_name(&mut self, name: &str) -> TypeDiagnostic {
705 let is_obviously_invalid = name.len() == 1
710 && matches!(
711 name.chars().next(),
712 Some(
713 ',' | ';'
714 | ':'
715 | '('
716 | ')'
717 | '['
718 | ']'
719 | '{'
720 | '}'
721 | '+'
722 | '-'
723 | '*'
724 | '/'
725 | '%'
726 | '&'
727 | '|'
728 | '^'
729 | '!'
730 | '~'
731 | '<'
732 | '>'
733 | '='
734 | '.'
735 )
736 );
737
738 if is_obviously_invalid {
739 return TypeDiagnostic::error("", 0);
741 }
742
743 let code = cannot_find_name_code(name);
744 TypeDiagnostic::error(format!("Cannot find name '{name}'."), code)
745 }
746
747 pub fn not_callable(&mut self, type_id: TypeId) -> TypeDiagnostic {
749 let type_str = self.formatter.format(type_id);
750 TypeDiagnostic::error(
751 format!("Type '{type_str}' has no call signatures."),
752 codes::NOT_CALLABLE,
753 )
754 }
755
756 pub fn this_type_mismatch(
757 &mut self,
758 expected_this: TypeId,
759 actual_this: TypeId,
760 ) -> TypeDiagnostic {
761 let expected_str = self.formatter.format(expected_this);
762 let actual_str = self.formatter.format(actual_this);
763 TypeDiagnostic::error(
764 format!(
765 "The 'this' context of type '{actual_str}' is not assignable to method's 'this' of type '{expected_str}'."
766 ),
767 codes::THIS_TYPE_MISMATCH,
768 )
769 }
770
771 pub fn argument_count_mismatch(&mut self, expected: usize, got: usize) -> TypeDiagnostic {
773 TypeDiagnostic::error(
774 format!("Expected {expected} arguments, but got {got}."),
775 codes::ARG_COUNT_MISMATCH,
776 )
777 }
778
779 pub fn readonly_property(&mut self, prop_name: &str) -> TypeDiagnostic {
781 TypeDiagnostic::error(
782 format!("Cannot assign to '{prop_name}' because it is a read-only property."),
783 codes::READONLY_PROPERTY,
784 )
785 }
786
787 pub fn excess_property(&mut self, prop_name: &str, target: TypeId) -> TypeDiagnostic {
789 let target_str = self.formatter.format(target);
790 TypeDiagnostic::error(
791 format!(
792 "Object literal may only specify known properties, and '{prop_name}' does not exist in type '{target_str}'."
793 ),
794 codes::EXCESS_PROPERTY,
795 )
796 }
797
798 pub fn implicit_any_parameter(&mut self, param_name: &str) -> TypeDiagnostic {
807 TypeDiagnostic::error(
808 format!("Parameter '{param_name}' implicitly has an 'any' type."),
809 codes::IMPLICIT_ANY_PARAMETER,
810 )
811 }
812
813 pub fn implicit_any_parameter_with_type(
818 &mut self,
819 param_name: &str,
820 implicit_type: TypeId,
821 ) -> TypeDiagnostic {
822 let type_str = self.formatter.format(implicit_type);
823 TypeDiagnostic::error(
824 format!("Parameter '{param_name}' implicitly has an '{type_str}' type."),
825 codes::IMPLICIT_ANY_PARAMETER,
826 )
827 }
828
829 pub fn implicit_any_member(&mut self, member_name: &str) -> TypeDiagnostic {
834 TypeDiagnostic::error(
835 format!("Member '{member_name}' implicitly has an 'any' type."),
836 codes::IMPLICIT_ANY_MEMBER,
837 )
838 }
839
840 pub fn implicit_any_variable(&mut self, var_name: &str, var_type: TypeId) -> TypeDiagnostic {
845 let type_str = self.formatter.format(var_type);
846 TypeDiagnostic::error(
847 format!("Variable '{var_name}' implicitly has an '{type_str}' type."),
848 codes::IMPLICIT_ANY,
849 )
850 }
851
852 pub fn implicit_any_return(&mut self, func_name: &str, return_type: TypeId) -> TypeDiagnostic {
857 let type_str = self.formatter.format(return_type);
858 TypeDiagnostic::error(
859 format!(
860 "'{func_name}', which lacks return-type annotation, implicitly has an '{type_str}' return type."
861 ),
862 codes::IMPLICIT_ANY_RETURN,
863 )
864 }
865
866 pub fn implicit_any_return_function_expression(
871 &mut self,
872 return_type: TypeId,
873 ) -> TypeDiagnostic {
874 let type_str = self.formatter.format(return_type);
875 TypeDiagnostic::error(
876 format!(
877 "Function expression, which lacks return-type annotation, implicitly has an '{type_str}' return type."
878 ),
879 codes::IMPLICIT_ANY_RETURN_FUNCTION_EXPRESSION,
880 )
881 }
882}
883
884pub struct PendingDiagnosticBuilder;
893
894impl SubtypeFailureReason {
899 pub const fn diagnostic_code(&self) -> u32 {
905 match self {
906 Self::MissingProperty { .. } | Self::OptionalPropertyRequired { .. } => {
907 codes::PROPERTY_MISSING
908 }
909 Self::MissingProperties { .. } => codes::MISSING_PROPERTIES,
910 Self::PropertyTypeMismatch { .. } => codes::PROPERTY_TYPE_MISMATCH,
911 Self::ReadonlyPropertyMismatch { .. } => codes::READONLY_PROPERTY,
912 Self::PropertyVisibilityMismatch { .. } => codes::PROPERTY_VISIBILITY_MISMATCH,
913 Self::PropertyNominalMismatch { .. } => codes::PROPERTY_NOMINAL_MISMATCH,
914 Self::ReturnTypeMismatch { .. }
915 | Self::ParameterTypeMismatch { .. }
916 | Self::TupleElementMismatch { .. }
917 | Self::TupleElementTypeMismatch { .. }
918 | Self::ArrayElementMismatch { .. }
919 | Self::IndexSignatureMismatch { .. }
920 | Self::MissingIndexSignature { .. }
921 | Self::NoUnionMemberMatches { .. }
922 | Self::NoIntersectionMemberMatches { .. }
923 | Self::TypeMismatch { .. }
924 | Self::IntrinsicTypeMismatch { .. }
925 | Self::LiteralTypeMismatch { .. }
926 | Self::ErrorType { .. }
927 | Self::RecursionLimitExceeded
928 | Self::ParameterCountMismatch { .. } => codes::TYPE_NOT_ASSIGNABLE,
929 Self::TooManyParameters { .. } => codes::ARG_COUNT_MISMATCH,
930 Self::NoCommonProperties { .. } => codes::NO_COMMON_PROPERTIES,
931 Self::ExcessProperty { .. } => codes::EXCESS_PROPERTY,
932 }
933 }
934
935 pub fn to_diagnostic(&self, source: TypeId, target: TypeId) -> PendingDiagnostic {
940 match self {
941 Self::MissingProperty {
942 property_name,
943 source_type,
944 target_type,
945 } => PendingDiagnostic::error(
946 codes::PROPERTY_MISSING,
947 vec![
948 (*property_name).into(),
949 (*source_type).into(),
950 (*target_type).into(),
951 ],
952 ),
953
954 Self::MissingProperties {
955 property_names: _,
956 source_type,
957 target_type,
958 } => PendingDiagnostic::error(
959 codes::MISSING_PROPERTIES,
960 vec![(*source_type).into(), (*target_type).into()],
961 ),
962
963 Self::PropertyTypeMismatch {
964 property_name,
965 source_property_type,
966 target_property_type,
967 nested_reason,
968 } => {
969 let mut diag = PendingDiagnostic::error(
971 codes::TYPE_NOT_ASSIGNABLE,
972 vec![source.into(), target.into()],
973 );
974
975 let elaboration = PendingDiagnostic::error(
977 codes::PROPERTY_TYPE_MISMATCH,
978 vec![(*property_name).into()],
979 );
980 diag = diag.with_related(elaboration);
981
982 if let Some(nested) = nested_reason {
984 let nested_diag =
985 nested.to_diagnostic(*source_property_type, *target_property_type);
986 diag = diag.with_related(nested_diag);
987 }
988
989 diag
990 }
991
992 Self::OptionalPropertyRequired { property_name } => {
993 PendingDiagnostic::error(
995 codes::TYPE_NOT_ASSIGNABLE,
996 vec![source.into(), target.into()],
997 )
998 .with_related(PendingDiagnostic::error(
999 codes::PROPERTY_MISSING, vec![(*property_name).into(), source.into(), target.into()],
1001 ))
1002 }
1003
1004 Self::ReadonlyPropertyMismatch { property_name } => PendingDiagnostic::error(
1005 codes::TYPE_NOT_ASSIGNABLE,
1006 vec![source.into(), target.into()],
1007 )
1008 .with_related(PendingDiagnostic::error(
1009 codes::READONLY_PROPERTY,
1010 vec![(*property_name).into()],
1011 )),
1012
1013 Self::PropertyVisibilityMismatch {
1014 property_name,
1015 source_visibility,
1016 target_visibility,
1017 } => {
1018 PendingDiagnostic::error(
1020 codes::TYPE_NOT_ASSIGNABLE,
1021 vec![source.into(), target.into()],
1022 )
1023 .with_related(PendingDiagnostic::error(
1024 codes::PROPERTY_VISIBILITY_MISMATCH,
1025 vec![
1026 (*property_name).into(),
1027 format!("{source_visibility:?}").into(),
1028 format!("{target_visibility:?}").into(),
1029 ],
1030 ))
1031 }
1032
1033 Self::PropertyNominalMismatch { property_name } => {
1034 PendingDiagnostic::error(
1036 codes::TYPE_NOT_ASSIGNABLE,
1037 vec![source.into(), target.into()],
1038 )
1039 .with_related(PendingDiagnostic::error(
1040 codes::PROPERTY_NOMINAL_MISMATCH,
1041 vec![(*property_name).into()],
1042 ))
1043 }
1044
1045 Self::ReturnTypeMismatch {
1046 source_return,
1047 target_return,
1048 nested_reason,
1049 } => {
1050 let mut diag = PendingDiagnostic::error(
1051 codes::TYPE_NOT_ASSIGNABLE,
1052 vec![source.into(), target.into()],
1053 );
1054
1055 let return_diag = PendingDiagnostic::error(
1057 codes::TYPE_NOT_ASSIGNABLE,
1058 vec![(*source_return).into(), (*target_return).into()],
1059 );
1060 diag = diag.with_related(return_diag);
1061
1062 if let Some(nested) = nested_reason {
1063 let nested_diag = nested.to_diagnostic(*source_return, *target_return);
1064 diag = diag.with_related(nested_diag);
1065 }
1066
1067 diag
1068 }
1069
1070 Self::ParameterTypeMismatch {
1071 param_index: _,
1072 source_param,
1073 target_param,
1074 } => PendingDiagnostic::error(
1075 codes::TYPE_NOT_ASSIGNABLE,
1076 vec![source.into(), target.into()],
1077 )
1078 .with_related(PendingDiagnostic::error(
1079 codes::TYPE_NOT_ASSIGNABLE,
1080 vec![(*source_param).into(), (*target_param).into()],
1081 )),
1082
1083 Self::TooManyParameters {
1084 source_count,
1085 target_count,
1086 } => PendingDiagnostic::error(
1087 codes::ARG_COUNT_MISMATCH,
1088 vec![(*target_count).into(), (*source_count).into()],
1089 ),
1090
1091 Self::TupleElementMismatch {
1092 source_count,
1093 target_count,
1094 } => PendingDiagnostic::error(
1095 codes::TYPE_NOT_ASSIGNABLE,
1096 vec![source.into(), target.into()],
1097 )
1098 .with_related(PendingDiagnostic::error(
1099 codes::ARG_COUNT_MISMATCH,
1100 vec![(*target_count).into(), (*source_count).into()],
1101 )),
1102
1103 Self::TupleElementTypeMismatch {
1104 index: _,
1105 source_element,
1106 target_element,
1107 }
1108 | Self::ArrayElementMismatch {
1109 source_element,
1110 target_element,
1111 } => PendingDiagnostic::error(
1112 codes::TYPE_NOT_ASSIGNABLE,
1113 vec![source.into(), target.into()],
1114 )
1115 .with_related(PendingDiagnostic::error(
1116 codes::TYPE_NOT_ASSIGNABLE,
1117 vec![(*source_element).into(), (*target_element).into()],
1118 )),
1119
1120 Self::IndexSignatureMismatch {
1121 index_kind: _,
1122 source_value_type,
1123 target_value_type,
1124 } => PendingDiagnostic::error(
1125 codes::TYPE_NOT_ASSIGNABLE,
1126 vec![source.into(), target.into()],
1127 )
1128 .with_related(PendingDiagnostic::error(
1129 codes::TYPE_NOT_ASSIGNABLE,
1130 vec![(*source_value_type).into(), (*target_value_type).into()],
1131 )),
1132
1133 Self::MissingIndexSignature { index_kind } => PendingDiagnostic::error(
1134 codes::TYPE_NOT_ASSIGNABLE,
1135 vec![source.into(), target.into()],
1136 )
1137 .with_related(PendingDiagnostic::error(
1138 codes::MISSING_INDEX_SIGNATURE,
1139 vec![index_kind.to_string().into(), source.into()],
1140 )),
1141
1142 Self::NoUnionMemberMatches {
1143 source_type,
1144 target_union_members,
1145 } => {
1146 const UNION_MEMBER_DIAGNOSTIC_LIMIT: usize = 3;
1147 let mut diag = PendingDiagnostic::error(
1148 codes::TYPE_NOT_ASSIGNABLE,
1149 vec![(*source_type).into(), target.into()],
1150 );
1151 for member in target_union_members
1152 .iter()
1153 .take(UNION_MEMBER_DIAGNOSTIC_LIMIT)
1154 {
1155 diag.related.push(PendingDiagnostic::error(
1156 codes::TYPE_NOT_ASSIGNABLE,
1157 vec![(*source_type).into(), (*member).into()],
1158 ));
1159 }
1160 diag
1161 }
1162
1163 Self::NoIntersectionMemberMatches {
1164 source_type,
1165 target_type,
1166 }
1167 | Self::TypeMismatch {
1168 source_type,
1169 target_type,
1170 }
1171 | Self::IntrinsicTypeMismatch {
1172 source_type,
1173 target_type,
1174 }
1175 | Self::LiteralTypeMismatch {
1176 source_type,
1177 target_type,
1178 }
1179 | Self::ErrorType {
1180 source_type,
1181 target_type,
1182 } => PendingDiagnostic::error(
1183 codes::TYPE_NOT_ASSIGNABLE,
1184 vec![(*source_type).into(), (*target_type).into()],
1185 ),
1186
1187 Self::NoCommonProperties {
1188 source_type,
1189 target_type,
1190 } => PendingDiagnostic::error(
1191 codes::NO_COMMON_PROPERTIES,
1192 vec![(*source_type).into(), (*target_type).into()],
1193 ),
1194
1195 Self::RecursionLimitExceeded => {
1196 PendingDiagnostic::error(
1198 codes::TYPE_NOT_ASSIGNABLE,
1199 vec![source.into(), target.into()],
1200 )
1201 }
1202
1203 Self::ParameterCountMismatch {
1204 source_count: _,
1205 target_count: _,
1206 } => {
1207 PendingDiagnostic::error(
1209 codes::TYPE_NOT_ASSIGNABLE,
1210 vec![source.into(), target.into()],
1211 )
1212 }
1213
1214 Self::ExcessProperty {
1215 property_name,
1216 target_type,
1217 } => {
1218 PendingDiagnostic::error(
1220 codes::EXCESS_PROPERTY,
1221 vec![(*property_name).into(), (*target_type).into()],
1222 )
1223 }
1224 }
1225 }
1226}
1227
1228impl PendingDiagnosticBuilder {
1229 pub fn argument_not_assignable(arg_type: TypeId, param_type: TypeId) -> PendingDiagnostic {
1231 PendingDiagnostic::error(
1232 codes::ARG_NOT_ASSIGNABLE,
1233 vec![arg_type.into(), param_type.into()],
1234 )
1235 }
1236
1237 pub fn argument_count_mismatch(expected: usize, got: usize) -> PendingDiagnostic {
1239 PendingDiagnostic::error(codes::ARG_COUNT_MISMATCH, vec![expected.into(), got.into()])
1240 }
1241}
1242
1243#[cfg(test)]
1244impl PendingDiagnosticBuilder {
1245 pub fn type_not_assignable(source: TypeId, target: TypeId) -> PendingDiagnostic {
1247 PendingDiagnostic::error(
1248 codes::TYPE_NOT_ASSIGNABLE,
1249 vec![source.into(), target.into()],
1250 )
1251 }
1252
1253 pub fn property_missing(prop_name: &str, source: TypeId, target: TypeId) -> PendingDiagnostic {
1255 PendingDiagnostic::error(
1256 codes::PROPERTY_MISSING,
1257 vec![prop_name.into(), source.into(), target.into()],
1258 )
1259 }
1260
1261 pub fn property_not_exist(prop_name: &str, type_id: TypeId) -> PendingDiagnostic {
1263 PendingDiagnostic::error(
1264 codes::PROPERTY_NOT_EXIST,
1265 vec![prop_name.into(), type_id.into()],
1266 )
1267 }
1268
1269 pub fn cannot_find_name(name: &str) -> PendingDiagnostic {
1271 let code = cannot_find_name_code(name);
1272 PendingDiagnostic::error(code, vec![name.into()])
1273 }
1274
1275 pub fn not_callable(type_id: TypeId) -> PendingDiagnostic {
1277 PendingDiagnostic::error(codes::NOT_CALLABLE, vec![type_id.into()])
1278 }
1279
1280 pub fn this_type_mismatch(expected_this: TypeId, actual_this: TypeId) -> PendingDiagnostic {
1281 PendingDiagnostic::error(
1282 codes::THIS_TYPE_MISMATCH,
1283 vec![actual_this.into(), expected_this.into()],
1284 )
1285 }
1286
1287 pub fn readonly_property(prop_name: &str) -> PendingDiagnostic {
1289 PendingDiagnostic::error(codes::READONLY_PROPERTY, vec![prop_name.into()])
1290 }
1291
1292 pub fn excess_property(prop_name: &str, target: TypeId) -> PendingDiagnostic {
1294 PendingDiagnostic::error(
1295 codes::EXCESS_PROPERTY,
1296 vec![prop_name.into(), target.into()],
1297 )
1298 }
1299}
1300
1301pub struct SpannedDiagnosticBuilder<'a> {
1310 builder: DiagnosticBuilder<'a>,
1311 file: Arc<str>,
1312}
1313
1314impl<'a> SpannedDiagnosticBuilder<'a> {
1315 pub fn new(interner: &'a dyn TypeDatabase, file: impl Into<Arc<str>>) -> Self {
1316 SpannedDiagnosticBuilder {
1317 builder: DiagnosticBuilder::new(interner),
1318 file: file.into(),
1319 }
1320 }
1321
1322 pub fn with_symbols(
1327 interner: &'a dyn TypeDatabase,
1328 symbol_arena: &'a tsz_binder::SymbolArena,
1329 file: impl Into<Arc<str>>,
1330 ) -> Self {
1331 SpannedDiagnosticBuilder {
1332 builder: DiagnosticBuilder::with_symbols(interner, symbol_arena),
1333 file: file.into(),
1334 }
1335 }
1336
1337 pub fn with_def_store(mut self, def_store: &'a DefinitionStore) -> Self {
1342 self.builder = self.builder.with_def_store(def_store);
1343 self
1344 }
1345
1346 pub fn span(&self, start: u32, length: u32) -> SourceSpan {
1348 SourceSpan::new(std::sync::Arc::clone(&self.file), start, length)
1349 }
1350
1351 pub fn type_not_assignable(
1353 &mut self,
1354 source: TypeId,
1355 target: TypeId,
1356 start: u32,
1357 length: u32,
1358 ) -> TypeDiagnostic {
1359 self.builder
1360 .type_not_assignable(source, target)
1361 .with_span(self.span(start, length))
1362 }
1363
1364 pub fn property_missing(
1366 &mut self,
1367 prop_name: &str,
1368 source: TypeId,
1369 target: TypeId,
1370 start: u32,
1371 length: u32,
1372 ) -> TypeDiagnostic {
1373 self.builder
1374 .property_missing(prop_name, source, target)
1375 .with_span(self.span(start, length))
1376 }
1377
1378 pub fn property_not_exist(
1380 &mut self,
1381 prop_name: &str,
1382 type_id: TypeId,
1383 start: u32,
1384 length: u32,
1385 ) -> TypeDiagnostic {
1386 self.builder
1387 .property_not_exist(prop_name, type_id)
1388 .with_span(self.span(start, length))
1389 }
1390
1391 pub fn property_not_exist_did_you_mean(
1393 &mut self,
1394 prop_name: &str,
1395 type_id: TypeId,
1396 suggestion: &str,
1397 start: u32,
1398 length: u32,
1399 ) -> TypeDiagnostic {
1400 self.builder
1401 .property_not_exist_did_you_mean(prop_name, type_id, suggestion)
1402 .with_span(self.span(start, length))
1403 }
1404
1405 pub fn argument_not_assignable(
1407 &mut self,
1408 arg_type: TypeId,
1409 param_type: TypeId,
1410 start: u32,
1411 length: u32,
1412 ) -> TypeDiagnostic {
1413 self.builder
1414 .argument_not_assignable(arg_type, param_type)
1415 .with_span(self.span(start, length))
1416 }
1417
1418 pub fn cannot_find_name(&mut self, name: &str, start: u32, length: u32) -> TypeDiagnostic {
1420 self.builder
1421 .cannot_find_name(name)
1422 .with_span(self.span(start, length))
1423 }
1424
1425 pub fn argument_count_mismatch(
1427 &mut self,
1428 expected: usize,
1429 got: usize,
1430 start: u32,
1431 length: u32,
1432 ) -> TypeDiagnostic {
1433 self.builder
1434 .argument_count_mismatch(expected, got)
1435 .with_span(self.span(start, length))
1436 }
1437
1438 pub fn not_callable(&mut self, type_id: TypeId, start: u32, length: u32) -> TypeDiagnostic {
1440 self.builder
1441 .not_callable(type_id)
1442 .with_span(self.span(start, length))
1443 }
1444
1445 pub fn this_type_mismatch(
1446 &mut self,
1447 expected_this: TypeId,
1448 actual_this: TypeId,
1449 start: u32,
1450 length: u32,
1451 ) -> TypeDiagnostic {
1452 self.builder
1453 .this_type_mismatch(expected_this, actual_this)
1454 .with_span(self.span(start, length))
1455 }
1456
1457 pub fn excess_property(
1459 &mut self,
1460 prop_name: &str,
1461 target: TypeId,
1462 start: u32,
1463 length: u32,
1464 ) -> TypeDiagnostic {
1465 self.builder
1466 .excess_property(prop_name, target)
1467 .with_span(self.span(start, length))
1468 }
1469
1470 pub fn readonly_property(
1472 &mut self,
1473 prop_name: &str,
1474 start: u32,
1475 length: u32,
1476 ) -> TypeDiagnostic {
1477 self.builder
1478 .readonly_property(prop_name)
1479 .with_span(self.span(start, length))
1480 }
1481
1482 pub fn add_related(
1484 &self,
1485 diag: TypeDiagnostic,
1486 message: impl Into<String>,
1487 start: u32,
1488 length: u32,
1489 ) -> TypeDiagnostic {
1490 diag.with_related(self.span(start, length), message)
1491 }
1492}
1493
1494impl TypeDiagnostic {
1503 pub fn to_checker_diagnostic(&self, default_file: &str) -> tsz_common::diagnostics::Diagnostic {
1507 use tsz_common::diagnostics::{
1508 Diagnostic, DiagnosticCategory, DiagnosticRelatedInformation,
1509 };
1510
1511 let (file, start, length) = if let Some(ref span) = self.span {
1512 (span.file.to_string(), span.start, span.length)
1513 } else {
1514 (default_file.to_string(), 0, 0)
1515 };
1516
1517 let category = match self.severity {
1518 DiagnosticSeverity::Error => DiagnosticCategory::Error,
1519 DiagnosticSeverity::Warning => DiagnosticCategory::Warning,
1520 DiagnosticSeverity::Suggestion => DiagnosticCategory::Suggestion,
1521 DiagnosticSeverity::Message => DiagnosticCategory::Message,
1522 };
1523
1524 let related_information: Vec<DiagnosticRelatedInformation> = self
1525 .related
1526 .iter()
1527 .map(|rel| DiagnosticRelatedInformation {
1528 file: rel.span.file.to_string(),
1529 start: rel.span.start,
1530 length: rel.span.length,
1531 message_text: rel.message.clone(),
1532 category: DiagnosticCategory::Message,
1533 code: 0,
1534 })
1535 .collect();
1536
1537 Diagnostic {
1538 file,
1539 start,
1540 length,
1541 message_text: self.message.clone(),
1542 category,
1543 code: self.code,
1544 related_information,
1545 }
1546 }
1547}
1548
1549#[derive(Clone)]
1558pub struct SourceLocation {
1559 pub file: Arc<str>,
1561 pub start: u32,
1563 pub end: u32,
1565}
1566
1567impl SourceLocation {
1568 pub fn new(file: impl Into<Arc<str>>, start: u32, end: u32) -> Self {
1569 Self {
1570 file: file.into(),
1571 start,
1572 end,
1573 }
1574 }
1575
1576 pub const fn length(&self) -> u32 {
1578 self.end.saturating_sub(self.start)
1579 }
1580
1581 pub fn to_span(&self) -> SourceSpan {
1583 SourceSpan::new(std::sync::Arc::clone(&self.file), self.start, self.length())
1584 }
1585}
1586
1587pub struct DiagnosticCollector<'a> {
1589 interner: &'a dyn TypeDatabase,
1590 file: Arc<str>,
1591 diagnostics: Vec<TypeDiagnostic>,
1592}
1593
1594impl<'a> DiagnosticCollector<'a> {
1595 pub fn new(interner: &'a dyn TypeDatabase, file: impl Into<Arc<str>>) -> Self {
1596 DiagnosticCollector {
1597 interner,
1598 file: file.into(),
1599 diagnostics: Vec::new(),
1600 }
1601 }
1602
1603 pub fn diagnostics(&self) -> &[TypeDiagnostic] {
1605 &self.diagnostics
1606 }
1607
1608 pub fn take_diagnostics(&mut self) -> Vec<TypeDiagnostic> {
1610 std::mem::take(&mut self.diagnostics)
1611 }
1612
1613 pub fn type_not_assignable(&mut self, source: TypeId, target: TypeId, loc: &SourceLocation) {
1615 let mut builder = DiagnosticBuilder::new(self.interner);
1616 let diag = builder
1617 .type_not_assignable(source, target)
1618 .with_span(loc.to_span());
1619 self.diagnostics.push(diag);
1620 }
1621
1622 pub fn property_missing(
1624 &mut self,
1625 prop_name: &str,
1626 source: TypeId,
1627 target: TypeId,
1628 loc: &SourceLocation,
1629 ) {
1630 let mut builder = DiagnosticBuilder::new(self.interner);
1631 let diag = builder
1632 .property_missing(prop_name, source, target)
1633 .with_span(loc.to_span());
1634 self.diagnostics.push(diag);
1635 }
1636
1637 pub fn property_not_exist(&mut self, prop_name: &str, type_id: TypeId, loc: &SourceLocation) {
1639 let mut builder = DiagnosticBuilder::new(self.interner);
1640 let diag = builder
1641 .property_not_exist(prop_name, type_id)
1642 .with_span(loc.to_span());
1643 self.diagnostics.push(diag);
1644 }
1645
1646 pub fn argument_not_assignable(
1648 &mut self,
1649 arg_type: TypeId,
1650 param_type: TypeId,
1651 loc: &SourceLocation,
1652 ) {
1653 let mut builder = DiagnosticBuilder::new(self.interner);
1654 let diag = builder
1655 .argument_not_assignable(arg_type, param_type)
1656 .with_span(loc.to_span());
1657 self.diagnostics.push(diag);
1658 }
1659
1660 pub fn cannot_find_name(&mut self, name: &str, loc: &SourceLocation) {
1662 let mut builder = DiagnosticBuilder::new(self.interner);
1663 let diag = builder.cannot_find_name(name).with_span(loc.to_span());
1664 self.diagnostics.push(diag);
1665 }
1666
1667 pub fn argument_count_mismatch(&mut self, expected: usize, got: usize, loc: &SourceLocation) {
1669 let mut builder = DiagnosticBuilder::new(self.interner);
1670 let diag = builder
1671 .argument_count_mismatch(expected, got)
1672 .with_span(loc.to_span());
1673 self.diagnostics.push(diag);
1674 }
1675
1676 pub fn to_checker_diagnostics(&self) -> Vec<tsz_common::diagnostics::Diagnostic> {
1678 self.diagnostics
1679 .iter()
1680 .map(|d| d.to_checker_diagnostic(&self.file))
1681 .collect()
1682 }
1683}
1684
1685#[cfg(test)]
1686use crate::types::*;
1687
1688#[cfg(test)]
1689#[path = "../tests/diagnostics_tests.rs"]
1690mod tests;