aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres/mod_resolution.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres/mod_resolution.rs')
-rw-r--r--crates/ra_hir/src/nameres/mod_resolution.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/mod_resolution.rs b/crates/ra_hir/src/nameres/mod_resolution.rs
new file mode 100644
index 000000000..918c9591f
--- /dev/null
+++ b/crates/ra_hir/src/nameres/mod_resolution.rs
@@ -0,0 +1,186 @@
1//! This module resolves `mod foo;` declaration to file.
2
3use std::{borrow::Cow, sync::Arc};
4
5use ra_db::{FileId, SourceRoot};
6use ra_syntax::SmolStr;
7use relative_path::RelativePathBuf;
8
9use crate::{DefDatabase, HirFileId, Name};
10
11#[derive(Clone, Copy)]
12pub(super) struct ParentModule<'a> {
13 pub(super) name: &'a Name,
14 pub(super) attr_path: Option<&'a SmolStr>,
15}
16
17impl<'a> ParentModule<'a> {
18 fn attribute_path(&self) -> Option<&SmolStr> {
19 self.attr_path.filter(|p| !p.is_empty())
20 }
21}
22
23pub(super) fn resolve_submodule(
24 db: &impl DefDatabase,
25 file_id: HirFileId,
26 name: &Name,
27 is_root: bool,
28 attr_path: Option<&SmolStr>,
29 parent_module: Option<ParentModule<'_>>,
30) -> Result<FileId, RelativePathBuf> {
31 let file_id = file_id.original_file(db);
32 let source_root_id = db.file_source_root(file_id);
33 let path = db.file_relative_path(file_id);
34 let root = RelativePathBuf::default();
35 let dir_path = path.parent().unwrap_or(&root);
36 let mod_name = path.file_stem().unwrap_or("unknown");
37
38 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) {
39 (Some(file_path), Some(parent_module)) => {
40 let file_path = normalize_attribute_path(file_path);
41 match parent_module.attribute_path() {
42 Some(parent_module_attr_path) => {
43 let path = dir_path
44 .join(format!(
45 "{}/{}",
46 normalize_attribute_path(parent_module_attr_path),
47 file_path
48 ))
49 .normalize();
50 ResolutionMode::InlineModuleWithAttributePath(
51 InsideInlineModuleMode::WithAttributePath(path),
52 )
53 }
54 None => {
55 let path =
56 dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize();
57 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(
58 path,
59 ))
60 }
61 }
62 }
63 (None, Some(parent_module)) => match parent_module.attribute_path() {
64 Some(parent_module_attr_path) => {
65 let path = dir_path.join(format!(
66 "{}/{}.rs",
67 normalize_attribute_path(parent_module_attr_path),
68 name
69 ));
70 ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path))
71 }
72 None => {
73 let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name));
74 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
75 }
76 },
77 (Some(file_path), None) => {
78 let file_path = normalize_attribute_path(file_path);
79 let path = dir_path.join(file_path.as_ref()).normalize();
80 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path))
81 }
82 (None, None) => {
83 let is_dir_owner = is_root || mod_name == "mod";
84 if is_dir_owner {
85 let file_mod = dir_path.join(format!("{}.rs", name));
86 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
87 ResolutionMode::OutOfLine(OutOfLineMode::RootOrModRs {
88 file: file_mod,
89 directory: dir_mod,
90 })
91 } else {
92 let path = dir_path.join(format!("{}/{}.rs", mod_name, name));
93 ResolutionMode::OutOfLine(OutOfLineMode::FileInDirectory(path))
94 }
95 }
96 };
97
98 resolve_mode.resolve(db.source_root(source_root_id))
99}
100
101fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> {
102 let current_dir = "./";
103 let windows_path_separator = r#"\"#;
104 let current_dir_normalize = if file_path.starts_with(current_dir) {
105 &file_path[current_dir.len()..]
106 } else {
107 file_path.as_str()
108 };
109 if current_dir_normalize.contains(windows_path_separator) {
110 Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/"))
111 } else {
112 Cow::Borrowed(current_dir_normalize)
113 }
114}
115
116enum OutOfLineMode {
117 RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf },
118 FileInDirectory(RelativePathBuf),
119 WithAttributePath(RelativePathBuf),
120}
121
122impl OutOfLineMode {
123 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
124 match self {
125 OutOfLineMode::RootOrModRs { file, directory } => {
126 match source_root.file_by_relative_path(file) {
127 None => resolve_simple_path(source_root, directory).map_err(|_| file.clone()),
128 file_id => resolve_find_result(file_id, file),
129 }
130 }
131 OutOfLineMode::FileInDirectory(path) => resolve_simple_path(source_root, path),
132 OutOfLineMode::WithAttributePath(path) => resolve_simple_path(source_root, path),
133 }
134 }
135}
136
137enum InsideInlineModuleMode {
138 File(RelativePathBuf),
139 WithAttributePath(RelativePathBuf),
140}
141
142impl InsideInlineModuleMode {
143 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
144 match self {
145 InsideInlineModuleMode::File(path) => resolve_simple_path(source_root, path),
146 InsideInlineModuleMode::WithAttributePath(path) => {
147 resolve_simple_path(source_root, path)
148 }
149 }
150 }
151}
152
153enum ResolutionMode {
154 OutOfLine(OutOfLineMode),
155 InsideInlineModule(InsideInlineModuleMode),
156 InlineModuleWithAttributePath(InsideInlineModuleMode),
157}
158
159impl ResolutionMode {
160 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
161 use self::ResolutionMode::*;
162
163 match self {
164 OutOfLine(mode) => mode.resolve(source_root),
165 InsideInlineModule(mode) => mode.resolve(source_root),
166 InlineModuleWithAttributePath(mode) => mode.resolve(source_root),
167 }
168 }
169}
170
171fn resolve_simple_path(
172 source_root: Arc<SourceRoot>,
173 path: &RelativePathBuf,
174) -> Result<FileId, RelativePathBuf> {
175 resolve_find_result(source_root.file_by_relative_path(path), path)
176}
177
178fn resolve_find_result(
179 file_id: Option<FileId>,
180 path: &RelativePathBuf,
181) -> Result<FileId, RelativePathBuf> {
182 match file_id {
183 Some(file_id) => Ok(file_id.clone()),
184 None => Err(path.clone()),
185 }
186}