diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-06 20:28:28 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-06 20:28:28 +0000 |
commit | f18b7e18c479144325ec150be00837aae3329ae2 (patch) | |
tree | 2c8fbda35c5d9f43b70fe3c6e91ce3e4a8bb3226 /crates | |
parent | d3702c02cdff158f05d2af1bd7106cca8a3e4ba9 (diff) | |
parent | 8c86963d47953045f2f33ee6620d305a6589641e (diff) |
Merge #2484
2484: DynMap r=matklad a=matklad
Implement a `DynMap` a semi-dynamic, semi-static map, which helps to thread heterogeneously typed info in a uniform way. Totally inspired by https://github.com/JetBrains/kotlin/blob/df3bee30384787d8951ea548a4257c2cb52a16a3/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java.
@flodiebold wdyt? Seems like a potentially useful pattern for various source-map-like things.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/from_source.rs | 74 | ||||
-rw-r--r-- | crates/ra_hir_def/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_hir_def/src/child_by_source.rs | 139 | ||||
-rw-r--r-- | crates/ra_hir_def/src/child_from_source.rs | 276 | ||||
-rw-r--r-- | crates/ra_hir_def/src/dyn_map.rs | 108 | ||||
-rw-r--r-- | crates/ra_hir_def/src/keys.rs | 48 | ||||
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 8 | ||||
-rw-r--r-- | crates/ra_syntax/src/ptr.rs | 14 |
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 |
2 | use either::Either; | ||
3 | |||
4 | use hir_def::{ | 2 | use 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 | }; |
8 | use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; | 6 | use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; |
9 | use ra_syntax::{ | 7 | use ra_syntax::{ |
@@ -53,8 +51,9 @@ impl FromSource for Trait { | |||
53 | impl FromSource for Function { | 51 | impl 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 { | |||
62 | impl FromSource for Const { | 61 | impl 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 | } |
70 | impl FromSource for Static { | 70 | impl 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 | ||
80 | impl FromSource for TypeAlias { | 80 | impl 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 | ||
123 | impl FromSource for StructField { | 128 | impl 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 | ||
258 | impl<CHILD, SOURCE> ChildFromSource<CHILD, SOURCE> for Container | 269 | impl ChildBySource for Container { |
259 | where | 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" | |||
12 | once_cell = "1.0.1" | 12 | once_cell = "1.0.1" |
13 | rustc-hash = "1.0" | 13 | rustc-hash = "1.0" |
14 | either = "1.5" | 14 | either = "1.5" |
15 | anymap = "0.12" | ||
15 | 16 | ||
16 | ra_arena = { path = "../ra_arena" } | 17 | ra_arena = { path = "../ra_arena" } |
17 | ra_db = { path = "../ra_db" } | 18 | ra_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 | |||
7 | use either::Either; | ||
8 | |||
9 | use 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 | |||
18 | pub trait ChildBySource { | ||
19 | fn child_by_source(&self, db: &impl DefDatabase) -> DynMap; | ||
20 | } | ||
21 | |||
22 | impl 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 | |||
48 | impl 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 | |||
74 | impl 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 | |||
105 | impl 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 | |||
126 | impl 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 | |||
7 | use either::Either; | ||
8 | use hir_expand::InFile; | ||
9 | use ra_syntax::{ast, AstNode, AstPtr}; | ||
10 | |||
11 | use 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 | |||
18 | pub 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 | |||
26 | impl 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 | |||
46 | impl 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 | |||
66 | impl 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 | |||
88 | impl 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 | |||
108 | impl 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 | |||
128 | impl 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 | |||
150 | impl 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 | |||
170 | impl 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 | |||
190 | impl 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 | |||
212 | impl 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 | |||
234 | impl 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 | |||
253 | impl 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. | ||
274 | fn 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. | ||
24 | use std::{ | ||
25 | hash::Hash, | ||
26 | marker::PhantomData, | ||
27 | ops::{Index, IndexMut}, | ||
28 | }; | ||
29 | |||
30 | use anymap::Map; | ||
31 | use rustc_hash::FxHashMap; | ||
32 | |||
33 | pub struct Key<K, V, P = (K, V)> { | ||
34 | _phantom: PhantomData<(K, V, P)>, | ||
35 | } | ||
36 | |||
37 | impl<K, V, P> Key<K, V, P> { | ||
38 | pub(crate) const fn new() -> Key<K, V, P> { | ||
39 | Key { _phantom: PhantomData } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | impl<K, V, P> Copy for Key<K, V, P> {} | ||
44 | |||
45 | impl<K, V, P> Clone for Key<K, V, P> { | ||
46 | fn clone(&self) -> Key<K, V, P> { | ||
47 | *self | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub 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 | |||
59 | impl<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 | |||
70 | pub struct DynMap { | ||
71 | pub(crate) map: Map, | ||
72 | } | ||
73 | |||
74 | impl Default for DynMap { | ||
75 | fn default() -> Self { | ||
76 | DynMap { map: Map::new() } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | #[repr(transparent)] | ||
81 | pub struct KeyMap<KEY> { | ||
82 | map: DynMap, | ||
83 | _phantom: PhantomData<KEY>, | ||
84 | } | ||
85 | |||
86 | impl<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 | |||
95 | impl<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 | |||
103 | impl<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 | |||
3 | use std::marker::PhantomData; | ||
4 | |||
5 | use hir_expand::InFile; | ||
6 | use ra_syntax::{ast, AstNode, AstPtr}; | ||
7 | use rustc_hash::FxHashMap; | ||
8 | |||
9 | use crate::{ | ||
10 | dyn_map::{DynMap, Policy}, | ||
11 | ConstId, EnumVariantId, FunctionId, StaticId, StructFieldId, TypeAliasId, | ||
12 | }; | ||
13 | |||
14 | type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; | ||
15 | |||
16 | pub const FUNCTION: Key<ast::FnDef, FunctionId> = Key::new(); | ||
17 | pub const CONST: Key<ast::ConstDef, ConstId> = Key::new(); | ||
18 | pub const STATIC: Key<ast::StaticDef, StaticId> = Key::new(); | ||
19 | pub const ENUM_VARIANT: Key<ast::EnumVariant, EnumVariantId> = Key::new(); | ||
20 | pub const TYPE_ALIAS: Key<ast::TypeAliasDef, TypeAliasId> = Key::new(); | ||
21 | pub const TUPLE_FIELD: Key<ast::TupleFieldDef, StructFieldId> = Key::new(); | ||
22 | pub 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. | ||
30 | pub struct AstPtrPolicy<AST, ID> { | ||
31 | _phantom: PhantomData<(AST, ID)>, | ||
32 | } | ||
33 | |||
34 | impl<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; | |||
16 | pub mod diagnostics; | 16 | pub mod diagnostics; |
17 | pub mod per_ns; | 17 | pub mod per_ns; |
18 | 18 | ||
19 | pub mod dyn_map; | ||
20 | pub mod keys; | ||
21 | |||
19 | pub mod adt; | 22 | pub mod adt; |
20 | pub mod data; | 23 | pub mod data; |
21 | pub mod generics; | 24 | pub mod generics; |
@@ -30,7 +33,7 @@ mod trace; | |||
30 | pub mod nameres; | 33 | pub mod nameres; |
31 | 34 | ||
32 | pub mod src; | 35 | pub mod src; |
33 | pub mod child_from_source; | 36 | pub mod child_by_source; |
34 | 37 | ||
35 | #[cfg(test)] | 38 | #[cfg(test)] |
36 | mod test_db; | 39 | mod test_db; |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index f1b67555f..d724ee122 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; | |||
11 | use std::sync::Arc; | 11 | use std::sync::Arc; |
12 | 12 | ||
13 | use hir_def::{ | 13 | use 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 | }; |
17 | use hir_expand::InFile; | 17 | use hir_expand::InFile; |
18 | use insta::assert_snapshot; | 18 | use 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 | ||
3 | use std::{iter::successors, marker::PhantomData}; | 3 | use std::{ |
4 | hash::{Hash, Hasher}, | ||
5 | iter::successors, | ||
6 | marker::PhantomData, | ||
7 | }; | ||
4 | 8 | ||
5 | use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange}; | 9 | use 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)] |
47 | pub struct AstPtr<N: AstNode> { | 51 | pub 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 | ||
71 | impl<N: AstNode> Hash for AstPtr<N> { | ||
72 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
73 | self.raw.hash(state) | ||
74 | } | ||
75 | } | ||
76 | |||
67 | impl<N: AstNode> AstPtr<N> { | 77 | impl<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 } |