aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/module_tree.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/module_tree.rs')
-rw-r--r--crates/ra_hir/src/module_tree.rs340
1 files changed, 0 insertions, 340 deletions
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
index 4d0f40e85..e69de29bb 100644
--- a/crates/ra_hir/src/module_tree.rs
+++ b/crates/ra_hir/src/module_tree.rs
@@ -1,340 +0,0 @@
1use std::sync::Arc;
2
3use arrayvec::ArrayVec;
4use relative_path::RelativePathBuf;
5use ra_db::{FileId, SourceRoot};
6use ra_syntax::{
7 SyntaxNode, TreeArc,
8 algo::generate,
9 ast::{self, AstNode, NameOwner},
10};
11use ra_arena::{Arena, RawId, impl_arena_id};
12use test_utils::tested_by;
13
14use crate::{
15 Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource,
16 PersistentHirDatabase,
17 Crate,
18 ids::SourceFileItemId,
19};
20
21impl ModuleSource {
22 pub(crate) fn new(
23 db: &impl PersistentHirDatabase,
24 file_id: HirFileId,
25 decl_id: Option<SourceFileItemId>,
26 ) -> ModuleSource {
27 match decl_id {
28 Some(item_id) => {
29 let module = db.file_item(SourceItemId { file_id, item_id });
30 let module = ast::Module::cast(&*module).unwrap();
31 assert!(module.item_list().is_some(), "expected inline module");
32 ModuleSource::Module(module.to_owned())
33 }
34 None => {
35 let source_file = db.hir_parse(file_id);
36 ModuleSource::SourceFile(source_file)
37 }
38 }
39 }
40}
41
42#[derive(Clone, Hash, PartialEq, Eq, Debug)]
43pub struct Submodule {
44 name: Name,
45 is_declaration: bool,
46 decl_id: SourceFileItemId,
47}
48
49impl Submodule {
50 pub(crate) fn submodules_query(
51 db: &impl PersistentHirDatabase,
52 file_id: HirFileId,
53 decl_id: Option<SourceFileItemId>,
54 ) -> Arc<Vec<Submodule>> {
55 db.check_canceled();
56 let file_items = db.file_items(file_id);
57 let module_source = ModuleSource::new(db, file_id, decl_id);
58 let submodules = match module_source {
59 ModuleSource::SourceFile(source_file) => {
60 collect_submodules(file_id, &file_items, &*source_file)
61 }
62 ModuleSource::Module(module) => {
63 collect_submodules(file_id, &file_items, module.item_list().unwrap())
64 }
65 };
66
67 return Arc::new(submodules);
68
69 fn collect_submodules(
70 file_id: HirFileId,
71 file_items: &SourceFileItems,
72 root: &impl ast::ModuleItemOwner,
73 ) -> Vec<Submodule> {
74 root.items()
75 .filter_map(|item| match item.kind() {
76 ast::ModuleItemKind::Module(m) => Some(m),
77 _ => None,
78 })
79 .filter_map(|module| {
80 let name = module.name()?.as_name();
81 if !module.has_semi() && module.item_list().is_none() {
82 tested_by!(name_res_works_for_broken_modules);
83 return None;
84 }
85 let sub = Submodule {
86 name,
87 is_declaration: module.has_semi(),
88 decl_id: file_items.id_of(file_id, module.syntax()),
89 };
90 Some(sub)
91 })
92 .collect()
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
98pub struct ModuleId(RawId);
99impl_arena_id!(ModuleId);
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
102pub struct LinkId(RawId);
103impl_arena_id!(LinkId);
104
105/// Physically, rust source is organized as a set of files, but logically it is
106/// organized as a tree of modules. Usually, a single file corresponds to a
107/// single module, but it is not neccessarily always the case.
108///
109/// `ModuleTree` encapsulates the logic of transitioning from the fuzzy world of files
110/// (which can have multiple parents) to the precise world of modules (which
111/// always have one parent).
112#[derive(Default, Debug, PartialEq, Eq)]
113pub struct ModuleTree {
114 mods: Arena<ModuleId, ModuleData>,
115 links: Arena<LinkId, LinkData>,
116}
117
118#[derive(Debug, PartialEq, Eq)]
119pub struct ModuleData {
120 file_id: HirFileId,
121 /// Points to `ast::Module`, `None` for the whole file.
122 decl_id: Option<SourceFileItemId>,
123 parent: Option<LinkId>,
124 children: Vec<LinkId>,
125}
126
127#[derive(Hash, Debug, PartialEq, Eq)]
128struct LinkData {
129 source: SourceItemId,
130 owner: ModuleId,
131 name: Name,
132 points_to: Vec<ModuleId>,
133 problem: Option<Problem>,
134}
135
136impl ModuleTree {
137 pub(crate) fn module_tree_query(
138 db: &impl PersistentHirDatabase,
139 krate: Crate,
140 ) -> Arc<ModuleTree> {
141 db.check_canceled();
142 let mut res = ModuleTree::default();
143 res.init_crate(db, krate);
144 Arc::new(res)
145 }
146
147 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
148 self.mods.iter().map(|(id, _)| id)
149 }
150
151 pub(crate) fn find_module_by_source(
152 &self,
153 file_id: HirFileId,
154 decl_id: Option<SourceFileItemId>,
155 ) -> Option<ModuleId> {
156 let (res, _) =
157 self.mods.iter().find(|(_, m)| (m.file_id, m.decl_id) == (file_id, decl_id))?;
158 Some(res)
159 }
160
161 fn init_crate(&mut self, db: &impl PersistentHirDatabase, krate: Crate) {
162 let crate_graph = db.crate_graph();
163 let file_id = crate_graph.crate_root(krate.crate_id);
164 let source_root_id = db.file_source_root(file_id);
165
166 let source_root = db.source_root(source_root_id);
167 self.init_subtree(db, &source_root, None, file_id.into(), None);
168 }
169
170 fn init_subtree(
171 &mut self,
172 db: &impl PersistentHirDatabase,
173 source_root: &SourceRoot,
174 parent: Option<LinkId>,
175 file_id: HirFileId,
176 decl_id: Option<SourceFileItemId>,
177 ) -> ModuleId {
178 let is_root = parent.is_none();
179 let id = self.alloc_mod(ModuleData { file_id, decl_id, parent, children: Vec::new() });
180 for sub in db.submodules(file_id, decl_id).iter() {
181 let link = self.alloc_link(LinkData {
182 source: SourceItemId { file_id, item_id: sub.decl_id },
183 name: sub.name.clone(),
184 owner: id,
185 points_to: Vec::new(),
186 problem: None,
187 });
188
189 let (points_to, problem) = if sub.is_declaration {
190 let (points_to, problem) = resolve_submodule(db, file_id, &sub.name, is_root);
191 let points_to = points_to
192 .into_iter()
193 .map(|file_id| {
194 self.init_subtree(db, source_root, Some(link), file_id.into(), None)
195 })
196 .collect::<Vec<_>>();
197 (points_to, problem)
198 } else {
199 let points_to =
200 self.init_subtree(db, source_root, Some(link), file_id, Some(sub.decl_id));
201 (vec![points_to], None)
202 };
203
204 self.links[link].points_to = points_to;
205 self.links[link].problem = problem;
206 }
207 id
208 }
209
210 fn alloc_mod(&mut self, data: ModuleData) -> ModuleId {
211 self.mods.alloc(data)
212 }
213
214 fn alloc_link(&mut self, data: LinkData) -> LinkId {
215 let owner = data.owner;
216 let id = self.links.alloc(data);
217 self.mods[owner].children.push(id);
218 id
219 }
220}
221
222impl ModuleId {
223 pub(crate) fn file_id(self, tree: &ModuleTree) -> HirFileId {
224 tree.mods[self].file_id
225 }
226 pub(crate) fn decl_id(self, tree: &ModuleTree) -> Option<SourceFileItemId> {
227 tree.mods[self].decl_id
228 }
229 pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
230 tree.mods[self].parent
231 }
232 pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
233 let link = self.parent_link(tree)?;
234 Some(tree.links[link].owner)
235 }
236 pub(crate) fn crate_root(self, tree: &ModuleTree) -> ModuleId {
237 generate(Some(self), move |it| it.parent(tree)).last().unwrap()
238 }
239 pub(crate) fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
240 let link = tree.mods[self]
241 .children
242 .iter()
243 .map(|&it| &tree.links[it])
244 .find(|it| it.name == *name)?;
245 Some(*link.points_to.first()?)
246 }
247 pub(crate) fn children<'a>(
248 self,
249 tree: &'a ModuleTree,
250 ) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
251 tree.mods[self].children.iter().filter_map(move |&it| {
252 let link = &tree.links[it];
253 let module = *link.points_to.first()?;
254 Some((link.name.clone(), module))
255 })
256 }
257 pub(crate) fn problems(
258 self,
259 tree: &ModuleTree,
260 db: &impl HirDatabase,
261 ) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
262 tree.mods[self]
263 .children
264 .iter()
265 .filter_map(|&link| {
266 let p = tree.links[link].problem.clone()?;
267 let s = link.source(tree, db);
268 let s = s.name().unwrap().syntax().to_owned();
269 Some((s, p))
270 })
271 .collect()
272 }
273}
274
275impl LinkId {
276 pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
277 tree.links[self].owner
278 }
279 pub(crate) fn name(self, tree: &ModuleTree) -> &Name {
280 &tree.links[self].name
281 }
282 pub(crate) fn source(
283 self,
284 tree: &ModuleTree,
285 db: &impl PersistentHirDatabase,
286 ) -> TreeArc<ast::Module> {
287 let syntax_node = db.file_item(tree.links[self].source);
288 ast::Module::cast(&syntax_node).unwrap().to_owned()
289 }
290}
291
292pub(crate) fn resolve_module_declaration(
293 db: &impl PersistentHirDatabase,
294 file_id: HirFileId,
295 name: &Name,
296 is_root: bool,
297) -> Option<FileId> {
298 resolve_submodule(db, file_id, name, is_root).0.first().map(|it| *it)
299}
300
301fn resolve_submodule(
302 db: &impl PersistentHirDatabase,
303 file_id: HirFileId,
304 name: &Name,
305 is_root: bool,
306) -> (Vec<FileId>, Option<Problem>) {
307 // FIXME: handle submodules of inline modules properly
308 let file_id = file_id.original_file(db);
309 let source_root_id = db.file_source_root(file_id);
310 let path = db.file_relative_path(file_id);
311 let root = RelativePathBuf::default();
312 let dir_path = path.parent().unwrap_or(&root);
313 let mod_name = path.file_stem().unwrap_or("unknown");
314 let is_dir_owner = is_root || mod_name == "mod";
315
316 let file_mod = dir_path.join(format!("{}.rs", name));
317 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
318 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
319 let mut candidates = ArrayVec::<[_; 2]>::new();
320 if is_dir_owner {
321 candidates.push(file_mod.clone());
322 candidates.push(dir_mod);
323 } else {
324 candidates.push(file_dir_mod.clone());
325 };
326 let sr = db.source_root(source_root_id);
327 let points_to = candidates
328 .into_iter()
329 .filter_map(|path| sr.files.get(&path))
330 .map(|&it| it)
331 .collect::<Vec<_>>();
332 let problem = if points_to.is_empty() {
333 Some(Problem::UnresolvedModule {
334 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
335 })
336 } else {
337 None
338 };
339 (points_to, problem)
340}