1use crate::TypeDatabase;
13use crate::def::DefinitionStore;
14use crate::diagnostics::{DiagnosticSeverity, SourceSpan, TypeDiagnostic, codes};
15use crate::format::TypeFormatter;
16use crate::types::TypeId;
17use std::sync::Arc;
18
19pub struct DiagnosticBuilder<'a> {
25 formatter: TypeFormatter<'a>,
26}
27
28impl<'a> DiagnosticBuilder<'a> {
29 pub fn new(interner: &'a dyn TypeDatabase) -> Self {
30 DiagnosticBuilder {
31 formatter: TypeFormatter::new(interner),
32 }
33 }
34
35 pub fn with_symbols(
40 interner: &'a dyn TypeDatabase,
41 symbol_arena: &'a tsz_binder::SymbolArena,
42 ) -> Self {
43 DiagnosticBuilder {
44 formatter: TypeFormatter::with_symbols(interner, symbol_arena),
45 }
46 }
47
48 pub fn with_def_store(mut self, def_store: &'a DefinitionStore) -> Self {
53 self.formatter = self.formatter.with_def_store(def_store);
54 self
55 }
56
57 pub fn type_not_assignable(&mut self, source: TypeId, target: TypeId) -> TypeDiagnostic {
59 let source_str = self.formatter.format(source);
60 let target_str = self.formatter.format(target);
61 TypeDiagnostic::error(
62 format!("Type '{source_str}' is not assignable to type '{target_str}'."),
63 codes::TYPE_NOT_ASSIGNABLE,
64 )
65 }
66
67 pub fn property_missing(
69 &mut self,
70 prop_name: &str,
71 source: TypeId,
72 target: TypeId,
73 ) -> TypeDiagnostic {
74 let source_str = self.formatter.format(source);
75 let target_str = self.formatter.format(target);
76 TypeDiagnostic::error(
77 format!(
78 "Property '{prop_name}' is missing in type '{source_str}' but required in type '{target_str}'."
79 ),
80 codes::PROPERTY_MISSING,
81 )
82 }
83
84 pub fn property_not_exist(&mut self, prop_name: &str, type_id: TypeId) -> TypeDiagnostic {
86 let type_str = self.formatter.format(type_id);
87 TypeDiagnostic::error(
88 format!("Property '{prop_name}' does not exist on type '{type_str}'."),
89 codes::PROPERTY_NOT_EXIST,
90 )
91 }
92
93 pub fn property_not_exist_did_you_mean(
95 &mut self,
96 prop_name: &str,
97 type_id: TypeId,
98 suggestion: &str,
99 ) -> TypeDiagnostic {
100 let type_str = self.formatter.format(type_id);
101 TypeDiagnostic::error(
102 format!(
103 "Property '{prop_name}' does not exist on type '{type_str}'. Did you mean '{suggestion}'?"
104 ),
105 codes::PROPERTY_NOT_EXIST_DID_YOU_MEAN,
106 )
107 }
108
109 pub fn argument_not_assignable(
111 &mut self,
112 arg_type: TypeId,
113 param_type: TypeId,
114 ) -> TypeDiagnostic {
115 let arg_str = self.formatter.format(arg_type);
116 let param_str = self.formatter.format(param_type);
117 TypeDiagnostic::error(
118 format!(
119 "Argument of type '{arg_str}' is not assignable to parameter of type '{param_str}'."
120 ),
121 codes::ARG_NOT_ASSIGNABLE,
122 )
123 }
124
125 pub fn cannot_find_name(&mut self, name: &str) -> TypeDiagnostic {
127 let is_obviously_invalid = name.len() == 1
132 && matches!(
133 name.chars().next(),
134 Some(
135 ',' | ';'
136 | ':'
137 | '('
138 | ')'
139 | '['
140 | ']'
141 | '{'
142 | '}'
143 | '+'
144 | '-'
145 | '*'
146 | '/'
147 | '%'
148 | '&'
149 | '|'
150 | '^'
151 | '!'
152 | '~'
153 | '<'
154 | '>'
155 | '='
156 | '.'
157 )
158 );
159
160 if is_obviously_invalid {
161 return TypeDiagnostic::error("", 0);
163 }
164
165 let code = crate::diagnostics::cannot_find_name_code(name);
166 TypeDiagnostic::error(format!("Cannot find name '{name}'."), code)
167 }
168
169 pub fn not_callable(&mut self, type_id: TypeId) -> TypeDiagnostic {
171 let type_str = self.formatter.format(type_id);
172 TypeDiagnostic::error(
173 format!("Type '{type_str}' has no call signatures."),
174 codes::NOT_CALLABLE,
175 )
176 }
177
178 pub fn this_type_mismatch(
179 &mut self,
180 expected_this: TypeId,
181 actual_this: TypeId,
182 ) -> TypeDiagnostic {
183 let expected_str = self.formatter.format(expected_this);
184 let actual_str = self.formatter.format(actual_this);
185 TypeDiagnostic::error(
186 format!(
187 "The 'this' context of type '{actual_str}' is not assignable to method's 'this' of type '{expected_str}'."
188 ),
189 codes::THIS_TYPE_MISMATCH,
190 )
191 }
192
193 pub fn argument_count_mismatch(&mut self, expected: usize, got: usize) -> TypeDiagnostic {
195 TypeDiagnostic::error(
196 format!("Expected {expected} arguments, but got {got}."),
197 codes::ARG_COUNT_MISMATCH,
198 )
199 }
200
201 pub fn readonly_property(&mut self, prop_name: &str) -> TypeDiagnostic {
203 TypeDiagnostic::error(
204 format!("Cannot assign to '{prop_name}' because it is a read-only property."),
205 codes::READONLY_PROPERTY,
206 )
207 }
208
209 pub fn excess_property(&mut self, prop_name: &str, target: TypeId) -> TypeDiagnostic {
211 let target_str = self.formatter.format(target);
212 TypeDiagnostic::error(
213 format!(
214 "Object literal may only specify known properties, and '{prop_name}' does not exist in type '{target_str}'."
215 ),
216 codes::EXCESS_PROPERTY,
217 )
218 }
219
220 pub fn implicit_any_parameter(&mut self, param_name: &str) -> TypeDiagnostic {
226 TypeDiagnostic::error(
227 format!("Parameter '{param_name}' implicitly has an 'any' type."),
228 codes::IMPLICIT_ANY_PARAMETER,
229 )
230 }
231
232 pub fn implicit_any_parameter_with_type(
234 &mut self,
235 param_name: &str,
236 implicit_type: TypeId,
237 ) -> TypeDiagnostic {
238 let type_str = self.formatter.format(implicit_type);
239 TypeDiagnostic::error(
240 format!("Parameter '{param_name}' implicitly has an '{type_str}' type."),
241 codes::IMPLICIT_ANY_PARAMETER,
242 )
243 }
244
245 pub fn implicit_any_member(&mut self, member_name: &str) -> TypeDiagnostic {
247 TypeDiagnostic::error(
248 format!("Member '{member_name}' implicitly has an 'any' type."),
249 codes::IMPLICIT_ANY_MEMBER,
250 )
251 }
252
253 pub fn implicit_any_variable(&mut self, var_name: &str, var_type: TypeId) -> TypeDiagnostic {
255 let type_str = self.formatter.format(var_type);
256 TypeDiagnostic::error(
257 format!("Variable '{var_name}' implicitly has an '{type_str}' type."),
258 codes::IMPLICIT_ANY,
259 )
260 }
261
262 pub fn implicit_any_return(&mut self, func_name: &str, return_type: TypeId) -> TypeDiagnostic {
264 let type_str = self.formatter.format(return_type);
265 TypeDiagnostic::error(
266 format!(
267 "'{func_name}', which lacks return-type annotation, implicitly has an '{type_str}' return type."
268 ),
269 codes::IMPLICIT_ANY_RETURN,
270 )
271 }
272
273 pub fn implicit_any_return_function_expression(
275 &mut self,
276 return_type: TypeId,
277 ) -> TypeDiagnostic {
278 let type_str = self.formatter.format(return_type);
279 TypeDiagnostic::error(
280 format!(
281 "Function expression, which lacks return-type annotation, implicitly has an '{type_str}' return type."
282 ),
283 codes::IMPLICIT_ANY_RETURN_FUNCTION_EXPRESSION,
284 )
285 }
286}
287
288pub struct SpannedDiagnosticBuilder<'a> {
297 builder: DiagnosticBuilder<'a>,
298 file: Arc<str>,
299}
300
301impl<'a> SpannedDiagnosticBuilder<'a> {
302 pub fn new(interner: &'a dyn TypeDatabase, file: impl Into<Arc<str>>) -> Self {
303 SpannedDiagnosticBuilder {
304 builder: DiagnosticBuilder::new(interner),
305 file: file.into(),
306 }
307 }
308
309 pub fn with_symbols(
311 interner: &'a dyn TypeDatabase,
312 symbol_arena: &'a tsz_binder::SymbolArena,
313 file: impl Into<Arc<str>>,
314 ) -> Self {
315 SpannedDiagnosticBuilder {
316 builder: DiagnosticBuilder::with_symbols(interner, symbol_arena),
317 file: file.into(),
318 }
319 }
320
321 pub fn with_def_store(mut self, def_store: &'a DefinitionStore) -> Self {
323 self.builder = self.builder.with_def_store(def_store);
324 self
325 }
326
327 pub fn span(&self, start: u32, length: u32) -> SourceSpan {
329 SourceSpan::new(std::sync::Arc::clone(&self.file), start, length)
330 }
331
332 pub fn type_not_assignable(
334 &mut self,
335 source: TypeId,
336 target: TypeId,
337 start: u32,
338 length: u32,
339 ) -> TypeDiagnostic {
340 self.builder
341 .type_not_assignable(source, target)
342 .with_span(self.span(start, length))
343 }
344
345 pub fn property_missing(
347 &mut self,
348 prop_name: &str,
349 source: TypeId,
350 target: TypeId,
351 start: u32,
352 length: u32,
353 ) -> TypeDiagnostic {
354 self.builder
355 .property_missing(prop_name, source, target)
356 .with_span(self.span(start, length))
357 }
358
359 pub fn property_not_exist(
361 &mut self,
362 prop_name: &str,
363 type_id: TypeId,
364 start: u32,
365 length: u32,
366 ) -> TypeDiagnostic {
367 self.builder
368 .property_not_exist(prop_name, type_id)
369 .with_span(self.span(start, length))
370 }
371
372 pub fn property_not_exist_did_you_mean(
374 &mut self,
375 prop_name: &str,
376 type_id: TypeId,
377 suggestion: &str,
378 start: u32,
379 length: u32,
380 ) -> TypeDiagnostic {
381 self.builder
382 .property_not_exist_did_you_mean(prop_name, type_id, suggestion)
383 .with_span(self.span(start, length))
384 }
385
386 pub fn argument_not_assignable(
388 &mut self,
389 arg_type: TypeId,
390 param_type: TypeId,
391 start: u32,
392 length: u32,
393 ) -> TypeDiagnostic {
394 self.builder
395 .argument_not_assignable(arg_type, param_type)
396 .with_span(self.span(start, length))
397 }
398
399 pub fn cannot_find_name(&mut self, name: &str, start: u32, length: u32) -> TypeDiagnostic {
401 self.builder
402 .cannot_find_name(name)
403 .with_span(self.span(start, length))
404 }
405
406 pub fn argument_count_mismatch(
408 &mut self,
409 expected: usize,
410 got: usize,
411 start: u32,
412 length: u32,
413 ) -> TypeDiagnostic {
414 self.builder
415 .argument_count_mismatch(expected, got)
416 .with_span(self.span(start, length))
417 }
418
419 pub fn not_callable(&mut self, type_id: TypeId, start: u32, length: u32) -> TypeDiagnostic {
421 self.builder
422 .not_callable(type_id)
423 .with_span(self.span(start, length))
424 }
425
426 pub fn this_type_mismatch(
427 &mut self,
428 expected_this: TypeId,
429 actual_this: TypeId,
430 start: u32,
431 length: u32,
432 ) -> TypeDiagnostic {
433 self.builder
434 .this_type_mismatch(expected_this, actual_this)
435 .with_span(self.span(start, length))
436 }
437
438 pub fn excess_property(
440 &mut self,
441 prop_name: &str,
442 target: TypeId,
443 start: u32,
444 length: u32,
445 ) -> TypeDiagnostic {
446 self.builder
447 .excess_property(prop_name, target)
448 .with_span(self.span(start, length))
449 }
450
451 pub fn readonly_property(
453 &mut self,
454 prop_name: &str,
455 start: u32,
456 length: u32,
457 ) -> TypeDiagnostic {
458 self.builder
459 .readonly_property(prop_name)
460 .with_span(self.span(start, length))
461 }
462
463 pub fn add_related(
465 &self,
466 diag: TypeDiagnostic,
467 message: impl Into<String>,
468 start: u32,
469 length: u32,
470 ) -> TypeDiagnostic {
471 diag.with_related(self.span(start, length), message)
472 }
473}
474
475impl TypeDiagnostic {
484 pub fn to_checker_diagnostic(&self, default_file: &str) -> tsz_common::diagnostics::Diagnostic {
488 use tsz_common::diagnostics::{
489 Diagnostic, DiagnosticCategory, DiagnosticRelatedInformation,
490 };
491
492 let (file, start, length) = if let Some(ref span) = self.span {
493 (span.file.to_string(), span.start, span.length)
494 } else {
495 (default_file.to_string(), 0, 0)
496 };
497
498 let category = match self.severity {
499 DiagnosticSeverity::Error => DiagnosticCategory::Error,
500 DiagnosticSeverity::Warning => DiagnosticCategory::Warning,
501 DiagnosticSeverity::Suggestion => DiagnosticCategory::Suggestion,
502 DiagnosticSeverity::Message => DiagnosticCategory::Message,
503 };
504
505 let related_information: Vec<DiagnosticRelatedInformation> = self
506 .related
507 .iter()
508 .map(|rel| DiagnosticRelatedInformation {
509 file: rel.span.file.to_string(),
510 start: rel.span.start,
511 length: rel.span.length,
512 message_text: rel.message.clone(),
513 category: DiagnosticCategory::Message,
514 code: 0,
515 })
516 .collect();
517
518 Diagnostic {
519 file,
520 start,
521 length,
522 message_text: self.message.clone(),
523 category,
524 code: self.code,
525 related_information,
526 }
527 }
528}
529
530#[derive(Clone)]
539pub struct SourceLocation {
540 pub file: Arc<str>,
542 pub start: u32,
544 pub end: u32,
546}
547
548impl SourceLocation {
549 pub fn new(file: impl Into<Arc<str>>, start: u32, end: u32) -> Self {
550 Self {
551 file: file.into(),
552 start,
553 end,
554 }
555 }
556
557 pub const fn length(&self) -> u32 {
559 self.end.saturating_sub(self.start)
560 }
561
562 pub fn to_span(&self) -> SourceSpan {
564 SourceSpan::new(std::sync::Arc::clone(&self.file), self.start, self.length())
565 }
566}
567
568pub struct DiagnosticCollector<'a> {
570 interner: &'a dyn TypeDatabase,
571 file: Arc<str>,
572 diagnostics: Vec<TypeDiagnostic>,
573}
574
575impl<'a> DiagnosticCollector<'a> {
576 pub fn new(interner: &'a dyn TypeDatabase, file: impl Into<Arc<str>>) -> Self {
577 DiagnosticCollector {
578 interner,
579 file: file.into(),
580 diagnostics: Vec::new(),
581 }
582 }
583
584 pub fn diagnostics(&self) -> &[TypeDiagnostic] {
586 &self.diagnostics
587 }
588
589 pub fn take_diagnostics(&mut self) -> Vec<TypeDiagnostic> {
591 std::mem::take(&mut self.diagnostics)
592 }
593
594 pub fn type_not_assignable(&mut self, source: TypeId, target: TypeId, loc: &SourceLocation) {
596 let mut builder = DiagnosticBuilder::new(self.interner);
597 let diag = builder
598 .type_not_assignable(source, target)
599 .with_span(loc.to_span());
600 self.diagnostics.push(diag);
601 }
602
603 pub fn property_missing(
605 &mut self,
606 prop_name: &str,
607 source: TypeId,
608 target: TypeId,
609 loc: &SourceLocation,
610 ) {
611 let mut builder = DiagnosticBuilder::new(self.interner);
612 let diag = builder
613 .property_missing(prop_name, source, target)
614 .with_span(loc.to_span());
615 self.diagnostics.push(diag);
616 }
617
618 pub fn property_not_exist(&mut self, prop_name: &str, type_id: TypeId, loc: &SourceLocation) {
620 let mut builder = DiagnosticBuilder::new(self.interner);
621 let diag = builder
622 .property_not_exist(prop_name, type_id)
623 .with_span(loc.to_span());
624 self.diagnostics.push(diag);
625 }
626
627 pub fn argument_not_assignable(
629 &mut self,
630 arg_type: TypeId,
631 param_type: TypeId,
632 loc: &SourceLocation,
633 ) {
634 let mut builder = DiagnosticBuilder::new(self.interner);
635 let diag = builder
636 .argument_not_assignable(arg_type, param_type)
637 .with_span(loc.to_span());
638 self.diagnostics.push(diag);
639 }
640
641 pub fn cannot_find_name(&mut self, name: &str, loc: &SourceLocation) {
643 let mut builder = DiagnosticBuilder::new(self.interner);
644 let diag = builder.cannot_find_name(name).with_span(loc.to_span());
645 self.diagnostics.push(diag);
646 }
647
648 pub fn argument_count_mismatch(&mut self, expected: usize, got: usize, loc: &SourceLocation) {
650 let mut builder = DiagnosticBuilder::new(self.interner);
651 let diag = builder
652 .argument_count_mismatch(expected, got)
653 .with_span(loc.to_span());
654 self.diagnostics.push(diag);
655 }
656
657 pub fn to_checker_diagnostics(&self) -> Vec<tsz_common::diagnostics::Diagnostic> {
659 self.diagnostics
660 .iter()
661 .map(|d| d.to_checker_diagnostic(&self.file))
662 .collect()
663 }
664}