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)]
121 pub node_to_scope_end: HashMap<u32, u32>,
122
123 #[serde(default)]
127 pub reference_to_binding: IndexMap<u32, BindingId>,
128
129 #[serde(default, rename = "refNodeIdToBinding")]
133 pub ref_node_id_to_binding: IndexMap<u32, BindingId>,
134
135 #[serde(default, rename = "nodeIdToScope")]
137 pub node_id_to_scope: HashMap<u32, ScopeId>,
138
139 pub program_scope: ScopeId,
141}
142
143impl ScopeInfo {
144 pub fn get_binding(&self, scope_id: ScopeId, name: &str) -> Option<BindingId> {
147 let mut current = Some(scope_id);
148 while let Some(id) = current {
149 let scope = &self.scopes[id.0 as usize];
150 if let Some(&binding_id) = scope.bindings.get(name) {
151 return Some(binding_id);
152 }
153 current = scope.parent;
154 }
155 None
156 }
157
158 pub fn resolve_scope_by_node_id(&self, node_id: u32) -> Option<ScopeId> {
160 self.node_id_to_scope.get(&node_id).copied()
161 }
162
163 pub fn resolve_scope_for_node(&self, node_id: Option<u32>) -> Option<ScopeId> {
170 let nid = node_id?;
171 self.node_id_to_scope.get(&nid).copied()
172 }
173
174 pub fn resolve_reference_by_node_id(&self, node_id: u32) -> Option<BindingId> {
177 self.ref_node_id_to_binding.get(&node_id).copied()
178 }
179
180 pub fn resolve_reference_id_for_node(&self, node_id: Option<u32>) -> Option<BindingId> {
184 let nid = node_id?;
185 self.ref_node_id_to_binding.get(&nid).copied()
186 }
187
188 pub fn resolve_reference_for_node(&self, node_id: Option<u32>) -> Option<&BindingData> {
192 self.resolve_reference_id_for_node(node_id)
193 .map(|id| &self.bindings[id.0 as usize])
194 }
195
196 pub fn find_binding_in_descendants(
198 &self,
199 name: &str,
200 ancestor: ScopeId,
201 ) -> Option<&BindingData> {
202 let mut descendants = std::collections::HashSet::new();
203 descendants.insert(ancestor);
204 let mut changed = true;
205 while changed {
206 changed = false;
207 for (i, scope) in self.scopes.iter().enumerate() {
208 let sid = ScopeId(i as u32);
209 if let Some(parent) = scope.parent {
210 if descendants.contains(&parent) && !descendants.contains(&sid) {
211 descendants.insert(sid);
212 changed = true;
213 }
214 }
215 }
216 }
217 for sid in &descendants {
218 let scope = &self.scopes[sid.0 as usize];
219 if let Some(id) = scope.bindings.get(name) {
220 return Some(&self.bindings[id.0 as usize]);
221 }
222 }
223 None
224 }
225
226 pub fn find_binding_id_in_descendants(
229 &self,
230 name: &str,
231 ancestor: ScopeId,
232 ) -> Option<(BindingId, &BindingData)> {
233 let mut descendants = std::collections::HashSet::new();
234 descendants.insert(ancestor);
235 let mut changed = true;
236 while changed {
237 changed = false;
238 for (i, scope) in self.scopes.iter().enumerate() {
239 let sid = ScopeId(i as u32);
240 if let Some(parent) = scope.parent {
241 if descendants.contains(&parent) && !descendants.contains(&sid) {
242 descendants.insert(sid);
243 changed = true;
244 }
245 }
246 }
247 }
248 for sid in &descendants {
249 let scope = &self.scopes[sid.0 as usize];
250 if let Some(&id) = scope.bindings.get(name) {
251 return Some((id, &self.bindings[id.0 as usize]));
252 }
253 }
254 None
255 }
256
257 pub fn scope_bindings(&self, scope_id: ScopeId) -> impl Iterator<Item = &BindingData> {
259 self.scopes[scope_id.0 as usize]
260 .bindings
261 .values()
262 .map(|id| &self.bindings[id.0 as usize])
263 }
264
265 pub fn scope_bindings_with_children(
271 &self,
272 scope_id: ScopeId,
273 ) -> impl Iterator<Item = &BindingData> {
274 let mut binding_ids: Vec<BindingId> = Vec::new();
275 for &id in self.scopes[scope_id.0 as usize].bindings.values() {
277 binding_ids.push(id);
278 }
279 for scope in self.scopes.iter() {
281 if scope.parent == Some(scope_id) && matches!(scope.kind, ScopeKind::Block) {
282 for &id in scope.bindings.values() {
283 binding_ids.push(id);
284 }
285 }
286 }
287 binding_ids
288 .into_iter()
289 .map(|id| &self.bindings[id.0 as usize])
290 }
291
292 pub fn find_block_scope_by_bindings(
296 &self,
297 names: &[&str],
298 ancestor: ScopeId,
299 is_claimed: impl Fn(ScopeId) -> bool,
300 ) -> Option<ScopeId> {
301 let mut descendants = std::collections::HashSet::new();
302 descendants.insert(ancestor);
303 let mut changed = true;
304 while changed {
305 changed = false;
306 for (i, scope) in self.scopes.iter().enumerate() {
307 let sid = ScopeId(i as u32);
308 if let Some(parent) = scope.parent {
309 if descendants.contains(&parent) && !descendants.contains(&sid) {
310 descendants.insert(sid);
311 changed = true;
312 }
313 }
314 }
315 }
316 for sid in &descendants {
317 let scope = &self.scopes[sid.0 as usize];
318 if matches!(scope.kind, ScopeKind::Function) {
319 continue;
320 }
321 if is_claimed(*sid) {
322 continue;
323 }
324 let all_match = names.iter().all(|name| scope.bindings.contains_key(*name));
325 if all_match {
326 return Some(*sid);
327 }
328 }
329 None
330 }
331}