llmcc_python/
bind.rs

1use llmcc_core::context::CompileUnit;
2use llmcc_core::interner::InternedStr;
3use llmcc_core::ir::HirNode;
4use llmcc_core::symbol::{Scope, ScopeStack, Symbol, SymbolKind};
5use std::collections::HashSet;
6use std::path::{Path, PathBuf};
7
8use crate::token::{AstVisitorPython, LangPython};
9
10#[derive(Debug, Default)]
11pub struct BindingResult {
12    pub calls: Vec<CallBinding>,
13}
14
15#[derive(Debug, Clone)]
16pub struct CallBinding {
17    pub caller: String,
18    pub target: String,
19}
20
21#[derive(Debug)]
22struct SymbolBinder<'tcx> {
23    unit: CompileUnit<'tcx>,
24    scopes: ScopeStack<'tcx>,
25    calls: Vec<CallBinding>,
26    module_imports: Vec<&'tcx Symbol>,
27}
28
29impl<'tcx> SymbolBinder<'tcx> {
30    pub fn new(unit: CompileUnit<'tcx>, globals: &'tcx Scope<'tcx>) -> Self {
31        let mut scopes = ScopeStack::new(&unit.cc.arena, &unit.cc.interner, &unit.cc.symbol_map);
32        scopes.push(globals);
33        Self {
34            unit,
35            scopes,
36            calls: Vec::new(),
37            module_imports: Vec::new(),
38        }
39    }
40
41    fn interner(&self) -> &llmcc_core::interner::InternPool {
42        self.unit.interner()
43    }
44
45    fn current_symbol(&self) -> Option<&'tcx Symbol> {
46        self.scopes.scoped_symbol()
47    }
48
49    fn module_segments_from_path(path: &Path) -> Vec<String> {
50        if path.extension().and_then(|ext| ext.to_str()) != Some("py") {
51            return Vec::new();
52        }
53
54        let mut segments: Vec<String> = Vec::new();
55
56        if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
57            if stem != "__init__" && !stem.is_empty() {
58                segments.push(stem.to_string());
59            }
60        }
61
62        let mut current = path.parent();
63        while let Some(dir) = current {
64            let dir_name = match dir.file_name().and_then(|n| n.to_str()) {
65                Some(name) if !name.is_empty() => name.to_string(),
66                _ => break,
67            };
68
69            let has_init = dir.join("__init__.py").exists() || dir.join("__init__.pyi").exists();
70            if has_init {
71                segments.push(dir_name);
72                current = dir.parent();
73                continue;
74            }
75
76            if segments.is_empty() {
77                segments.push(dir_name);
78            }
79            break;
80        }
81
82        segments.reverse();
83        segments
84    }
85
86    fn ensure_module_symbol(&mut self, node: &HirNode<'tcx>) -> Option<&'tcx Symbol> {
87        let scope = self.unit.alloc_scope(node.hir_id());
88        if let Some(symbol) = scope.symbol() {
89            return Some(symbol);
90        }
91
92        let raw_path = self.unit.file_path().or_else(|| self.unit.file().path());
93        let path = raw_path
94            .map(PathBuf::from)
95            .and_then(|p| p.canonicalize().ok().or(Some(p)))
96            .unwrap_or_else(|| PathBuf::from("__module__"));
97
98        let segments = Self::module_segments_from_path(&path);
99        let interner = self.unit.interner();
100
101        let (name, fqn) = if segments.is_empty() {
102            let fallback = path
103                .file_stem()
104                .and_then(|s| s.to_str())
105                .unwrap_or("__module__")
106                .to_string();
107            (fallback.clone(), fallback)
108        } else {
109            let name = segments
110                .last()
111                .cloned()
112                .unwrap_or_else(|| "__module__".to_string());
113            let fqn = segments.join("::");
114            (name, fqn)
115        };
116
117        let key = interner.intern(&name);
118        let symbol = Symbol::new(node.hir_id(), name.clone(), key);
119        let symbol = self.unit.cc.arena.alloc(symbol);
120        symbol.set_kind(SymbolKind::Module);
121        symbol.set_unit_index(self.unit.index);
122        symbol.set_fqn(fqn, interner);
123
124        self.unit
125            .cc
126            .symbol_map
127            .borrow_mut()
128            .insert(symbol.id, symbol);
129
130        let _ = self.scopes.insert_symbol(symbol, true);
131        scope.set_symbol(Some(symbol));
132        Some(symbol)
133    }
134
135    #[allow(dead_code)]
136    fn visit_children_scope(&mut self, node: &HirNode<'tcx>, symbol: Option<&'tcx Symbol>) {
137        let depth = self.scopes.depth();
138        if let Some(symbol) = symbol {
139            if let Some(parent) = self.scopes.scoped_symbol() {
140                parent.add_dependency(symbol);
141            }
142        }
143
144        let scope = self.unit.opt_get_scope(node.hir_id());
145        if let Some(scope) = scope {
146            self.scopes.push_with_symbol(scope, symbol);
147            self.visit_children(node);
148            self.scopes.pop_until(depth);
149        } else {
150            self.visit_children(node);
151        }
152    }
153
154    fn lookup_symbol_suffix(
155        &mut self,
156        suffix: &[InternedStr],
157        kind: Option<SymbolKind>,
158    ) -> Option<&'tcx Symbol> {
159        let file_index = self.unit.index;
160        self.scopes
161            .find_scoped_suffix_with_filters(suffix, kind, Some(file_index))
162            .or_else(|| {
163                self.scopes
164                    .find_scoped_suffix_with_filters(suffix, kind, None)
165            })
166            .or_else(|| {
167                self.scopes
168                    .find_global_suffix_with_filters(suffix, kind, Some(file_index))
169            })
170            .or_else(|| {
171                self.scopes
172                    .find_global_suffix_with_filters(suffix, kind, None)
173            })
174    }
175
176    fn add_symbol_relation(&mut self, symbol: Option<&'tcx Symbol>) {
177        let Some(target) = symbol else { return };
178        let Some(current) = self.current_symbol() else {
179            return;
180        };
181
182        current.add_dependency(target);
183
184        match current.kind() {
185            SymbolKind::Function => {
186                let parent_class = self
187                    .scopes
188                    .iter()
189                    .rev()
190                    .filter_map(|scope| scope.symbol())
191                    .find(|symbol| symbol.kind() == SymbolKind::Struct);
192
193                if let Some(class_symbol) = parent_class {
194                    class_symbol.add_dependency(target);
195                }
196            }
197            SymbolKind::Module => {
198                if !self.module_imports.iter().any(|&sym| sym.id == target.id) {
199                    self.module_imports.push(target);
200                }
201            }
202            _ => {}
203        }
204    }
205
206    fn record_segments_dependency(&mut self, segments: &[String]) {
207        if segments.is_empty() {
208            return;
209        }
210
211        let interner = self.interner();
212        let suffix: Vec<_> = segments.iter().rev().map(|s| interner.intern(s)).collect();
213
214        let target = self
215            .lookup_symbol_suffix(&suffix, Some(SymbolKind::Struct))
216            .or_else(|| self.lookup_symbol_suffix(&suffix, Some(SymbolKind::Enum)))
217            .or_else(|| self.lookup_symbol_suffix(&suffix, Some(SymbolKind::Module)))
218            .or_else(|| self.lookup_symbol_suffix(&suffix, None));
219
220        self.add_symbol_relation(target);
221    }
222
223    fn build_attribute_path(&mut self, node: &HirNode<'tcx>, out: &mut Vec<String>) {
224        if node.kind_id() == LangPython::attribute {
225            if let Some(object_node) = node.opt_child_by_field(self.unit, LangPython::field_object)
226            {
227                self.build_attribute_path(&object_node, out);
228            }
229            if let Some(attr_node) = node.opt_child_by_field(self.unit, LangPython::field_attribute)
230            {
231                if let Some(ident) = attr_node.as_ident() {
232                    out.push(ident.name.clone());
233                }
234            }
235        } else if node.kind_id() == LangPython::identifier {
236            if let Some(ident) = node.as_ident() {
237                out.push(ident.name.clone());
238            }
239        } else {
240            for child_id in node.children() {
241                let child = self.unit.hir_node(*child_id);
242                self.build_attribute_path(&child, out);
243            }
244        }
245    }
246
247    fn collect_identifier_paths(&mut self, node: &HirNode<'tcx>, results: &mut Vec<Vec<String>>) {
248        if node.kind_id() == LangPython::identifier {
249            if let Some(ident) = node.as_ident() {
250                results.push(vec![ident.name.clone()]);
251            }
252            return;
253        }
254
255        if node.kind_id() == LangPython::attribute {
256            let mut path = Vec::new();
257            self.build_attribute_path(node, &mut path);
258            if !path.is_empty() {
259                results.push(path);
260            }
261            return;
262        }
263
264        for child_id in node.children() {
265            let child = self.unit.hir_node(*child_id);
266            self.collect_identifier_paths(&child, results);
267        }
268    }
269
270    fn add_type_dependencies(&mut self, node: &HirNode<'tcx>) {
271        let mut paths = Vec::new();
272        self.collect_identifier_paths(node, &mut paths);
273
274        let mut seen = HashSet::new();
275        for path in paths {
276            if path.is_empty() {
277                continue;
278            }
279            let key = path.join("::");
280            if seen.insert(key) {
281                self.record_segments_dependency(&path);
282            }
283        }
284    }
285
286    fn record_import_path(&mut self, path: &str) {
287        let segments: Vec<String> = path
288            .split('.')
289            .filter(|segment| !segment.is_empty())
290            .map(|segment| segment.trim().to_string())
291            .collect();
292        if segments.is_empty() {
293            return;
294        }
295
296        let interner = self.interner();
297        let suffix: Vec<_> = segments.iter().rev().map(|s| interner.intern(s)).collect();
298
299        let target = self
300            .lookup_symbol_suffix(&suffix, Some(SymbolKind::Struct))
301            .or_else(|| self.lookup_symbol_suffix(&suffix, Some(SymbolKind::Enum)))
302            .or_else(|| self.lookup_symbol_suffix(&suffix, Some(SymbolKind::Module)))
303            .or_else(|| self.lookup_symbol_suffix(&suffix, None));
304
305        self.add_symbol_relation(target);
306    }
307
308    fn visit_children(&mut self, node: &HirNode<'tcx>) {
309        // Use HIR children instead of tree-sitter children
310        for child_id in node.children() {
311            let child = self.unit.hir_node(*child_id);
312            self.visit_node(child);
313        }
314    }
315
316    fn visit_decorated_def(&mut self, node: &HirNode<'tcx>) {
317        let mut decorator_symbols = Vec::new();
318        let mut definition_idx = None;
319
320        for (idx, child_id) in node.children().iter().enumerate() {
321            let child = self.unit.hir_node(*child_id);
322            let kind_id = child.kind_id();
323
324            if kind_id == LangPython::decorator {
325                let content = self.unit.file().content();
326                let ts_node = child.inner_ts_node();
327                if let Ok(decorator_text) = ts_node.utf8_text(&content) {
328                    let decorator_name = decorator_text.trim_start_matches('@').trim();
329                    let key = self.interner().intern(decorator_name);
330                    if let Some(decorator_symbol) =
331                        self.lookup_symbol_suffix(&[key], Some(SymbolKind::Function))
332                    {
333                        decorator_symbols.push(decorator_symbol);
334                    }
335                }
336            } else if kind_id == LangPython::function_definition
337                || kind_id == LangPython::class_definition
338            {
339                definition_idx = Some(idx);
340                break;
341            }
342        }
343
344        if let Some(idx) = definition_idx {
345            let definition_id = node.children()[idx];
346            let definition = self.unit.hir_node(definition_id);
347            self.visit_definition_node(&definition, &decorator_symbols);
348        }
349    }
350
351    fn visit_call_impl(&mut self, node: &HirNode<'tcx>) {
352        // Extract function being called
353        let ts_node = node.inner_ts_node();
354
355        // In tree-sitter-python, call has a `function` field
356        if let Some(func_node) = ts_node.child_by_field_name("function") {
357            let content = self.unit.file().content();
358            let record_target = |name: &str, this: &mut SymbolBinder<'tcx>| {
359                let key = this.interner().intern(name);
360
361                // First try to find in current scoped context (for method calls)
362                if let Some(target) = this.lookup_symbol_suffix(&[key], Some(SymbolKind::Function))
363                {
364                    this.add_symbol_relation(Some(target));
365                    let caller_name = this
366                        .current_symbol()
367                        .map(|s| s.fqn_name.borrow().clone())
368                        .unwrap_or_else(|| "<module>".to_string());
369                    let target_name = target.fqn_name.borrow().clone();
370                    this.calls.push(CallBinding {
371                        caller: caller_name,
372                        target: target_name,
373                    });
374                    return true;
375                }
376
377                // Try to find a struct (class) constructor call
378                if let Some(target) = this.lookup_symbol_suffix(&[key], Some(SymbolKind::Struct)) {
379                    this.add_symbol_relation(Some(target));
380                    return true;
381                }
382
383                // For method calls (self.method()), try looking up within parent class
384                // If current symbol is a method, parent is the class
385                if let Some(current) = this.current_symbol() {
386                    if current.kind() == SymbolKind::Function {
387                        let fqn = current.fqn_name.borrow();
388                        // Split "ClassName.method_name" to get class name
389                        if let Some(dot_pos) = fqn.rfind("::") {
390                            let class_name = &fqn[..dot_pos];
391                            // Build the method FQN: "ClassName.method_name"
392                            let method_fqn = format!("{}::{}", class_name, name);
393                            // Look up the method with no kind filter first
394                            if let Some(target) = this.scopes.find_global_suffix_with_filters(
395                                &[this.interner().intern(&method_fqn)],
396                                None,
397                                None,
398                            ) {
399                                if target.kind() == SymbolKind::Function {
400                                    this.add_symbol_relation(Some(target));
401                                    let caller_name = fqn.clone();
402                                    let target_name = target.fqn_name.borrow().clone();
403                                    this.calls.push(CallBinding {
404                                        caller: caller_name,
405                                        target: target_name,
406                                    });
407                                    return true;
408                                }
409                            }
410                        }
411                    }
412                }
413
414                // If not found, try looking with no kind filter (generic lookup)
415                if let Some(target) = this.lookup_symbol_suffix(&[key], None) {
416                    this.add_symbol_relation(Some(target));
417                    if target.kind() == SymbolKind::Function {
418                        let caller_name = this
419                            .current_symbol()
420                            .map(|s| s.fqn_name.borrow().clone())
421                            .unwrap_or_else(|| "<module>".to_string());
422                        let target_name = target.fqn_name.borrow().clone();
423                        this.calls.push(CallBinding {
424                            caller: caller_name,
425                            target: target_name,
426                        });
427                    }
428                    return true;
429                }
430
431                false
432            };
433            let handled = match func_node.kind_id() {
434                id if id == LangPython::identifier => {
435                    if let Ok(name) = func_node.utf8_text(&content) {
436                        record_target(name, self)
437                    } else {
438                        false
439                    }
440                }
441                id if id == LangPython::attribute => {
442                    // For attribute access (e.g., self.method()), extract the method name
443                    if let Some(attr_node) = func_node.child_by_field_name("attribute") {
444                        if let Ok(name) = attr_node.utf8_text(&content) {
445                            record_target(name, self)
446                        } else {
447                            false
448                        }
449                    } else {
450                        false
451                    }
452                }
453                _ => false,
454            };
455
456            if !handled {
457                if let Ok(name) = func_node.utf8_text(&content) {
458                    let _ = record_target(name.trim(), self);
459                }
460            }
461        }
462
463        self.visit_children(node);
464    }
465
466    fn visit_definition_node(&mut self, node: &HirNode<'tcx>, decorator_symbols: &[&'tcx Symbol]) {
467        let kind_id = node.kind_id();
468        let name_node = match node.opt_child_by_field(self.unit, LangPython::field_name) {
469            Some(name) => name,
470            None => {
471                self.visit_children(node);
472                return;
473            }
474        };
475
476        let ident = match name_node.as_ident() {
477            Some(ident) => ident,
478            None => {
479                self.visit_children(node);
480                return;
481            }
482        };
483
484        let key = self.interner().intern(&ident.name);
485        let preferred_kind = if kind_id == LangPython::function_definition {
486            Some(SymbolKind::Function)
487        } else if kind_id == LangPython::class_definition {
488            Some(SymbolKind::Struct)
489        } else {
490            None
491        };
492
493        let mut symbol = preferred_kind
494            .and_then(|kind| self.lookup_symbol_suffix(&[key], Some(kind)))
495            .or_else(|| self.lookup_symbol_suffix(&[key], None));
496
497        let parent_symbol = self.current_symbol();
498
499        if let Some(scope) = self.unit.opt_get_scope(node.hir_id()) {
500            if symbol.is_none() {
501                symbol = scope.symbol();
502            }
503
504            let depth = self.scopes.depth();
505            self.scopes.push_with_symbol(scope, symbol);
506
507            if let Some(current_symbol) = self.current_symbol() {
508                if kind_id == LangPython::function_definition {
509                    if let Some(class_symbol) = parent_symbol {
510                        if class_symbol.kind() == SymbolKind::Struct {
511                            class_symbol.add_dependency(current_symbol);
512                        }
513                    }
514                } else if kind_id == LangPython::class_definition {
515                    self.add_base_class_dependencies(node, current_symbol);
516                }
517
518                for decorator_symbol in decorator_symbols {
519                    current_symbol.add_dependency(decorator_symbol);
520                }
521            }
522
523            self.visit_children(node);
524            self.scopes.pop_until(depth);
525        } else {
526            self.visit_children(node);
527        }
528    }
529
530    fn add_base_class_dependencies(&mut self, node: &HirNode<'tcx>, class_symbol: &Symbol) {
531        for child_id in node.children() {
532            let child = self.unit.hir_node(*child_id);
533            if child.kind_id() == LangPython::argument_list {
534                for base_id in child.children() {
535                    let base_node = self.unit.hir_node(*base_id);
536
537                    if let Some(ident) = base_node.as_ident() {
538                        let key = self.interner().intern(&ident.name);
539                        if let Some(base_symbol) =
540                            self.lookup_symbol_suffix(&[key], Some(SymbolKind::Struct))
541                        {
542                            class_symbol.add_dependency(base_symbol);
543                        }
544                    } else if base_node.kind_id() == LangPython::attribute {
545                        if let Some(attr_node) =
546                            base_node.inner_ts_node().child_by_field_name("attribute")
547                        {
548                            let content = self.unit.file().content();
549                            if let Ok(name) = attr_node.utf8_text(&content) {
550                                let key = self.interner().intern(name);
551                                if let Some(base_symbol) =
552                                    self.lookup_symbol_suffix(&[key], Some(SymbolKind::Struct))
553                                {
554                                    class_symbol.add_dependency(base_symbol);
555                                }
556                            }
557                        }
558                    }
559                }
560            }
561        }
562    }
563
564    fn add_parameter_type_dependencies(&mut self, params_node: &HirNode<'tcx>) {
565        for child_id in params_node.children() {
566            let child = self.unit.hir_node(*child_id);
567            match child.kind_id() {
568                id if id == LangPython::typed_parameter
569                    || id == LangPython::typed_default_parameter =>
570                {
571                    self.collect_parameter_type_annotations(&child);
572                }
573                id if id == LangPython::type_node => {
574                    self.add_type_dependencies(&child);
575                }
576                _ => {}
577            }
578        }
579    }
580
581    fn collect_parameter_type_annotations(&mut self, param_node: &HirNode<'tcx>) {
582        for child_id in param_node.children() {
583            let child = self.unit.hir_node(*child_id);
584            if child.kind_id() == LangPython::type_node {
585                self.add_type_dependencies(&child);
586            }
587        }
588    }
589
590    fn propagate_child_dependencies(&mut self, parent: &'tcx Symbol, child: &'tcx Symbol) {
591        let dependencies: Vec<_> = child.depends.borrow().iter().copied().collect();
592        for dep_id in dependencies {
593            if dep_id == parent.id {
594                continue;
595            }
596
597            if let Some(dep_symbol) = self.unit.opt_get_symbol(dep_id) {
598                if dep_symbol.kind() == SymbolKind::Function {
599                    continue;
600                }
601
602                if dep_symbol.depends.borrow().contains(&parent.id) {
603                    continue;
604                }
605
606                parent.add_dependency(dep_symbol);
607            }
608        }
609    }
610}
611
612impl<'tcx> AstVisitorPython<'tcx> for SymbolBinder<'tcx> {
613    fn unit(&self) -> CompileUnit<'tcx> {
614        self.unit
615    }
616
617    fn visit_source_file(&mut self, node: HirNode<'tcx>) {
618        self.module_imports.clear();
619        let module_symbol = self.ensure_module_symbol(&node);
620        self.visit_children_scope(&node, module_symbol);
621    }
622
623    fn visit_function_definition(&mut self, node: HirNode<'tcx>) {
624        let name_node = match node.opt_child_by_field(self.unit, LangPython::field_name) {
625            Some(n) => n,
626            None => {
627                self.visit_children(&node);
628                return;
629            }
630        };
631
632        let ident = match name_node.as_ident() {
633            Some(id) => id,
634            None => {
635                self.visit_children(&node);
636                return;
637            }
638        };
639
640        let key = self.interner().intern(&ident.name);
641        let mut symbol = self.lookup_symbol_suffix(&[key], Some(SymbolKind::Function));
642
643        // Get the parent symbol before pushing a new scope
644        let parent_symbol = self.current_symbol();
645
646        if let Some(scope) = self.unit.opt_get_scope(node.hir_id()) {
647            // If symbol not found by lookup, get it from the scope
648            if symbol.is_none() {
649                symbol = scope.symbol();
650            }
651
652            let depth = self.scopes.depth();
653            self.scopes.push_with_symbol(scope, symbol);
654
655            if let Some(current_symbol) = self.current_symbol() {
656                // If parent is a class, class depends on method
657                if let Some(parent) = parent_symbol {
658                    if parent.kind() == SymbolKind::Struct {
659                        parent.add_dependency(current_symbol);
660                        self.propagate_child_dependencies(parent, current_symbol);
661                    }
662                }
663
664                for child_id in node.children() {
665                    let child = self.unit.hir_node(*child_id);
666                    if child.kind_id() == LangPython::parameters {
667                        self.add_parameter_type_dependencies(&child);
668                        break;
669                    }
670                }
671            }
672
673            self.visit_children(&node);
674            self.scopes.pop_until(depth);
675        } else {
676            self.visit_children(&node);
677        }
678    }
679
680    fn visit_class_definition(&mut self, node: HirNode<'tcx>) {
681        let name_node = match node.opt_child_by_field(self.unit, LangPython::field_name) {
682            Some(n) => n,
683            None => {
684                self.visit_children(&node);
685                return;
686            }
687        };
688
689        let ident = match name_node.as_ident() {
690            Some(id) => id,
691            None => {
692                self.visit_children(&node);
693                return;
694            }
695        };
696
697        let key = self.interner().intern(&ident.name);
698        let mut symbol = self.lookup_symbol_suffix(&[key], Some(SymbolKind::Struct));
699
700        if let Some(scope) = self.unit.opt_get_scope(node.hir_id()) {
701            // If symbol not found by lookup, get it from the scope
702            if symbol.is_none() {
703                symbol = scope.symbol();
704            }
705
706            let depth = self.scopes.depth();
707            self.scopes.push_with_symbol(scope, symbol);
708
709            if let Some(current_symbol) = self.current_symbol() {
710                self.add_base_class_dependencies(&node, current_symbol);
711                for import_symbol in &self.module_imports {
712                    current_symbol.add_dependency(import_symbol);
713                }
714            }
715
716            self.visit_children(&node);
717            self.scopes.pop_until(depth);
718        } else {
719            self.visit_children(&node);
720        }
721    }
722
723    fn visit_decorated_definition(&mut self, node: HirNode<'tcx>) {
724        self.visit_decorated_def(&node);
725    }
726
727    fn visit_block(&mut self, node: HirNode<'tcx>) {
728        self.visit_children_scope(&node, None);
729    }
730
731    fn visit_call(&mut self, node: HirNode<'tcx>) {
732        // Delegate to the existing visit_call method
733        self.visit_call_impl(&node);
734    }
735
736    fn visit_assignment(&mut self, node: HirNode<'tcx>) {
737        if let Some(type_node) = node.opt_child_by_field(self.unit, LangPython::field_type) {
738            self.add_type_dependencies(&type_node);
739        } else {
740            for child_id in node.children() {
741                let child = self.unit.hir_node(*child_id);
742                if child.kind_id() == LangPython::type_node {
743                    self.add_type_dependencies(&child);
744                }
745            }
746        }
747
748        self.visit_children(&node);
749    }
750
751    fn visit_import_statement(&mut self, node: HirNode<'tcx>) {
752        let content = self.unit.file().content();
753        let ts_node = node.inner_ts_node();
754        let mut cursor = ts_node.walk();
755
756        for child in ts_node.children(&mut cursor) {
757            match child.kind() {
758                "dotted_name" | "identifier" => {
759                    if let Ok(text) = child.utf8_text(&content) {
760                        self.record_import_path(text);
761                    }
762                }
763                "aliased_import" => {
764                    if let Some(name_node) = child.child_by_field_name("name") {
765                        if let Ok(text) = name_node.utf8_text(&content) {
766                            self.record_import_path(text);
767                        }
768                    }
769                }
770                _ => {}
771            }
772        }
773
774        self.visit_children(&node);
775    }
776
777    fn visit_import_from(&mut self, node: HirNode<'tcx>) {
778        let content = self.unit.file().content();
779        let ts_node = node.inner_ts_node();
780        let mut cursor = ts_node.walk();
781
782        for child in ts_node.children(&mut cursor) {
783            match child.kind() {
784                "dotted_name" | "identifier" => {
785                    if let Ok(text) = child.utf8_text(&content) {
786                        self.record_import_path(text);
787                    }
788                }
789                "aliased_import" => {
790                    if let Some(name_node) = child.child_by_field_name("name") {
791                        if let Ok(text) = name_node.utf8_text(&content) {
792                            self.record_import_path(text);
793                        }
794                    }
795                }
796                _ => {}
797            }
798        }
799
800        self.visit_children(&node);
801    }
802
803    fn visit_unknown(&mut self, node: HirNode<'tcx>) {
804        self.visit_children(&node);
805    }
806}
807
808pub fn bind_symbols<'tcx>(unit: CompileUnit<'tcx>, globals: &'tcx Scope<'tcx>) -> BindingResult {
809    let mut binder = SymbolBinder::new(unit, globals);
810
811    if let Some(file_start_id) = unit.file_start_hir_id() {
812        if let Some(root) = unit.opt_hir_node(file_start_id) {
813            binder.visit_children(&root);
814        }
815    }
816
817    BindingResult {
818        calls: binder.calls,
819    }
820}