frs/cmds/rsl/rules/common/
import_resolution.rs1use syn::UseTree;
2
3use super::module_idx::ModuleIdx;
4use super::module_idx::ScopeInfo;
5use super::path_resolution::normalize_path;
6
7pub struct ImportBinding {
8 pub name: String,
9 pub path: Vec<String>,
10 pub source_path: Vec<String>,
11}
12
13#[derive(Default)]
14pub struct UseBindings {
15 pub bindings: Vec<ImportBinding>,
16 pub glob_imports: Vec<Vec<String>>,
17 pub unknown: bool,
18}
19
20pub fn use_bindings(tree: &UseTree, current_module: &[String]) -> UseBindings {
21 let mut bindings = UseBindings::default();
22 let mut pending = vec![(tree, Vec::<String>::new())];
23
24 while let Some((tree, prefix)) = pending.pop() {
25 match tree {
26 UseTree::Path(path) => {
27 let mut next_prefix = prefix;
28 next_prefix.push(path.ident.to_string());
29 pending.push((path.tree.as_ref(), next_prefix));
30 }
31 UseTree::Group(group) => {
32 pending.extend(group.items.iter().map(|tree| (tree, prefix.clone())));
33 }
34 UseTree::Name(name) => {
35 if name.ident == "self" {
36 if let Some(binding) = prefix.last() {
37 bindings.bindings.push(ImportBinding {
38 name: binding.clone(),
39 path: normalize_path(current_module, &prefix),
40 source_path: prefix,
41 });
42 }
43 } else {
44 let mut path = prefix;
45 path.push(name.ident.to_string());
46 bindings.bindings.push(ImportBinding {
47 name: name.ident.to_string(),
48 path: normalize_path(current_module, &path),
49 source_path: path,
50 });
51 }
52 }
53 UseTree::Rename(rename) => {
54 if rename.rename != "_" {
55 let mut path = prefix;
56 path.push(rename.ident.to_string());
57 bindings.bindings.push(ImportBinding {
58 name: rename.rename.to_string(),
59 path: normalize_path(current_module, &path),
60 source_path: path,
61 });
62 }
63 }
64 UseTree::Glob(_) => {
65 bindings.glob_imports.push(normalize_path(current_module, &prefix));
66 bindings.unknown = true;
67 }
68 }
69 }
70
71 bindings
72}
73
74pub fn has_name_clash_parts(idx: &ModuleIdx<'_>, current_module: &[String], parts: &[String]) -> bool {
75 let Some(scope) = idx.info.get(current_module) else {
76 return true;
77 };
78 if scope.unknown_imports {
79 return true;
80 }
81
82 let Some(name) = parts.last() else {
83 return true;
84 };
85 let Some(target_module_parts) = parts.get(..parts.len().saturating_sub(1)) else {
86 return true;
87 };
88 let target_module = normalize_path(current_module, target_module_parts);
89 let target_path = normalize_path(current_module, parts);
90
91 if target_module != current_module && scope.definitions.contains(name) {
92 return true;
93 }
94
95 scope
96 .imports
97 .iter()
98 .any(|binding| binding.name == *name && binding.path != target_path)
99}
100
101pub fn imported_binding<'idx>(
102 idx: &'idx ModuleIdx<'_>,
103 current_module: &[String],
104 name: &str,
105) -> Option<&'idx ImportBinding> {
106 let scope = idx.info.get(current_module)?;
107 if let Some(binding) = self::direct_imported_binding(scope, name) {
108 return Some(binding);
109 }
110
111 let mut candidate = None;
112 for glob_module in &scope.glob_imports {
113 let Some(glob_scope) = idx.info.get(glob_module) else {
114 continue;
116 };
117 let Some(binding) = self::direct_imported_binding(glob_scope, name) else {
118 continue;
119 };
120 if candidate.is_some() {
121 return None;
123 }
124 candidate = Some(binding);
125 }
126 candidate
127}
128
129fn direct_imported_binding<'idx>(scope: &'idx ScopeInfo, name: &str) -> Option<&'idx ImportBinding> {
130 let mut bindings = scope.imports.iter().filter(|binding| binding.name == name);
131 let binding = bindings.next()?;
132 bindings.next().is_none().then_some(binding)
133}