diff options
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r-- | crates/ra_hir_def/src/nameres.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/mod_resolution.rs | 77 |
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 | ||
3 | pub mod raw; | 4 | pub mod raw; |
5 | pub 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. | ||
2 | use hir_expand::name::Name; | ||
3 | use ra_db::FileId; | ||
4 | use ra_syntax::SmolStr; | ||
5 | use relative_path::RelativePathBuf; | ||
6 | |||
7 | use crate::{db::DefDatabase2, HirFileId}; | ||
8 | |||
9 | #[derive(Clone, Debug)] | ||
10 | pub 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 | |||
19 | impl 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 | |||
75 | fn attr_to_path(attr: Option<&SmolStr>) -> Option<RelativePathBuf> { | ||
76 | attr.and_then(|it| RelativePathBuf::from_path(&it.replace("\\", "/")).ok()) | ||
77 | } | ||