1use shape_ast::ast::{BlockItem, Expr, Item, Pattern, Program, Span, Spanned, Statement};
7
8#[derive(Debug, Clone)]
10pub struct Binding {
11 pub name: String,
13 pub def_span: (usize, usize),
15 pub references: Vec<(usize, usize)>,
17}
18
19#[derive(Debug)]
21pub struct Scope {
22 pub range: (usize, usize),
24 pub parent: Option<usize>,
26 pub bindings: Vec<Binding>,
28 pub children: Vec<usize>,
30}
31
32#[derive(Debug)]
34pub struct ScopeTree {
35 pub scopes: Vec<Scope>,
36}
37
38impl ScopeTree {
39 pub fn build(program: &Program, source: &str) -> Self {
41 let mut tree = ScopeTree { scopes: Vec::new() };
42
43 let root = tree.push_scope(0, source.len(), None);
45
46 for item in &program.items {
48 tree.collect_item_definitions(item, root);
49 }
50
51 for item in &program.items {
53 tree.collect_item_references(item, root);
54 }
55
56 tree
57 }
58
59 pub fn references_of(&self, offset: usize) -> Option<Vec<(usize, usize)>> {
64 let binding = self.find_binding_at(offset)?;
66
67 let mut result = vec![binding.def_span];
68 result.extend_from_slice(&binding.references);
69 Some(result)
70 }
71
72 pub fn definition_of(&self, offset: usize) -> Option<(usize, usize)> {
74 let binding = self.find_binding_at(offset)?;
75 Some(binding.def_span)
76 }
77
78 pub fn binding_at(&self, offset: usize) -> Option<&Binding> {
81 self.find_binding_at(offset)
82 }
83
84 fn push_scope(&mut self, start: usize, end: usize, parent: Option<usize>) -> usize {
87 let idx = self.scopes.len();
88 self.scopes.push(Scope {
89 range: (start, end),
90 parent,
91 bindings: Vec::new(),
92 children: Vec::new(),
93 });
94 if let Some(parent_idx) = parent {
95 self.scopes[parent_idx].children.push(idx);
96 }
97 idx
98 }
99
100 fn add_binding(&mut self, scope_idx: usize, name: String, def_span: (usize, usize)) {
101 self.scopes[scope_idx].bindings.push(Binding {
102 name,
103 def_span,
104 references: Vec::new(),
105 });
106 }
107
108 fn find_binding_at(&self, offset: usize) -> Option<&Binding> {
111 for scope in &self.scopes {
112 for binding in &scope.bindings {
113 if offset >= binding.def_span.0 && offset < binding.def_span.1 {
115 return Some(binding);
116 }
117 for &(start, end) in &binding.references {
119 if offset >= start && offset < end {
120 return Some(binding);
121 }
122 }
123 }
124 }
125 None
126 }
127
128 fn resolve_name(&self, name: &str, scope_idx: usize) -> Option<(usize, usize)> {
130 let scope = &self.scopes[scope_idx];
131 for binding in scope.bindings.iter().rev() {
133 if binding.name == name {
134 return Some((
135 scope_idx,
136 scope.bindings.iter().rposition(|b| b.name == name).unwrap(),
137 ));
138 }
139 }
140 if let Some(parent) = scope.parent {
142 self.resolve_name(name, parent)
143 } else {
144 None
145 }
146 }
147
148 fn add_reference(&mut self, name: &str, ref_span: (usize, usize), scope_idx: usize) {
150 if let Some((scope_id, binding_id)) = self.resolve_name(name, scope_idx) {
151 self.scopes[scope_id].bindings[binding_id]
152 .references
153 .push(ref_span);
154 }
155 }
156
157 fn scope_at(&self, offset: usize) -> usize {
159 let mut best = 0; for (idx, scope) in self.scopes.iter().enumerate() {
161 if offset >= scope.range.0
162 && offset < scope.range.1
163 && (scope.range.1 - scope.range.0)
164 < (self.scopes[best].range.1 - self.scopes[best].range.0)
165 {
166 best = idx;
167 }
168 }
169 best
170 }
171
172 fn collect_item_definitions(&mut self, item: &Item, scope_idx: usize) {
175 match item {
176 Item::Function(func, _span) => {
177 let name_span = func.name_span;
179 if !name_span.is_dummy() {
180 self.add_binding(
181 scope_idx,
182 func.name.clone(),
183 (name_span.start, name_span.end),
184 );
185 }
186
187 let func_span = _span;
189 let func_scope = self.push_scope(func_span.start, func_span.end, Some(scope_idx));
190
191 for param in &func.params {
193 if let Some(name) = param.simple_name() {
194 let ps = param.span();
195 if !ps.is_dummy() {
196 self.add_binding(func_scope, name.to_string(), (ps.start, ps.end));
197 }
198 }
199 }
200
201 for stmt in &func.body {
203 self.collect_stmt_definitions(stmt, func_scope);
204 }
205 }
206 Item::VariableDecl(decl, span) => {
207 self.collect_var_decl_def(decl, span, scope_idx);
208 }
209 Item::Statement(stmt, _) => {
210 self.collect_stmt_definitions(stmt, scope_idx);
211 }
212 Item::StructType(s, span) => {
214 if !span.is_dummy() {
215 self.add_binding(
216 scope_idx,
217 s.name.clone(),
218 (span.start, span.start + s.name.len()),
219 );
220 }
221 }
222 Item::Enum(e, span) => {
223 if !span.is_dummy() {
224 self.add_binding(
225 scope_idx,
226 e.name.clone(),
227 (span.start, span.start + e.name.len()),
228 );
229 }
230 }
231 Item::Trait(t, span) => {
232 if !span.is_dummy() {
233 self.add_binding(
234 scope_idx,
235 t.name.clone(),
236 (span.start, span.start + t.name.len()),
237 );
238 }
239 }
240 Item::TypeAlias(ta, span) => {
241 if !span.is_dummy() {
242 self.add_binding(
243 scope_idx,
244 ta.name.clone(),
245 (span.start, span.start + ta.name.len()),
246 );
247 }
248 }
249 Item::ForeignFunction(foreign_fn, _span) => {
250 let name_span = foreign_fn.name_span;
251 if !name_span.is_dummy() {
252 self.add_binding(
253 scope_idx,
254 foreign_fn.name.clone(),
255 (name_span.start, name_span.end),
256 );
257 }
258 }
259 _ => {}
260 }
261 }
262
263 fn collect_stmt_definitions(&mut self, stmt: &Statement, scope_idx: usize) {
264 match stmt {
265 Statement::VariableDecl(decl, span) => {
266 self.collect_var_decl_def(decl, span, scope_idx);
267 }
268 Statement::Expression(expr, _) => {
269 self.collect_expr_definitions(expr, scope_idx);
270 }
271 _ => {}
272 }
273 }
274
275 fn collect_var_decl_def(
276 &mut self,
277 decl: &shape_ast::ast::VariableDecl,
278 _span: &Span,
279 scope_idx: usize,
280 ) {
281 for (name, span) in crate::symbols::get_pattern_names(&decl.pattern) {
282 if !span.is_dummy() {
283 self.add_binding(scope_idx, name, (span.start, span.end));
284 }
285 }
286
287 if let Some(value) = &decl.value {
289 self.collect_expr_definitions(value, scope_idx);
290 }
291 }
292
293 fn collect_expr_definitions(&mut self, expr: &Expr, scope_idx: usize) {
294 match expr {
295 Expr::Block(block, span) => {
296 let block_scope = self.push_scope(span.start, span.end, Some(scope_idx));
297 for item in &block.items {
298 match item {
299 BlockItem::Statement(stmt) => {
300 self.collect_stmt_definitions(stmt, block_scope);
301 }
302 BlockItem::Expression(e) => {
303 self.collect_expr_definitions(e, block_scope);
304 }
305 BlockItem::VariableDecl(vd) => {
306 self.collect_stmt_definitions(
307 &shape_ast::ast::Statement::VariableDecl(
308 vd.clone(),
309 shape_ast::ast::Span::DUMMY,
310 ),
311 block_scope,
312 );
313 }
314 BlockItem::Assignment(_) => {
315 }
317 }
318 }
319 }
320 Expr::For(for_expr, span) => {
321 let for_scope = self.push_scope(span.start, span.end, Some(scope_idx));
322 if let Pattern::Identifier(name) = &for_expr.pattern {
324 let name_start = span.start;
327 let name_end = name_start + name.len();
328 self.add_binding(for_scope, name.clone(), (name_start, name_end));
329 }
330 self.collect_expr_definitions(&for_expr.body, for_scope);
331 }
332 Expr::If(if_expr, _) => {
333 self.collect_expr_definitions(&if_expr.then_branch, scope_idx);
334 if let Some(else_branch) = &if_expr.else_branch {
335 self.collect_expr_definitions(else_branch, scope_idx);
336 }
337 }
338 Expr::FunctionExpr {
339 body, params, span, ..
340 } => {
341 let closure_scope = self.push_scope(span.start, span.end, Some(scope_idx));
342 for param in params {
343 if let Some(name) = param.simple_name() {
344 let ps = param.span();
345 if !ps.is_dummy() {
346 self.add_binding(closure_scope, name.to_string(), (ps.start, ps.end));
347 }
348 }
349 }
350 for stmt in body {
351 self.collect_stmt_definitions(stmt, closure_scope);
352 }
353 }
354 Expr::Match(match_expr, _span) => {
355 for arm in &match_expr.arms {
356 let arm_span = (*arm.body).span();
358 if !arm_span.is_dummy() {
359 let arm_scope =
360 self.push_scope(arm_span.start, arm_span.end, Some(scope_idx));
361 self.collect_pattern_bindings(
362 &arm.pattern,
363 arm.pattern_span.as_ref(),
364 arm_scope,
365 );
366 self.collect_expr_definitions(&arm.body, arm_scope);
367 }
368 }
369 }
370 _ => {}
371 }
372 }
373
374 fn collect_pattern_bindings(
375 &mut self,
376 pattern: &shape_ast::ast::Pattern,
377 pattern_span: Option<&Span>,
378 scope_idx: usize,
379 ) {
380 match pattern {
381 shape_ast::ast::Pattern::Identifier(name) => {
382 if let Some(span) = pattern_span {
383 if !span.is_dummy() {
384 let start = span.start;
385 let end = start.saturating_add(name.len());
386 self.add_binding(scope_idx, name.clone(), (start, end));
387 }
388 }
389 }
390 shape_ast::ast::Pattern::Typed {
391 name,
392 type_annotation: _,
393 } => {
394 if let Some(span) = pattern_span {
395 if !span.is_dummy() {
396 let start = span.start;
397 let end = start.saturating_add(name.len());
398 self.add_binding(scope_idx, name.clone(), (start, end));
399 }
400 }
401 }
402 shape_ast::ast::Pattern::Constructor { .. } => {
403 }
405 _ => {}
406 }
407 }
408
409 fn collect_item_references(&mut self, item: &Item, scope_idx: usize) {
412 match item {
413 Item::Function(func, span) => {
414 let func_scope = self.find_child_scope(scope_idx, span);
415 for stmt in &func.body {
416 self.collect_stmt_references(stmt, func_scope);
417 }
418 }
419 Item::VariableDecl(decl, _) => {
420 if let Some(value) = &decl.value {
421 self.collect_expr_references(value, scope_idx);
422 }
423 }
424 Item::Statement(stmt, _) => {
425 self.collect_stmt_references(stmt, scope_idx);
426 }
427 Item::Expression(expr, _) => {
428 self.collect_expr_references(expr, scope_idx);
429 }
430 _ => {}
431 }
432 }
433
434 fn collect_stmt_references(&mut self, stmt: &Statement, scope_idx: usize) {
435 match stmt {
436 Statement::VariableDecl(decl, _) => {
437 if let Some(value) = &decl.value {
438 self.collect_expr_references(value, scope_idx);
439 }
440 }
441 Statement::Expression(expr, _) => {
442 self.collect_expr_references(expr, scope_idx);
443 }
444 Statement::Return(Some(expr), _) => {
445 self.collect_expr_references(expr, scope_idx);
446 }
447 _ => {}
448 }
449 }
450
451 fn collect_expr_references(&mut self, expr: &Expr, scope_idx: usize) {
452 match expr {
453 Expr::Identifier(name, span) => {
454 if !span.is_dummy() {
455 self.add_reference(name, (span.start, span.end), scope_idx);
456 }
457 }
458 Expr::Block(block, span) => {
459 let block_scope = self.find_child_scope(scope_idx, span);
460 for item in &block.items {
461 match item {
462 BlockItem::Statement(stmt) => {
463 self.collect_stmt_references(stmt, block_scope);
464 }
465 BlockItem::Expression(e) => {
466 self.collect_expr_references(e, block_scope);
467 }
468 BlockItem::VariableDecl(vd) => {
469 self.collect_stmt_references(
470 &shape_ast::ast::Statement::VariableDecl(
471 vd.clone(),
472 shape_ast::ast::Span::DUMMY,
473 ),
474 block_scope,
475 );
476 }
477 BlockItem::Assignment(assign) => {
478 self.collect_expr_references(&assign.value, block_scope);
479 }
480 }
481 }
482 }
483 Expr::For(for_expr, span) => {
484 let for_scope = self.find_child_scope(scope_idx, span);
485 self.collect_expr_references(&for_expr.iterable, scope_idx);
486 self.collect_expr_references(&for_expr.body, for_scope);
487 }
488 Expr::If(if_expr, _) => {
489 self.collect_expr_references(&if_expr.condition, scope_idx);
490 self.collect_expr_references(&if_expr.then_branch, scope_idx);
491 if let Some(else_branch) = &if_expr.else_branch {
492 self.collect_expr_references(else_branch, scope_idx);
493 }
494 }
495 Expr::BinaryOp { left, right, .. } => {
496 self.collect_expr_references(left, scope_idx);
497 self.collect_expr_references(right, scope_idx);
498 }
499 Expr::UnaryOp { operand, .. } => {
500 self.collect_expr_references(operand, scope_idx);
501 }
502 Expr::FunctionCall {
503 name, args, span, ..
504 } => {
505 if !span.is_dummy() {
507 let name_start = span.start;
509 let name_end = name_start + name.len();
510 self.add_reference(name, (name_start, name_end), scope_idx);
511 }
512 for arg in args {
513 self.collect_expr_references(arg, scope_idx);
514 }
515 }
516 Expr::MethodCall { receiver, args, .. } => {
517 self.collect_expr_references(receiver, scope_idx);
518 for arg in args {
519 self.collect_expr_references(arg, scope_idx);
520 }
521 }
522 Expr::PropertyAccess { object, .. } => {
523 self.collect_expr_references(object, scope_idx);
524 }
525 Expr::IndexAccess { object, index, .. } => {
526 self.collect_expr_references(object, scope_idx);
527 self.collect_expr_references(index, scope_idx);
528 }
529 Expr::Array(elements, _) => {
530 for el in elements {
531 self.collect_expr_references(el, scope_idx);
532 }
533 }
534 Expr::Object(entries, _) => {
535 for entry in entries {
536 if let shape_ast::ast::ObjectEntry::Field { value, .. } = entry {
537 self.collect_expr_references(value, scope_idx);
538 }
539 }
540 }
541 Expr::Assign(assign, _) => {
542 self.collect_expr_references(&assign.target, scope_idx);
543 self.collect_expr_references(&assign.value, scope_idx);
544 }
545 Expr::Return(Some(inner), _) => {
546 self.collect_expr_references(inner, scope_idx);
547 }
548 Expr::Conditional {
549 condition,
550 then_expr,
551 else_expr,
552 ..
553 } => {
554 self.collect_expr_references(condition, scope_idx);
555 self.collect_expr_references(then_expr, scope_idx);
556 if let Some(else_e) = else_expr {
557 self.collect_expr_references(else_e, scope_idx);
558 }
559 }
560 Expr::Match(match_expr, _span) => {
561 self.collect_expr_references(&match_expr.scrutinee, scope_idx);
562 for arm in &match_expr.arms {
563 let arm_span = (*arm.body).span();
564 let arm_scope = if !arm_span.is_dummy() {
565 self.find_child_scope(scope_idx, &arm_span)
566 } else {
567 scope_idx
568 };
569 self.collect_expr_references(&arm.body, arm_scope);
570 }
571 }
572 Expr::Literal(_, _) => {}
573 Expr::FunctionExpr { body, span, .. } => {
574 let closure_scope = self.find_child_scope(scope_idx, span);
575 for stmt in body {
576 self.collect_stmt_references(stmt, closure_scope);
577 }
578 }
579 Expr::Await(inner, _) => {
580 self.collect_expr_references(inner, scope_idx);
581 }
582 Expr::TryOperator(inner, _) => {
583 self.collect_expr_references(inner, scope_idx);
584 }
585 Expr::TypeAssertion { expr: inner, .. } => {
586 self.collect_expr_references(inner, scope_idx);
587 }
588 Expr::Spread(inner, _) => {
589 self.collect_expr_references(inner, scope_idx);
590 }
591 Expr::StructLiteral { fields, .. } => {
592 for (_, value) in fields {
593 self.collect_expr_references(value, scope_idx);
594 }
595 }
596 _ => {
597 }
599 }
600 }
601
602 fn find_child_scope(&self, parent_idx: usize, span: &Span) -> usize {
605 for &child in &self.scopes[parent_idx].children {
606 let cs = &self.scopes[child];
607 if cs.range.0 == span.start && cs.range.1 == span.end {
608 return child;
609 }
610 }
611 self.scope_at(span.start)
613 }
614}
615
616#[cfg(test)]
617mod tests {
618 use super::*;
619 use shape_ast::parser::parse_program;
620
621 #[test]
622 fn test_scope_tree_basic_variable() {
623 let code = "let x = 42\nlet y = x + 1";
624 let program = parse_program(code).unwrap();
625 let tree = ScopeTree::build(&program, code);
626
627 assert!(!tree.scopes.is_empty());
629
630 let x_binding = tree.scopes[0].bindings.iter().find(|b| b.name == "x");
632 assert!(x_binding.is_some(), "Should find binding for 'x'");
633 let x = x_binding.unwrap();
634 assert!(
635 !x.references.is_empty(),
636 "x should have at least one reference, got: {:?}",
637 x
638 );
639 }
640
641 #[test]
642 fn test_scope_tree_function_scope() {
643 let code = "let x = 1\nfn foo(a) {\n let b = a + x\n return b\n}";
644 let program = parse_program(code).unwrap();
645 let tree = ScopeTree::build(&program, code);
646
647 assert!(
649 tree.scopes.len() >= 2,
650 "Should have root + function scope, got {}",
651 tree.scopes.len()
652 );
653
654 let func_scope = &tree.scopes[1];
656 let a_binding = func_scope.bindings.iter().find(|b| b.name == "a");
657 assert!(a_binding.is_some(), "Should find 'a' in function scope");
658 }
659
660 #[test]
661 fn test_scope_tree_shadowing() {
662 let code = "let x = 1\nfn foo() {\n let x = 2\n return x\n}";
663 let program = parse_program(code).unwrap();
664 let tree = ScopeTree::build(&program, code);
665
666 let x_count: usize = tree
668 .scopes
669 .iter()
670 .flat_map(|s| &s.bindings)
671 .filter(|b| b.name == "x")
672 .count();
673 assert!(
674 x_count >= 2,
675 "Should have at least 2 bindings named 'x' (shadowing), got {}",
676 x_count
677 );
678 }
679
680 #[test]
681 fn test_references_of() {
682 let code = "let x = 1\nlet y = x";
683 let program = parse_program(code).unwrap();
684 let tree = ScopeTree::build(&program, code);
685
686 let x_binding = tree.scopes[0].bindings.iter().find(|b| b.name == "x");
688 assert!(x_binding.is_some());
689 let x = x_binding.unwrap();
690
691 let refs = tree.references_of(x.def_span.0);
693 assert!(refs.is_some(), "Should find references for x");
694 let refs = refs.unwrap();
695 assert!(
696 refs.len() >= 2,
697 "x should have def + at least 1 reference, got {}",
698 refs.len()
699 );
700 }
701
702 #[test]
703 fn test_shadowing_does_not_cross_scopes() {
704 let code = "let x = 1\nfn foo() {\n let x = 2\n return x\n}\nlet z = x";
706 let program = parse_program(code).unwrap();
707 let tree = ScopeTree::build(&program, code);
708
709 let outer_x = tree.scopes[0].bindings.iter().find(|b| b.name == "x");
712 assert!(outer_x.is_some(), "Should find outer x");
713 let outer_x = outer_x.unwrap();
714
715 let outer_refs = tree.references_of(outer_x.def_span.0).unwrap();
718 assert_eq!(
720 outer_refs.len(),
721 2,
722 "Outer x should have exactly 2 spans (def + z=x ref), got {}",
723 outer_refs.len()
724 );
725 }
726
727 #[test]
728 fn test_definition_of() {
729 let code = "let myVar = 42\nlet result = myVar + 1";
730 let program = parse_program(code).unwrap();
731 let tree = ScopeTree::build(&program, code);
732
733 let ref_offset = code.rfind("myVar").unwrap();
735 let def = tree.definition_of(ref_offset);
736 assert!(def.is_some(), "Should find definition of myVar");
737
738 let (start, end) = def.unwrap();
739 assert_eq!(
740 &code[start..end],
741 "myVar",
742 "Definition should point to 'myVar'"
743 );
744 }
745
746 #[test]
747 fn test_closure_scope() {
748 let code = "let x = 1\nlet f = |y| y + x";
749 let program = parse_program(code).unwrap();
750 let tree = ScopeTree::build(&program, code);
751
752 let outer_x = tree.scopes[0].bindings.iter().find(|b| b.name == "x");
754 assert!(outer_x.is_some(), "Should find outer x");
755 let outer_x = outer_x.unwrap();
756 assert!(
757 !outer_x.references.is_empty(),
758 "Outer x should be referenced from inside closure"
759 );
760 }
761}