diff options
Diffstat (limited to 'crates/ra_hir_def/src/body/scope.rs')
-rw-r--r-- | crates/ra_hir_def/src/body/scope.rs | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs new file mode 100644 index 000000000..09a39e721 --- /dev/null +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -0,0 +1,165 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use hir_expand::name::Name; | ||
5 | use ra_arena::{impl_arena_id, Arena, RawId}; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::{ | ||
9 | body::Body, | ||
10 | db::DefDatabase2, | ||
11 | expr::{Expr, ExprId, Pat, PatId, Statement}, | ||
12 | DefWithBodyId, | ||
13 | }; | ||
14 | |||
15 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
16 | pub struct ScopeId(RawId); | ||
17 | impl_arena_id!(ScopeId); | ||
18 | |||
19 | #[derive(Debug, PartialEq, Eq)] | ||
20 | pub struct ExprScopes { | ||
21 | scopes: Arena<ScopeId, ScopeData>, | ||
22 | scope_by_expr: FxHashMap<ExprId, ScopeId>, | ||
23 | } | ||
24 | |||
25 | #[derive(Debug, PartialEq, Eq)] | ||
26 | pub struct ScopeEntry { | ||
27 | name: Name, | ||
28 | pat: PatId, | ||
29 | } | ||
30 | |||
31 | impl ScopeEntry { | ||
32 | pub fn name(&self) -> &Name { | ||
33 | &self.name | ||
34 | } | ||
35 | |||
36 | pub fn pat(&self) -> PatId { | ||
37 | self.pat | ||
38 | } | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, PartialEq, Eq)] | ||
42 | pub struct ScopeData { | ||
43 | parent: Option<ScopeId>, | ||
44 | entries: Vec<ScopeEntry>, | ||
45 | } | ||
46 | |||
47 | impl ExprScopes { | ||
48 | pub(crate) fn expr_scopes_query(db: &impl DefDatabase2, def: DefWithBodyId) -> Arc<ExprScopes> { | ||
49 | let body = db.body(def); | ||
50 | Arc::new(ExprScopes::new(&*body)) | ||
51 | } | ||
52 | |||
53 | fn new(body: &Body) -> ExprScopes { | ||
54 | let mut scopes = | ||
55 | ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() }; | ||
56 | let root = scopes.root_scope(); | ||
57 | scopes.add_params_bindings(body, root, body.params()); | ||
58 | compute_expr_scopes(body.body_expr(), body, &mut scopes, root); | ||
59 | scopes | ||
60 | } | ||
61 | |||
62 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { | ||
63 | &self.scopes[scope].entries | ||
64 | } | ||
65 | |||
66 | pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ { | ||
67 | std::iter::successors(scope, move |&scope| self.scopes[scope].parent) | ||
68 | } | ||
69 | |||
70 | pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> { | ||
71 | self.scope_by_expr.get(&expr).copied() | ||
72 | } | ||
73 | |||
74 | pub fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> { | ||
75 | &self.scope_by_expr | ||
76 | } | ||
77 | |||
78 | fn root_scope(&mut self) -> ScopeId { | ||
79 | self.scopes.alloc(ScopeData { parent: None, entries: vec![] }) | ||
80 | } | ||
81 | |||
82 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | ||
83 | self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] }) | ||
84 | } | ||
85 | |||
86 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { | ||
87 | match &body[pat] { | ||
88 | Pat::Bind { name, .. } => { | ||
89 | // bind can have a sub pattern, but it's actually not allowed | ||
90 | // to bind to things in there | ||
91 | let entry = ScopeEntry { name: name.clone(), pat }; | ||
92 | self.scopes[scope].entries.push(entry) | ||
93 | } | ||
94 | p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)), | ||
95 | } | ||
96 | } | ||
97 | |||
98 | fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) { | ||
99 | params.iter().for_each(|pat| self.add_bindings(body, scope, *pat)); | ||
100 | } | ||
101 | |||
102 | fn set_scope(&mut self, node: ExprId, scope: ScopeId) { | ||
103 | self.scope_by_expr.insert(node, scope); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | fn compute_block_scopes( | ||
108 | statements: &[Statement], | ||
109 | tail: Option<ExprId>, | ||
110 | body: &Body, | ||
111 | scopes: &mut ExprScopes, | ||
112 | mut scope: ScopeId, | ||
113 | ) { | ||
114 | for stmt in statements { | ||
115 | match stmt { | ||
116 | Statement::Let { pat, initializer, .. } => { | ||
117 | if let Some(expr) = initializer { | ||
118 | scopes.set_scope(*expr, scope); | ||
119 | compute_expr_scopes(*expr, body, scopes, scope); | ||
120 | } | ||
121 | scope = scopes.new_scope(scope); | ||
122 | scopes.add_bindings(body, scope, *pat); | ||
123 | } | ||
124 | Statement::Expr(expr) => { | ||
125 | scopes.set_scope(*expr, scope); | ||
126 | compute_expr_scopes(*expr, body, scopes, scope); | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | if let Some(expr) = tail { | ||
131 | compute_expr_scopes(expr, body, scopes, scope); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { | ||
136 | scopes.set_scope(expr, scope); | ||
137 | match &body[expr] { | ||
138 | Expr::Block { statements, tail } => { | ||
139 | compute_block_scopes(&statements, *tail, body, scopes, scope); | ||
140 | } | ||
141 | Expr::For { iterable, pat, body: body_expr } => { | ||
142 | compute_expr_scopes(*iterable, body, scopes, scope); | ||
143 | let scope = scopes.new_scope(scope); | ||
144 | scopes.add_bindings(body, scope, *pat); | ||
145 | compute_expr_scopes(*body_expr, body, scopes, scope); | ||
146 | } | ||
147 | Expr::Lambda { args, body: body_expr, .. } => { | ||
148 | let scope = scopes.new_scope(scope); | ||
149 | scopes.add_params_bindings(body, scope, &args); | ||
150 | compute_expr_scopes(*body_expr, body, scopes, scope); | ||
151 | } | ||
152 | Expr::Match { expr, arms } => { | ||
153 | compute_expr_scopes(*expr, body, scopes, scope); | ||
154 | for arm in arms { | ||
155 | let scope = scopes.new_scope(scope); | ||
156 | for pat in &arm.pats { | ||
157 | scopes.add_bindings(body, scope, *pat); | ||
158 | } | ||
159 | scopes.set_scope(arm.expr, scope); | ||
160 | compute_expr_scopes(arm.expr, body, scopes, scope); | ||
161 | } | ||
162 | } | ||
163 | e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)), | ||
164 | }; | ||
165 | } | ||