1use std::collections::HashMap;
2
3use indexmap::IndexMap;
4use serde::Deserialize;
5use serde::Serialize;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct ScopeId(pub u32);
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct BindingId(pub u32);
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[serde(rename_all = "camelCase")]
17pub struct ScopeData {
18 pub id: ScopeId,
19 pub parent: Option<ScopeId>,
20 pub kind: ScopeKind,
21 pub bindings: HashMap<String, BindingId>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "lowercase")]
28pub enum ScopeKind {
29 Program,
30 Function,
31 Block,
32 #[serde(rename = "for")]
33 For,
34 Class,
35 Switch,
36 Catch,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct BindingData {
42 pub id: BindingId,
43 pub name: String,
44 pub kind: BindingKind,
45 pub scope: ScopeId,
47 pub declaration_type: String,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub declaration_start: Option<u32>,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub declaration_node_id: Option<u32>,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub import: Option<ImportBindingData>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(rename_all = "lowercase")]
67pub enum BindingKind {
68 Var,
69 Let,
70 Const,
71 Param,
72 Module,
74 Hoisted,
76 Local,
78 Unknown,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ImportBindingData {
84 pub source: String,
86 pub kind: ImportBindingKind,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub imported: Option<String>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
94#[serde(rename_all = "lowercase")]
95pub enum ImportBindingKind {
96 Default,
97 Named,
98 Namespace,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct ScopeInfo {
106 pub scopes: Vec<ScopeData>,
108 pub bindings: Vec<BindingData>,
110
111 pub node_to_scope: HashMap<u32, ScopeId>,
117
118 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
121 pub node_to_scope_end: HashMap<u32, u32>,
122
123 #[serde(default)]
127 pub reference_to_binding: IndexMap<u32, BindingId>,
128
129 #[serde(
133 default,
134 skip_serializing_if = "IndexMap::is_empty",
135 rename = "refNodeIdToBinding"
136 )]
137 pub ref_node_id_to_binding: IndexMap<u32, BindingId>,
138
139 #[serde(
141 default,
142 skip_serializing_if = "HashMap::is_empty",
143 rename = "nodeIdToScope"
144 )]
145 pub node_id_to_scope: HashMap<u32, ScopeId>,
146
147 pub program_scope: ScopeId,
149}
150
151impl ScopeInfo {
152 pub fn get_binding(&self, scope_id: ScopeId, name: &str) -> Option<BindingId> {
155 let mut current = Some(scope_id);
156 while let Some(id) = current {
157 let scope = &self.scopes[id.0 as usize];
158 if let Some(&binding_id) = scope.bindings.get(name) {
159 return Some(binding_id);
160 }
161 current = scope.parent;
162 }
163 None
164 }
165
166 pub fn resolve_scope_by_node_id(&self, node_id: u32) -> Option<ScopeId> {
168 self.node_id_to_scope.get(&node_id).copied()
169 }
170
171 pub fn resolve_scope_for_node(&self, node_id: Option<u32>) -> Option<ScopeId> {
178 let nid = node_id?;
179 self.node_id_to_scope.get(&nid).copied()
180 }
181
182 pub fn resolve_reference_by_node_id(&self, node_id: u32) -> Option<BindingId> {
185 self.ref_node_id_to_binding.get(&node_id).copied()
186 }
187
188 pub fn resolve_reference_id_for_node(&self, node_id: Option<u32>) -> Option<BindingId> {
192 let nid = node_id?;
193 self.ref_node_id_to_binding.get(&nid).copied()
194 }
195
196 pub fn resolve_reference_for_node(&self, node_id: Option<u32>) -> Option<&BindingData> {
200 self.resolve_reference_id_for_node(node_id)
201 .map(|id| &self.bindings[id.0 as usize])
202 }
203
204 pub fn find_binding_in_descendants(
206 &self,
207 name: &str,
208 ancestor: ScopeId,
209 ) -> Option<&BindingData> {
210 let mut descendants = std::collections::HashSet::new();
211 descendants.insert(ancestor);
212 let mut changed = true;
213 while changed {
214 changed = false;
215 for (i, scope) in self.scopes.iter().enumerate() {
216 let sid = ScopeId(i as u32);
217 if let Some(parent) = scope.parent {
218 if descendants.contains(&parent) && !descendants.contains(&sid) {
219 descendants.insert(sid);
220 changed = true;
221 }
222 }
223 }
224 }
225 for sid in &descendants {
226 let scope = &self.scopes[sid.0 as usize];
227 if let Some(id) = scope.bindings.get(name) {
228 return Some(&self.bindings[id.0 as usize]);
229 }
230 }
231 None
232 }
233
234 pub fn find_binding_id_in_descendants(
237 &self,
238 name: &str,
239 ancestor: ScopeId,
240 ) -> Option<(BindingId, &BindingData)> {
241 let mut descendants = std::collections::HashSet::new();
242 descendants.insert(ancestor);
243 let mut changed = true;
244 while changed {
245 changed = false;
246 for (i, scope) in self.scopes.iter().enumerate() {
247 let sid = ScopeId(i as u32);
248 if let Some(parent) = scope.parent {
249 if descendants.contains(&parent) && !descendants.contains(&sid) {
250 descendants.insert(sid);
251 changed = true;
252 }
253 }
254 }
255 }
256 for sid in &descendants {
257 let scope = &self.scopes[sid.0 as usize];
258 if let Some(&id) = scope.bindings.get(name) {
259 return Some((id, &self.bindings[id.0 as usize]));
260 }
261 }
262 None
263 }
264
265 pub fn scope_bindings(&self, scope_id: ScopeId) -> impl Iterator<Item = &BindingData> {
267 self.scopes[scope_id.0 as usize]
268 .bindings
269 .values()
270 .map(|id| &self.bindings[id.0 as usize])
271 }
272
273 pub fn scope_bindings_with_children(
279 &self,
280 scope_id: ScopeId,
281 ) -> impl Iterator<Item = &BindingData> {
282 let mut binding_ids: Vec<BindingId> = Vec::new();
283 for &id in self.scopes[scope_id.0 as usize].bindings.values() {
285 binding_ids.push(id);
286 }
287 for scope in self.scopes.iter() {
289 if scope.parent == Some(scope_id) && matches!(scope.kind, ScopeKind::Block) {
290 for &id in scope.bindings.values() {
291 binding_ids.push(id);
292 }
293 }
294 }
295 binding_ids
296 .into_iter()
297 .map(|id| &self.bindings[id.0 as usize])
298 }
299
300 pub fn find_block_scope_by_bindings(
304 &self,
305 names: &[&str],
306 ancestor: ScopeId,
307 is_claimed: impl Fn(ScopeId) -> bool,
308 ) -> Option<ScopeId> {
309 let mut descendants = std::collections::HashSet::new();
310 descendants.insert(ancestor);
311 let mut changed = true;
312 while changed {
313 changed = false;
314 for (i, scope) in self.scopes.iter().enumerate() {
315 let sid = ScopeId(i as u32);
316 if let Some(parent) = scope.parent {
317 if descendants.contains(&parent) && !descendants.contains(&sid) {
318 descendants.insert(sid);
319 changed = true;
320 }
321 }
322 }
323 }
324 for sid in &descendants {
325 let scope = &self.scopes[sid.0 as usize];
326 if matches!(scope.kind, ScopeKind::Function) {
327 continue;
328 }
329 if is_claimed(*sid) {
330 continue;
331 }
332 let all_match = names.iter().all(|name| scope.bindings.contains_key(*name));
333 if all_match {
334 return Some(*sid);
335 }
336 }
337 None
338 }
339}