aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/body
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/body')
-rw-r--r--crates/ra_hir_def/src/body/scope.rs157
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
3use hir_expand::name::Name;
4use ra_arena::{impl_arena_id, Arena, RawId};
5use rustc_hash::FxHashMap;
6
7use crate::{
8 body::Body,
9 expr::{Expr, ExprId, Pat, PatId, Statement},
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct ScopeId(RawId);
14impl_arena_id!(ScopeId);
15
16#[derive(Debug, PartialEq, Eq)]
17pub struct ExprScopes {
18 scopes: Arena<ScopeId, ScopeData>,
19 scope_by_expr: FxHashMap<ExprId, ScopeId>,
20}
21
22#[derive(Debug, PartialEq, Eq)]
23pub struct ScopeEntry {
24 name: Name,
25 pat: PatId,
26}
27
28impl 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)]
39pub struct ScopeData {
40 parent: Option<ScopeId>,
41 entries: Vec<ScopeEntry>,
42}
43
44impl 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
99fn 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
127fn 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}