aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src/scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor/src/scope.rs')
-rw-r--r--crates/libeditor/src/scope.rs183
1 files changed, 183 insertions, 0 deletions
diff --git a/crates/libeditor/src/scope.rs b/crates/libeditor/src/scope.rs
new file mode 100644
index 000000000..1fec0b24e
--- /dev/null
+++ b/crates/libeditor/src/scope.rs
@@ -0,0 +1,183 @@
1use std::{
2 fmt,
3 collections::HashMap,
4};
5
6use libsyntax2::{
7 SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
8 ast::{self, NameOwner},
9 algo::{ancestors, generate, walk::preorder}
10};
11
12pub fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
13 let mut scopes = FnScopes::new();
14 let root = scopes.root_scope();
15 fn_def.param_list().into_iter()
16 .flat_map(|it| it.params())
17 .filter_map(|it| it.pat())
18 .for_each(|it| scopes.add_bindings(root, it));
19
20 if let Some(body) = fn_def.body() {
21 compute_block_scopes(body, &mut scopes, root)
22 }
23 scopes
24}
25
26fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
27 for stmt in block.statements() {
28 match stmt {
29 ast::Stmt::LetStmt(stmt) => {
30 scope = scopes.new_scope(scope);
31 if let Some(pat) = stmt.pat() {
32 scopes.add_bindings(scope, pat);
33 }
34 if let Some(expr) = stmt.initializer() {
35 scopes.set_scope(expr.syntax(), scope)
36 }
37 }
38 ast::Stmt::ExprStmt(expr_stmt) => {
39 if let Some(expr) = expr_stmt.expr() {
40 scopes.set_scope(expr.syntax(), scope);
41 compute_expr_scopes(expr, scopes, scope);
42 }
43 }
44 }
45 }
46 if let Some(expr) = block.expr() {
47 scopes.set_scope(expr.syntax(), scope);
48 compute_expr_scopes(expr, scopes, scope);
49 }
50}
51
52fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
53 match expr {
54 ast::Expr::IfExpr(e) => {
55 let cond_scope = e.condition().and_then(|cond| {
56 compute_cond_scopes(cond, scopes, scope)
57 });
58 if let Some(block) = e.then_branch() {
59 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
60 }
61 if let Some(block) = e.else_branch() {
62 compute_block_scopes(block, scopes, scope);
63 }
64 },
65 ast::Expr::WhileExpr(e) => {
66 let cond_scope = e.condition().and_then(|cond| {
67 compute_cond_scopes(cond, scopes, scope)
68 });
69 if let Some(block) = e.body() {
70 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
71 }
72 },
73 ast::Expr::BlockExpr(e) => {
74 if let Some(block) = e.block() {
75 compute_block_scopes(block, scopes, scope);
76 }
77 }
78 // ForExpr(e) => TODO,
79 _ => {
80 expr.syntax().children()
81 .filter_map(ast::Expr::cast)
82 .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
83 }
84 };
85
86 fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
87 if let Some(expr) = cond.expr() {
88 compute_expr_scopes(expr, scopes, scope);
89 }
90 if let Some(pat) = cond.pat() {
91 let s = scopes.new_scope(scope);
92 scopes.add_bindings(s, pat);
93 Some(s)
94 } else {
95 None
96 }
97 }
98}
99
100type ScopeId = usize;
101
102#[derive(Debug)]
103pub struct FnScopes {
104 scopes: Vec<ScopeData>,
105 scope_for: HashMap<SyntaxNode, ScopeId>,
106}
107
108impl FnScopes {
109 fn new() -> FnScopes {
110 FnScopes {
111 scopes: vec![],
112 scope_for: HashMap::new(),
113 }
114 }
115 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
116 &self.scopes[scope].entries
117 }
118 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
119 generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
120 }
121 fn root_scope(&mut self) -> ScopeId {
122 let res = self.scopes.len();
123 self.scopes.push(ScopeData { parent: None, entries: vec![] });
124 res
125 }
126 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
127 let res = self.scopes.len();
128 self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
129 res
130 }
131 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
132 let entries = preorder(pat.syntax())
133 .filter_map(ast::BindPat::cast)
134 .filter_map(ScopeEntry::new);
135 self.scopes[scope].entries.extend(entries);
136 }
137 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
138 self.scope_for.insert(node.owned(), scope);
139 }
140 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
141 ancestors(node)
142 .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
143 .next()
144 }
145}
146
147#[derive(Debug)]
148struct ScopeData {
149 parent: Option<ScopeId>,
150 entries: Vec<ScopeEntry>
151}
152
153pub struct ScopeEntry {
154 syntax: SyntaxNode
155}
156
157impl ScopeEntry {
158 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
159 if pat.name().is_some() {
160 Some(ScopeEntry { syntax: pat.syntax().owned() })
161 } else {
162 None
163 }
164 }
165 pub fn name(&self) -> SmolStr {
166 self.ast().name()
167 .unwrap()
168 .text()
169 }
170 fn ast(&self) -> ast::BindPat {
171 ast::BindPat::cast(self.syntax.borrowed())
172 .unwrap()
173 }
174}
175
176impl fmt::Debug for ScopeEntry {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 f.debug_struct("ScopeEntry")
179 .field("name", &self.name())
180 .field("syntax", &self.syntax)
181 .finish()
182 }
183}