aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/body/scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/body/scope.rs')
-rw-r--r--crates/ra_hir_def/src/body/scope.rs165
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
2use std::sync::Arc;
3
4use hir_expand::name::Name;
5use ra_arena::{impl_arena_id, Arena, RawId};
6use rustc_hash::FxHashMap;
7
8use 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)]
16pub struct ScopeId(RawId);
17impl_arena_id!(ScopeId);
18
19#[derive(Debug, PartialEq, Eq)]
20pub struct ExprScopes {
21 scopes: Arena<ScopeId, ScopeData>,
22 scope_by_expr: FxHashMap<ExprId, ScopeId>,
23}
24
25#[derive(Debug, PartialEq, Eq)]
26pub struct ScopeEntry {
27 name: Name,
28 pat: PatId,
29}
30
31impl 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)]
42pub struct ScopeData {
43 parent: Option<ScopeId>,
44 entries: Vec<ScopeEntry>,
45}
46
47impl 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
107fn 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
135fn 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}