1use crate::state::CheckerState;
16use std::sync::Arc;
17use tracing::trace;
18use tsz_binder::{SymbolId, symbol_flags};
19use tsz_parser::parser::NodeIndex;
20use tsz_parser::parser::syntax_kind_ext;
21use tsz_scanner::SyntaxKind;
22use tsz_solver::TypeId;
23use tsz_solver::is_compiler_managed_type;
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum TypeSymbolResolution {
27 Type(SymbolId),
28 ValueOnly(SymbolId),
29 NotFound,
30}
31
32impl<'a> CheckerState<'a> {
37 pub fn get_symbol_type(&mut self, sym_id: SymbolId) -> TypeId {
46 self.get_type_of_symbol(sym_id)
47 }
48
49 pub fn is_global_intrinsic(&self, name: &str) -> bool {
57 matches!(
58 name,
59 "undefined"
60 | "NaN"
61 | "Infinity"
62 | "Math"
63 | "JSON"
64 | "Object"
65 | "Array"
66 | "String"
67 | "Number"
68 | "Boolean"
69 | "Symbol"
70 | "Date"
71 | "RegExp"
72 | "Error"
73 | "Function"
74 | "Promise"
75 )
76 }
77
78 pub fn is_global_constructor(&self, name: &str) -> bool {
82 matches!(
83 name,
84 "Object"
85 | "Array"
86 | "String"
87 | "Number"
88 | "Boolean"
89 | "Symbol"
90 | "Date"
91 | "RegExp"
92 | "Error"
93 | "Function"
94 | "Promise"
95 | "Map"
96 | "Set"
97 | "WeakMap"
98 | "WeakSet"
99 | "Proxy"
100 | "Reflect"
101 )
102 }
103
104 pub fn get_symbol_name(&self, sym_id: SymbolId) -> Option<String> {
112 self.ctx
113 .binder
114 .symbols
115 .get(sym_id)
116 .map(|symbol| symbol.escaped_name.clone())
117 }
118
119 pub fn is_symbol_exported(&self, sym_id: SymbolId) -> bool {
123 self.ctx
124 .binder
125 .symbols
126 .get(sym_id)
127 .is_some_and(|symbol| symbol.is_exported)
128 }
129
130 pub fn is_symbol_type_only(&self, sym_id: SymbolId) -> bool {
134 self.ctx
135 .binder
136 .symbols
137 .get(sym_id)
138 .is_some_and(|symbol| symbol.is_type_only)
139 }
140
141 pub fn get_symbol_value_declaration(
149 &self,
150 sym_id: SymbolId,
151 ) -> Option<tsz_parser::parser::NodeIndex> {
152 self.ctx.binder.symbols.get(sym_id).and_then(|symbol| {
153 let decl = symbol.value_declaration;
154 (decl.0 != u32::MAX).then_some(decl)
155 })
156 }
157
158 pub fn get_symbol_declarations(&self, sym_id: SymbolId) -> Vec<tsz_parser::parser::NodeIndex> {
162 self.ctx
163 .binder
164 .symbols
165 .get(sym_id)
166 .map(|symbol| symbol.declarations.clone())
167 .unwrap_or_default()
168 }
169
170 pub fn symbol_has_flag(&self, sym_id: SymbolId, flag: u32) -> bool {
175 self.ctx
176 .binder
177 .symbols
178 .get(sym_id)
179 .is_some_and(|symbol| (symbol.flags & flag) != 0)
180 }
181
182 pub fn symbol_flags_safe(&self, sym_id: SymbolId) -> u32 {
187 self.ctx
188 .binder
189 .symbols
190 .get(sym_id)
191 .map_or(0, |symbol| symbol.flags)
192 }
193
194 pub fn symbol_flags_with_libs(
198 &self,
199 sym_id: SymbolId,
200 lib_binders: &[Arc<tsz_binder::BinderState>],
201 ) -> u32 {
202 self.ctx
203 .binder
204 .get_symbol_with_libs(sym_id, lib_binders)
205 .map_or(0, |symbol| symbol.flags)
206 }
207
208 pub(crate) fn get_lib_binders(&self) -> Vec<Arc<tsz_binder::BinderState>> {
216 self.ctx
217 .lib_contexts
218 .iter()
219 .map(|lc| Arc::clone(&lc.binder))
220 .collect()
221 }
222
223 pub(crate) const fn is_class_member_symbol(flags: u32) -> bool {
228 let is_member = (flags
230 & (symbol_flags::PROPERTY
231 | symbol_flags::METHOD
232 | symbol_flags::GET_ACCESSOR
233 | symbol_flags::SET_ACCESSOR
234 | symbol_flags::CONSTRUCTOR))
235 != 0;
236
237 if !is_member {
238 return false;
239 }
240
241 if (flags & symbol_flags::CONSTRUCTOR) != 0 {
243 return false;
244 }
245
246 if (flags & symbol_flags::STATIC) != 0 {
248 return false;
249 }
250
251 true
253 }
254
255 pub(crate) fn resolve_identifier_symbol(&self, idx: NodeIndex) -> Option<SymbolId> {
266 let result = self.resolve_identifier_symbol_inner(idx);
267 if let Some(sym_id) = result {
268 self.ctx.referenced_symbols.borrow_mut().insert(sym_id);
269 trace!(sym_id = %sym_id.0, idx = %idx.0, "resolve_identifier_symbol: marked referenced");
270 }
271 result
272 }
273
274 pub(crate) fn resolve_identifier_symbol_for_write(&self, idx: NodeIndex) -> Option<SymbolId> {
276 let result = self.resolve_identifier_symbol_inner(idx);
277 if let Some(sym_id) = result {
278 self.ctx.written_symbols.borrow_mut().insert(sym_id);
279 }
280 result
281 }
282
283 fn resolve_identifier_symbol_inner(&self, idx: NodeIndex) -> Option<SymbolId> {
284 let ident_name = self
286 .ctx
287 .arena
288 .get_identifier_at(idx)
289 .map(|i| i.escaped_text.as_str().to_string());
290
291 let ignore_libs = !self.ctx.has_lib_loaded();
292 let lib_binders = if ignore_libs {
293 Vec::new()
294 } else {
295 self.get_lib_binders()
296 };
297 let is_from_lib = |sym_id: SymbolId| self.ctx.symbol_is_from_lib(sym_id);
298 let should_skip_lib_symbol = |sym_id: SymbolId| ignore_libs && is_from_lib(sym_id);
299
300 trace!(
301 ident_name = ?ident_name,
302 idx = ?idx,
303 ignore_libs = ignore_libs,
304 "Resolving identifier symbol"
305 );
306
307 let result = self.ctx.binder.resolve_identifier_with_filter(
309 self.ctx.arena,
310 idx,
311 &lib_binders,
312 |sym_id| {
313 if should_skip_lib_symbol(sym_id) {
314 return false;
315 }
316 if let Some(symbol) = self.ctx.binder.get_symbol_with_libs(sym_id, &lib_binders) {
317 let is_class_member = Self::is_class_member_symbol(symbol.flags);
318 if is_class_member {
319 return is_from_lib(sym_id)
320 && (symbol.flags & symbol_flags::EXPORT_VALUE) != 0;
321 }
322 }
323 true
324 },
325 );
326 let result = {
327 let expected_name = self
328 .ctx
329 .arena
330 .get_identifier_at(idx)
331 .map(|ident| ident.escaped_text.as_str());
332 result.filter(|&sym_id| {
333 let Some(expected_name) = expected_name else {
334 return false;
335 };
336
337 self.ctx
338 .binder
339 .get_symbol_with_libs(sym_id, &lib_binders)
340 .is_some_and(|symbol| symbol.escaped_name.as_str() == expected_name)
341 })
342 };
343
344 trace!(
345 ident_name = ?ident_name,
346 binder_result = ?result,
347 "Binder resolution result"
348 );
349
350 if result.is_none() && !ignore_libs {
357 let name = if let Some(ident) = self.ctx.arena.get_identifier_at(idx) {
359 ident.escaped_text.as_str()
360 } else {
361 return None;
362 };
363 for (lib_idx, lib_ctx) in self.ctx.lib_contexts.iter().enumerate() {
365 if let Some(lib_sym_id) = lib_ctx.binder.file_locals.get(name) {
366 trace!(
367 name = name,
368 lib_idx = lib_idx,
369 lib_sym_id = ?lib_sym_id,
370 "Found symbol in lib_context"
371 );
372 if !should_skip_lib_symbol(lib_sym_id) {
373 let Some(file_sym_id) = self.ctx.binder.file_locals.get(name) else {
377 continue;
378 };
379 trace!(
380 name = name,
381 file_sym_id = ?file_sym_id,
382 lib_sym_id = ?lib_sym_id,
383 "Returning symbol from lib_contexts fallback"
384 );
385 return Some(file_sym_id);
386 }
387 }
388 }
389 }
390
391 trace!(
392 ident_name = ?ident_name,
393 final_result = ?result,
394 "Symbol resolution final result"
395 );
396
397 if let Some(ident) = self.ctx.arena.get_identifier_at(idx)
398 && result.is_none()
399 {
400 let name = ident.escaped_text.as_str();
401 if let Some(sym_id) =
402 self.resolve_identifier_symbol_from_all_binders(name, |sym_id, symbol| {
403 if should_skip_lib_symbol(sym_id) {
404 return false;
405 }
406
407 let is_class_member = Self::is_class_member_symbol(symbol.flags);
408 if is_class_member {
409 return is_from_lib(sym_id)
410 && (symbol.flags & symbol_flags::EXPORT_VALUE) != 0;
411 }
412 true
413 })
414 {
415 return Some(sym_id);
416 }
417 }
418
419 trace!(
420 ident_name = ?ident_name,
421 final_result = ?result,
422 "Symbol resolution final result"
423 );
424
425 if let Some(sym_id) = result
426 && let Some(sym) = self.ctx.binder.get_symbol_with_libs(sym_id, &lib_binders)
427 {
428 trace!(
429 ident_name = ?ident_name,
430 sym_id = sym_id.0,
431 sym_name = sym.escaped_name.as_str(),
432 sym_flags = sym.flags,
433 "Symbol resolution resolved metadata"
434 );
435 }
436 result
437 }
438
439 pub(crate) fn resolve_identifier_symbol_in_type_position(
441 &self,
442 idx: NodeIndex,
443 ) -> TypeSymbolResolution {
444 let result = self.resolve_identifier_symbol_in_type_position_inner(idx);
445 if let TypeSymbolResolution::Type(sym_id) = result {
446 self.ctx.referenced_symbols.borrow_mut().insert(sym_id);
447 }
448 result
449 }
450
451 fn resolve_identifier_symbol_in_type_position_inner(
452 &self,
453 idx: NodeIndex,
454 ) -> TypeSymbolResolution {
455 let node = match self.ctx.arena.get(idx) {
456 Some(node) => node,
457 None => return TypeSymbolResolution::NotFound,
458 };
459 let ident = match self.ctx.arena.get_identifier(node) {
460 Some(ident) => ident,
461 None => return TypeSymbolResolution::NotFound,
462 };
463 let name = ident.escaped_text.as_str();
464
465 let ignore_libs = !self.ctx.has_lib_loaded();
466 let lib_binders = if ignore_libs {
468 Vec::new()
469 } else {
470 self.get_lib_binders()
471 };
472 let should_skip_lib_symbol =
473 |sym_id: SymbolId| ignore_libs && self.ctx.symbol_is_from_lib(sym_id);
474 let mut value_only_candidate = None;
475
476 let name_in_local_scope = if !ignore_libs {
480 self.ctx
481 .binder
482 .resolve_identifier_with_filter(
483 self.ctx.arena,
484 idx,
485 &lib_binders,
486 |_| true, )
488 .is_some_and(|found_sym_id| {
489 self.ctx.binder.file_locals.get(name) != Some(found_sym_id)
492 })
493 } else {
494 false
495 };
496
497 if !ignore_libs && !name_in_local_scope {
505 for lib_ctx in &self.ctx.lib_contexts {
506 if let Some(lib_sym_id) = lib_ctx.binder.file_locals.get(name) {
507 let Some(sym_id) = self.ctx.binder.file_locals.get(name) else {
511 continue;
512 };
513 if !should_skip_lib_symbol(sym_id) {
514 let flags = lib_ctx.binder.get_symbol(lib_sym_id).map_or(0, |s| s.flags);
516
517 let is_namespace_or_module = (flags
519 & (symbol_flags::NAMESPACE_MODULE | symbol_flags::VALUE_MODULE))
520 != 0;
521
522 if is_namespace_or_module {
523 return TypeSymbolResolution::Type(sym_id);
524 }
525
526 if flags & symbol_flags::ALIAS != 0 {
528 let mut visited = Vec::new();
529 if let Some(target_sym_id) =
530 self.resolve_alias_symbol(sym_id, &mut visited)
531 {
532 let target_flags = self
534 .ctx
535 .binder
536 .get_symbol_with_libs(target_sym_id, &lib_binders)
537 .map_or(0, |s| s.flags);
538 if (target_flags
539 & (symbol_flags::NAMESPACE_MODULE | symbol_flags::VALUE_MODULE))
540 != 0
541 {
542 return TypeSymbolResolution::Type(target_sym_id);
543 }
544 }
545 }
546
547 let is_value_only = (self.alias_resolves_to_value_only(sym_id, None)
549 || self.symbol_is_value_only(sym_id, None))
550 && !self.symbol_is_type_only(sym_id, None);
551 if is_value_only {
552 if value_only_candidate.is_none() {
553 value_only_candidate = Some(sym_id);
554 }
555 } else {
556 return TypeSymbolResolution::Type(sym_id);
558 }
559 }
560 }
561 }
562 }
563
564 let mut accept_type_symbol = |sym_id: SymbolId| -> bool {
565 let flags = self
567 .ctx
568 .binder
569 .get_symbol_with_libs(sym_id, &lib_binders)
570 .map_or(0, |s| s.flags);
571
572 let is_namespace_or_module =
575 (flags & (symbol_flags::NAMESPACE_MODULE | symbol_flags::VALUE_MODULE)) != 0;
576
577 if is_namespace_or_module {
578 return true;
579 }
580
581 if flags & symbol_flags::ALIAS != 0 {
584 let mut visited = Vec::new();
585 if let Some(target_sym_id) = self.resolve_alias_symbol(sym_id, &mut visited) {
586 let target_flags = self
587 .ctx
588 .binder
589 .get_symbol_with_libs(target_sym_id, &lib_binders)
590 .map_or(0, |s| s.flags);
591 if (target_flags
592 & (symbol_flags::NAMESPACE_MODULE | symbol_flags::VALUE_MODULE))
593 != 0
594 {
595 return true;
596 }
597 }
598 }
599
600 let is_value_only = (self.alias_resolves_to_value_only(sym_id, None)
601 || self.symbol_is_value_only(sym_id, None))
602 && !self.symbol_is_type_only(sym_id, None);
603 if is_value_only {
604 if value_only_candidate.is_none() {
605 value_only_candidate = Some(sym_id);
606 }
607 return false;
608 }
609 true
610 };
611
612 let resolved = self.ctx.binder.resolve_identifier_with_filter(
613 self.ctx.arena,
614 idx,
615 &lib_binders,
616 |sym_id| {
617 if should_skip_lib_symbol(sym_id) {
618 return false;
619 }
620 if let Some(symbol) = self.ctx.binder.get_symbol_with_libs(sym_id, &lib_binders) {
621 let is_class_member = Self::is_class_member_symbol(symbol.flags);
622 if is_class_member {
623 return false;
624 }
625 }
626 accept_type_symbol(sym_id)
627 },
628 );
629
630 if resolved.is_none()
631 && let Some(sym_id) =
632 self.resolve_identifier_symbol_from_all_binders(name, |sym_id, symbol| {
633 if should_skip_lib_symbol(sym_id) {
634 return false;
635 }
636
637 let is_class_member = Self::is_class_member_symbol(symbol.flags);
638 if is_class_member {
639 return false;
640 }
641 accept_type_symbol(sym_id)
642 })
643 {
644 let is_value_only = (self.alias_resolves_to_value_only(sym_id, None)
645 || self.symbol_is_value_only(sym_id, None))
646 && !self.symbol_is_type_only(sym_id, None);
647 if is_value_only {
648 return TypeSymbolResolution::ValueOnly(sym_id);
649 }
650 return TypeSymbolResolution::Type(sym_id);
651 }
652
653 let resolved = resolved.filter(|&sym_id| {
657 self.ctx
658 .binder
659 .get_symbol_with_libs(sym_id, &lib_binders)
660 .is_some_and(|s| s.escaped_name.as_str() == name)
661 });
662 if let Some(sym_id) = resolved {
663 if let Some(symbol) = self.ctx.binder.get_symbol_with_libs(sym_id, &lib_binders)
664 && symbol.flags & symbol_flags::ALIAS != 0
665 {
666 self.ctx.referenced_symbols.borrow_mut().insert(sym_id);
671 let mut visited_aliases = Vec::new();
672 if let Some(target_sym_id) = self.resolve_alias_symbol(sym_id, &mut visited_aliases)
673 {
674 let target_flags = self
675 .ctx
676 .binder
677 .get_symbol_with_libs(target_sym_id, &lib_binders)
678 .map_or(0, |s| s.flags);
679 let target_is_namespace_module = (target_flags
680 & (symbol_flags::NAMESPACE_MODULE | symbol_flags::VALUE_MODULE))
681 != 0;
682 let target_is_value_only = (self
683 .alias_resolves_to_value_only(target_sym_id, None)
684 || self.symbol_is_value_only(target_sym_id, None))
685 && !self.symbol_is_type_only(target_sym_id, None);
686 if target_is_value_only && !target_is_namespace_module {
687 return TypeSymbolResolution::ValueOnly(target_sym_id);
688 }
689 return TypeSymbolResolution::Type(target_sym_id);
690 }
691 }
692 return TypeSymbolResolution::Type(sym_id);
693 }
694
695 if let Some(value_only) = value_only_candidate {
696 TypeSymbolResolution::ValueOnly(value_only)
697 } else {
698 TypeSymbolResolution::NotFound
699 }
700 }
701
702 pub(crate) fn resolve_private_identifier_symbols(
712 &self,
713 idx: NodeIndex,
714 ) -> (Vec<SymbolId>, bool) {
715 self.ctx
716 .binder
717 .resolve_private_identifier_symbols(self.ctx.arena, idx)
718 }
719
720 pub(crate) fn resolve_qualified_symbol(&self, idx: NodeIndex) -> Option<SymbolId> {
725 let mut visited_aliases = Vec::new();
726 self.resolve_qualified_symbol_inner(idx, &mut visited_aliases, 0)
727 }
728
729 pub(crate) fn resolve_qualified_symbol_in_type_position(
731 &self,
732 idx: NodeIndex,
733 ) -> TypeSymbolResolution {
734 let mut visited_aliases = Vec::new();
735 self.resolve_qualified_symbol_inner_in_type_position(idx, &mut visited_aliases, 0)
736 }
737
738 pub(crate) fn resolve_qualified_symbol_inner_in_type_position(
740 &self,
741 idx: NodeIndex,
742 visited_aliases: &mut Vec<SymbolId>,
743 depth: usize,
744 ) -> TypeSymbolResolution {
745 const MAX_QUALIFIED_NAME_DEPTH: usize = 128;
747 if depth >= MAX_QUALIFIED_NAME_DEPTH {
748 return TypeSymbolResolution::NotFound;
749 }
750
751 let node = match self.ctx.arena.get(idx) {
752 Some(node) => node,
753 None => return TypeSymbolResolution::NotFound,
754 };
755
756 if node.kind == SyntaxKind::Identifier as u16 {
757 return match self.resolve_identifier_symbol_in_type_position(idx) {
758 TypeSymbolResolution::Type(sym_id) => {
759 let resolved = self
764 .resolve_alias_symbol(sym_id, visited_aliases)
765 .unwrap_or(sym_id);
766 TypeSymbolResolution::Type(resolved)
767 }
768 other => other,
769 };
770 }
771
772 if node.kind == SyntaxKind::StringLiteral as u16
773 || node.kind == SyntaxKind::NoSubstitutionTemplateLiteral as u16
774 {
775 let Some(literal) = self.ctx.arena.get_literal(node) else {
776 return TypeSymbolResolution::NotFound;
777 };
778 if let Some(sym_id) = self.ctx.binder.file_locals.get(&literal.text) {
779 let is_value_only = (self
780 .alias_resolves_to_value_only(sym_id, Some(&literal.text))
781 || self.symbol_is_value_only(sym_id, Some(&literal.text)))
782 && !self.symbol_is_type_only(sym_id, Some(&literal.text));
783 if is_value_only {
784 return TypeSymbolResolution::ValueOnly(sym_id);
785 }
786 let Some(sym_id) = self.resolve_alias_symbol(sym_id, visited_aliases) else {
787 return TypeSymbolResolution::NotFound;
788 };
789 return TypeSymbolResolution::Type(sym_id);
790 }
791 return TypeSymbolResolution::NotFound;
792 }
793
794 if node.kind == tsz_parser::parser::syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION {
795 let Some(access) = self.ctx.arena.get_access_expr(node) else {
796 return TypeSymbolResolution::NotFound;
797 };
798
799 let left_sym = match self.resolve_qualified_symbol_inner_in_type_position(
800 access.expression,
801 visited_aliases,
802 depth + 1,
803 ) {
804 TypeSymbolResolution::Type(sym_id) => sym_id,
805 other => return other,
806 };
807
808 let left_sym = self
809 .resolve_alias_symbol(left_sym, visited_aliases)
810 .unwrap_or(left_sym);
811
812 let right_name = match self
813 .ctx
814 .arena
815 .get_identifier_at(access.name_or_argument)
816 .map(|ident| ident.escaped_text.as_str())
817 {
818 Some(name) => name,
819 None => return TypeSymbolResolution::NotFound,
820 };
821
822 let lib_binders = self.get_lib_binders();
823 let Some(left_symbol) = self.ctx.binder.get_symbol_with_libs(left_sym, &lib_binders)
824 else {
825 return TypeSymbolResolution::NotFound;
826 };
827
828 if let Some(exports) = left_symbol.exports.as_ref()
829 && let Some(member_sym) = exports.get(right_name)
830 {
831 let is_value_only = (self
832 .alias_resolves_to_value_only(member_sym, Some(right_name))
833 || self.symbol_is_value_only(member_sym, Some(right_name)))
834 && !self.symbol_is_type_only(member_sym, Some(right_name));
835 if is_value_only {
836 return TypeSymbolResolution::ValueOnly(member_sym);
837 }
838 let member_sym = self
839 .resolve_alias_symbol(member_sym, visited_aliases)
840 .unwrap_or(member_sym);
841 return TypeSymbolResolution::Type(member_sym);
842 }
843
844 if let Some(ref module_specifier) = left_symbol.import_module
845 && !((left_symbol.flags & symbol_flags::ALIAS) != 0
846 && self
847 .ctx
848 .module_resolves_to_non_module_entity(module_specifier))
849 && let Some(reexported_sym) = self.resolve_reexported_member_symbol(
850 module_specifier,
851 right_name,
852 visited_aliases,
853 )
854 {
855 let is_value_only = (self
856 .alias_resolves_to_value_only(reexported_sym, Some(right_name))
857 || self.symbol_is_value_only(reexported_sym, Some(right_name)))
858 && !self.symbol_is_type_only(reexported_sym, Some(right_name));
859 if is_value_only {
860 return TypeSymbolResolution::ValueOnly(reexported_sym);
861 }
862 return TypeSymbolResolution::Type(reexported_sym);
863 }
864
865 if let Some(reexported_sym) =
866 self.resolve_member_from_import_equals_alias(left_sym, right_name, visited_aliases)
867 {
868 let is_value_only = (self
869 .alias_resolves_to_value_only(reexported_sym, Some(right_name))
870 || self.symbol_is_value_only(reexported_sym, Some(right_name)))
871 && !self.symbol_is_type_only(reexported_sym, Some(right_name));
872 if is_value_only {
873 return TypeSymbolResolution::ValueOnly(reexported_sym);
874 }
875 return TypeSymbolResolution::Type(reexported_sym);
876 }
877
878 return TypeSymbolResolution::NotFound;
879 }
880
881 if node.kind != tsz_parser::parser::syntax_kind_ext::QUALIFIED_NAME {
882 return TypeSymbolResolution::NotFound;
883 }
884
885 let qn = match self.ctx.arena.get_qualified_name(node) {
886 Some(qn) => qn,
887 None => return TypeSymbolResolution::NotFound,
888 };
889 let left_sym = match self.resolve_qualified_symbol_inner_in_type_position(
890 qn.left,
891 visited_aliases,
892 depth + 1,
893 ) {
894 TypeSymbolResolution::Type(sym_id) => sym_id,
895 other => return other,
896 };
897 let left_sym = self
898 .resolve_alias_symbol(left_sym, visited_aliases)
899 .unwrap_or(left_sym);
900 let right_name = match self
901 .ctx
902 .arena
903 .get(qn.right)
904 .and_then(|node| self.ctx.arena.get_identifier(node))
905 .map(|ident| ident.escaped_text.as_str())
906 {
907 Some(name) => name,
908 None => return TypeSymbolResolution::NotFound,
909 };
910
911 let lib_binders = self.get_lib_binders();
913 let Some(left_symbol) = self.ctx.binder.get_symbol_with_libs(left_sym, &lib_binders) else {
914 return TypeSymbolResolution::NotFound;
915 };
916 if let Some(exports) = left_symbol.exports.as_ref()
918 && let Some(member_sym) = exports.get(right_name)
919 {
920 let is_value_only = (self.alias_resolves_to_value_only(member_sym, Some(right_name))
921 || self.symbol_is_value_only(member_sym, Some(right_name)))
922 && !self.symbol_is_type_only(member_sym, Some(right_name));
923 if is_value_only {
924 return TypeSymbolResolution::ValueOnly(member_sym);
925 }
926 return TypeSymbolResolution::Type(
927 self.resolve_alias_symbol(member_sym, visited_aliases)
928 .unwrap_or(member_sym),
929 );
930 }
931
932 if let Some(ref module_specifier) = left_symbol.import_module {
934 if (left_symbol.flags & symbol_flags::ALIAS) != 0
935 && self
936 .ctx
937 .module_resolves_to_non_module_entity(module_specifier)
938 {
939 return TypeSymbolResolution::NotFound;
940 }
941 if let Some(reexported_sym) =
942 self.resolve_reexported_member_symbol(module_specifier, right_name, visited_aliases)
943 {
944 let is_value_only = (self
945 .alias_resolves_to_value_only(reexported_sym, Some(right_name))
946 || self.symbol_is_value_only(reexported_sym, Some(right_name)))
947 && !self.symbol_is_type_only(reexported_sym, Some(right_name));
948 if is_value_only {
949 return TypeSymbolResolution::ValueOnly(reexported_sym);
950 }
951 return TypeSymbolResolution::Type(reexported_sym);
952 }
953 }
954
955 if let Some(reexported_sym) =
956 self.resolve_member_from_import_equals_alias(left_sym, right_name, visited_aliases)
957 {
958 let is_value_only = (self
959 .alias_resolves_to_value_only(reexported_sym, Some(right_name))
960 || self.symbol_is_value_only(reexported_sym, Some(right_name)))
961 && !self.symbol_is_type_only(reexported_sym, Some(right_name));
962 if is_value_only {
963 return TypeSymbolResolution::ValueOnly(reexported_sym);
964 }
965 return TypeSymbolResolution::Type(reexported_sym);
966 }
967
968 TypeSymbolResolution::NotFound
969 }
970
971 fn resolve_identifier_symbol_from_all_binders(
972 &self,
973 name: &str,
974 mut accept: impl FnMut(SymbolId, &tsz_binder::Symbol) -> bool,
975 ) -> Option<SymbolId> {
976 let all_binders = self.ctx.all_binders.as_ref()?;
977
978 for (file_idx, binder) in all_binders.iter().enumerate() {
979 if let Some(sym_id) = binder.file_locals.get(name) {
980 let Some(sym_symbol) = binder.get_symbol(sym_id) else {
981 continue;
982 };
983 if !accept(sym_id, sym_symbol) {
984 continue;
985 }
986 if let Some(local_symbol) = self.ctx.binder.get_symbol(sym_id) {
987 if local_symbol.escaped_name != name {
988 self.ctx
989 .cross_file_symbol_targets
990 .borrow_mut()
991 .entry(sym_id)
992 .or_insert(file_idx);
993 }
994 } else {
995 self.ctx
996 .cross_file_symbol_targets
997 .borrow_mut()
998 .entry(sym_id)
999 .or_insert(file_idx);
1000 }
1001 return Some(sym_id);
1002 }
1003 }
1004
1005 None
1006 }
1007
1008 pub(crate) fn resolve_qualified_symbol_inner(
1010 &self,
1011 idx: NodeIndex,
1012 visited_aliases: &mut Vec<SymbolId>,
1013 depth: usize,
1014 ) -> Option<SymbolId> {
1015 const MAX_QUALIFIED_NAME_DEPTH: usize = 128;
1017 if depth >= MAX_QUALIFIED_NAME_DEPTH {
1018 return None;
1019 }
1020
1021 let node = self.ctx.arena.get(idx)?;
1022
1023 if node.kind == SyntaxKind::Identifier as u16 {
1024 let sym_id = self.resolve_identifier_symbol(idx)?;
1025 return self
1028 .resolve_alias_symbol(sym_id, visited_aliases)
1029 .or(Some(sym_id));
1030 }
1031
1032 if node.kind == SyntaxKind::StringLiteral as u16
1033 || node.kind == SyntaxKind::NoSubstitutionTemplateLiteral as u16
1034 {
1035 let literal = self.ctx.arena.get_literal(node)?;
1036 if let Some(sym_id) = self.ctx.binder.file_locals.get(&literal.text) {
1037 return self.resolve_alias_symbol(sym_id, visited_aliases);
1038 }
1039 return None;
1040 }
1041
1042 if node.kind == tsz_parser::parser::syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION {
1043 let access = self.ctx.arena.get_access_expr(node)?;
1044 let left_sym =
1045 self.resolve_qualified_symbol_inner(access.expression, visited_aliases, depth + 1)?;
1046 let left_sym = self
1047 .resolve_alias_symbol(left_sym, visited_aliases)
1048 .unwrap_or(left_sym);
1049 let right_name = self
1050 .ctx
1051 .arena
1052 .get_identifier_at(access.name_or_argument)
1053 .map(|ident| ident.escaped_text.as_str())?;
1054
1055 let lib_binders = self.get_lib_binders();
1056 let left_symbol = self
1057 .ctx
1058 .binder
1059 .get_symbol_with_libs(left_sym, &lib_binders)?;
1060
1061 if let Some(exports) = left_symbol.exports.as_ref()
1062 && let Some(member_sym) = exports.get(right_name)
1063 {
1064 return Some(
1065 self.resolve_alias_symbol(member_sym, visited_aliases)
1066 .unwrap_or(member_sym),
1067 );
1068 }
1069
1070 if let Some(ref module_specifier) = left_symbol.import_module {
1071 if (left_symbol.flags & symbol_flags::ALIAS) != 0
1072 && self
1073 .ctx
1074 .module_resolves_to_non_module_entity(module_specifier)
1075 {
1076 return None;
1077 }
1078 return self.resolve_reexported_member_symbol(
1079 module_specifier,
1080 right_name,
1081 visited_aliases,
1082 );
1083 }
1084
1085 if let Some(reexported_sym) =
1086 self.resolve_member_from_import_equals_alias(left_sym, right_name, visited_aliases)
1087 {
1088 return Some(reexported_sym);
1089 }
1090
1091 return None;
1092 }
1093
1094 if node.kind != tsz_parser::parser::syntax_kind_ext::QUALIFIED_NAME {
1095 return None;
1096 }
1097
1098 let qn = self.ctx.arena.get_qualified_name(node)?;
1099 let left_sym = self.resolve_qualified_symbol_inner(qn.left, visited_aliases, depth + 1)?;
1100 let left_sym = self
1101 .resolve_alias_symbol(left_sym, visited_aliases)
1102 .unwrap_or(left_sym);
1103 let right_name = self
1104 .ctx
1105 .arena
1106 .get(qn.right)
1107 .and_then(|node| self.ctx.arena.get_identifier(node))
1108 .map(|ident| ident.escaped_text.as_str())?;
1109
1110 let lib_binders = self.get_lib_binders();
1111 let left_symbol = self
1112 .ctx
1113 .binder
1114 .get_symbol_with_libs(left_sym, &lib_binders)?;
1115
1116 if let Some(exports) = left_symbol.exports.as_ref()
1118 && let Some(member_sym) = exports.get(right_name)
1119 {
1120 return Some(
1121 self.resolve_alias_symbol(member_sym, visited_aliases)
1122 .unwrap_or(member_sym),
1123 );
1124 }
1125
1126 if let Some(ref module_specifier) = left_symbol.import_module {
1129 if (left_symbol.flags & symbol_flags::ALIAS) != 0
1130 && self
1131 .ctx
1132 .module_resolves_to_non_module_entity(module_specifier)
1133 {
1134 return None;
1135 }
1136 if let Some(reexported_sym) =
1137 self.resolve_reexported_member_symbol(module_specifier, right_name, visited_aliases)
1138 {
1139 return Some(reexported_sym);
1140 }
1141 }
1142
1143 if let Some(reexported_sym) =
1144 self.resolve_member_from_import_equals_alias(left_sym, right_name, visited_aliases)
1145 {
1146 return Some(reexported_sym);
1147 }
1148
1149 None
1150 }
1151
1152 fn resolve_member_from_import_equals_alias(
1153 &self,
1154 alias_sym: SymbolId,
1155 member_name: &str,
1156 visited_aliases: &mut Vec<SymbolId>,
1157 ) -> Option<SymbolId> {
1158 let symbol = self.ctx.binder.get_symbol(alias_sym)?;
1159 if symbol.flags & symbol_flags::ALIAS == 0 {
1160 return None;
1161 }
1162
1163 let decl_idx = if symbol.value_declaration.is_some() {
1164 symbol.value_declaration
1165 } else {
1166 symbol
1167 .declarations
1168 .iter()
1169 .copied()
1170 .find(|idx| idx.is_some())
1171 .unwrap_or(NodeIndex::NONE)
1172 };
1173
1174 if decl_idx.is_some()
1175 && let Some(decl_node) = self.ctx.arena.get(decl_idx)
1176 && decl_node.kind == syntax_kind_ext::IMPORT_EQUALS_DECLARATION
1177 && let Some(import) = self.ctx.arena.get_import_decl(decl_node)
1178 && let Some(module_specifier) =
1179 self.get_require_module_specifier(import.module_specifier)
1180 {
1181 if self
1182 .ctx
1183 .module_resolves_to_non_module_entity(&module_specifier)
1184 {
1185 return None;
1186 }
1187 return self.resolve_reexported_member_symbol(
1188 &module_specifier,
1189 member_name,
1190 visited_aliases,
1191 );
1192 }
1193
1194 None
1195 }
1196
1197 pub(crate) fn resolve_reexported_member_symbol(
1202 &self,
1203 module_specifier: &str,
1204 member_name: &str,
1205 visited_aliases: &mut Vec<SymbolId>,
1206 ) -> Option<SymbolId> {
1207 let mut visited_modules = rustc_hash::FxHashSet::default();
1208 self.resolve_reexported_member_symbol_inner(
1209 module_specifier,
1210 member_name,
1211 visited_aliases,
1212 &mut visited_modules,
1213 )
1214 }
1215
1216 fn resolve_member_from_module_exports(
1217 &self,
1218 binder: &tsz_binder::BinderState,
1219 exports_table: &tsz_binder::SymbolTable,
1220 member_name: &str,
1221 visited_aliases: &mut Vec<SymbolId>,
1222 ) -> Option<SymbolId> {
1223 let can_resolve_aliases = std::ptr::eq(binder, self.ctx.binder);
1224
1225 if let Some(sym_id) = exports_table.get(member_name) {
1226 if can_resolve_aliases {
1227 return Some(
1228 self.resolve_alias_symbol(sym_id, visited_aliases)
1229 .unwrap_or(sym_id),
1230 );
1231 }
1232 return Some(sym_id);
1233 }
1234
1235 let export_equals_sym = exports_table.get("export=")?;
1236 let mut candidate_symbol_ids = vec![export_equals_sym];
1237 if can_resolve_aliases {
1238 let resolved_export_equals = self
1239 .resolve_alias_symbol(export_equals_sym, visited_aliases)
1240 .unwrap_or(export_equals_sym);
1241 if resolved_export_equals != export_equals_sym {
1242 candidate_symbol_ids.push(resolved_export_equals);
1243 }
1244 }
1245
1246 for candidate_symbol_id in candidate_symbol_ids {
1247 let Some(target_symbol) = binder.get_symbol(candidate_symbol_id) else {
1248 continue;
1249 };
1250
1251 if let Some(exports) = target_symbol.exports.as_ref()
1252 && let Some(sym_id) = exports.get(member_name)
1253 {
1254 if can_resolve_aliases {
1255 return Some(
1256 self.resolve_alias_symbol(sym_id, visited_aliases)
1257 .unwrap_or(sym_id),
1258 );
1259 }
1260 return Some(sym_id);
1261 }
1262
1263 if let Some(members) = target_symbol.members.as_ref()
1264 && let Some(sym_id) = members.get(member_name)
1265 {
1266 if can_resolve_aliases {
1267 return Some(
1268 self.resolve_alias_symbol(sym_id, visited_aliases)
1269 .unwrap_or(sym_id),
1270 );
1271 }
1272 return Some(sym_id);
1273 }
1274
1275 for merged_candidate_id in binder
1278 .get_symbols()
1279 .find_all_by_name(&target_symbol.escaped_name)
1280 {
1281 let Some(merged_symbol) = binder.get_symbol(merged_candidate_id) else {
1282 continue;
1283 };
1284 if (merged_symbol.flags
1285 & (symbol_flags::MODULE
1286 | symbol_flags::NAMESPACE_MODULE
1287 | symbol_flags::VALUE_MODULE))
1288 == 0
1289 {
1290 continue;
1291 }
1292
1293 if let Some(exports) = merged_symbol.exports.as_ref()
1294 && let Some(sym_id) = exports.get(member_name)
1295 {
1296 if can_resolve_aliases {
1297 return Some(
1298 self.resolve_alias_symbol(sym_id, visited_aliases)
1299 .unwrap_or(sym_id),
1300 );
1301 }
1302 return Some(sym_id);
1303 }
1304
1305 if let Some(members) = merged_symbol.members.as_ref()
1306 && let Some(sym_id) = members.get(member_name)
1307 {
1308 if can_resolve_aliases {
1309 return Some(
1310 self.resolve_alias_symbol(sym_id, visited_aliases)
1311 .unwrap_or(sym_id),
1312 );
1313 }
1314 return Some(sym_id);
1315 }
1316 }
1317 }
1318
1319 None
1320 }
1321
1322 fn resolve_reexported_member_symbol_inner(
1324 &self,
1325 module_specifier: &str,
1326 member_name: &str,
1327 visited_aliases: &mut Vec<SymbolId>,
1328 visited_modules: &mut rustc_hash::FxHashSet<(String, String)>,
1329 ) -> Option<SymbolId> {
1330 let key = (module_specifier.to_string(), member_name.to_string());
1332 if visited_modules.contains(&key) {
1333 return None;
1334 }
1335 visited_modules.insert(key);
1336
1337 if let Some(module_exports) = self.ctx.binder.module_exports.get(module_specifier)
1339 && let Some(sym_id) = self.resolve_member_from_module_exports(
1340 self.ctx.binder,
1341 module_exports,
1342 member_name,
1343 visited_aliases,
1344 )
1345 {
1346 return Some(sym_id);
1347 }
1348
1349 if let Some(sym_id) = self.resolve_cross_file_export(module_specifier, member_name) {
1351 return Some(
1352 self.resolve_alias_symbol(sym_id, visited_aliases)
1353 .unwrap_or(sym_id),
1354 );
1355 }
1356
1357 if let Some(file_reexports) = self.ctx.binder.reexports.get(module_specifier)
1359 && let Some((source_module, original_name)) = file_reexports.get(member_name)
1360 {
1361 let name_to_lookup = original_name.as_deref().unwrap_or(member_name);
1362 return self.resolve_reexported_member_symbol_inner(
1363 source_module,
1364 name_to_lookup,
1365 visited_aliases,
1366 visited_modules,
1367 );
1368 }
1369
1370 if let Some(source_modules) = self.ctx.binder.wildcard_reexports.get(module_specifier) {
1375 let mut found_result: Option<SymbolId> = None;
1376 let mut found_count = 0;
1377
1378 for source_module in source_modules {
1379 if let Some(sym_id) = self.resolve_reexported_member_symbol_inner(
1380 source_module,
1381 member_name,
1382 visited_aliases,
1383 visited_modules,
1384 ) {
1385 found_count += 1;
1386 if found_count == 1 {
1387 found_result = Some(sym_id);
1388 } else {
1389 return None;
1391 }
1392 }
1393 }
1394
1395 if found_result.is_some() {
1396 return found_result;
1397 }
1398 }
1399
1400 None
1401 }
1402
1403 pub(crate) fn lookup_type_parameter(&self, name: &str) -> Option<TypeId> {
1412 self.ctx.type_parameter_scope.get(name).copied()
1413 }
1414
1415 pub(crate) fn get_type_param_bindings(&self) -> Vec<(tsz_common::interner::Atom, TypeId)> {
1419 self.ctx
1420 .type_parameter_scope
1421 .iter()
1422 .map(|(name, &type_id)| (self.ctx.types.intern_string(name), type_id))
1423 .collect()
1424 }
1425
1426 pub(crate) fn expression_text(&self, idx: NodeIndex) -> Option<String> {
1435 let node = self.ctx.arena.get(idx)?;
1436 match node.kind {
1437 k if k == SyntaxKind::Identifier as u16 => self
1438 .ctx
1439 .arena
1440 .get_identifier(node)
1441 .map(|ident| ident.escaped_text.clone()),
1442 k if k == syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION => {
1443 let access = self.ctx.arena.get_access_expr(node)?;
1444 let left = self.expression_text(access.expression)?;
1445 let right = self.expression_text(access.name_or_argument)?;
1446 Some(format!("{left}.{right}"))
1447 }
1448 _ => None,
1449 }
1450 }
1451
1452 pub(crate) fn entity_name_text(&self, idx: NodeIndex) -> Option<String> {
1455 let node = self.ctx.arena.get(idx)?;
1456 if node.kind == SyntaxKind::Identifier as u16 {
1457 return self
1458 .ctx
1459 .arena
1460 .get_identifier(node)
1461 .map(|ident| ident.escaped_text.clone());
1462 }
1463 if node.kind == syntax_kind_ext::QUALIFIED_NAME {
1464 let qn = self.ctx.arena.get_qualified_name(node)?;
1465 let left = self.entity_name_text(qn.left)?;
1466 let right = self.entity_name_text(qn.right)?;
1467 let mut combined = String::with_capacity(left.len() + 1 + right.len());
1468 combined.push_str(&left);
1469 combined.push('.');
1470 combined.push_str(&right);
1471 return Some(combined);
1472 }
1473 None
1474 }
1475
1476 pub(crate) fn resolve_type_symbol_for_lowering(&self, idx: NodeIndex) -> Option<u32> {
1485 if let Some(node) = self.ctx.arena.get(idx)
1488 && let Some(ident) = self.ctx.arena.get_identifier(node)
1489 && is_compiler_managed_type(ident.escaped_text.as_str())
1490 {
1491 return None;
1492 }
1493
1494 let sym_id = match self.resolve_qualified_symbol_in_type_position(idx) {
1495 TypeSymbolResolution::Type(sym_id) => sym_id,
1496 _ => return None,
1497 };
1498 let lib_binders = self.get_lib_binders();
1499 let symbol = self.ctx.binder.get_symbol_with_libs(sym_id, &lib_binders)?;
1500 ((symbol.flags & symbol_flags::TYPE) != 0).then_some(sym_id.0)
1501 }
1502
1503 pub(crate) fn resolve_value_symbol_for_lowering(&self, idx: NodeIndex) -> Option<u32> {
1507 if let Some(node) = self.ctx.arena.get(idx) {
1508 if node.kind == SyntaxKind::Identifier as u16
1509 && let Some(sym_id) = self.resolve_identifier_symbol(idx)
1510 && self.alias_resolves_to_type_only(sym_id)
1511 {
1512 return None;
1513 }
1514 if node.kind == syntax_kind_ext::QUALIFIED_NAME {
1515 let mut current = idx;
1516 while let Some(node) = self.ctx.arena.get(current) {
1517 if node.kind == SyntaxKind::Identifier as u16 {
1518 if let Some(sym_id) = self.resolve_identifier_symbol(current)
1519 && self.alias_resolves_to_type_only(sym_id)
1520 {
1521 return None;
1522 }
1523 break;
1524 }
1525 if node.kind != syntax_kind_ext::QUALIFIED_NAME {
1526 break;
1527 }
1528 let Some(qn) = self.ctx.arena.get_qualified_name(node) else {
1529 break;
1530 };
1531 current = qn.left;
1532 }
1533 }
1534 }
1535 let sym_id = self.resolve_qualified_symbol(idx)?;
1536 let lib_binders = self.get_lib_binders();
1537 let symbol = self.ctx.binder.get_symbol_with_libs(sym_id, &lib_binders)?;
1538 if symbol.is_type_only {
1539 return None;
1540 }
1541 if (symbol.flags & (symbol_flags::VALUE | symbol_flags::ALIAS)) != 0 {
1542 return Some(sym_id.0);
1543 }
1544
1545 let name = self
1550 .ctx
1551 .arena
1552 .get(idx)
1553 .and_then(|n| self.ctx.arena.get_identifier(n))
1554 .map(|i| i.escaped_text.as_str());
1555 if let Some(name) = name {
1556 if let Some(val_sym_id) = self.ctx.binder.file_locals.get(name)
1558 && let Some(val_symbol) = self
1559 .ctx
1560 .binder
1561 .get_symbol_with_libs(val_sym_id, &lib_binders)
1562 && (val_symbol.flags & (symbol_flags::VALUE | symbol_flags::ALIAS)) != 0
1563 && !val_symbol.is_type_only
1564 {
1565 return Some(val_sym_id.0);
1566 }
1567 for lib_binder in &lib_binders {
1569 if let Some(val_sym_id) = lib_binder.file_locals.get(name)
1570 && let Some(val_symbol) = lib_binder.get_symbol(val_sym_id)
1571 && (val_symbol.flags & (symbol_flags::VALUE | symbol_flags::ALIAS)) != 0
1572 && !val_symbol.is_type_only
1573 {
1574 return Some(val_sym_id.0);
1575 }
1576 }
1577 }
1578
1579 None
1580 }
1581}