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