aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-12-05 14:16:59 +0000
committerAleksey Kladov <[email protected]>2019-12-05 15:55:54 +0000
commit0c0ce1ae418a2f3f4fc125bd701cdb327f607002 (patch)
tree596e533284a136297c8443094434c9738d24c435
parent4c0bd068da39e74c66104206e27c270454e3562e (diff)
Introduce ChildFromSource
-rw-r--r--crates/ra_hir/src/from_source.rs141
-rw-r--r--crates/ra_hir_def/src/child_from_source.rs276
-rw-r--r--crates/ra_hir_def/src/lib.rs1
3 files changed, 317 insertions, 101 deletions
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index 18d87f6d7..58203c721 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -1,17 +1,20 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use either::Either;
2 3
3use hir_def::{nameres::ModuleSource, AstItemDef, LocationCtx, ModuleId}; 4use hir_def::{
5 child_from_source::ChildFromSource, nameres::ModuleSource, AstItemDef, EnumVariantId,
6 LocationCtx, ModuleId, VariantId,
7};
4use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; 8use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
5use ra_syntax::{ 9use ra_syntax::{
6 ast::{self, AstNode, NameOwner}, 10 ast::{self, AstNode, NameOwner},
7 match_ast, AstPtr, SyntaxNode, 11 match_ast, SyntaxNode,
8}; 12};
9 13
10use crate::{ 14use crate::{
11 db::{AstDatabase, DefDatabase, HirDatabase}, 15 db::{AstDatabase, DefDatabase, HirDatabase},
12 AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, 16 Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
13 InFile, Local, MacroDef, Module, ModuleDef, Static, Struct, StructField, Trait, TypeAlias, 17 MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, Union,
14 Union, VariantDef,
15}; 18};
16 19
17pub trait FromSource: Sized { 20pub trait FromSource: Sized {
@@ -50,98 +53,45 @@ impl FromSource for Trait {
50impl FromSource for Function { 53impl FromSource for Function {
51 type Ast = ast::FnDef; 54 type Ast = ast::FnDef;
52 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 55 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
53 let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 56 match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
54 Container::Trait(it) => it.items(db), 57 Container::Trait(it) => it.id.child_from_source(db, src),
55 Container::ImplBlock(it) => it.items(db), 58 Container::ImplBlock(it) => it.id.child_from_source(db, src),
56 Container::Module(m) => { 59 Container::Module(it) => it.id.child_from_source(db, src),
57 return m 60 }
58 .declarations(db) 61 .map(Function::from)
59 .into_iter()
60 .filter_map(|it| match it {
61 ModuleDef::Function(it) => Some(it),
62 _ => None,
63 })
64 .find(|it| same_source(&it.source(db), &src))
65 }
66 };
67 items
68 .into_iter()
69 .filter_map(|it| match it {
70 AssocItem::Function(it) => Some(it),
71 _ => None,
72 })
73 .find(|it| same_source(&it.source(db), &src))
74 } 62 }
75} 63}
76 64
77impl FromSource for Const { 65impl FromSource for Const {
78 type Ast = ast::ConstDef; 66 type Ast = ast::ConstDef;
79 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 67 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
80 let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 68 match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
81 Container::Trait(it) => it.items(db), 69 Container::Trait(it) => it.id.child_from_source(db, src),
82 Container::ImplBlock(it) => it.items(db), 70 Container::ImplBlock(it) => it.id.child_from_source(db, src),
83 Container::Module(m) => { 71 Container::Module(it) => it.id.child_from_source(db, src),
84 return m 72 }
85 .declarations(db) 73 .map(Const::from)
86 .into_iter()
87 .filter_map(|it| match it {
88 ModuleDef::Const(it) => Some(it),
89 _ => None,
90 })
91 .find(|it| same_source(&it.source(db), &src))
92 }
93 };
94 items
95 .into_iter()
96 .filter_map(|it| match it {
97 AssocItem::Const(it) => Some(it),
98 _ => None,
99 })
100 .find(|it| same_source(&it.source(db), &src))
101 } 74 }
102} 75}
103impl FromSource for Static { 76impl FromSource for Static {
104 type Ast = ast::StaticDef; 77 type Ast = ast::StaticDef;
105 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 78 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
106 let module = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 79 match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
107 Container::Module(it) => it, 80 Container::Module(it) => it.id.child_from_source(db, src).map(Static::from),
108 Container::Trait(_) | Container::ImplBlock(_) => return None, 81 Container::Trait(_) | Container::ImplBlock(_) => None,
109 }; 82 }
110 module
111 .declarations(db)
112 .into_iter()
113 .filter_map(|it| match it {
114 ModuleDef::Static(it) => Some(it),
115 _ => None,
116 })
117 .find(|it| same_source(&it.source(db), &src))
118 } 83 }
119} 84}
120 85
121impl FromSource for TypeAlias { 86impl FromSource for TypeAlias {
122 type Ast = ast::TypeAliasDef; 87 type Ast = ast::TypeAliasDef;
123 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 88 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
124 let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 89 match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
125 Container::Trait(it) => it.items(db), 90 Container::Trait(it) => it.id.child_from_source(db, src),
126 Container::ImplBlock(it) => it.items(db), 91 Container::ImplBlock(it) => it.id.child_from_source(db, src),
127 Container::Module(m) => { 92 Container::Module(it) => it.id.child_from_source(db, src),
128 return m 93 }
129 .declarations(db) 94 .map(TypeAlias::from)
130 .into_iter()
131 .filter_map(|it| match it {
132 ModuleDef::TypeAlias(it) => Some(it),
133 _ => None,
134 })
135 .find(|it| same_source(&it.source(db), &src))
136 }
137 };
138 items
139 .into_iter()
140 .filter_map(|it| match it {
141 AssocItem::TypeAlias(it) => Some(it),
142 _ => None,
143 })
144 .find(|it| same_source(&it.source(db), &src))
145 } 95 }
146} 96}
147 97
@@ -174,34 +124,33 @@ impl FromSource for EnumVariant {
174 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 124 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
175 let parent_enum = src.value.parent_enum(); 125 let parent_enum = src.value.parent_enum();
176 let src_enum = InFile { file_id: src.file_id, value: parent_enum }; 126 let src_enum = InFile { file_id: src.file_id, value: parent_enum };
177 let variants = Enum::from_source(db, src_enum)?.variants(db); 127 let parent_enum = Enum::from_source(db, src_enum)?;
178 variants.into_iter().find(|v| same_source(&v.source(db), &src)) 128 parent_enum.id.child_from_source(db, src).map(EnumVariant::from)
179 } 129 }
180} 130}
181 131
182impl FromSource for StructField { 132impl FromSource for StructField {
183 type Ast = FieldSource; 133 type Ast = FieldSource;
184 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> { 134 fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
185 let variant_def: VariantDef = match src.value { 135 let variant_id: VariantId = match src.value {
186 FieldSource::Named(ref field) => { 136 FieldSource::Named(ref field) => {
187 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; 137 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
188 let src = InFile { file_id: src.file_id, value }; 138 let src = InFile { file_id: src.file_id, value };
189 let def = Struct::from_source(db, src)?; 139 let def = Struct::from_source(db, src)?;
190 VariantDef::from(def) 140 def.id.into()
191 } 141 }
192 FieldSource::Pos(ref field) => { 142 FieldSource::Pos(ref field) => {
193 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; 143 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
194 let src = InFile { file_id: src.file_id, value }; 144 let src = InFile { file_id: src.file_id, value };
195 let def = EnumVariant::from_source(db, src)?; 145 let def = EnumVariant::from_source(db, src)?;
196 VariantDef::from(def) 146 EnumVariantId::from(def).into()
197 } 147 }
198 }; 148 };
199 variant_def 149 let src = src.map(|field_source| match field_source {
200 .variant_data(db) 150 FieldSource::Pos(it) => Either::Left(it),
201 .fields() 151 FieldSource::Named(it) => Either::Right(it),
202 .iter() 152 });
203 .map(|(id, _)| StructField { parent: variant_def, id }) 153 variant_id.child_from_source(db, src).map(StructField::from)
204 .find(|f| f.source(db) == src)
205 } 154 }
206} 155}
207 156
@@ -314,13 +263,3 @@ impl Container {
314 Some(Container::Module(c)) 263 Some(Container::Module(c))
315 } 264 }
316} 265}
317
318/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
319/// equal if they point to exactly the same object.
320///
321/// In general, we do not guarantee that we have exactly one instance of a
322/// syntax tree for each file. We probably should add such guarantee, but, for
323/// the time being, we will use identity-less AstPtr comparison.
324fn same_source<N: AstNode>(s1: &InFile<N>, s2: &InFile<N>) -> bool {
325 s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new)
326}
diff --git a/crates/ra_hir_def/src/child_from_source.rs b/crates/ra_hir_def/src/child_from_source.rs
new file mode 100644
index 000000000..37d4b7870
--- /dev/null
+++ b/crates/ra_hir_def/src/child_from_source.rs
@@ -0,0 +1,276 @@
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/lib.rs b/crates/ra_hir_def/src/lib.rs
index cfeacfded..e02622f62 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -30,6 +30,7 @@ mod trace;
30pub mod nameres; 30pub mod nameres;
31 31
32pub mod src; 32pub mod src;
33pub mod child_from_source;
33 34
34#[cfg(test)] 35#[cfg(test)]
35mod test_db; 36mod test_db;