aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/src/nameres.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs77
2 files changed, 79 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 5893708e8..11ba8a777 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -1,3 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3// FIXME: review privacy of submodules
3pub mod raw; 4pub mod raw;
5pub mod mod_resolution;
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
new file mode 100644
index 000000000..7d7e2779a
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -0,0 +1,77 @@
1//! This module resolves `mod foo;` declaration to file.
2use hir_expand::name::Name;
3use ra_db::FileId;
4use ra_syntax::SmolStr;
5use relative_path::RelativePathBuf;
6
7use crate::{db::DefDatabase2, HirFileId};
8
9#[derive(Clone, Debug)]
10pub struct ModDir {
11 /// `.` for `mod.rs`, `lib.rs`
12 /// `./foo` for `foo.rs`
13 /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
14 path: RelativePathBuf,
15 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
16 root_non_dir_owner: bool,
17}
18
19impl ModDir {
20 pub fn root() -> ModDir {
21 ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
22 }
23
24 pub fn descend_into_definition(&self, name: &Name, attr_path: Option<&SmolStr>) -> ModDir {
25 let mut path = self.path.clone();
26 match attr_to_path(attr_path) {
27 None => path.push(&name.to_string()),
28 Some(attr_path) => {
29 if self.root_non_dir_owner {
30 assert!(path.pop());
31 }
32 path.push(attr_path);
33 }
34 }
35 ModDir { path, root_non_dir_owner: false }
36 }
37
38 pub fn resolve_declaration(
39 &self,
40 db: &impl DefDatabase2,
41 file_id: HirFileId,
42 name: &Name,
43 attr_path: Option<&SmolStr>,
44 ) -> Result<(FileId, ModDir), RelativePathBuf> {
45 let file_id = file_id.original_file(db);
46
47 let mut candidate_files = Vec::new();
48 match attr_to_path(attr_path) {
49 Some(attr_path) => {
50 let base =
51 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
52 candidate_files.push(base.join(attr_path))
53 }
54 None => {
55 candidate_files.push(self.path.join(&format!("{}.rs", name)));
56 candidate_files.push(self.path.join(&format!("{}/mod.rs", name)));
57 }
58 };
59
60 for candidate in candidate_files.iter() {
61 if let Some(file_id) = db.resolve_relative_path(file_id, candidate) {
62 let mut root_non_dir_owner = false;
63 let mut mod_path = RelativePathBuf::new();
64 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
65 root_non_dir_owner = true;
66 mod_path.push(&name.to_string());
67 }
68 return Ok((file_id, ModDir { path: mod_path, root_non_dir_owner }));
69 }
70 }
71 Err(candidate_files.remove(0))
72 }
73}
74
75fn attr_to_path(attr: Option<&SmolStr>) -> Option<RelativePathBuf> {
76 attr.and_then(|it| RelativePathBuf::from_path(&it.replace("\\", "/")).ok())
77}