1use crate::diagnostics::{
4 Diagnostic, DiagnosticCategory, DiagnosticRelatedInformation, diagnostic_codes,
5 diagnostic_messages, format_message,
6};
7use crate::state::{CheckerState, MemberAccessLevel};
8use tracing::{Level, trace};
9use tsz_parser::parser::NodeIndex;
10use tsz_solver::TypeId;
11
12impl<'a> CheckerState<'a> {
13 pub fn error_type_not_assignable_at(&mut self, source: TypeId, target: TypeId, idx: NodeIndex) {
19 let anchor_idx = self.assignment_diagnostic_anchor_idx(idx);
20 self.diagnose_assignment_failure_with_anchor(source, target, anchor_idx);
21 }
22
23 pub fn error_type_not_assignable_at_with_anchor(
25 &mut self,
26 source: TypeId,
27 target: TypeId,
28 anchor_idx: NodeIndex,
29 ) {
30 self.diagnose_assignment_failure_with_anchor(source, target, anchor_idx);
31 }
32 pub fn error_type_does_not_satisfy_the_expected_type(
33 &mut self,
34 source: TypeId,
35 target: TypeId,
36 idx: NodeIndex,
37 ) {
38 let anchor_idx = self.assignment_diagnostic_anchor_idx(idx);
39
40 if self.should_suppress_assignability_diagnostic(source, target) {
41 return;
42 }
43
44 let reason = self
45 .analyze_assignability_failure(source, target)
46 .failure_reason;
47
48 let mut base_diag = match reason {
49 Some(reason) => self.render_failure_reason(&reason, source, target, anchor_idx, 0),
50 None => {
51 let Some(loc) = self.get_source_location(anchor_idx) else {
52 return;
53 };
54 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
55 self.ctx.types,
56 &self.ctx.binder.symbols,
57 self.ctx.file_name.as_str(),
58 )
59 .with_def_store(&self.ctx.definition_store);
60 let diag = builder.type_not_assignable(source, target, loc.start, loc.length());
61 diag.to_checker_diagnostic(&self.ctx.file_name)
62 }
63 };
64
65 let src_str = self.format_type_for_assignability_message(source);
67 let tgt_str = self.format_type_for_assignability_message(target);
68 use tsz_common::diagnostics::data::diagnostic_codes;
69 use tsz_common::diagnostics::data::diagnostic_messages;
70 use tsz_common::diagnostics::format_message;
71
72 let msg = format_message(
73 diagnostic_messages::TYPE_DOES_NOT_SATISFY_THE_EXPECTED_TYPE,
74 &[&src_str, &tgt_str],
75 );
76
77 if base_diag.code != diagnostic_codes::TYPE_DOES_NOT_SATISFY_THE_EXPECTED_TYPE {
78 let mut new_related = vec![];
79
80 new_related.push(tsz_common::diagnostics::DiagnosticRelatedInformation {
81 category: tsz_common::diagnostics::DiagnosticCategory::Error,
82 code: base_diag.code,
83 file: base_diag.file.clone(),
84 start: base_diag.start,
85 length: base_diag.length,
86 message_text: base_diag.message_text.clone(),
87 });
88
89 new_related.extend(base_diag.related_information);
90
91 base_diag.code = diagnostic_codes::TYPE_DOES_NOT_SATISFY_THE_EXPECTED_TYPE;
92 base_diag.message_text = msg;
93 base_diag.related_information = new_related;
94 }
95
96 self.ctx.diagnostics.push(base_diag);
97 }
98
99 pub fn diagnose_assignment_failure(&mut self, source: TypeId, target: TypeId, idx: NodeIndex) {
101 let anchor_idx = self.assignment_diagnostic_anchor_idx(idx);
102 self.diagnose_assignment_failure_with_anchor(source, target, anchor_idx);
103 }
104
105 fn diagnose_assignment_failure_with_anchor(
108 &mut self,
109 source: TypeId,
110 target: TypeId,
111 anchor_idx: NodeIndex,
112 ) {
113 if self.should_suppress_assignability_diagnostic(source, target) {
115 if tracing::enabled!(Level::TRACE) {
116 trace!(
117 source = source.0,
118 target = target.0,
119 node_idx = anchor_idx.0,
120 file = %self.ctx.file_name,
121 "suppressing TS2322 for non-actionable source/target types"
122 );
123 }
124 return;
125 }
126
127 if let Some((source_level, target_level)) =
129 self.constructor_accessibility_mismatch(source, target, None)
130 {
131 self.error_constructor_accessibility_not_assignable(
132 source,
133 target,
134 source_level,
135 target_level,
136 anchor_idx,
137 );
138 return;
139 }
140
141 if let Some(detail) = self.private_brand_mismatch_error(source, target) {
143 let Some(loc) = self.get_node_span(anchor_idx) else {
144 return;
145 };
146
147 let source_type = self.format_type(source);
148 let target_type = self.format_type(target);
149 let message = format_message(
150 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
151 &[&source_type, &target_type],
152 );
153
154 let diag = Diagnostic::error(
155 self.ctx.file_name.clone(),
156 loc.0,
157 loc.1 - loc.0,
158 message,
159 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
160 )
161 .with_related(self.ctx.file_name.clone(), loc.0, loc.1 - loc.0, detail);
162
163 self.ctx.diagnostics.push(diag);
164 return;
165 }
166
167 let analysis = self.analyze_assignability_failure(source, target);
169 let reason = analysis.failure_reason;
170
171 if tracing::enabled!(Level::TRACE) {
172 let source_type = self.format_type(source);
173 let target_type = self.format_type(target);
174 let reason_ref = reason.as_ref();
175 trace!(
176 source = %source_type,
177 target = %target_type,
178 reason = ?reason_ref,
179 node_idx = anchor_idx.0,
180 file = %self.ctx.file_name,
181 "assignability failure diagnostics"
182 );
183 }
184
185 match reason {
186 Some(failure_reason) => {
187 let diag =
188 self.render_failure_reason(&failure_reason, source, target, anchor_idx, 0);
189 self.ctx.diagnostics.push(diag);
190 }
191 None => {
192 self.error_type_not_assignable_generic_at(source, target, anchor_idx);
194 }
195 }
196 }
197
198 pub(crate) fn error_type_not_assignable_generic_at(
200 &mut self,
201 source: TypeId,
202 target: TypeId,
203 idx: NodeIndex,
204 ) {
205 let anchor_idx = self.assignment_diagnostic_anchor_idx(idx);
206
207 if source == TypeId::ERROR
209 || target == TypeId::ERROR
210 || source == TypeId::ANY
211 || target == TypeId::ANY
212 || source == TypeId::UNKNOWN
213 || target == TypeId::UNKNOWN
214 {
215 return;
216 }
217
218 if let Some(loc) = self.get_source_location(anchor_idx) {
219 if self.has_more_specific_diagnostic_at_span(loc.start, loc.length()) {
222 return;
223 }
224
225 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
226 self.ctx.types,
227 &self.ctx.binder.symbols,
228 self.ctx.file_name.as_str(),
229 )
230 .with_def_store(&self.ctx.definition_store);
231 let diag = builder.type_not_assignable(source, target, loc.start, loc.length());
232 self.ctx
233 .diagnostics
234 .push(diag.to_checker_diagnostic(&self.ctx.file_name));
235 }
236 }
237
238 fn render_failure_reason(
240 &mut self,
241 reason: &tsz_solver::SubtypeFailureReason,
242 source: TypeId,
243 target: TypeId,
244 idx: NodeIndex,
245 depth: u32,
246 ) -> Diagnostic {
247 use tsz_solver::SubtypeFailureReason;
248
249 let (start, length) = self.get_node_span(idx).unwrap_or((0, 0));
250 let file_name = self.ctx.file_name.clone();
251
252 match reason {
253 SubtypeFailureReason::MissingProperty {
254 property_name,
255 source_type,
256 target_type,
257 } => {
258 if tsz_solver::is_primitive_type(self.ctx.types, *source_type) {
263 let src_str = self.format_type(*source_type);
264 let tgt_str = self.format_type(*target_type);
265 let message = format_message(
266 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
267 &[&src_str, &tgt_str],
268 );
269 return Diagnostic::error(
270 file_name,
271 start,
272 length,
273 message,
274 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
275 );
276 }
277
278 let tgt_str = self.format_type(*target_type);
285 if tgt_str == "Boolean"
286 || tgt_str == "Number"
287 || tgt_str == "String"
288 || tgt_str == "Object"
289 {
290 let src_str = self.format_type(*source_type);
291 let message = format_message(
292 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
293 &[&src_str, &tgt_str],
294 );
295 return Diagnostic::error(
296 file_name,
297 start,
298 length,
299 message,
300 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
301 );
302 }
303
304 let prop_name = self.ctx.types.resolve_atom_ref(*property_name);
308 if prop_name.starts_with("__private_brand") {
309 let src_str = self.format_type(*source_type);
310 let message = format_message(
311 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
312 &[&src_str, &tgt_str],
313 );
314 return Diagnostic::error(
315 file_name,
316 start,
317 length,
318 message,
319 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
320 );
321 }
322
323 let src_str = self.format_type(*source_type);
325 let message = format_message(
326 diagnostic_messages::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
327 &[&prop_name, &src_str, &tgt_str],
328 );
329 Diagnostic::error(
330 file_name,
331 start,
332 length,
333 message,
334 diagnostic_codes::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
335 )
336 }
337
338 SubtypeFailureReason::MissingProperties {
339 property_names,
340 source_type,
341 target_type,
342 } => {
343 if tsz_solver::is_primitive_type(self.ctx.types, *source_type) {
349 let src_str = self.format_type(*source_type);
350 let tgt_str = self.format_type(*target_type);
351 let message = format_message(
352 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
353 &[&src_str, &tgt_str],
354 );
355 return Diagnostic::error(
356 file_name,
357 start,
358 length,
359 message,
360 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
361 );
362 }
363
364 let tgt_str_check = self.format_type(*target_type);
369 if tgt_str_check == "Boolean"
370 || tgt_str_check == "Number"
371 || tgt_str_check == "String"
372 || tgt_str_check == "Object"
373 {
374 let src_str = self.format_type(*source_type);
375 let message = format_message(
376 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
377 &[&src_str, &tgt_str_check],
378 );
379 return Diagnostic::error(
380 file_name,
381 start,
382 length,
383 message,
384 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
385 );
386 }
387
388 let filtered_names: Vec<_> = property_names
391 .iter()
392 .filter(|name| {
393 !self
394 .ctx
395 .types
396 .resolve_atom_ref(**name)
397 .starts_with("__private_brand")
398 })
399 .collect();
400
401 let all_numeric = !filtered_names.is_empty()
404 && filtered_names.iter().all(|name| {
405 let s = self.ctx.types.resolve_atom_ref(**name);
406 s.parse::<usize>().is_ok()
407 });
408
409 if all_numeric {
410 let src_str = self.format_type(*source_type);
411 let tgt_str = self.format_type(*target_type);
412 let message = format_message(
413 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
414 &[&src_str, &tgt_str],
415 );
416 return Diagnostic::error(
417 file_name,
418 start,
419 length,
420 message,
421 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
422 );
423 }
424
425 if filtered_names.is_empty() {
427 let src_str = self.format_type(*source_type);
428 let tgt_str = self.format_type(*target_type);
429 let message = format_message(
430 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
431 &[&src_str, &tgt_str],
432 );
433 return Diagnostic::error(
434 file_name,
435 start,
436 length,
437 message,
438 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
439 );
440 }
441
442 let src_str = self.format_type(*source_type);
445 let tgt_str = self.format_type(*target_type);
446 let prop_list: Vec<String> = filtered_names
447 .iter()
448 .take(5)
449 .map(|name| self.ctx.types.resolve_atom_ref(**name).to_string())
450 .collect();
451 let props_joined = prop_list.join(", ");
452 if filtered_names.len() > 5 {
454 let more_count = (filtered_names.len() - 5).to_string();
455 let message = format_message(
456 diagnostic_messages::TYPE_IS_MISSING_THE_FOLLOWING_PROPERTIES_FROM_TYPE_AND_MORE,
457 &[&src_str, &tgt_str, &props_joined, &more_count],
458 );
459 Diagnostic::error(
460 file_name,
461 start,
462 length,
463 message,
464 diagnostic_codes::TYPE_IS_MISSING_THE_FOLLOWING_PROPERTIES_FROM_TYPE_AND_MORE,
465 )
466 } else {
467 let message = format_message(
468 diagnostic_messages::TYPE_IS_MISSING_THE_FOLLOWING_PROPERTIES_FROM_TYPE,
469 &[&src_str, &tgt_str, &props_joined],
470 );
471 Diagnostic::error(
472 file_name,
473 start,
474 length,
475 message,
476 diagnostic_codes::TYPE_IS_MISSING_THE_FOLLOWING_PROPERTIES_FROM_TYPE,
477 )
478 }
479 }
480
481 SubtypeFailureReason::PropertyTypeMismatch {
482 property_name,
483 source_property_type,
484 target_property_type,
485 nested_reason,
486 } => {
487 if depth == 0 {
488 let source_str = self.format_type_for_assignability_message(source);
489 let target_str = self.format_type_for_assignability_message(target);
490 let base = format_message(
491 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
492 &[&source_str, &target_str],
493 );
494 return Diagnostic::error(
496 file_name,
497 start,
498 length,
499 base,
500 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
501 );
502 }
503
504 let prop_name = self.ctx.types.resolve_atom_ref(*property_name);
505 let message = format_message(
506 diagnostic_messages::TYPES_OF_PROPERTY_ARE_INCOMPATIBLE,
507 &[&prop_name],
508 );
509 let mut diag =
510 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code());
511
512 if let Some(nested) = nested_reason
513 && depth < 5
514 {
515 let nested_diag = self.render_failure_reason(
516 nested,
517 *source_property_type,
518 *target_property_type,
519 idx,
520 depth + 1,
521 );
522 diag.related_information.push(DiagnosticRelatedInformation {
523 file: nested_diag.file,
524 start: nested_diag.start,
525 length: nested_diag.length,
526 message_text: nested_diag.message_text,
527 category: DiagnosticCategory::Message,
528 code: nested_diag.code,
529 });
530 }
531 diag
532 }
533
534 SubtypeFailureReason::OptionalPropertyRequired { property_name } => {
535 if depth == 0 {
537 let source_str = self.format_type_for_assignability_message(source);
538 let target_str = self.format_type_for_assignability_message(target);
539 let base = format_message(
540 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
541 &[&source_str, &target_str],
542 );
543 Diagnostic::error(
545 file_name,
546 start,
547 length,
548 base,
549 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
550 )
551 } else {
552 let prop_name = self.ctx.types.resolve_atom_ref(*property_name);
553 let source_str = self.format_type(source);
554 let target_str = self.format_type(target);
555 let message = format_message(
556 diagnostic_messages::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
557 &[&prop_name, &source_str, &target_str],
558 );
559 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
560 }
561 }
562
563 SubtypeFailureReason::ReadonlyPropertyMismatch { property_name } => {
564 let prop_name = self.ctx.types.resolve_atom_ref(*property_name);
565 let message = format_message(
566 diagnostic_messages::CANNOT_ASSIGN_TO_BECAUSE_IT_IS_A_READ_ONLY_PROPERTY,
567 &[&prop_name],
568 );
569 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
570 }
571
572 SubtypeFailureReason::PropertyVisibilityMismatch { .. } => {
573 let source_str = self.format_type_for_assignability_message(source);
574 let target_str = self.format_type_for_assignability_message(target);
575 let base = format_message(
576 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
577 &[&source_str, &target_str],
578 );
579 Diagnostic::error(
581 file_name,
582 start,
583 length,
584 base,
585 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
586 )
587 }
588
589 SubtypeFailureReason::PropertyNominalMismatch { .. } => {
590 let source_str = self.format_type_for_assignability_message(source);
591 let target_str = self.format_type_for_assignability_message(target);
592 let base = format_message(
593 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
594 &[&source_str, &target_str],
595 );
596 Diagnostic::error(
598 file_name,
599 start,
600 length,
601 base,
602 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
603 )
604 }
605
606 SubtypeFailureReason::ExcessProperty {
607 property_name,
608 target_type,
609 } => {
610 let prop_name = self.ctx.types.resolve_atom_ref(*property_name);
611 let target_str = self.format_type(*target_type);
612 let message = format_message(
613 diagnostic_messages::OBJECT_LITERAL_MAY_ONLY_SPECIFY_KNOWN_PROPERTIES_AND_DOES_NOT_EXIST_IN_TYPE,
614 &[&prop_name, &target_str],
615 );
616 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
617 }
618
619 SubtypeFailureReason::ReturnTypeMismatch {
620 source_return,
621 target_return,
622 nested_reason,
623 } => {
624 let source_str = self.format_type(*source_return);
625 let target_str = self.format_type(*target_return);
626 let message =
627 format!("Return type '{source_str}' is not assignable to '{target_str}'.");
628 let mut diag =
629 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code());
630
631 if let Some(nested) = nested_reason
632 && depth < 5
633 {
634 let nested_diag = self.render_failure_reason(
635 nested,
636 *source_return,
637 *target_return,
638 idx,
639 depth + 1,
640 );
641 diag.related_information.push(DiagnosticRelatedInformation {
642 file: nested_diag.file,
643 start: nested_diag.start,
644 length: nested_diag.length,
645 message_text: nested_diag.message_text,
646 category: DiagnosticCategory::Message,
647 code: nested_diag.code,
648 });
649 }
650 diag
651 }
652
653 SubtypeFailureReason::TooManyParameters {
654 source_count,
655 target_count,
656 } => {
657 let message = format_message(
658 diagnostic_messages::EXPECTED_ARGUMENTS_BUT_GOT,
659 &[&target_count.to_string(), &source_count.to_string()],
660 );
661 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
662 }
663
664 SubtypeFailureReason::TupleElementMismatch {
665 source_count,
666 target_count,
667 } => {
668 let message = format!(
669 "Tuple type has {source_count} elements but target requires {target_count}."
670 );
671 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
672 }
673
674 SubtypeFailureReason::TupleElementTypeMismatch {
675 index,
676 source_element,
677 target_element,
678 } => {
679 let source_str = self.format_type(*source_element);
680 let target_str = self.format_type(*target_element);
681 let message = format!(
682 "Type of element at index {index} is incompatible: '{source_str}' is not assignable to '{target_str}'."
683 );
684 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
685 }
686
687 SubtypeFailureReason::ArrayElementMismatch {
688 source_element,
689 target_element,
690 } => {
691 let source_str = self.format_type(*source_element);
692 let target_str = self.format_type(*target_element);
693 let message = format!(
694 "Array element type '{source_str}' is not assignable to '{target_str}'."
695 );
696 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
697 }
698
699 SubtypeFailureReason::IndexSignatureMismatch {
700 index_kind,
701 source_value_type,
702 target_value_type,
703 } => {
704 let source_str = self.format_type(*source_value_type);
705 let target_str = self.format_type(*target_value_type);
706 let message = format!(
707 "{index_kind} index signature is incompatible: '{source_str}' is not assignable to '{target_str}'."
708 );
709 Diagnostic::error(file_name, start, length, message, reason.diagnostic_code())
710 }
711
712 SubtypeFailureReason::NoUnionMemberMatches {
713 source_type,
714 target_union_members: _,
715 } => {
716 let source_str = self.format_type(*source_type);
717 let target_str = self.format_type(target);
718 let message = format_message(
719 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
720 &[&source_str, &target_str],
721 );
722 Diagnostic::error(
723 file_name,
724 start,
725 length,
726 message,
727 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
728 )
729 }
730
731 SubtypeFailureReason::NoCommonProperties {
732 source_type: _,
733 target_type: _,
734 } => {
735 let source_str = self.format_type_for_assignability_message(source);
736 let target_str = self.format_type_for_assignability_message(target);
737 let message = format_message(
738 diagnostic_messages::TYPE_HAS_NO_PROPERTIES_IN_COMMON_WITH_TYPE,
739 &[&source_str, &target_str],
740 );
741 Diagnostic::error(
742 file_name,
743 start,
744 length,
745 message,
746 diagnostic_codes::TYPE_HAS_NO_PROPERTIES_IN_COMMON_WITH_TYPE,
747 )
748 }
749
750 SubtypeFailureReason::TypeMismatch {
751 source_type: _,
752 target_type: _,
753 } => {
754 let source_str = self.format_type_for_assignability_message(source);
755 let target_str = self.format_type_for_assignability_message(target);
756
757 if depth == 0
758 && (target_str == "Callable" || target_str == "Applicable")
759 && !tsz_solver::is_primitive_type(self.ctx.types, source)
760 {
761 let prop_name = if target_str == "Callable" {
762 "call"
763 } else {
764 "apply"
765 };
766 let message = format_message(
767 diagnostic_messages::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
768 &[prop_name, &source_str, &target_str],
769 );
770 return Diagnostic::error(
771 file_name,
772 start,
773 length,
774 message,
775 diagnostic_codes::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
776 );
777 }
778
779 if depth == 0
780 && let Some(property_name) =
781 self.missing_single_required_property(source, target)
782 {
783 let prop_name = self.ctx.types.resolve_atom_ref(property_name);
784 let message = format_message(
785 diagnostic_messages::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
786 &[&prop_name, &source_str, &target_str],
787 );
788 return Diagnostic::error(
789 file_name,
790 start,
791 length,
792 message,
793 diagnostic_codes::PROPERTY_IS_MISSING_IN_TYPE_BUT_REQUIRED_IN_TYPE,
794 );
795 }
796
797 let base = format_message(
798 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
799 &[&source_str, &target_str],
800 );
801
802 if depth == 0 {
803 let nonpublic = self.first_nonpublic_constructor_param_property(target);
804 if tracing::enabled!(Level::TRACE) {
805 trace!(
806 target = %target_str,
807 nonpublic = ?nonpublic,
808 "nonpublic constructor param property probe"
809 );
810 }
811 if nonpublic.is_some() {
812 return Diagnostic::error(
814 file_name,
815 start,
816 length,
817 base,
818 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
819 );
820 }
821 }
822
823 Diagnostic::error(
825 file_name,
826 start,
827 length,
828 base,
829 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
830 )
831 }
832
833 _ => {
834 let source_str = self.format_type_for_assignability_message(source);
840 let target_str = self.format_type_for_assignability_message(target);
841 let message = format_message(
842 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
843 &[&source_str, &target_str],
844 );
845 Diagnostic::error(
846 file_name,
847 start,
848 length,
849 message,
850 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
851 )
852 }
853 }
854 }
855
856 pub fn error_type_not_assignable_with_reason_at(
866 &mut self,
867 source: TypeId,
868 target: TypeId,
869 idx: NodeIndex,
870 ) {
871 self.diagnose_assignment_failure(source, target, idx);
872 }
873
874 pub(crate) fn error_constructor_accessibility_not_assignable(
876 &mut self,
877 source: TypeId,
878 target: TypeId,
879 source_level: Option<MemberAccessLevel>,
880 target_level: Option<MemberAccessLevel>,
881 idx: NodeIndex,
882 ) {
883 let Some(loc) = self.get_source_location(idx) else {
884 return;
885 };
886
887 let source_type = self.format_type(source);
888 let target_type = self.format_type(target);
889 let message = format_message(
890 diagnostic_messages::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
891 &[&source_type, &target_type],
892 );
893 let detail = format!(
894 "Cannot assign a '{}' constructor type to a '{}' constructor type.",
895 Self::constructor_access_name(source_level),
896 Self::constructor_access_name(target_level),
897 );
898
899 let diag = Diagnostic::error(
900 self.ctx.file_name.clone(),
901 loc.start,
902 loc.length(),
903 message,
904 diagnostic_codes::TYPE_IS_NOT_ASSIGNABLE_TO_TYPE,
905 )
906 .with_related(self.ctx.file_name.clone(), loc.start, loc.length(), detail);
907 self.ctx.diagnostics.push(diag);
908 }
909}