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