diff options
Diffstat (limited to 'crates/ra_hir/src/nameres/mod_resolution.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/mod_resolution.rs | 186 |
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 | |||
3 | use std::{borrow::Cow, sync::Arc}; | ||
4 | |||
5 | use ra_db::{FileId, SourceRoot}; | ||
6 | use ra_syntax::SmolStr; | ||
7 | use relative_path::RelativePathBuf; | ||
8 | |||
9 | use crate::{DefDatabase, HirFileId, Name}; | ||
10 | |||
11 | #[derive(Clone, Copy)] | ||
12 | pub(super) struct ParentModule<'a> { | ||
13 | pub(super) name: &'a Name, | ||
14 | pub(super) attr_path: Option<&'a SmolStr>, | ||
15 | } | ||
16 | |||
17 | impl<'a> ParentModule<'a> { | ||
18 | fn attribute_path(&self) -> Option<&SmolStr> { | ||
19 | self.attr_path.filter(|p| !p.is_empty()) | ||
20 | } | ||
21 | } | ||
22 | |||
23 | pub(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 | |||
101 | fn 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 | |||
116 | enum OutOfLineMode { | ||
117 | RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf }, | ||
118 | FileInDirectory(RelativePathBuf), | ||
119 | WithAttributePath(RelativePathBuf), | ||
120 | } | ||
121 | |||
122 | impl 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 | |||
137 | enum InsideInlineModuleMode { | ||
138 | File(RelativePathBuf), | ||
139 | WithAttributePath(RelativePathBuf), | ||
140 | } | ||
141 | |||
142 | impl 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 | |||
153 | enum ResolutionMode { | ||
154 | OutOfLine(OutOfLineMode), | ||
155 | InsideInlineModule(InsideInlineModuleMode), | ||
156 | InlineModuleWithAttributePath(InsideInlineModuleMode), | ||
157 | } | ||
158 | |||
159 | impl 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 | |||
171 | fn 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 | |||
178 | fn 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 | } | ||