aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/from_source.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/from_source.rs')
-rw-r--r--crates/ra_hir/src/from_source.rs362
1 files changed, 150 insertions, 212 deletions
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index 9f7c22b21..6314be8d4 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -1,219 +1,140 @@
1//! FIXME: write short doc here 1//! Finds a corresponding hir data structure for a syntax node in a specific
2//! file.
2 3
3use hir_def::{AstItemDef, LocationCtx, ModuleId}; 4use hir_def::{
5 child_by_source::ChildBySource, dyn_map::DynMap, keys, keys::Key, nameres::ModuleSource,
6 ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
7 StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
8};
4use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; 9use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
10use ra_db::FileId;
11use ra_prof::profile;
5use ra_syntax::{ 12use ra_syntax::{
6 ast::{self, AstNode, NameOwner}, 13 ast::{self, AstNode, NameOwner},
7 match_ast, AstPtr, SyntaxNode, 14 match_ast, SyntaxNode,
8}; 15};
9 16
10use crate::{ 17use crate::{
11 db::{AstDatabase, DefDatabase, HirDatabase}, 18 db::{DefDatabase, HirDatabase},
12 AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, 19 Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
13 Local, MacroDef, Module, ModuleDef, ModuleSource, Source, Static, Struct, StructField, Trait, 20 MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
14 TypeAlias, Union, VariantDef,
15}; 21};
16 22
17pub trait FromSource: Sized { 23pub trait FromSource: Sized {
18 type Ast; 24 type Ast;
19 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self>; 25 fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self>;
20} 26}
21 27
22impl FromSource for Struct { 28pub trait FromSourceByContainer: Sized {
23 type Ast = ast::StructDef; 29 type Ast: AstNode + 'static;
24 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { 30 type Id: Copy + 'static;
25 let id = from_source(db, src)?; 31 const KEY: Key<Self::Ast, Self::Id>;
26 Some(Struct { id })
27 }
28}
29impl FromSource for Union {
30 type Ast = ast::UnionDef;
31 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
32 let id = from_source(db, src)?;
33 Some(Union { id })
34 }
35}
36impl FromSource for Enum {
37 type Ast = ast::EnumDef;
38 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
39 let id = from_source(db, src)?;
40 Some(Enum { id })
41 }
42}
43impl FromSource for Trait {
44 type Ast = ast::TraitDef;
45 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
46 let id = from_source(db, src)?;
47 Some(Trait { id })
48 }
49}
50impl FromSource for Function {
51 type Ast = ast::FnDef;
52 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
53 let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
54 Container::Trait(it) => it.items(db),
55 Container::ImplBlock(it) => it.items(db),
56 Container::Module(m) => {
57 return m
58 .declarations(db)
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 }
75} 32}
76 33
77impl FromSource for Const { 34impl<T: FromSourceByContainer> FromSource for T
78 type Ast = ast::ConstDef; 35where
79 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { 36 T: From<<T as FromSourceByContainer>::Id>,
80 let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 37{
81 Container::Trait(it) => it.items(db), 38 type Ast = <T as FromSourceByContainer>::Ast;
82 Container::ImplBlock(it) => it.items(db), 39 fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
83 Container::Module(m) => { 40 analyze_container(db, src.as_ref().map(|it| it.syntax()))[T::KEY]
84 return m 41 .get(&src)
85 .declarations(db) 42 .copied()
86 .into_iter() 43 .map(Self::from)
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 }
102}
103impl FromSource for Static {
104 type Ast = ast::StaticDef;
105 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
106 let module = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
107 Container::Module(it) => it,
108 Container::Trait(_) | Container::ImplBlock(_) => return None,
109 };
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 } 44 }
119} 45}
120 46
121impl FromSource for TypeAlias { 47macro_rules! from_source_by_container_impls {
122 type Ast = ast::TypeAliasDef; 48 ($(($hir:ident, $id:ident, $ast:path, $key:path)),* ,) => {$(
123 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { 49 impl FromSourceByContainer for $hir {
124 let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? { 50 type Ast = $ast;
125 Container::Trait(it) => it.items(db), 51 type Id = $id;
126 Container::ImplBlock(it) => it.items(db), 52 const KEY: Key<Self::Ast, Self::Id> = $key;
127 Container::Module(m) => { 53 }
128 return m 54 )*}
129 .declarations(db)
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 }
146} 55}
147 56
57from_source_by_container_impls![
58 (Struct, StructId, ast::StructDef, keys::STRUCT),
59 (Union, UnionId, ast::UnionDef, keys::UNION),
60 (Enum, EnumId, ast::EnumDef, keys::ENUM),
61 (Trait, TraitId, ast::TraitDef, keys::TRAIT),
62 (Function, FunctionId, ast::FnDef, keys::FUNCTION),
63 (Static, StaticId, ast::StaticDef, keys::STATIC),
64 (Const, ConstId, ast::ConstDef, keys::CONST),
65 (TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
66 (ImplBlock, ImplId, ast::ImplBlock, keys::IMPL),
67];
68
148impl FromSource for MacroDef { 69impl FromSource for MacroDef {
149 type Ast = ast::MacroCall; 70 type Ast = ast::MacroCall;
150 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { 71 fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
151 let kind = MacroDefKind::Declarative; 72 let kind = MacroDefKind::Declarative;
152 73
153 let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax())); 74 let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
154 let module = Module::from_definition(db, Source::new(src.file_id, module_src))?; 75 let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
155 let krate = module.krate().crate_id(); 76 let krate = Some(module.krate().id);
156 77
157 let ast_id = AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)); 78 let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));
158 79
159 let id: MacroDefId = MacroDefId { krate, ast_id, kind }; 80 let id: MacroDefId = MacroDefId { krate, ast_id, kind };
160 Some(MacroDef { id }) 81 Some(MacroDef { id })
161 } 82 }
162} 83}
163 84
164impl FromSource for ImplBlock {
165 type Ast = ast::ImplBlock;
166 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
167 let id = from_source(db, src)?;
168 Some(ImplBlock { id })
169 }
170}
171
172impl FromSource for EnumVariant { 85impl FromSource for EnumVariant {
173 type Ast = ast::EnumVariant; 86 type Ast = ast::EnumVariant;
174 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { 87 fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
175 let parent_enum = src.value.parent_enum(); 88 let parent_enum = src.value.parent_enum();
176 let src_enum = Source { file_id: src.file_id, value: parent_enum }; 89 let src_enum = InFile { file_id: src.file_id, value: parent_enum };
177 let variants = Enum::from_source(db, src_enum)?.variants(db); 90 let parent_enum = Enum::from_source(db, src_enum)?;
178 variants.into_iter().find(|v| same_source(&v.source(db), &src)) 91 parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
92 .get(&src)
93 .copied()
94 .map(EnumVariant::from)
179 } 95 }
180} 96}
181 97
182impl FromSource for StructField { 98impl FromSource for StructField {
183 type Ast = FieldSource; 99 type Ast = FieldSource;
184 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { 100 fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
185 let variant_def: VariantDef = match src.value { 101 let src = src.as_ref();
186 FieldSource::Named(ref field) => { 102
103 // FIXME this is buggy
104 let variant_id: VariantId = match src.value {
105 FieldSource::Named(field) => {
187 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; 106 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
188 let src = Source { file_id: src.file_id, value }; 107 let src = InFile { file_id: src.file_id, value };
189 let def = Struct::from_source(db, src)?; 108 let def = Struct::from_source(db, src)?;
190 VariantDef::from(def) 109 def.id.into()
191 } 110 }
192 FieldSource::Pos(ref field) => { 111 FieldSource::Pos(field) => {
193 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; 112 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
194 let src = Source { file_id: src.file_id, value }; 113 let src = InFile { file_id: src.file_id, value };
195 let def = EnumVariant::from_source(db, src)?; 114 let def = EnumVariant::from_source(db, src)?;
196 VariantDef::from(def) 115 EnumVariantId::from(def).into()
197 } 116 }
198 }; 117 };
199 variant_def 118
200 .variant_data(db) 119 let dyn_map = variant_id.child_by_source(db);
201 .fields() 120 match src.value {
202 .iter() 121 FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
203 .map(|(id, _)| StructField { parent: variant_def, id }) 122 FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
204 .find(|f| f.source(db) == src) 123 }
124 .copied()
125 .map(StructField::from)
205 } 126 }
206} 127}
207 128
208impl Local { 129impl Local {
209 pub fn from_source(db: &impl HirDatabase, src: Source<ast::BindPat>) -> Option<Self> { 130 pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
210 let file_id = src.file_id; 131 let file_id = src.file_id;
211 let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| { 132 let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
212 let res = match_ast! { 133 let res = match_ast! {
213 match it { 134 match it {
214 ast::ConstDef(value) => { Const::from_source(db, Source { value, file_id})?.into() }, 135 ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
215 ast::StaticDef(value) => { Static::from_source(db, Source { value, file_id})?.into() }, 136 ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
216 ast::FnDef(value) => { Function::from_source(db, Source { value, file_id})?.into() }, 137 ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
217 _ => return None, 138 _ => return None,
218 } 139 }
219 }; 140 };
@@ -226,84 +147,111 @@ impl Local {
226 } 147 }
227} 148}
228 149
150impl TypeParam {
151 pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
152 let file_id = src.file_id;
153 let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
154 let res = match_ast! {
155 match it {
156 ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() },
157 ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() },
158 ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() },
159 ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() },
160 ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() },
161 ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() },
162 _ => return None,
163 }
164 };
165 Some(res)
166 })?;
167 let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
168 Some(TypeParam { id })
169 }
170}
171
229impl Module { 172impl Module {
230 pub fn from_declaration(db: &impl DefDatabase, src: Source<ast::Module>) -> Option<Self> { 173 pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
174 let _p = profile("Module::from_declaration");
231 let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast); 175 let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);
232 176
233 let parent_module = match parent_declaration { 177 let parent_module = match parent_declaration {
234 Some(parent_declaration) => { 178 Some(parent_declaration) => {
235 let src_parent = Source { file_id: src.file_id, value: parent_declaration }; 179 let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
236 Module::from_declaration(db, src_parent) 180 Module::from_declaration(db, src_parent)
237 } 181 }
238 _ => { 182 None => {
239 let src_parent = Source { 183 let source_file = db.parse(src.file_id.original_file(db)).tree();
240 file_id: src.file_id, 184 let src_parent =
241 value: ModuleSource::new(db, Some(src.file_id.original_file(db)), None), 185 InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
242 };
243 Module::from_definition(db, src_parent) 186 Module::from_definition(db, src_parent)
244 } 187 }
245 }?; 188 }?;
246 189
247 let child_name = src.value.name()?; 190 let child_name = src.value.name()?.as_name();
248 parent_module.child(db, &child_name.as_name()) 191 let def_map = db.crate_def_map(parent_module.id.krate);
192 let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?;
193 Some(parent_module.with_module_id(*child_id))
249 } 194 }
250 195
251 pub fn from_definition(db: &impl DefDatabase, src: Source<ModuleSource>) -> Option<Self> { 196 pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
197 let _p = profile("Module::from_definition");
252 match src.value { 198 match src.value {
253 ModuleSource::Module(ref module) => { 199 ModuleSource::Module(ref module) => {
254 assert!(!module.has_semi()); 200 assert!(!module.has_semi());
255 return Module::from_declaration( 201 return Module::from_declaration(
256 db, 202 db,
257 Source { file_id: src.file_id, value: module.clone() }, 203 InFile { file_id: src.file_id, value: module.clone() },
258 ); 204 );
259 } 205 }
260 ModuleSource::SourceFile(_) => (), 206 ModuleSource::SourceFile(_) => (),
261 }; 207 };
262 208
263 let original_file = src.file_id.original_file(db); 209 let original_file = src.file_id.original_file(db);
210 Module::from_file(db, original_file)
211 }
264 212
265 let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| { 213 fn from_file(db: &impl DefDatabase, file: FileId) -> Option<Self> {
214 let _p = profile("Module::from_file");
215 let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
266 let crate_def_map = db.crate_def_map(crate_id); 216 let crate_def_map = db.crate_def_map(crate_id);
267 let local_id = crate_def_map.modules_for_file(original_file).next()?; 217 let local_id = crate_def_map.modules_for_file(file).next()?;
268 Some((crate_id, local_id)) 218 Some((crate_id, local_id))
269 })?; 219 })?;
270 Some(Module { id: ModuleId { krate, local_id } }) 220 Some(Module { id: ModuleId { krate, local_id } })
271 } 221 }
272} 222}
273 223
274fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: Source<N>) -> Option<DEF> 224fn analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> DynMap {
275where 225 let _p = profile("analyze_container");
276 N: AstNode, 226 return child_by_source(db, src).unwrap_or_default();
277 DEF: AstItemDef<N>,
278{
279 let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
280 let module = Module::from_definition(db, Source::new(src.file_id, module_src))?;
281 let ctx = LocationCtx::new(db, module.id, src.file_id);
282 let items = db.ast_id_map(src.file_id);
283 let item_id = items.ast_id(&src.value);
284 Some(DEF::from_ast_id(ctx, item_id))
285}
286
287enum Container {
288 Trait(Trait),
289 ImplBlock(ImplBlock),
290 Module(Module),
291}
292 227
293impl Container { 228 fn child_by_source(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<DynMap> {
294 fn find(db: &impl DefDatabase, src: Source<&SyntaxNode>) -> Option<Container> { 229 for container in src.value.ancestors().skip(1) {
295 // FIXME: this doesn't try to handle nested declarations
296 for container in src.value.ancestors() {
297 let res = match_ast! { 230 let res = match_ast! {
298 match container { 231 match container {
299 ast::TraitDef(it) => { 232 ast::TraitDef(it) => {
300 let c = Trait::from_source(db, src.with_value(it))?; 233 let def = Trait::from_source(db, src.with_value(it))?;
301 Container::Trait(c) 234 def.id.child_by_source(db)
302 }, 235 },
303 ast::ImplBlock(it) => { 236 ast::ImplBlock(it) => {
304 let c = ImplBlock::from_source(db, src.with_value(it))?; 237 let def = ImplBlock::from_source(db, src.with_value(it))?;
305 Container::ImplBlock(c) 238 def.id.child_by_source(db)
306 }, 239 },
240 ast::FnDef(it) => {
241 let def = Function::from_source(db, src.with_value(it))?;
242 DefWithBodyId::from(def.id)
243 .child_by_source(db)
244 },
245 ast::StaticDef(it) => {
246 let def = Static::from_source(db, src.with_value(it))?;
247 DefWithBodyId::from(def.id)
248 .child_by_source(db)
249 },
250 ast::ConstDef(it) => {
251 let def = Const::from_source(db, src.with_value(it))?;
252 DefWithBodyId::from(def.id)
253 .child_by_source(db)
254 },
307 _ => { continue }, 255 _ => { continue },
308 } 256 }
309 }; 257 };
@@ -312,16 +260,6 @@ impl Container {
312 260
313 let module_source = ModuleSource::from_child_node(db, src); 261 let module_source = ModuleSource::from_child_node(db, src);
314 let c = Module::from_definition(db, src.with_value(module_source))?; 262 let c = Module::from_definition(db, src.with_value(module_source))?;
315 Some(Container::Module(c)) 263 Some(c.id.child_by_source(db))
316 } 264 }
317} 265}
318
319/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
320/// equal if they point to exactly the same object.
321///
322/// In general, we do not guarantee that we have exactly one instance of a
323/// syntax tree for each file. We probably should add such guarantee, but, for
324/// the time being, we will use identity-less AstPtr comparison.
325fn same_source<N: AstNode>(s1: &Source<N>, s2: &Source<N>) -> bool {
326 s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new)
327}