aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/hir/module/imp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/hir/module/imp.rs')
-rw-r--r--crates/ra_analysis/src/hir/module/imp.rs229
1 files changed, 229 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/hir/module/imp.rs b/crates/ra_analysis/src/hir/module/imp.rs
new file mode 100644
index 000000000..d8539ed85
--- /dev/null
+++ b/crates/ra_analysis/src/hir/module/imp.rs
@@ -0,0 +1,229 @@
1use std::sync::Arc;
2
3use ra_syntax::{
4 ast::{self, NameOwner},
5 SmolStr,
6};
7use relative_path::RelativePathBuf;
8use rustc_hash::{FxHashMap, FxHashSet};
9
10use crate::{
11 db,
12 hir::DescriptorDatabase,
13 input::{SourceRoot, SourceRootId},
14 Cancelable, FileId, FileResolverImp,
15};
16
17use super::{
18 LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode,
19 ModuleTree, Problem,
20};
21
22#[derive(Clone, Hash, PartialEq, Eq, Debug)]
23pub(crate) enum Submodule {
24 Declaration(SmolStr),
25 Definition(SmolStr, ModuleSource),
26}
27
28impl Submodule {
29 fn name(&self) -> &SmolStr {
30 match self {
31 Submodule::Declaration(name) => name,
32 Submodule::Definition(name, _) => name,
33 }
34 }
35}
36
37pub(crate) fn submodules(
38 db: &impl DescriptorDatabase,
39 source: ModuleSource,
40) -> Cancelable<Arc<Vec<Submodule>>> {
41 db::check_canceled(db)?;
42 let file_id = source.file_id();
43 let submodules = match source.resolve(db) {
44 ModuleSourceNode::SourceFile(it) => collect_submodules(file_id, it.borrowed()),
45 ModuleSourceNode::Module(it) => it
46 .borrowed()
47 .item_list()
48 .map(|it| collect_submodules(file_id, it))
49 .unwrap_or_else(Vec::new),
50 };
51 return Ok(Arc::new(submodules));
52
53 fn collect_submodules<'a>(
54 file_id: FileId,
55 root: impl ast::ModuleItemOwner<'a>,
56 ) -> Vec<Submodule> {
57 modules(root)
58 .map(|(name, m)| {
59 if m.has_semi() {
60 Submodule::Declaration(name)
61 } else {
62 let src = ModuleSource::new_inline(file_id, m);
63 Submodule::Definition(name, src)
64 }
65 })
66 .collect()
67 }
68}
69
70pub(crate) fn modules<'a>(
71 root: impl ast::ModuleItemOwner<'a>,
72) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
73 root.items()
74 .filter_map(|item| match item {
75 ast::ModuleItem::Module(m) => Some(m),
76 _ => None,
77 })
78 .filter_map(|module| {
79 let name = module.name()?.text();
80 Some((name, module))
81 })
82}
83
84pub(crate) fn module_tree(
85 db: &impl DescriptorDatabase,
86 source_root: SourceRootId,
87) -> Cancelable<Arc<ModuleTree>> {
88 db::check_canceled(db)?;
89 let res = create_module_tree(db, source_root)?;
90 Ok(Arc::new(res))
91}
92
93fn create_module_tree<'a>(
94 db: &impl DescriptorDatabase,
95 source_root: SourceRootId,
96) -> Cancelable<ModuleTree> {
97 let mut tree = ModuleTree::default();
98
99 let mut roots = FxHashMap::default();
100 let mut visited = FxHashSet::default();
101
102 let source_root = db.source_root(source_root);
103 for &file_id in source_root.files.iter() {
104 let source = ModuleSource::SourceFile(file_id);
105 if visited.contains(&source) {
106 continue; // TODO: use explicit crate_roots here
107 }
108 assert!(!roots.contains_key(&file_id));
109 let module_id = build_subtree(
110 db,
111 &source_root,
112 &mut tree,
113 &mut visited,
114 &mut roots,
115 None,
116 source,
117 )?;
118 roots.insert(file_id, module_id);
119 }
120 Ok(tree)
121}
122
123fn build_subtree(
124 db: &impl DescriptorDatabase,
125 source_root: &SourceRoot,
126 tree: &mut ModuleTree,
127 visited: &mut FxHashSet<ModuleSource>,
128 roots: &mut FxHashMap<FileId, ModuleId>,
129 parent: Option<LinkId>,
130 source: ModuleSource,
131) -> Cancelable<ModuleId> {
132 visited.insert(source);
133 let id = tree.push_mod(ModuleData {
134 source,
135 parent,
136 children: Vec::new(),
137 });
138 for sub in db._submodules(source)?.iter() {
139 let link = tree.push_link(LinkData {
140 name: sub.name().clone(),
141 owner: id,
142 points_to: Vec::new(),
143 problem: None,
144 });
145
146 let (points_to, problem) = match sub {
147 Submodule::Declaration(name) => {
148 let (points_to, problem) =
149 resolve_submodule(source, &name, &source_root.file_resolver);
150 let points_to = points_to
151 .into_iter()
152 .map(|file_id| match roots.remove(&file_id) {
153 Some(module_id) => {
154 tree.mods[module_id].parent = Some(link);
155 Ok(module_id)
156 }
157 None => build_subtree(
158 db,
159 source_root,
160 tree,
161 visited,
162 roots,
163 Some(link),
164 ModuleSource::SourceFile(file_id),
165 ),
166 })
167 .collect::<Cancelable<Vec<_>>>()?;
168 (points_to, problem)
169 }
170 Submodule::Definition(_name, submodule_source) => {
171 let points_to = build_subtree(
172 db,
173 source_root,
174 tree,
175 visited,
176 roots,
177 Some(link),
178 *submodule_source,
179 )?;
180 (vec![points_to], None)
181 }
182 };
183
184 tree.links[link].points_to = points_to;
185 tree.links[link].problem = problem;
186 }
187 Ok(id)
188}
189
190fn resolve_submodule(
191 source: ModuleSource,
192 name: &SmolStr,
193 file_resolver: &FileResolverImp,
194) -> (Vec<FileId>, Option<Problem>) {
195 let file_id = match source {
196 ModuleSource::SourceFile(it) => it,
197 ModuleSource::Module(..) => {
198 // TODO
199 return (Vec::new(), None);
200 }
201 };
202 let mod_name = file_resolver.file_stem(file_id);
203 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
204
205 let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
206 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
207 let points_to: Vec<FileId>;
208 let problem: Option<Problem>;
209 if is_dir_owner {
210 points_to = [&file_mod, &dir_mod]
211 .iter()
212 .filter_map(|path| file_resolver.resolve(file_id, path))
213 .collect();
214 problem = if points_to.is_empty() {
215 Some(Problem::UnresolvedModule {
216 candidate: file_mod,
217 })
218 } else {
219 None
220 }
221 } else {
222 points_to = Vec::new();
223 problem = Some(Problem::NotDirOwner {
224 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
225 candidate: file_mod,
226 });
227 }
228 (points_to, problem)
229}