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