tsz_checker/error_reporter/
name_resolution.rs1use crate::diagnostics::{
4 Diagnostic, DiagnosticCategory, diagnostic_codes, diagnostic_messages, format_message,
5};
6use crate::state::CheckerState;
7use tsz_parser::parser::NodeIndex;
8
9impl<'a> CheckerState<'a> {
10 fn unresolved_name_matches_enclosing_param(&self, name: &str, idx: NodeIndex) -> bool {
15 use tsz_parser::parser::syntax_kind_ext;
16
17 let mut current = idx;
18 let mut guard = 0;
19 while current.is_some() {
20 guard += 1;
21 if guard > 256 {
22 break;
23 }
24
25 let Some(ext) = self.ctx.arena.get_extended(current) else {
26 break;
27 };
28 if ext.parent.is_none() {
29 break;
30 }
31 let parent = ext.parent;
32 let Some(parent_node) = self.ctx.arena.get(parent) else {
33 break;
34 };
35
36 let matches_param = match parent_node.kind {
37 k if k == syntax_kind_ext::FUNCTION_DECLARATION
38 || k == syntax_kind_ext::FUNCTION_EXPRESSION
39 || k == syntax_kind_ext::ARROW_FUNCTION =>
40 {
41 self.ctx
42 .arena
43 .get_function(parent_node)
44 .is_some_and(|func| {
45 func.parameters.nodes.iter().any(|¶m_idx| {
46 let Some(param_node) = self.ctx.arena.get(param_idx) else {
47 return false;
48 };
49 let Some(param) = self.ctx.arena.get_parameter(param_node) else {
50 return false;
51 };
52 let Some(name_node) = self.ctx.arena.get(param.name) else {
53 return false;
54 };
55 self.ctx
56 .arena
57 .get_identifier(name_node)
58 .is_some_and(|id| id.escaped_text == name)
59 })
60 })
61 }
62 k if k == syntax_kind_ext::METHOD_DECLARATION => self
63 .ctx
64 .arena
65 .get_method_decl(parent_node)
66 .is_some_and(|method| {
67 method.parameters.nodes.iter().any(|¶m_idx| {
68 let Some(param_node) = self.ctx.arena.get(param_idx) else {
69 return false;
70 };
71 let Some(param) = self.ctx.arena.get_parameter(param_node) else {
72 return false;
73 };
74 let Some(name_node) = self.ctx.arena.get(param.name) else {
75 return false;
76 };
77 self.ctx
78 .arena
79 .get_identifier(name_node)
80 .is_some_and(|id| id.escaped_text == name)
81 })
82 }),
83 k if k == syntax_kind_ext::CONSTRUCTOR => self
84 .ctx
85 .arena
86 .get_constructor(parent_node)
87 .is_some_and(|ctor| {
88 ctor.parameters.nodes.iter().any(|¶m_idx| {
89 let Some(param_node) = self.ctx.arena.get(param_idx) else {
90 return false;
91 };
92 let Some(param) = self.ctx.arena.get_parameter(param_node) else {
93 return false;
94 };
95 let Some(name_node) = self.ctx.arena.get(param.name) else {
96 return false;
97 };
98 self.ctx
99 .arena
100 .get_identifier(name_node)
101 .is_some_and(|id| id.escaped_text == name)
102 })
103 }),
104 _ => false,
105 };
106
107 if matches_param {
108 return true;
109 }
110 current = parent;
111 }
112
113 false
114 }
115
116 fn is_in_use_strict_block(&self, idx: NodeIndex) -> bool {
119 use tsz_parser::parser::syntax_kind_ext;
120
121 let mut current = idx;
122 let mut guard = 0;
123 while current.is_some() {
124 guard += 1;
125 if guard > 256 {
126 break;
127 }
128 let Some(node) = self.ctx.arena.get(current) else {
129 break;
130 };
131 if node.kind == syntax_kind_ext::SOURCE_FILE {
133 if let Some(sf) = self.ctx.arena.get_source_file(node) {
134 for &stmt_idx in &sf.statements.nodes {
135 let Some(stmt) = self.ctx.arena.get(stmt_idx) else {
136 continue;
137 };
138 if stmt.kind != syntax_kind_ext::EXPRESSION_STATEMENT {
139 break;
140 }
141 if let Some(expr_stmt) = self.ctx.arena.get_expression_statement(stmt)
142 && let Some(expr_node) = self.ctx.arena.get(expr_stmt.expression)
143 && expr_node.kind == tsz_scanner::SyntaxKind::StringLiteral as u16
144 && let Some(lit) = self.ctx.arena.get_literal(expr_node)
145 && lit.text == "use strict"
146 {
147 return true;
148 }
149 }
150 }
151 return false;
152 }
153 let Some(ext) = self.ctx.arena.get_extended(current) else {
154 break;
155 };
156 if ext.parent.is_none() {
157 break;
158 }
159 current = ext.parent;
160 }
161 false
162 }
163
164 fn is_in_type_context(&self, idx: NodeIndex) -> bool {
167 use tsz_parser::parser::syntax_kind_ext;
168
169 let mut current = idx;
171 let mut guard = 0;
172 while current.is_some() {
173 guard += 1;
174 if guard > 64 {
175 break;
176 }
177 if let Some(node) = self.ctx.arena.get(current) {
178 match node.kind {
179 syntax_kind_ext::TYPE_REFERENCE
180 | syntax_kind_ext::HERITAGE_CLAUSE
181 | syntax_kind_ext::TYPE_ALIAS_DECLARATION
182 | syntax_kind_ext::INTERFACE_DECLARATION
183 | syntax_kind_ext::TYPE_PARAMETER
184 | syntax_kind_ext::MAPPED_TYPE
185 | syntax_kind_ext::CONDITIONAL_TYPE
186 | syntax_kind_ext::INDEXED_ACCESS_TYPE
187 | syntax_kind_ext::UNION_TYPE
188 | syntax_kind_ext::INTERSECTION_TYPE
189 | syntax_kind_ext::ARRAY_TYPE
190 | syntax_kind_ext::TUPLE_TYPE
191 | syntax_kind_ext::TYPE_LITERAL
192 | syntax_kind_ext::FUNCTION_TYPE
193 | syntax_kind_ext::CONSTRUCTOR_TYPE
194 | syntax_kind_ext::PARENTHESIZED_TYPE
195 | syntax_kind_ext::TYPE_OPERATOR
196 | syntax_kind_ext::TYPE_QUERY
197 | syntax_kind_ext::INFER_TYPE => return true,
198 syntax_kind_ext::FUNCTION_DECLARATION
200 | syntax_kind_ext::FUNCTION_EXPRESSION
201 | syntax_kind_ext::ARROW_FUNCTION
202 | syntax_kind_ext::CLASS_DECLARATION
203 | syntax_kind_ext::CLASS_EXPRESSION
204 | syntax_kind_ext::VARIABLE_STATEMENT
205 | syntax_kind_ext::EXPRESSION_STATEMENT
206 | syntax_kind_ext::BLOCK
207 | syntax_kind_ext::SOURCE_FILE => return false,
208 _ => {}
209 }
210 }
211 let Some(ext) = self.ctx.arena.get_extended(current) else {
212 break;
213 };
214 if ext.parent.is_none() {
215 break;
216 }
217 current = ext.parent;
218 }
219 false
220 }
221
222 pub fn error_cannot_find_name_at(&mut self, name: &str, idx: NodeIndex) {
226 use tsz_binder::lib_loader;
227 use tsz_parser::parser::syntax_kind_ext;
228
229 if crate::state_checking::is_strict_mode_reserved_name(name) {
232 let in_class = {
235 let mut cur = idx;
236 let mut found = false;
237 let mut g = 0;
238 while cur.is_some() {
239 g += 1;
240 if g > 256 {
241 break;
242 }
243 if let Some(n) = self.ctx.arena.get(cur) {
244 if n.kind == syntax_kind_ext::CLASS_DECLARATION
245 || n.kind == syntax_kind_ext::CLASS_EXPRESSION
246 {
247 found = true;
248 break;
249 }
250 if n.kind == syntax_kind_ext::SOURCE_FILE {
251 break;
252 }
253 }
254 let Some(ext) = self.ctx.arena.get_extended(cur) else {
255 break;
256 };
257 if ext.parent.is_none() {
258 break;
259 }
260 cur = ext.parent;
261 }
262 found
263 };
264
265 let is_strict = self.ctx.compiler_options.always_strict
266 || self.ctx.compiler_options.strict
267 || self.ctx.binder.is_external_module()
268 || in_class
269 || self.is_in_use_strict_block(idx);
270
271 if is_strict {
272 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
273 if in_class {
274 let message = format_message(
275 diagnostic_messages::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_CLASS_DEFINITIONS_ARE_AUTO,
276 &[name],
277 );
278 self.error_at_node(
279 idx,
280 &message,
281 diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_CLASS_DEFINITIONS_ARE_AUTO,
282 );
283 } else if self.ctx.binder.is_external_module() {
284 let message = format_message(
285 diagnostic_messages::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_MODULES_ARE_AUTOMATICALLY,
286 &[name],
287 );
288 self.error_at_node(
289 idx,
290 &message,
291 diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_MODULES_ARE_AUTOMATICALLY,
292 );
293 } else {
294 let message = format_message(
295 diagnostic_messages::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE,
296 &[name],
297 );
298 self.error_at_node(
299 idx,
300 &message,
301 diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE,
302 );
303 }
304 }
305 }
306
307 let force_emit_for_ambiguous_generic = self
311 .ctx
312 .arena
313 .get(idx)
314 .and_then(|node| {
315 let source = self.ctx.arena.source_files.first()?.text.as_ref();
316 let pos = node.pos as usize;
317 if pos < 2 {
318 return Some(false);
319 }
320 let bytes = source.as_bytes();
321 Some(
322 bytes.get(pos.saturating_sub(2)) == Some(&b'<')
323 && bytes.get(pos.saturating_sub(1)) == Some(&b'<'),
324 )
325 })
326 .unwrap_or(false);
327
328 let is_primitive_type_keyword = matches!(
332 name,
333 "number"
334 | "string"
335 | "boolean"
336 | "void"
337 | "undefined"
338 | "null"
339 | "any"
340 | "unknown"
341 | "never"
342 | "object"
343 | "bigint"
344 );
345 let is_import_equals_module_specifier = self
346 .ctx
347 .arena
348 .get_extended(idx)
349 .and_then(|ext| self.ctx.arena.get(ext.parent))
350 .is_some_and(|parent_node| {
351 if parent_node.kind
352 != tsz_parser::parser::syntax_kind_ext::IMPORT_EQUALS_DECLARATION
353 {
354 return false;
355 }
356 self.ctx
357 .arena
358 .get_import_decl(parent_node)
359 .is_some_and(|imp| imp.module_specifier == idx)
360 });
361
362 if is_primitive_type_keyword && !is_import_equals_module_specifier {
363 self.error_type_only_value_at(name, idx);
364 return;
365 }
366
367 if !force_emit_for_ambiguous_generic
368 && self.unresolved_name_matches_enclosing_param(name, idx)
369 {
370 return;
371 }
372
373 let mut cur = idx;
376 while let Some(ext) = self.ctx.arena.get_extended(cur) {
377 let parent = ext.parent;
378 if parent.is_none() {
379 break;
380 }
381 if let Some(parent_node) = self.ctx.arena.get(parent)
382 && parent_node.kind == syntax_kind_ext::IMPORT_EQUALS_DECLARATION
383 {
384 return;
385 }
386 cur = parent;
387 }
388
389 if name.is_empty() {
394 return;
395 }
396 let is_obviously_invalid = name.len() == 1
397 && matches!(
398 name.chars().next(),
399 Some(
400 ',' | ';'
401 | ':'
402 | '('
403 | ')'
404 | '['
405 | ']'
406 | '{'
407 | '}'
408 | '+'
409 | '-'
410 | '*'
411 | '/'
412 | '%'
413 | '&'
414 | '|'
415 | '^'
416 | '!'
417 | '~'
418 | '<'
419 | '>'
420 | '='
421 | '.'
422 )
423 );
424 if is_obviously_invalid {
425 return;
426 }
427
428 let computed_ctx = self.ctx.arena.get_extended(idx).and_then(|ext| {
432 let parent = self.ctx.arena.get(ext.parent)?;
433 if parent.kind != syntax_kind_ext::COMPUTED_PROPERTY_NAME {
434 return None;
435 }
436 let gp_ext = self.ctx.arena.get_extended(ext.parent)?;
437 let gp = self.ctx.arena.get(gp_ext.parent)?;
438 if gp.kind == syntax_kind_ext::ENUM_MEMBER {
439 Some(false) } else {
441 Some(true) }
443 });
444 if computed_ctx == Some(false) {
447 return;
448 }
449 let is_in_computed_property = computed_ctx == Some(true);
450 if self.has_parse_errors()
455 && !is_in_computed_property
456 && matches!(
457 name,
458 "static"
459 | "public"
460 | "private"
461 | "protected"
462 | "readonly"
463 | "abstract"
464 | "declare"
465 | "override"
466 | "accessor"
467 )
468 {
469 return;
470 }
471
472 if self.has_syntax_parse_errors()
481 && !is_in_computed_property
482 && !force_emit_for_ambiguous_generic
483 && !self.ctx.syntax_parse_error_positions.is_empty()
484 && let Some(node) = self.ctx.arena.get(idx)
485 {
486 let ident_pos = node.pos;
487 let has_nearby_preceding_error =
488 self.ctx
489 .syntax_parse_error_positions
490 .iter()
491 .any(|&err_pos| {
492 err_pos <= ident_pos && (ident_pos - err_pos) <= 10
494 });
495 if has_nearby_preceding_error {
496 let mut current = idx;
497 let mut guard = 0;
498 let mut in_class = false;
499 let mut in_class_member_body = false;
500 while current.is_some() {
501 guard += 1;
502 if guard > 256 {
503 break;
504 }
505 let Some(inner_node) = self.ctx.arena.get(current) else {
506 break;
507 };
508 if inner_node.kind == syntax_kind_ext::CLASS_DECLARATION
509 || inner_node.kind == syntax_kind_ext::CLASS_EXPRESSION
510 {
511 in_class = true;
512 }
513 if inner_node.kind == syntax_kind_ext::CONSTRUCTOR
514 || inner_node.kind == syntax_kind_ext::METHOD_DECLARATION
515 || inner_node.kind == syntax_kind_ext::GET_ACCESSOR
516 || inner_node.kind == syntax_kind_ext::SET_ACCESSOR
517 {
518 in_class_member_body = true;
519 }
520 let Some(ext) = self.ctx.arena.get_extended(current) else {
521 break;
522 };
523 if ext.parent.is_none() {
524 break;
525 }
526 current = ext.parent;
527 }
528 if in_class && in_class_member_body {
529 return;
530 }
531 }
532 }
533
534 if self.ctx.has_real_syntax_errors
550 && !is_in_computed_property
551 && !force_emit_for_ambiguous_generic
552 {
553 return;
554 }
555
556 if self.has_syntax_parse_errors() && self.node_span_contains_parse_error(idx) {
564 let mut current = idx;
565 let mut guard = 0;
566 while current.is_some() {
567 guard += 1;
568 if guard > 256 {
569 break;
570 }
571 if let Some(node) = self.ctx.arena.get(current)
572 && node.kind == syntax_kind_ext::TYPE_QUERY
573 {
574 return;
575 }
576 let Some(ext) = self.ctx.arena.get_extended(current) else {
577 break;
578 };
579 if ext.parent.is_none() {
580 break;
581 }
582 current = ext.parent;
583 }
584 }
585
586 if let Some(original_name) =
587 self.unresolved_unused_renaming_property_in_type_query(name, idx)
588 {
589 let message = format!(
590 "'{name}' is an unused renaming of '{original_name}'. Did you intend to use it as a type annotation?"
591 );
592 self.error_at_node(
593 idx,
594 &message,
595 diagnostic_codes::IS_AN_UNUSED_RENAMING_OF_DID_YOU_INTEND_TO_USE_IT_AS_A_TYPE_ANNOTATION,
596 );
597 return;
598 }
599
600 if lib_loader::is_es2015_plus_type(name) {
603 self.error_cannot_find_name_change_lib(name, idx);
604 return;
605 }
606
607 if super::is_known_dom_global(name) {
610 self.error_cannot_find_name_change_target_lib(name, idx);
611 return;
612 }
613
614 if super::is_known_node_global(name) {
616 self.error_cannot_find_name_install_node_types(name, idx);
617 return;
618 }
619
620 if super::is_known_test_runner_global(name) {
622 self.error_cannot_find_name_install_test_types(name, idx);
623 return;
624 }
625
626 let is_accessibility_modifier_name = matches!(name, "public" | "private" | "protected");
629 let mut is_in_spread_element = false;
630 let mut current = idx;
631 let mut guard = 0;
632 while current.is_some() {
633 guard += 1;
634 if guard > 256 {
635 break;
636 }
637 let Some(node) = self.ctx.arena.get(current) else {
638 break;
639 };
640 if node.kind == syntax_kind_ext::SPREAD_ELEMENT {
641 is_in_spread_element = true;
642 break;
643 }
644 let Some(ext) = self.ctx.arena.get_extended(current) else {
645 break;
646 };
647 if ext.parent.is_none() {
648 break;
649 }
650 current = ext.parent;
651 }
652 let is_arguments_name = name == "arguments";
655 let suppress_spelling_suggestion =
656 is_accessibility_modifier_name || is_in_spread_element || is_arguments_name;
657
658 let suggestion_meaning = if self.is_in_type_context(idx) {
663 tsz_binder::symbol_flags::TYPE
664 } else {
665 tsz_binder::symbol_flags::VALUE
666 };
667
668 let reached_max_suggestions = self.ctx.spelling_suggestions_emitted >= 10;
669 self.ctx.spelling_suggestions_emitted += 1;
670
671 if !suppress_spelling_suggestion
673 && !reached_max_suggestions
674 && let Some(suggestions) = self.find_similar_identifiers(name, idx, suggestion_meaning)
675 && !suggestions.is_empty()
676 {
677 self.error_cannot_find_name_with_suggestions(name, &suggestions, idx);
679 return;
680 }
681
682 if let Some(loc) = self.get_source_location(idx) {
684 let mut builder = tsz_solver::SpannedDiagnosticBuilder::with_symbols(
685 self.ctx.types,
686 &self.ctx.binder.symbols,
687 self.ctx.file_name.as_str(),
688 )
689 .with_def_store(&self.ctx.definition_store);
690 let diag = builder.cannot_find_name(name, loc.start, loc.length());
691 self.ctx
692 .push_diagnostic(diag.to_checker_diagnostic(&self.ctx.file_name));
693 }
694 }
695
696 pub fn error_cannot_find_global_type(&mut self, name: &str, idx: NodeIndex) {
700 use tsz_binder::lib_loader;
701
702 let is_es2015_type = lib_loader::is_es2015_plus_type(name);
704
705 if let Some(loc) = self.get_source_location(idx) {
706 let (code, message) = if is_es2015_type {
707 (
708 lib_loader::MISSING_ES2015_LIB_SUPPORT,
709 format!(
710 "Cannot find name '{name}'. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later."
711 ),
712 )
713 } else {
714 (
715 lib_loader::CANNOT_FIND_GLOBAL_TYPE,
716 format!("Cannot find global type '{name}'."),
717 )
718 };
719
720 self.ctx.push_diagnostic(Diagnostic::error(
721 self.ctx.file_name.clone(),
722 loc.start,
723 loc.length(),
724 message,
725 code,
726 ));
727 }
728 }
729
730 pub fn error_cannot_find_name_change_lib(&mut self, name: &str, idx: NodeIndex) {
736 if let Some(loc) = self.get_source_location(idx) {
737 let message = format_message(
738 diagnostic_messages::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB,
739 &[name],
740 );
741 self.ctx.push_diagnostic(Diagnostic::error(self.ctx.file_name.clone(), loc.start, loc.length(), message, diagnostic_codes::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB));
742 }
743 }
744
745 pub fn error_cannot_find_name_change_target_lib(&mut self, name: &str, idx: NodeIndex) {
750 if let Some(loc) = self.get_source_location(idx) {
751 let message = format_message(
752 diagnostic_messages::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB_2,
753 &[name],
754 );
755 self.ctx.push_diagnostic(Diagnostic::error(self.ctx.file_name.clone(), loc.start, loc.length(), message, diagnostic_codes::CANNOT_FIND_NAME_DO_YOU_NEED_TO_CHANGE_YOUR_TARGET_LIBRARY_TRY_CHANGING_THE_LIB_2));
756 }
757 }
758
759 pub fn error_cannot_find_name_install_node_types(&mut self, name: &str, idx: NodeIndex) {
764 if let Some(loc) = self.get_source_location(idx) {
765 let message = format_message(
766 diagnostic_messages::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_NODE_TRY_NPM_I_SAVE,
767 &[name],
768 );
769 self.ctx.push_diagnostic(Diagnostic::error(self.ctx.file_name.clone(), loc.start, loc.length(), message, diagnostic_codes::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_NODE_TRY_NPM_I_SAVE));
770 }
771 }
772
773 pub fn error_cannot_find_name_install_test_types(&mut self, name: &str, idx: NodeIndex) {
775 if let Some(loc) = self.get_source_location(idx) {
776 let message = format_message(
777 diagnostic_messages::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_A_TEST_RUNNER_TRY_N,
778 &[name],
779 );
780 self.ctx.push_diagnostic(Diagnostic::error(self.ctx.file_name.clone(), loc.start, loc.length(), message, diagnostic_codes::CANNOT_FIND_NAME_DO_YOU_NEED_TO_INSTALL_TYPE_DEFINITIONS_FOR_A_TEST_RUNNER_TRY_N));
781 }
782 }
783
784 pub fn error_cannot_find_name_with_suggestions(
787 &mut self,
788 name: &str,
789 suggestions: &[String],
790 idx: NodeIndex,
791 ) {
792 let is_obviously_invalid = name.len() == 1
795 && matches!(
796 name.chars().next(),
797 Some(
798 ',' | ';'
799 | ':'
800 | '('
801 | ')'
802 | '['
803 | ']'
804 | '{'
805 | '}'
806 | '+'
807 | '-'
808 | '*'
809 | '/'
810 | '%'
811 | '&'
812 | '|'
813 | '^'
814 | '!'
815 | '~'
816 | '<'
817 | '>'
818 | '='
819 | '.'
820 )
821 );
822 if is_obviously_invalid {
823 return;
824 }
825
826 if let Some(loc) = self.get_source_location(idx) {
827 let suggestions_text = if suggestions.len() == 1 {
829 format!("'{}'", suggestions[0])
830 } else {
831 let formatted: Vec<String> = suggestions.iter().map(|s| format!("'{s}")).collect();
832 formatted.join(", ")
833 };
834
835 let message = if suggestions.len() == 1 {
836 format!("Cannot find name '{name}'. Did you mean {suggestions_text}?")
837 } else {
838 format!("Cannot find name '{name}'. Did you mean one of: {suggestions_text}?")
839 };
840
841 self.ctx.push_diagnostic(Diagnostic {
842 code: if suggestions.len() == 1 {
843 diagnostic_codes::CANNOT_FIND_NAME_DID_YOU_MEAN
844 } else {
845 diagnostic_codes::CANNOT_FIND_NAME
846 },
847 category: DiagnosticCategory::Error,
848 message_text: message,
849 file: self.ctx.file_name.clone(),
850 start: loc.start,
851 length: loc.length(),
852 related_information: Vec::new(),
853 });
854 }
855 }
856
857 pub fn error_cannot_find_name_did_you_mean_at(
859 &mut self,
860 name: &str,
861 suggestion: &str,
862 idx: NodeIndex,
863 ) {
864 if let Some(loc) = self.get_source_location(idx) {
865 let message = format!("Cannot find name '{name}'. Did you mean '{suggestion}'?");
866 self.ctx.push_diagnostic(Diagnostic::error(
867 self.ctx.file_name.clone(),
868 loc.start,
869 loc.length(),
870 message,
871 diagnostic_codes::CANNOT_FIND_NAME_DID_YOU_MEAN,
872 ));
873 }
874 }
875
876 pub fn error_cannot_find_name_static_member_at(
878 &mut self,
879 name: &str,
880 class_name: &str,
881 idx: NodeIndex,
882 ) {
883 if let Some(loc) = self.get_source_location(idx) {
884 let message = format!(
885 "Cannot find name '{name}'. Did you mean the static member '{class_name}.{name}'?"
886 );
887 self.ctx.push_diagnostic(Diagnostic::error(
888 self.ctx.file_name.clone(),
889 loc.start,
890 loc.length(),
891 message,
892 diagnostic_codes::CANNOT_FIND_NAME_DID_YOU_MEAN_THE_STATIC_MEMBER,
893 ));
894 }
895 }
896}