aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/descriptors/module
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/descriptors/module')
-rw-r--r--crates/ra_analysis/src/descriptors/module/imp.rs146
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs176
2 files changed, 322 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_analysis/src/descriptors/module/imp.rs
new file mode 100644
index 000000000..22e4bd785
--- /dev/null
+++ b/crates/ra_analysis/src/descriptors/module/imp.rs
@@ -0,0 +1,146 @@
1use std::sync::Arc;
2
3use relative_path::RelativePathBuf;
4use rustc_hash::{FxHashMap, FxHashSet};
5use ra_syntax::{
6 SmolStr,
7 ast::{self, NameOwner},
8};
9
10use crate::{
11 FileId, Cancelable, FileResolverImp,
12 db,
13};
14
15use super::{
16 ModuleData, ModuleTree, ModuleId, LinkId, LinkData, Problem, ModulesDatabase
17};
18
19
20pub(super) fn submodules(db: &impl ModulesDatabase, file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> {
21 db::check_canceled(db)?;
22 let file = db.file_syntax(file_id);
23 let root = file.ast();
24 let submodules = modules(root).map(|(name, _)| name).collect();
25 Ok(Arc::new(submodules))
26}
27
28pub(super) fn modules(root: ast::Root<'_>) -> impl Iterator<Item = (SmolStr, ast::Module<'_>)> {
29 root.modules().filter_map(|module| {
30 let name = module.name()?.text();
31 if !module.has_semi() {
32 return None;
33 }
34 Some((name, module))
35 })
36}
37
38pub(super) fn module_tree(db: &impl ModulesDatabase) -> Cancelable<Arc<ModuleTree>> {
39 db::check_canceled(db)?;
40 let res = create_module_tree(db)?;
41 Ok(Arc::new(res))
42}
43
44
45#[derive(Clone, Hash, PartialEq, Eq, Debug)]
46pub struct Submodule {
47 pub name: SmolStr,
48}
49
50
51fn create_module_tree<'a>(
52 db: &impl ModulesDatabase,
53) -> Cancelable<ModuleTree> {
54 let mut tree = ModuleTree {
55 mods: Vec::new(),
56 links: Vec::new(),
57 };
58
59 let mut roots = FxHashMap::default();
60 let mut visited = FxHashSet::default();
61
62 for &file_id in db.file_set().files.iter() {
63 if visited.contains(&file_id) {
64 continue; // TODO: use explicit crate_roots here
65 }
66 assert!(!roots.contains_key(&file_id));
67 let module_id = build_subtree(db, &mut tree, &mut visited, &mut roots, None, file_id)?;
68 roots.insert(file_id, module_id);
69 }
70 Ok(tree)
71}
72
73fn build_subtree(
74 db: &impl ModulesDatabase,
75 tree: &mut ModuleTree,
76 visited: &mut FxHashSet<FileId>,
77 roots: &mut FxHashMap<FileId, ModuleId>,
78 parent: Option<LinkId>,
79 file_id: FileId,
80) -> Cancelable<ModuleId> {
81 visited.insert(file_id);
82 let id = tree.push_mod(ModuleData {
83 file_id,
84 parent,
85 children: Vec::new(),
86 });
87 let file_set = db.file_set();
88 let file_resolver = &file_set.resolver;
89 for name in db.submodules(file_id)?.iter() {
90 let (points_to, problem) = resolve_submodule(file_id, name, file_resolver);
91 let link = tree.push_link(LinkData {
92 name: name.clone(),
93 owner: id,
94 points_to: Vec::new(),
95 problem: None,
96 });
97
98 let points_to = points_to
99 .into_iter()
100 .map(|file_id| match roots.remove(&file_id) {
101 Some(module_id) => {
102 tree.module_mut(module_id).parent = Some(link);
103 Ok(module_id)
104 }
105 None => build_subtree(db, tree, visited, roots, Some(link), file_id),
106 })
107 .collect::<Cancelable<Vec<_>>>()?;
108 tree.link_mut(link).points_to = points_to;
109 tree.link_mut(link).problem = problem;
110 }
111 Ok(id)
112}
113
114fn resolve_submodule(
115 file_id: FileId,
116 name: &SmolStr,
117 file_resolver: &FileResolverImp,
118) -> (Vec<FileId>, Option<Problem>) {
119 let mod_name = file_resolver.file_stem(file_id);
120 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
121
122 let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
123 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
124 let points_to: Vec<FileId>;
125 let problem: Option<Problem>;
126 if is_dir_owner {
127 points_to = [&file_mod, &dir_mod]
128 .iter()
129 .filter_map(|path| file_resolver.resolve(file_id, path))
130 .collect();
131 problem = if points_to.is_empty() {
132 Some(Problem::UnresolvedModule {
133 candidate: file_mod,
134 })
135 } else {
136 None
137 }
138 } else {
139 points_to = Vec::new();
140 problem = Some(Problem::NotDirOwner {
141 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
142 candidate: file_mod,
143 });
144 }
145 (points_to, problem)
146}
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs
new file mode 100644
index 000000000..52da650b3
--- /dev/null
+++ b/crates/ra_analysis/src/descriptors/module/mod.rs
@@ -0,0 +1,176 @@
1mod imp;
2
3use std::sync::Arc;
4
5use relative_path::RelativePathBuf;
6use ra_syntax::{ast::{self, NameOwner, AstNode}, SmolStr, SyntaxNode};
7
8use crate::{
9 FileId, Cancelable,
10 db::SyntaxDatabase,
11};
12
13salsa::query_group! {
14 pub(crate) trait ModulesDatabase: SyntaxDatabase {
15 fn module_tree() -> Cancelable<Arc<ModuleTree>> {
16 type ModuleTreeQuery;
17 use fn imp::module_tree;
18 }
19 fn submodules(file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> {
20 type SubmodulesQuery;
21 use fn imp::submodules;
22 }
23 }
24}
25
26
27#[derive(Debug, PartialEq, Eq, Hash)]
28pub(crate) struct ModuleTree {
29 mods: Vec<ModuleData>,
30 links: Vec<LinkData>,
31}
32
33impl ModuleTree {
34 pub(crate) fn modules_for_file(&self, file_id: FileId) -> Vec<ModuleId> {
35 self.mods.iter()
36 .enumerate()
37 .filter(|(_idx, it)| it.file_id == file_id).map(|(idx, _)| ModuleId(idx as u32))
38 .collect()
39 }
40
41 pub(crate) fn any_module_for_file(&self, file_id: FileId) -> Option<ModuleId> {
42 self.modules_for_file(file_id).pop()
43 }
44}
45
46#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
47pub(crate) struct ModuleId(u32);
48
49#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
50pub(crate) struct LinkId(u32);
51
52#[derive(Clone, Debug, Hash, PartialEq, Eq)]
53pub enum Problem {
54 UnresolvedModule {
55 candidate: RelativePathBuf,
56 },
57 NotDirOwner {
58 move_to: RelativePathBuf,
59 candidate: RelativePathBuf,
60 },
61}
62
63impl ModuleId {
64 pub(crate) fn file_id(self, tree: &ModuleTree) -> FileId {
65 tree.module(self).file_id
66 }
67 pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
68 tree.module(self).parent
69 }
70 pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
71 let link = self.parent_link(tree)?;
72 Some(tree.link(link).owner)
73 }
74 pub(crate) fn root(self, tree: &ModuleTree) -> ModuleId {
75 let mut curr = self;
76 let mut i = 0;
77 while let Some(next) = curr.parent(tree) {
78 curr = next;
79 i += 1;
80 if i > 100 {
81 return self;
82 }
83 }
84 curr
85 }
86 pub(crate) fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
87 let link = tree.module(self)
88 .children
89 .iter()
90 .map(|&it| tree.link(it))
91 .find(|it| it.name == name)?;
92 Some(*link.points_to.first()?)
93 }
94 pub(crate) fn problems(
95 self,
96 tree: &ModuleTree,
97 root: ast::Root,
98 ) -> Vec<(SyntaxNode, Problem)> {
99 tree.module(self)
100 .children
101 .iter()
102 .filter_map(|&it| {
103 let p = tree.link(it).problem.clone()?;
104 let s = it.bind_source(tree, root);
105 let s = s.name().unwrap().syntax().owned();
106 Some((s, p))
107 })
108 .collect()
109 }
110}
111
112impl LinkId {
113 pub(crate) fn name(self, tree: &ModuleTree) -> SmolStr {
114 tree.link(self).name.clone()
115 }
116 pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
117 tree.link(self).owner
118 }
119 fn points_to(self, tree: &ModuleTree) -> &[ModuleId] {
120 &tree.link(self).points_to
121 }
122 pub(crate) fn bind_source<'a>(
123 self,
124 tree: &ModuleTree,
125 root: ast::Root<'a>,
126 ) -> ast::Module<'a> {
127 imp::modules(root)
128 .find(|(name, _)| name == &tree.link(self).name)
129 .unwrap()
130 .1
131 }
132}
133
134#[derive(Debug, PartialEq, Eq, Hash)]
135struct ModuleData {
136 file_id: FileId,
137 parent: Option<LinkId>,
138 children: Vec<LinkId>,
139}
140
141#[derive(Hash, Debug, PartialEq, Eq)]
142struct LinkData {
143 owner: ModuleId,
144 name: SmolStr,
145 points_to: Vec<ModuleId>,
146 problem: Option<Problem>,
147}
148
149
150impl ModuleTree {
151 fn module(&self, id: ModuleId) -> &ModuleData {
152 &self.mods[id.0 as usize]
153 }
154 fn module_mut(&mut self, id: ModuleId) -> &mut ModuleData {
155 &mut self.mods[id.0 as usize]
156 }
157 fn link(&self, id: LinkId) -> &LinkData {
158 &self.links[id.0 as usize]
159 }
160 fn link_mut(&mut self, id: LinkId) -> &mut LinkData {
161 &mut self.links[id.0 as usize]
162 }
163
164 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
165 let id = ModuleId(self.mods.len() as u32);
166 self.mods.push(data);
167 id
168 }
169 fn push_link(&mut self, data: LinkData) -> LinkId {
170 let id = LinkId(self.links.len() as u32);
171 self.mods[data.owner.0 as usize].children.push(id);
172 self.links.push(data);
173 id
174 }
175}
176