aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/from_source.rs74
-rw-r--r--crates/ra_hir_def/Cargo.toml1
-rw-r--r--crates/ra_hir_def/src/child_by_source.rs139
-rw-r--r--crates/ra_hir_def/src/child_from_source.rs276
-rw-r--r--crates/ra_hir_def/src/dyn_map.rs108
-rw-r--r--crates/ra_hir_def/src/keys.rs48
-rw-r--r--crates/ra_hir_def/src/lib.rs5
-rw-r--r--crates/ra_hir_ty/src/tests.rs8
-rw-r--r--crates/ra_syntax/src/ptr.rs14
9 files changed, 355 insertions, 318 deletions
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index 5cb222bd3..437f800c1 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -1,9 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use either::Either;
3
4use hir_def::{ 2use hir_def::{
5 child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId, ImplId, 3 child_by_source::ChildBySource, dyn_map::DynMap, keys, nameres::ModuleSource, AstItemDef,
6 LocationCtx, ModuleId, TraitId, VariantId, 4 EnumVariantId, LocationCtx, ModuleId, VariantId,
7}; 5};
8use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; 6use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
9use ra_syntax::{ 7use ra_syntax::{
@@ -53,8 +51,9 @@ impl FromSource for Trait {
53impl FromSource for Function { 51impl FromSource for Function {
54 type Ast = ast::FnDef; 52 type Ast = ast::FnDef;
55 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 53 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
56 Container::find(db, src.as_ref().map(|it| it.syntax()))? 54 Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::FUNCTION]
57 .child_from_source(db, src) 55 .get(&src)
56 .copied()
58 .map(Function::from) 57 .map(Function::from)
59 } 58 }
60} 59}
@@ -62,26 +61,29 @@ impl FromSource for Function {
62impl FromSource for Const { 61impl FromSource for Const {
63 type Ast = ast::ConstDef; 62 type Ast = ast::ConstDef;
64 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 63 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
65 Container::find(db, src.as_ref().map(|it| it.syntax()))? 64 Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::CONST]
66 .child_from_source(db, src) 65 .get(&src)
66 .copied()
67 .map(Const::from) 67 .map(Const::from)
68 } 68 }
69} 69}
70impl FromSource for Static { 70impl FromSource for Static {
71 type Ast = ast::StaticDef; 71 type Ast = ast::StaticDef;
72 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 72 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
73 match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 73 Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::STATIC]
74 Container::Module(it) => it.id.child_from_source(db, src).map(Static::from), 74 .get(&src)
75 Container::Trait(_) | Container::ImplBlock(_) => None, 75 .copied()
76 } 76 .map(Static::from)
77 } 77 }
78} 78}
79 79
80impl FromSource for TypeAlias { 80impl FromSource for TypeAlias {
81 type Ast = ast::TypeAliasDef; 81 type Ast = ast::TypeAliasDef;
82 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 82 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
83 Container::find(db, src.as_ref().map(|it| it.syntax()))? 83 Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)
84 .child_from_source(db, src) 84 [keys::TYPE_ALIAS]
85 .get(&src)
86 .copied()
85 .map(TypeAlias::from) 87 .map(TypeAlias::from)
86 } 88 }
87} 89}
@@ -116,32 +118,41 @@ impl FromSource for EnumVariant {
116 let parent_enum = src.value.parent_enum(); 118 let parent_enum = src.value.parent_enum();
117 let src_enum = InFile { file_id: src.file_id, value: parent_enum }; 119 let src_enum = InFile { file_id: src.file_id, value: parent_enum };
118 let parent_enum = Enum::from_source(db, src_enum)?; 120 let parent_enum = Enum::from_source(db, src_enum)?;
119 parent_enum.id.child_from_source(db, src).map(EnumVariant::from) 121 parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
122 .get(&src)
123 .copied()
124 .map(EnumVariant::from)
120 } 125 }
121} 126}
122 127
123impl FromSource for StructField { 128impl FromSource for StructField {
124 type Ast = FieldSource; 129 type Ast = FieldSource;
125 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 130 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
131 let src = src.as_ref();
132
133 // FIXME this is buggy
126 let variant_id: VariantId = match src.value { 134 let variant_id: VariantId = match src.value {
127 FieldSource::Named(ref field) => { 135 FieldSource::Named(field) => {
128 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; 136 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
129 let src = InFile { file_id: src.file_id, value }; 137 let src = InFile { file_id: src.file_id, value };
130 let def = Struct::from_source(db, src)?; 138 let def = Struct::from_source(db, src)?;
131 def.id.into() 139 def.id.into()
132 } 140 }
133 FieldSource::Pos(ref field) => { 141 FieldSource::Pos(field) => {
134 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; 142 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
135 let src = InFile { file_id: src.file_id, value }; 143 let src = InFile { file_id: src.file_id, value };
136 let def = EnumVariant::from_source(db, src)?; 144 let def = EnumVariant::from_source(db, src)?;
137 EnumVariantId::from(def).into() 145 EnumVariantId::from(def).into()
138 } 146 }
139 }; 147 };
140 let src = src.map(|field_source| match field_source { 148
141 FieldSource::Pos(it) => Either::Left(it), 149 let dyn_map = variant_id.child_by_source(db);
142 FieldSource::Named(it) => Either::Right(it), 150 match src.value {
143 }); 151 FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
144 variant_id.child_from_source(db, src).map(StructField::from) 152 FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
153 }
154 .copied()
155 .map(StructField::from)
145 } 156 }
146} 157}
147 158
@@ -255,21 +266,12 @@ impl Container {
255 } 266 }
256} 267}
257 268
258impl<CHILD, SOURCE> ChildFromSource<CHILD, SOURCE> for Container 269impl ChildBySource for Container {
259where 270 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
260 TraitId: ChildFromSource<CHILD, SOURCE>,
261 ImplId: ChildFromSource<CHILD, SOURCE>,
262 ModuleId: ChildFromSource<CHILD, SOURCE>,
263{
264 fn child_from_source(
265 &self,
266 db: &impl DefDatabase,
267 child_source: InFile<SOURCE>,
268 ) -> Option<CHILD> {
269 match self { 271 match self {
270 Container::Trait(it) => it.id.child_from_source(db, child_source), 272 Container::Trait(it) => it.id.child_by_source(db),
271 Container::ImplBlock(it) => it.id.child_from_source(db, child_source), 273 Container::ImplBlock(it) => it.id.child_by_source(db),
272 Container::Module(it) => it.id.child_from_source(db, child_source), 274 Container::Module(it) => it.id.child_by_source(db),
273 } 275 }
274 } 276 }
275} 277}
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index cf3a446fc..b1923bbf2 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -12,6 +12,7 @@ log = "0.4.5"
12once_cell = "1.0.1" 12once_cell = "1.0.1"
13rustc-hash = "1.0" 13rustc-hash = "1.0"
14either = "1.5" 14either = "1.5"
15anymap = "0.12"
15 16
16ra_arena = { path = "../ra_arena" } 17ra_arena = { path = "../ra_arena" }
17ra_db = { path = "../ra_db" } 18ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir_def/src/child_by_source.rs b/crates/ra_hir_def/src/child_by_source.rs
new file mode 100644
index 000000000..a3574a9db
--- /dev/null
+++ b/crates/ra_hir_def/src/child_by_source.rs
@@ -0,0 +1,139 @@
1//! When *constructing* `hir`, we start at some parent syntax node and recursively
2//! lower the children.
3//!
4//! This modules allows one to go in the opposite direction: start with a syntax
5//! node for a *child*, and get its hir.
6
7use either::Either;
8
9use crate::{
10 db::DefDatabase,
11 dyn_map::DynMap,
12 keys,
13 src::{HasChildSource, HasSource},
14 AssocItemId, EnumId, EnumVariantId, ImplId, Lookup, ModuleDefId, ModuleId, StructFieldId,
15 TraitId, VariantId,
16};
17
18pub trait ChildBySource {
19 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap;
20}
21
22impl ChildBySource for TraitId {
23 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
24 let mut res = DynMap::default();
25
26 let data = db.trait_data(*self);
27 for (_name, item) in data.items.iter() {
28 match *item {
29 AssocItemId::FunctionId(func) => {
30 let src = func.lookup(db).source(db);
31 res[keys::FUNCTION].insert(src, func)
32 }
33 AssocItemId::ConstId(konst) => {
34 let src = konst.lookup(db).source(db);
35 res[keys::CONST].insert(src, konst)
36 }
37 AssocItemId::TypeAliasId(ty) => {
38 let src = ty.lookup(db).source(db);
39 res[keys::TYPE_ALIAS].insert(src, ty)
40 }
41 }
42 }
43
44 res
45 }
46}
47
48impl ChildBySource for ImplId {
49 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
50 let mut res = DynMap::default();
51
52 let data = db.impl_data(*self);
53 for &item in data.items.iter() {
54 match item {
55 AssocItemId::FunctionId(func) => {
56 let src = func.lookup(db).source(db);
57 res[keys::FUNCTION].insert(src, func)
58 }
59 AssocItemId::ConstId(konst) => {
60 let src = konst.lookup(db).source(db);
61 res[keys::CONST].insert(src, konst)
62 }
63 AssocItemId::TypeAliasId(ty) => {
64 let src = ty.lookup(db).source(db);
65 res[keys::TYPE_ALIAS].insert(src, ty)
66 }
67 }
68 }
69
70 res
71 }
72}
73
74impl ChildBySource for ModuleId {
75 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
76 let mut res = DynMap::default();
77
78 let crate_def_map = db.crate_def_map(self.krate);
79 for item in crate_def_map[self.local_id].scope.declarations() {
80 match item {
81 ModuleDefId::FunctionId(func) => {
82 let src = func.lookup(db).source(db);
83 res[keys::FUNCTION].insert(src, func)
84 }
85 ModuleDefId::ConstId(konst) => {
86 let src = konst.lookup(db).source(db);
87 res[keys::CONST].insert(src, konst)
88 }
89 ModuleDefId::StaticId(statik) => {
90 let src = statik.lookup(db).source(db);
91 res[keys::STATIC].insert(src, statik)
92 }
93 ModuleDefId::TypeAliasId(ty) => {
94 let src = ty.lookup(db).source(db);
95 res[keys::TYPE_ALIAS].insert(src, ty)
96 }
97 _ => (),
98 }
99 }
100
101 res
102 }
103}
104
105impl ChildBySource for VariantId {
106 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
107 let mut res = DynMap::default();
108
109 let arena_map = self.child_source(db);
110 let arena_map = arena_map.as_ref();
111 for (local_id, source) in arena_map.value.iter() {
112 let id = StructFieldId { parent: *self, local_id };
113 match source {
114 Either::Left(source) => {
115 res[keys::TUPLE_FIELD].insert(arena_map.with_value(source.clone()), id)
116 }
117 Either::Right(source) => {
118 res[keys::RECORD_FIELD].insert(arena_map.with_value(source.clone()), id)
119 }
120 }
121 }
122 res
123 }
124}
125
126impl ChildBySource for EnumId {
127 fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
128 let mut res = DynMap::default();
129
130 let arena_map = self.child_source(db);
131 let arena_map = arena_map.as_ref();
132 for (local_id, source) in arena_map.value.iter() {
133 let id = EnumVariantId { parent: *self, local_id };
134 res[keys::ENUM_VARIANT].insert(arena_map.with_value(source.clone()), id)
135 }
136
137 res
138 }
139}
diff --git a/crates/ra_hir_def/src/child_from_source.rs b/crates/ra_hir_def/src/child_from_source.rs
deleted file mode 100644
index 37d4b7870..000000000
--- a/crates/ra_hir_def/src/child_from_source.rs
+++ /dev/null
@@ -1,276 +0,0 @@
1//! When *constructing* `hir`, we start at some parent syntax node and recursively
2//! lower the children.
3//!
4//! This modules allows one to go in the opposite direction: start with a syntax
5//! node for a *child*, and get its hir.
6
7use either::Either;
8use hir_expand::InFile;
9use ra_syntax::{ast, AstNode, AstPtr};
10
11use crate::{
12 db::DefDatabase,
13 src::{HasChildSource, HasSource},
14 AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, ImplId, Lookup, ModuleDefId, ModuleId,
15 StaticId, StructFieldId, TraitId, TypeAliasId, VariantId,
16};
17
18pub trait ChildFromSource<CHILD, SOURCE> {
19 fn child_from_source(
20 &self,
21 db: &impl DefDatabase,
22 child_source: InFile<SOURCE>,
23 ) -> Option<CHILD>;
24}
25
26impl ChildFromSource<FunctionId, ast::FnDef> for TraitId {
27 fn child_from_source(
28 &self,
29 db: &impl DefDatabase,
30 child_source: InFile<ast::FnDef>,
31 ) -> Option<FunctionId> {
32 let data = db.trait_data(*self);
33 data.items
34 .iter()
35 .filter_map(|(_, item)| match item {
36 AssocItemId::FunctionId(it) => Some(*it),
37 _ => None,
38 })
39 .find(|func| {
40 let source = func.lookup(db).source(db);
41 same_source(&source, &child_source)
42 })
43 }
44}
45
46impl ChildFromSource<FunctionId, ast::FnDef> for ImplId {
47 fn child_from_source(
48 &self,
49 db: &impl DefDatabase,
50 child_source: InFile<ast::FnDef>,
51 ) -> Option<FunctionId> {
52 let data = db.impl_data(*self);
53 data.items
54 .iter()
55 .filter_map(|item| match item {
56 AssocItemId::FunctionId(it) => Some(*it),
57 _ => None,
58 })
59 .find(|func| {
60 let source = func.lookup(db).source(db);
61 same_source(&source, &child_source)
62 })
63 }
64}
65
66impl ChildFromSource<FunctionId, ast::FnDef> for ModuleId {
67 fn child_from_source(
68 &self,
69 db: &impl DefDatabase,
70 child_source: InFile<ast::FnDef>,
71 ) -> Option<FunctionId> {
72 let crate_def_map = db.crate_def_map(self.krate);
73 let res = crate_def_map[self.local_id]
74 .scope
75 .declarations()
76 .filter_map(|item| match item {
77 ModuleDefId::FunctionId(it) => Some(it),
78 _ => None,
79 })
80 .find(|func| {
81 let source = func.lookup(db).source(db);
82 same_source(&source, &child_source)
83 });
84 res
85 }
86}
87
88impl ChildFromSource<ConstId, ast::ConstDef> for TraitId {
89 fn child_from_source(
90 &self,
91 db: &impl DefDatabase,
92 child_source: InFile<ast::ConstDef>,
93 ) -> Option<ConstId> {
94 let data = db.trait_data(*self);
95 data.items
96 .iter()
97 .filter_map(|(_, item)| match item {
98 AssocItemId::ConstId(it) => Some(*it),
99 _ => None,
100 })
101 .find(|func| {
102 let source = func.lookup(db).source(db);
103 same_source(&source, &child_source)
104 })
105 }
106}
107
108impl ChildFromSource<ConstId, ast::ConstDef> for ImplId {
109 fn child_from_source(
110 &self,
111 db: &impl DefDatabase,
112 child_source: InFile<ast::ConstDef>,
113 ) -> Option<ConstId> {
114 let data = db.impl_data(*self);
115 data.items
116 .iter()
117 .filter_map(|item| match item {
118 AssocItemId::ConstId(it) => Some(*it),
119 _ => None,
120 })
121 .find(|func| {
122 let source = func.lookup(db).source(db);
123 same_source(&source, &child_source)
124 })
125 }
126}
127
128impl ChildFromSource<ConstId, ast::ConstDef> for ModuleId {
129 fn child_from_source(
130 &self,
131 db: &impl DefDatabase,
132 child_source: InFile<ast::ConstDef>,
133 ) -> Option<ConstId> {
134 let crate_def_map = db.crate_def_map(self.krate);
135 let res = crate_def_map[self.local_id]
136 .scope
137 .declarations()
138 .filter_map(|item| match item {
139 ModuleDefId::ConstId(it) => Some(it),
140 _ => None,
141 })
142 .find(|func| {
143 let source = func.lookup(db).source(db);
144 same_source(&source, &child_source)
145 });
146 res
147 }
148}
149
150impl ChildFromSource<TypeAliasId, ast::TypeAliasDef> for TraitId {
151 fn child_from_source(
152 &self,
153 db: &impl DefDatabase,
154 child_source: InFile<ast::TypeAliasDef>,
155 ) -> Option<TypeAliasId> {
156 let data = db.trait_data(*self);
157 data.items
158 .iter()
159 .filter_map(|(_, item)| match item {
160 AssocItemId::TypeAliasId(it) => Some(*it),
161 _ => None,
162 })
163 .find(|func| {
164 let source = func.lookup(db).source(db);
165 same_source(&source, &child_source)
166 })
167 }
168}
169
170impl ChildFromSource<TypeAliasId, ast::TypeAliasDef> for ImplId {
171 fn child_from_source(
172 &self,
173 db: &impl DefDatabase,
174 child_source: InFile<ast::TypeAliasDef>,
175 ) -> Option<TypeAliasId> {
176 let data = db.impl_data(*self);
177 data.items
178 .iter()
179 .filter_map(|item| match item {
180 AssocItemId::TypeAliasId(it) => Some(*it),
181 _ => None,
182 })
183 .find(|func| {
184 let source = func.lookup(db).source(db);
185 same_source(&source, &child_source)
186 })
187 }
188}
189
190impl ChildFromSource<TypeAliasId, ast::TypeAliasDef> for ModuleId {
191 fn child_from_source(
192 &self,
193 db: &impl DefDatabase,
194 child_source: InFile<ast::TypeAliasDef>,
195 ) -> Option<TypeAliasId> {
196 let crate_def_map = db.crate_def_map(self.krate);
197 let res = crate_def_map[self.local_id]
198 .scope
199 .declarations()
200 .filter_map(|item| match item {
201 ModuleDefId::TypeAliasId(it) => Some(it),
202 _ => None,
203 })
204 .find(|func| {
205 let source = func.lookup(db).source(db);
206 same_source(&source, &child_source)
207 });
208 res
209 }
210}
211
212impl ChildFromSource<StaticId, ast::StaticDef> for ModuleId {
213 fn child_from_source(
214 &self,
215 db: &impl DefDatabase,
216 child_source: InFile<ast::StaticDef>,
217 ) -> Option<StaticId> {
218 let crate_def_map = db.crate_def_map(self.krate);
219 let res = crate_def_map[self.local_id]
220 .scope
221 .declarations()
222 .filter_map(|item| match item {
223 ModuleDefId::StaticId(it) => Some(it),
224 _ => None,
225 })
226 .find(|func| {
227 let source = func.lookup(db).source(db);
228 same_source(&source, &child_source)
229 });
230 res
231 }
232}
233
234impl ChildFromSource<StructFieldId, Either<ast::TupleFieldDef, ast::RecordFieldDef>> for VariantId {
235 fn child_from_source(
236 &self,
237 db: &impl DefDatabase,
238 child_source: InFile<Either<ast::TupleFieldDef, ast::RecordFieldDef>>,
239 ) -> Option<StructFieldId> {
240 let arena_map = self.child_source(db);
241 let (local_id, _) = arena_map.as_ref().value.iter().find(|(_local_id, source)| {
242 child_source.file_id == arena_map.file_id
243 && match (source, &child_source.value) {
244 (Either::Left(a), Either::Left(b)) => AstPtr::new(a) == AstPtr::new(b),
245 (Either::Right(a), Either::Right(b)) => AstPtr::new(a) == AstPtr::new(b),
246 _ => false,
247 }
248 })?;
249 Some(StructFieldId { parent: *self, local_id })
250 }
251}
252
253impl ChildFromSource<EnumVariantId, ast::EnumVariant> for EnumId {
254 fn child_from_source(
255 &self,
256 db: &impl DefDatabase,
257 child_source: InFile<ast::EnumVariant>,
258 ) -> Option<EnumVariantId> {
259 let arena_map = self.child_source(db);
260 let (local_id, _) = arena_map.as_ref().value.iter().find(|(_local_id, source)| {
261 child_source.file_id == arena_map.file_id
262 && AstPtr::new(*source) == AstPtr::new(&child_source.value)
263 })?;
264 Some(EnumVariantId { parent: *self, local_id })
265 }
266}
267
268/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
269/// equal if they point to exactly the same object.
270///
271/// In general, we do not guarantee that we have exactly one instance of a
272/// syntax tree for each file. We probably should add such guarantee, but, for
273/// the time being, we will use identity-less AstPtr comparison.
274fn same_source<N: AstNode>(s1: &InFile<N>, s2: &InFile<N>) -> bool {
275 s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new)
276}
diff --git a/crates/ra_hir_def/src/dyn_map.rs b/crates/ra_hir_def/src/dyn_map.rs
new file mode 100644
index 000000000..6f269d7b0
--- /dev/null
+++ b/crates/ra_hir_def/src/dyn_map.rs
@@ -0,0 +1,108 @@
1//! This module defines a `DynMap` -- a container for heterogeneous maps.
2//!
3//! This means that `DynMap` stores a bunch of hash maps inside, and those maps
4//! can be of different types.
5//!
6//! It is used like this:
7//!
8//! ```
9//! // keys define submaps of a `DynMap`
10//! const STRING_TO_U32: Key<String, u32> = Key::new();
11//! const U32_TO_VEC: Key<u32, Vec<bool>> = Key::new();
12//!
13//! // Note: concrete type, no type params!
14//! let mut map = DynMap::new();
15//!
16//! // To access a specific map, index the `DynMap` by `Key`:
17//! map[STRING_TO_U32].insert("hello".to_string(), 92);
18//! let value = map[U32_TO_VEC].get(92);
19//! assert!(value.is_none());
20//! ```
21//!
22//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
23//! a coincidence.
24use std::{
25 hash::Hash,
26 marker::PhantomData,
27 ops::{Index, IndexMut},
28};
29
30use anymap::Map;
31use rustc_hash::FxHashMap;
32
33pub struct Key<K, V, P = (K, V)> {
34 _phantom: PhantomData<(K, V, P)>,
35}
36
37impl<K, V, P> Key<K, V, P> {
38 pub(crate) const fn new() -> Key<K, V, P> {
39 Key { _phantom: PhantomData }
40 }
41}
42
43impl<K, V, P> Copy for Key<K, V, P> {}
44
45impl<K, V, P> Clone for Key<K, V, P> {
46 fn clone(&self) -> Key<K, V, P> {
47 *self
48 }
49}
50
51pub trait Policy {
52 type K;
53 type V;
54
55 fn insert(map: &mut DynMap, key: Self::K, value: Self::V);
56 fn get<'a>(map: &'a DynMap, key: &Self::K) -> Option<&'a Self::V>;
57}
58
59impl<K: Hash + Eq + 'static, V: 'static> Policy for (K, V) {
60 type K = K;
61 type V = V;
62 fn insert(map: &mut DynMap, key: K, value: V) {
63 map.map.entry::<FxHashMap<K, V>>().or_insert_with(Default::default).insert(key, value);
64 }
65 fn get<'a>(map: &'a DynMap, key: &K) -> Option<&'a V> {
66 map.map.get::<FxHashMap<K, V>>()?.get(key)
67 }
68}
69
70pub struct DynMap {
71 pub(crate) map: Map,
72}
73
74impl Default for DynMap {
75 fn default() -> Self {
76 DynMap { map: Map::new() }
77 }
78}
79
80#[repr(transparent)]
81pub struct KeyMap<KEY> {
82 map: DynMap,
83 _phantom: PhantomData<KEY>,
84}
85
86impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
87 pub fn insert(&mut self, key: P::K, value: P::V) {
88 P::insert(&mut self.map, key, value)
89 }
90 pub fn get(&self, key: &P::K) -> Option<&P::V> {
91 P::get(&self.map, key)
92 }
93}
94
95impl<P: Policy> Index<Key<P::K, P::V, P>> for DynMap {
96 type Output = KeyMap<Key<P::K, P::V, P>>;
97 fn index(&self, _key: Key<P::K, P::V, P>) -> &Self::Output {
98 // Safe due to `#[repr(transparent)]`.
99 unsafe { std::mem::transmute::<&DynMap, &KeyMap<Key<P::K, P::V, P>>>(self) }
100 }
101}
102
103impl<P: Policy> IndexMut<Key<P::K, P::V, P>> for DynMap {
104 fn index_mut(&mut self, _key: Key<P::K, P::V, P>) -> &mut Self::Output {
105 // Safe due to `#[repr(transparent)]`.
106 unsafe { std::mem::transmute::<&mut DynMap, &mut KeyMap<Key<P::K, P::V, P>>>(self) }
107 }
108}
diff --git a/crates/ra_hir_def/src/keys.rs b/crates/ra_hir_def/src/keys.rs
new file mode 100644
index 000000000..447b7e3ba
--- /dev/null
+++ b/crates/ra_hir_def/src/keys.rs
@@ -0,0 +1,48 @@
1//! keys to be used with `DynMap`
2
3use std::marker::PhantomData;
4
5use hir_expand::InFile;
6use ra_syntax::{ast, AstNode, AstPtr};
7use rustc_hash::FxHashMap;
8
9use crate::{
10 dyn_map::{DynMap, Policy},
11 ConstId, EnumVariantId, FunctionId, StaticId, StructFieldId, TypeAliasId,
12};
13
14type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
15
16pub const FUNCTION: Key<ast::FnDef, FunctionId> = Key::new();
17pub const CONST: Key<ast::ConstDef, ConstId> = Key::new();
18pub const STATIC: Key<ast::StaticDef, StaticId> = Key::new();
19pub const ENUM_VARIANT: Key<ast::EnumVariant, EnumVariantId> = Key::new();
20pub const TYPE_ALIAS: Key<ast::TypeAliasDef, TypeAliasId> = Key::new();
21pub const TUPLE_FIELD: Key<ast::TupleFieldDef, StructFieldId> = Key::new();
22pub const RECORD_FIELD: Key<ast::RecordFieldDef, StructFieldId> = Key::new();
23
24/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
25/// equal if they point to exactly the same object.
26///
27/// In general, we do not guarantee that we have exactly one instance of a
28/// syntax tree for each file. We probably should add such guarantee, but, for
29/// the time being, we will use identity-less AstPtr comparison.
30pub struct AstPtrPolicy<AST, ID> {
31 _phantom: PhantomData<(AST, ID)>,
32}
33
34impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
35 type K = InFile<AST>;
36 type V = ID;
37 fn insert(map: &mut DynMap, key: InFile<AST>, value: ID) {
38 let key = key.as_ref().map(AstPtr::new);
39 map.map
40 .entry::<FxHashMap<InFile<AstPtr<AST>>, ID>>()
41 .or_insert_with(Default::default)
42 .insert(key, value);
43 }
44 fn get<'a>(map: &'a DynMap, key: &InFile<AST>) -> Option<&'a ID> {
45 let key = key.as_ref().map(AstPtr::new);
46 map.map.get::<FxHashMap<InFile<AstPtr<AST>>, ID>>()?.get(&key)
47 }
48}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index e02622f62..68e66d276 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -16,6 +16,9 @@ pub mod builtin_type;
16pub mod diagnostics; 16pub mod diagnostics;
17pub mod per_ns; 17pub mod per_ns;
18 18
19pub mod dyn_map;
20pub mod keys;
21
19pub mod adt; 22pub mod adt;
20pub mod data; 23pub mod data;
21pub mod generics; 24pub mod generics;
@@ -30,7 +33,7 @@ mod trace;
30pub mod nameres; 33pub mod nameres;
31 34
32pub mod src; 35pub mod src;
33pub mod child_from_source; 36pub mod child_by_source;
34 37
35#[cfg(test)] 38#[cfg(test)]
36mod test_db; 39mod test_db;
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 9f373a8f4..6920ac747 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -11,8 +11,8 @@ use std::fmt::Write;
11use std::sync::Arc; 11use std::sync::Arc;
12 12
13use hir_def::{ 13use hir_def::{
14 body::BodySourceMap, child_from_source::ChildFromSource, db::DefDatabase, nameres::CrateDefMap, 14 body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys,
15 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 15 nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
16}; 16};
17use hir_expand::InFile; 17use hir_expand::InFile;
18use insta::assert_snapshot; 18use insta::assert_snapshot;
@@ -33,7 +33,9 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
33 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 33 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
34 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 34 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
35 let module = db.module_for_file(pos.file_id); 35 let module = db.module_for_file(pos.file_id);
36 let func = module.child_from_source(db, InFile::new(pos.file_id.into(), fn_def)).unwrap(); 36 let func = *module.child_by_source(db)[keys::FUNCTION]
37 .get(&InFile::new(pos.file_id.into(), fn_def))
38 .unwrap();
37 39
38 let (_body, source_map) = db.body_with_source_map(func.into()); 40 let (_body, source_map) = db.body_with_source_map(func.into());
39 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { 41 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs
index e049fce61..db6230aab 100644
--- a/crates/ra_syntax/src/ptr.rs
+++ b/crates/ra_syntax/src/ptr.rs
@@ -1,6 +1,10 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{iter::successors, marker::PhantomData}; 3use std::{
4 hash::{Hash, Hasher},
5 iter::successors,
6 marker::PhantomData,
7};
4 8
5use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange}; 9use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
6 10
@@ -43,7 +47,7 @@ impl SyntaxNodePtr {
43} 47}
44 48
45/// Like `SyntaxNodePtr`, but remembers the type of node 49/// Like `SyntaxNodePtr`, but remembers the type of node
46#[derive(Debug, Hash)] 50#[derive(Debug)]
47pub struct AstPtr<N: AstNode> { 51pub struct AstPtr<N: AstNode> {
48 raw: SyntaxNodePtr, 52 raw: SyntaxNodePtr,
49 _ty: PhantomData<fn() -> N>, 53 _ty: PhantomData<fn() -> N>,
@@ -64,6 +68,12 @@ impl<N: AstNode> PartialEq for AstPtr<N> {
64 } 68 }
65} 69}
66 70
71impl<N: AstNode> Hash for AstPtr<N> {
72 fn hash<H: Hasher>(&self, state: &mut H) {
73 self.raw.hash(state)
74 }
75}
76
67impl<N: AstNode> AstPtr<N> { 77impl<N: AstNode> AstPtr<N> {
68 pub fn new(node: &N) -> AstPtr<N> { 78 pub fn new(node: &N) -> AstPtr<N> {
69 AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData } 79 AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }