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