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