aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/module.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/module.rs')
-rw-r--r--crates/ra_hir/src/module.rs320
1 files changed, 320 insertions, 0 deletions
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
new file mode 100644
index 000000000..580c737c3
--- /dev/null
+++ b/crates/ra_hir/src/module.rs
@@ -0,0 +1,320 @@
1pub(super) mod imp;
2pub(super) mod nameres;
3
4use std::sync::Arc;
5
6use ra_syntax::{
7 algo::generate,
8 ast::{self, AstNode, NameOwner},
9 SmolStr, SyntaxNode,
10};
11use ra_db::{SourceRootId, FileId, Cancelable};
12use relative_path::RelativePathBuf;
13
14use crate::{
15 DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId,
16 arena::{Arena, Id},
17};
18
19pub use self::nameres::ModuleScope;
20
21/// `Module` is API entry point to get all the information
22/// about a particular module.
23#[derive(Debug, Clone)]
24pub struct Module {
25 tree: Arc<ModuleTree>,
26 pub(crate) source_root_id: SourceRootId,
27 pub(crate) module_id: ModuleId,
28}
29
30impl Module {
31 pub(super) fn new(
32 db: &impl HirDatabase,
33 source_root_id: SourceRootId,
34 module_id: ModuleId,
35 ) -> Cancelable<Module> {
36 let module_tree = db.module_tree(source_root_id)?;
37 let res = Module {
38 tree: module_tree,
39 source_root_id,
40 module_id,
41 };
42 Ok(res)
43 }
44
45 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
46 /// Returns `None` for the root module
47 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
48 let link = self.module_id.parent_link(&self.tree)?;
49 let file_id = link.owner(&self.tree).source(&self.tree).file_id();
50 let src = link.bind_source(&self.tree, db);
51 Some((file_id, src))
52 }
53
54 pub fn source(&self) -> ModuleSource {
55 self.module_id.source(&self.tree)
56 }
57
58 /// Parent module. Returns `None` if this is a root module.
59 pub fn parent(&self) -> Option<Module> {
60 let parent_id = self.module_id.parent(&self.tree)?;
61 Some(Module {
62 module_id: parent_id,
63 ..self.clone()
64 })
65 }
66
67 /// The root of the tree this module is part of
68 pub fn crate_root(&self) -> Module {
69 let root_id = self.module_id.crate_root(&self.tree);
70 Module {
71 module_id: root_id,
72 ..self.clone()
73 }
74 }
75
76 /// `name` is `None` for the crate's root module
77 pub fn name(&self) -> Option<SmolStr> {
78 let link = self.module_id.parent_link(&self.tree)?;
79 Some(link.name(&self.tree))
80 }
81
82 pub fn def_id(&self, db: &impl HirDatabase) -> DefId {
83 let def_loc = DefLoc {
84 kind: DefKind::Module,
85 source_root_id: self.source_root_id,
86 module_id: self.module_id,
87 source_item_id: self.module_id.source(&self.tree).0,
88 };
89 def_loc.id(db)
90 }
91
92 /// Finds a child module with the specified name.
93 pub fn child(&self, name: &str) -> Option<Module> {
94 let child_id = self.module_id.child(&self.tree, name)?;
95 Some(Module {
96 module_id: child_id,
97 ..self.clone()
98 })
99 }
100
101 /// Returns a `ModuleScope`: a set of items, visible in this module.
102 pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
103 let item_map = db.item_map(self.source_root_id)?;
104 let res = item_map.per_module[&self.module_id].clone();
105 Ok(res)
106 }
107
108 pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> {
109 let mut curr = match path.kind {
110 PathKind::Crate => self.crate_root(),
111 PathKind::Self_ | PathKind::Plain => self.clone(),
112 PathKind::Super => ctry!(self.parent()),
113 }
114 .def_id(db);
115
116 let segments = path.segments;
117 for name in segments.iter() {
118 let module = match curr.loc(db) {
119 DefLoc {
120 kind: DefKind::Module,
121 source_root_id,
122 module_id,
123 ..
124 } => Module::new(db, source_root_id, module_id)?,
125 _ => return Ok(None),
126 };
127 let scope = module.scope(db)?;
128 curr = ctry!(ctry!(scope.get(&name)).def_id);
129 }
130 Ok(Some(curr))
131 }
132
133 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
134 self.module_id.problems(&self.tree, db)
135 }
136}
137
138/// Phisically, rust source is organized as a set of files, but logically it is
139/// organized as a tree of modules. Usually, a single file corresponds to a
140/// single module, but it is not nessary the case.
141///
142/// Module encapsulate the logic of transitioning from the fuzzy world of files
143/// (which can have multiple parents) to the precise world of modules (which
144/// always have one parent).
145#[derive(Default, Debug, PartialEq, Eq)]
146pub struct ModuleTree {
147 mods: Arena<ModuleData>,
148 links: Arena<LinkData>,
149}
150
151impl ModuleTree {
152 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
153 self.mods.iter().map(|(id, _)| id)
154 }
155
156 pub(crate) fn modules_with_sources<'a>(
157 &'a self,
158 ) -> impl Iterator<Item = (ModuleId, ModuleSource)> + 'a {
159 self.mods.iter().map(|(id, m)| (id, m.source))
160 }
161}
162
163/// `ModuleSource` is the syntax tree element that produced this module:
164/// either a file, or an inlinde module.
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
166pub struct ModuleSource(SourceItemId);
167
168/// An owned syntax node for a module. Unlike `ModuleSource`,
169/// this holds onto the AST for the whole file.
170pub(crate) enum ModuleSourceNode {
171 SourceFile(ast::SourceFileNode),
172 Module(ast::ModuleNode),
173}
174
175pub type ModuleId = Id<ModuleData>;
176type LinkId = Id<LinkData>;
177
178#[derive(Clone, Debug, Hash, PartialEq, Eq)]
179pub enum Problem {
180 UnresolvedModule {
181 candidate: RelativePathBuf,
182 },
183 NotDirOwner {
184 move_to: RelativePathBuf,
185 candidate: RelativePathBuf,
186 },
187}
188
189impl ModuleId {
190 pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
191 tree.mods[self].source
192 }
193 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
194 tree.mods[self].parent
195 }
196 fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
197 let link = self.parent_link(tree)?;
198 Some(tree.links[link].owner)
199 }
200 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
201 generate(Some(self), move |it| it.parent(tree))
202 .last()
203 .unwrap()
204 }
205 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
206 let link = tree.mods[self]
207 .children
208 .iter()
209 .map(|&it| &tree.links[it])
210 .find(|it| it.name == name)?;
211 Some(*link.points_to.first()?)
212 }
213 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a {
214 tree.mods[self].children.iter().filter_map(move |&it| {
215 let link = &tree.links[it];
216 let module = *link.points_to.first()?;
217 Some((link.name.clone(), module))
218 })
219 }
220 fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
221 tree.mods[self]
222 .children
223 .iter()
224 .filter_map(|&it| {
225 let p = tree.links[it].problem.clone()?;
226 let s = it.bind_source(tree, db);
227 let s = s.borrowed().name().unwrap().syntax().owned();
228 Some((s, p))
229 })
230 .collect()
231 }
232}
233
234impl LinkId {
235 fn owner(self, tree: &ModuleTree) -> ModuleId {
236 tree.links[self].owner
237 }
238 fn name(self, tree: &ModuleTree) -> SmolStr {
239 tree.links[self].name.clone()
240 }
241 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
242 let owner = self.owner(tree);
243 match owner.source(tree).resolve(db) {
244 ModuleSourceNode::SourceFile(root) => {
245 let ast = imp::modules(root.borrowed())
246 .find(|(name, _)| name == &tree.links[self].name)
247 .unwrap()
248 .1;
249 ast.owned()
250 }
251 ModuleSourceNode::Module(it) => it,
252 }
253 }
254}
255
256#[derive(Debug, PartialEq, Eq, Hash)]
257pub struct ModuleData {
258 source: ModuleSource,
259 parent: Option<LinkId>,
260 children: Vec<LinkId>,
261}
262
263impl ModuleSource {
264 // precondition: item_id **must** point to module
265 fn new(file_id: FileId, item_id: SourceFileItemId) -> ModuleSource {
266 let source_item_id = SourceItemId { file_id, item_id };
267 ModuleSource(source_item_id)
268 }
269
270 pub(crate) fn new_file(db: &impl HirDatabase, file_id: FileId) -> ModuleSource {
271 let file_items = db.file_items(file_id);
272 let item_id = file_items.id_of_source_file();
273 ModuleSource::new(file_id, item_id)
274 }
275
276 pub(crate) fn new_inline(
277 db: &impl HirDatabase,
278 file_id: FileId,
279 m: ast::Module,
280 ) -> ModuleSource {
281 assert!(!m.has_semi());
282 let file_items = db.file_items(file_id);
283 let item_id = file_items.id_of(m.syntax());
284 ModuleSource::new(file_id, item_id)
285 }
286
287 pub fn file_id(self) -> FileId {
288 self.0.file_id
289 }
290
291 pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode {
292 let syntax_node = db.file_item(self.0);
293 let syntax_node = syntax_node.borrowed();
294 if let Some(file) = ast::SourceFile::cast(syntax_node) {
295 return ModuleSourceNode::SourceFile(file.owned());
296 }
297 let module = ast::Module::cast(syntax_node).unwrap();
298 ModuleSourceNode::Module(module.owned())
299 }
300}
301
302#[derive(Hash, Debug, PartialEq, Eq)]
303struct LinkData {
304 owner: ModuleId,
305 name: SmolStr,
306 points_to: Vec<ModuleId>,
307 problem: Option<Problem>,
308}
309
310impl ModuleTree {
311 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
312 self.mods.alloc(data)
313 }
314 fn push_link(&mut self, data: LinkData) -> LinkId {
315 let owner = data.owner;
316 let id = self.links.alloc(data);
317 self.mods[owner].children.push(id);
318 id
319 }
320}