diff options
Diffstat (limited to 'crates/ra_hir/src/module_tree.rs')
-rw-r--r-- | crates/ra_hir/src/module_tree.rs | 245 |
1 files changed, 102 insertions, 143 deletions
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs index b7912ba5e..c7a442319 100644 --- a/crates/ra_hir/src/module_tree.rs +++ b/crates/ra_hir/src/module_tree.rs | |||
@@ -11,55 +11,70 @@ use ra_syntax::{ | |||
11 | }; | 11 | }; |
12 | use ra_arena::{Arena, RawId, impl_arena_id}; | 12 | use ra_arena::{Arena, RawId, impl_arena_id}; |
13 | 13 | ||
14 | use crate::{Name, AsName, HirDatabase, SourceItemId, SourceFileItemId, HirFileId, Problem}; | 14 | use crate::{Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource}; |
15 | |||
16 | impl ModuleSource { | ||
17 | pub fn from_source_item_id( | ||
18 | db: &impl HirDatabase, | ||
19 | source_item_id: SourceItemId, | ||
20 | ) -> ModuleSource { | ||
21 | let module_syntax = db.file_item(source_item_id); | ||
22 | let module_syntax = module_syntax.borrowed(); | ||
23 | if let Some(source_file) = ast::SourceFile::cast(module_syntax) { | ||
24 | ModuleSource::SourceFile(source_file.owned()) | ||
25 | } else if let Some(module) = ast::Module::cast(module_syntax) { | ||
26 | assert!(module.item_list().is_some(), "expected inline module"); | ||
27 | ModuleSource::Module(module.owned()) | ||
28 | } else { | ||
29 | panic!("expected file or inline module") | ||
30 | } | ||
31 | } | ||
32 | } | ||
15 | 33 | ||
16 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | 34 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] |
17 | pub enum Submodule { | 35 | pub struct Submodule { |
18 | Declaration(Name), | 36 | name: Name, |
19 | Definition(Name, ModuleSource), | 37 | is_declaration: bool, |
38 | source: SourceItemId, | ||
20 | } | 39 | } |
21 | 40 | ||
22 | impl Submodule { | 41 | impl Submodule { |
23 | pub(crate) fn submodules_query( | 42 | pub(crate) fn submodules_query( |
24 | db: &impl HirDatabase, | 43 | db: &impl HirDatabase, |
25 | source: ModuleSource, | 44 | source: SourceItemId, |
26 | ) -> Cancelable<Arc<Vec<Submodule>>> { | 45 | ) -> Cancelable<Arc<Vec<Submodule>>> { |
27 | db.check_canceled()?; | 46 | db.check_canceled()?; |
28 | let file_id = source.file_id(); | 47 | let file_id = source.file_id; |
29 | let submodules = match source.resolve(db) { | 48 | let file_items = db.file_items(file_id); |
30 | ModuleSourceNode::SourceFile(it) => collect_submodules(db, file_id, it.borrowed()), | 49 | let module_source = ModuleSource::from_source_item_id(db, source); |
31 | ModuleSourceNode::Module(it) => it | 50 | let submodules = match module_source { |
32 | .borrowed() | 51 | ModuleSource::SourceFile(source_file) => { |
33 | .item_list() | 52 | collect_submodules(file_id, &file_items, source_file.borrowed()) |
34 | .map(|it| collect_submodules(db, file_id, it)) | 53 | } |
35 | .unwrap_or_else(Vec::new), | 54 | ModuleSource::Module(module) => { |
55 | let module = module.borrowed(); | ||
56 | collect_submodules(file_id, &file_items, module.item_list().unwrap()) | ||
57 | } | ||
36 | }; | 58 | }; |
37 | return Ok(Arc::new(submodules)); | 59 | return Ok(Arc::new(submodules)); |
38 | 60 | ||
39 | fn collect_submodules<'a>( | 61 | fn collect_submodules<'a>( |
40 | db: &impl HirDatabase, | ||
41 | file_id: HirFileId, | 62 | file_id: HirFileId, |
63 | file_items: &SourceFileItems, | ||
42 | root: impl ast::ModuleItemOwner<'a>, | 64 | root: impl ast::ModuleItemOwner<'a>, |
43 | ) -> Vec<Submodule> { | 65 | ) -> Vec<Submodule> { |
44 | modules(root) | 66 | modules(root) |
45 | .map(|(name, m)| { | 67 | .map(|(name, m)| Submodule { |
46 | if m.has_semi() { | 68 | name, |
47 | Submodule::Declaration(name) | 69 | is_declaration: m.has_semi(), |
48 | } else { | 70 | source: SourceItemId { |
49 | let src = ModuleSource::new_inline(db, file_id, m); | 71 | file_id, |
50 | Submodule::Definition(name, src) | 72 | item_id: Some(file_items.id_of(file_id, m.syntax())), |
51 | } | 73 | }, |
52 | }) | 74 | }) |
53 | .collect() | 75 | .collect() |
54 | } | 76 | } |
55 | } | 77 | } |
56 | |||
57 | fn name(&self) -> &Name { | ||
58 | match self { | ||
59 | Submodule::Declaration(name) => name, | ||
60 | Submodule::Definition(name, _) => name, | ||
61 | } | ||
62 | } | ||
63 | } | 78 | } |
64 | 79 | ||
65 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 80 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
@@ -85,13 +100,14 @@ pub struct ModuleTree { | |||
85 | 100 | ||
86 | #[derive(Debug, PartialEq, Eq, Hash)] | 101 | #[derive(Debug, PartialEq, Eq, Hash)] |
87 | pub struct ModuleData { | 102 | pub struct ModuleData { |
88 | source: ModuleSource, | 103 | source: SourceItemId, |
89 | parent: Option<LinkId>, | 104 | parent: Option<LinkId>, |
90 | children: Vec<LinkId>, | 105 | children: Vec<LinkId>, |
91 | } | 106 | } |
92 | 107 | ||
93 | #[derive(Hash, Debug, PartialEq, Eq)] | 108 | #[derive(Hash, Debug, PartialEq, Eq)] |
94 | struct LinkData { | 109 | struct LinkData { |
110 | source: SourceItemId, | ||
95 | owner: ModuleId, | 111 | owner: ModuleId, |
96 | name: Name, | 112 | name: Name, |
97 | points_to: Vec<ModuleId>, | 113 | points_to: Vec<ModuleId>, |
@@ -112,27 +128,14 @@ impl ModuleTree { | |||
112 | self.mods.iter().map(|(id, _)| id) | 128 | self.mods.iter().map(|(id, _)| id) |
113 | } | 129 | } |
114 | 130 | ||
115 | pub(crate) fn modules_with_sources<'a>( | 131 | pub(crate) fn find_module_by_source(&self, source: SourceItemId) -> Option<ModuleId> { |
116 | &'a self, | 132 | let (res, _) = self.mods.iter().find(|(_, m)| m.source == source)?; |
117 | ) -> impl Iterator<Item = (ModuleId, ModuleSource)> + 'a { | 133 | Some(res) |
118 | self.mods.iter().map(|(id, m)| (id, m.source)) | ||
119 | } | 134 | } |
120 | } | 135 | } |
121 | 136 | ||
122 | /// `ModuleSource` is the syntax tree element that produced this module: | ||
123 | /// either a file, or an inlinde module. | ||
124 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
125 | pub struct ModuleSource(pub(crate) SourceItemId); | ||
126 | |||
127 | /// An owned syntax node for a module. Unlike `ModuleSource`, | ||
128 | /// this holds onto the AST for the whole file. | ||
129 | pub(crate) enum ModuleSourceNode { | ||
130 | SourceFile(ast::SourceFileNode), | ||
131 | Module(ast::ModuleNode), | ||
132 | } | ||
133 | |||
134 | impl ModuleId { | 137 | impl ModuleId { |
135 | pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource { | 138 | pub(crate) fn source(self, tree: &ModuleTree) -> SourceItemId { |
136 | tree.mods[self].source | 139 | tree.mods[self].source |
137 | } | 140 | } |
138 | pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> { | 141 | pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> { |
@@ -173,9 +176,9 @@ impl ModuleId { | |||
173 | tree.mods[self] | 176 | tree.mods[self] |
174 | .children | 177 | .children |
175 | .iter() | 178 | .iter() |
176 | .filter_map(|&it| { | 179 | .filter_map(|&link| { |
177 | let p = tree.links[it].problem.clone()?; | 180 | let p = tree.links[link].problem.clone()?; |
178 | let s = it.bind_source(tree, db); | 181 | let s = link.source(tree, db); |
179 | let s = s.borrowed().name().unwrap().syntax().owned(); | 182 | let s = s.borrowed().name().unwrap().syntax().owned(); |
180 | Some((s, p)) | 183 | Some((s, p)) |
181 | }) | 184 | }) |
@@ -190,59 +193,11 @@ impl LinkId { | |||
190 | pub(crate) fn name(self, tree: &ModuleTree) -> &Name { | 193 | pub(crate) fn name(self, tree: &ModuleTree) -> &Name { |
191 | &tree.links[self].name | 194 | &tree.links[self].name |
192 | } | 195 | } |
193 | pub(crate) fn bind_source<'a>( | 196 | pub(crate) fn source(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode { |
194 | self, | 197 | let syntax_node = db.file_item(tree.links[self].source); |
195 | tree: &ModuleTree, | 198 | ast::ModuleNode::cast(syntax_node.borrowed()) |
196 | db: &impl HirDatabase, | 199 | .unwrap() |
197 | ) -> ast::ModuleNode { | 200 | .owned() |
198 | let owner = self.owner(tree); | ||
199 | match owner.source(tree).resolve(db) { | ||
200 | ModuleSourceNode::SourceFile(root) => { | ||
201 | let ast = modules(root.borrowed()) | ||
202 | .find(|(name, _)| name == &tree.links[self].name) | ||
203 | .unwrap() | ||
204 | .1; | ||
205 | ast.owned() | ||
206 | } | ||
207 | ModuleSourceNode::Module(it) => it, | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | impl ModuleSource { | ||
213 | // precondition: item_id **must** point to module | ||
214 | fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource { | ||
215 | let source_item_id = SourceItemId { file_id, item_id }; | ||
216 | ModuleSource(source_item_id) | ||
217 | } | ||
218 | |||
219 | pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource { | ||
220 | ModuleSource::new(file_id, None) | ||
221 | } | ||
222 | |||
223 | pub(crate) fn new_inline( | ||
224 | db: &impl HirDatabase, | ||
225 | file_id: HirFileId, | ||
226 | m: ast::Module, | ||
227 | ) -> ModuleSource { | ||
228 | assert!(!m.has_semi()); | ||
229 | let file_items = db.file_items(file_id); | ||
230 | let item_id = file_items.id_of(file_id, m.syntax()); | ||
231 | ModuleSource::new(file_id, Some(item_id)) | ||
232 | } | ||
233 | |||
234 | pub(crate) fn file_id(self) -> HirFileId { | ||
235 | self.0.file_id | ||
236 | } | ||
237 | |||
238 | pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode { | ||
239 | let syntax_node = db.file_item(self.0); | ||
240 | let syntax_node = syntax_node.borrowed(); | ||
241 | if let Some(file) = ast::SourceFile::cast(syntax_node) { | ||
242 | return ModuleSourceNode::SourceFile(file.owned()); | ||
243 | } | ||
244 | let module = ast::Module::cast(syntax_node).unwrap(); | ||
245 | ModuleSourceNode::Module(module.owned()) | ||
246 | } | 201 | } |
247 | } | 202 | } |
248 | 203 | ||
@@ -283,7 +238,10 @@ fn create_module_tree<'a>( | |||
283 | 238 | ||
284 | let source_root = db.source_root(source_root); | 239 | let source_root = db.source_root(source_root); |
285 | for &file_id in source_root.files.values() { | 240 | for &file_id in source_root.files.values() { |
286 | let source = ModuleSource::new_file(file_id.into()); | 241 | let source = SourceItemId { |
242 | file_id: file_id.into(), | ||
243 | item_id: None, | ||
244 | }; | ||
287 | if visited.contains(&source) { | 245 | if visited.contains(&source) { |
288 | continue; // TODO: use explicit crate_roots here | 246 | continue; // TODO: use explicit crate_roots here |
289 | } | 247 | } |
@@ -306,10 +264,10 @@ fn build_subtree( | |||
306 | db: &impl HirDatabase, | 264 | db: &impl HirDatabase, |
307 | source_root: &SourceRoot, | 265 | source_root: &SourceRoot, |
308 | tree: &mut ModuleTree, | 266 | tree: &mut ModuleTree, |
309 | visited: &mut FxHashSet<ModuleSource>, | 267 | visited: &mut FxHashSet<SourceItemId>, |
310 | roots: &mut FxHashMap<FileId, ModuleId>, | 268 | roots: &mut FxHashMap<FileId, ModuleId>, |
311 | parent: Option<LinkId>, | 269 | parent: Option<LinkId>, |
312 | source: ModuleSource, | 270 | source: SourceItemId, |
313 | ) -> Cancelable<ModuleId> { | 271 | ) -> Cancelable<ModuleId> { |
314 | visited.insert(source); | 272 | visited.insert(source); |
315 | let id = tree.push_mod(ModuleData { | 273 | let id = tree.push_mod(ModuleData { |
@@ -319,47 +277,48 @@ fn build_subtree( | |||
319 | }); | 277 | }); |
320 | for sub in db.submodules(source)?.iter() { | 278 | for sub in db.submodules(source)?.iter() { |
321 | let link = tree.push_link(LinkData { | 279 | let link = tree.push_link(LinkData { |
322 | name: sub.name().clone(), | 280 | source: sub.source, |
281 | name: sub.name.clone(), | ||
323 | owner: id, | 282 | owner: id, |
324 | points_to: Vec::new(), | 283 | points_to: Vec::new(), |
325 | problem: None, | 284 | problem: None, |
326 | }); | 285 | }); |
327 | 286 | ||
328 | let (points_to, problem) = match sub { | 287 | let (points_to, problem) = if sub.is_declaration { |
329 | Submodule::Declaration(name) => { | 288 | let (points_to, problem) = resolve_submodule(db, source.file_id, &sub.name); |
330 | let (points_to, problem) = resolve_submodule(db, source, &name); | 289 | let points_to = points_to |
331 | let points_to = points_to | 290 | .into_iter() |
332 | .into_iter() | 291 | .map(|file_id| match roots.remove(&file_id) { |
333 | .map(|file_id| match roots.remove(&file_id) { | 292 | Some(module_id) => { |
334 | Some(module_id) => { | 293 | tree.mods[module_id].parent = Some(link); |
335 | tree.mods[module_id].parent = Some(link); | 294 | Ok(module_id) |
336 | Ok(module_id) | 295 | } |
337 | } | 296 | None => build_subtree( |
338 | None => build_subtree( | 297 | db, |
339 | db, | 298 | source_root, |
340 | source_root, | 299 | tree, |
341 | tree, | 300 | visited, |
342 | visited, | 301 | roots, |
343 | roots, | 302 | Some(link), |
344 | Some(link), | 303 | SourceItemId { |
345 | ModuleSource::new_file(file_id.into()), | 304 | file_id: file_id.into(), |
346 | ), | 305 | item_id: None, |
347 | }) | 306 | }, |
348 | .collect::<Cancelable<Vec<_>>>()?; | 307 | ), |
349 | (points_to, problem) | 308 | }) |
350 | } | 309 | .collect::<Cancelable<Vec<_>>>()?; |
351 | Submodule::Definition(_name, submodule_source) => { | 310 | (points_to, problem) |
352 | let points_to = build_subtree( | 311 | } else { |
353 | db, | 312 | let points_to = build_subtree( |
354 | source_root, | 313 | db, |
355 | tree, | 314 | source_root, |
356 | visited, | 315 | tree, |
357 | roots, | 316 | visited, |
358 | Some(link), | 317 | roots, |
359 | *submodule_source, | 318 | Some(link), |
360 | )?; | 319 | sub.source, |
361 | (vec![points_to], None) | 320 | )?; |
362 | } | 321 | (vec![points_to], None) |
363 | }; | 322 | }; |
364 | 323 | ||
365 | tree.links[link].points_to = points_to; | 324 | tree.links[link].points_to = points_to; |
@@ -370,11 +329,11 @@ fn build_subtree( | |||
370 | 329 | ||
371 | fn resolve_submodule( | 330 | fn resolve_submodule( |
372 | db: &impl HirDatabase, | 331 | db: &impl HirDatabase, |
373 | source: ModuleSource, | 332 | file_id: HirFileId, |
374 | name: &Name, | 333 | name: &Name, |
375 | ) -> (Vec<FileId>, Option<Problem>) { | 334 | ) -> (Vec<FileId>, Option<Problem>) { |
376 | // FIXME: handle submodules of inline modules properly | 335 | // FIXME: handle submodules of inline modules properly |
377 | let file_id = source.file_id().original_file(db); | 336 | let file_id = file_id.original_file(db); |
378 | let source_root_id = db.file_source_root(file_id); | 337 | let source_root_id = db.file_source_root(file_id); |
379 | let path = db.file_relative_path(file_id); | 338 | let path = db.file_relative_path(file_id); |
380 | let root = RelativePathBuf::default(); | 339 | let root = RelativePathBuf::default(); |