1use std::collections::HashMap;
11
12use crate::context::TypeId;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub struct ScopeId(pub(crate) u32);
17
18#[derive(Clone, Debug)]
20pub struct TypeScope {
21 pub parent: Option<ScopeId>,
23 names: HashMap<String, TypeId>,
25}
26
27impl TypeScope {
28 fn new(parent: Option<ScopeId>) -> Self {
29 Self {
30 parent,
31 names: HashMap::new(),
32 }
33 }
34
35 pub fn insert(&mut self, name: String, type_id: TypeId) {
37 self.names.insert(name, type_id);
38 }
39
40 pub fn get(&self, name: &str) -> Option<TypeId> {
42 self.names.get(name).copied()
43 }
44
45 pub fn iter(&self) -> impl Iterator<Item = (&String, &TypeId)> {
47 self.names.iter()
48 }
49}
50
51#[derive(Clone, Debug)]
54pub struct ScopeArena {
55 scopes: Vec<TypeScope>,
56}
57
58impl ScopeArena {
59 pub fn new() -> Self {
60 Self { scopes: Vec::new() }
61 }
62
63 pub fn create_root(&mut self) -> ScopeId {
65 let id = ScopeId(self.scopes.len() as u32);
66 self.scopes.push(TypeScope::new(None));
67 id
68 }
69
70 pub fn create_child(&mut self, parent: ScopeId) -> ScopeId {
72 let id = ScopeId(self.scopes.len() as u32);
73 self.scopes.push(TypeScope::new(Some(parent)));
74 id
75 }
76
77 pub fn get(&self, id: ScopeId) -> &TypeScope {
79 &self.scopes[id.0 as usize]
80 }
81
82 pub fn get_mut(&mut self, id: ScopeId) -> &mut TypeScope {
84 &mut self.scopes[id.0 as usize]
85 }
86
87 pub fn insert(&mut self, scope: ScopeId, name: String, type_id: TypeId) {
89 self.get_mut(scope).insert(name, type_id);
90 }
91
92 pub fn resolve(&self, scope: ScopeId, name: &str) -> Option<TypeId> {
94 let s = self.get(scope);
95 if let Some(type_id) = s.get(name) {
96 return Some(type_id);
97 }
98 if let Some(parent) = s.parent {
99 return self.resolve(parent, name);
100 }
101 None
102 }
103}
104
105impl Default for ScopeArena {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111#[derive(Clone, Debug)]
114pub struct PendingImport {
115 pub scope: ScopeId,
117 pub local_name: String,
119 pub from_module: String,
121 pub original_name: String,
123}
124
125#[cfg(test)]
126mod tests {
127 use crate::context::GlobalContext;
128
129 #[test]
130 fn test_basic_resolution() {
131 let mut gctx = GlobalContext::new();
132 let root = gctx.create_root_scope();
133 let type_id = gctx.insert_type(crate::ir::TypeDeclaration {
134 kind: crate::ir::TypeKind::Interface(crate::ir::InterfaceDecl {
135 name: "Foo".to_string(),
136 js_name: "Foo".to_string(),
137 type_params: vec![],
138 extends: vec![],
139 members: vec![],
140 classification: crate::ir::InterfaceClassification::ClassLike,
141 }),
142 module_context: crate::ir::ModuleContext::Global,
143 doc: None,
144 scope_id: root,
145 exported: false,
146 });
147 gctx.scopes.insert(root, "Foo".to_string(), type_id);
148
149 assert!(gctx.scopes.resolve(root, "Foo").is_some());
150 assert!(gctx.scopes.resolve(root, "Bar").is_none());
151 }
152
153 #[test]
154 fn test_child_scope_shadows_parent() {
155 let mut gctx = GlobalContext::new();
156 let parent = gctx.create_root_scope();
157 let child = gctx.scopes.create_child(parent);
158
159 let id_a = gctx.insert_type(crate::ir::TypeDeclaration {
160 kind: crate::ir::TypeKind::Interface(crate::ir::InterfaceDecl {
161 name: "Foo".to_string(),
162 js_name: "Foo".to_string(),
163 type_params: vec![],
164 extends: vec![],
165 members: vec![],
166 classification: crate::ir::InterfaceClassification::ClassLike,
167 }),
168 module_context: crate::ir::ModuleContext::Global,
169 doc: None,
170 scope_id: parent,
171 exported: false,
172 });
173 let id_b = gctx.insert_type(crate::ir::TypeDeclaration {
174 kind: crate::ir::TypeKind::Interface(crate::ir::InterfaceDecl {
175 name: "Foo".to_string(),
176 js_name: "Foo".to_string(),
177 type_params: vec![],
178 extends: vec![],
179 members: vec![],
180 classification: crate::ir::InterfaceClassification::ClassLike,
181 }),
182 module_context: crate::ir::ModuleContext::Global,
183 doc: None,
184 scope_id: child,
185 exported: false,
186 });
187
188 gctx.scopes.insert(parent, "Foo".to_string(), id_a);
189 gctx.scopes.insert(child, "Foo".to_string(), id_b);
190
191 assert_eq!(gctx.scopes.resolve(child, "Foo"), Some(id_b));
193 assert_eq!(gctx.scopes.resolve(parent, "Foo"), Some(id_a));
195 }
196
197 #[test]
198 fn test_child_inherits_parent() {
199 let mut gctx = GlobalContext::new();
200 let parent = gctx.create_root_scope();
201 let child = gctx.scopes.create_child(parent);
202
203 let id = gctx.insert_type(crate::ir::TypeDeclaration {
204 kind: crate::ir::TypeKind::Interface(crate::ir::InterfaceDecl {
205 name: "Foo".to_string(),
206 js_name: "Foo".to_string(),
207 type_params: vec![],
208 extends: vec![],
209 members: vec![],
210 classification: crate::ir::InterfaceClassification::ClassLike,
211 }),
212 module_context: crate::ir::ModuleContext::Global,
213 doc: None,
214 scope_id: parent,
215 exported: false,
216 });
217 gctx.scopes.insert(parent, "Foo".to_string(), id);
218
219 assert_eq!(gctx.scopes.resolve(child, "Foo"), Some(id));
221 }
222}