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.rs232
1 files changed, 77 insertions, 155 deletions
diff --git a/crates/ra_hir/src/nameres/mod_resolution.rs b/crates/ra_hir/src/nameres/mod_resolution.rs
index 3aa32bd66..f50f9abe6 100644
--- a/crates/ra_hir/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir/src/nameres/mod_resolution.rs
@@ -1,111 +1,105 @@
1//! This module resolves `mod foo;` declaration to file. 1//! This module resolves `mod foo;` declaration to file.
2use std::borrow::Cow;
2 3
3use std::{borrow::Cow, sync::Arc}; 4use ra_db::FileId;
4
5use ra_db::{FileId, SourceRoot};
6use ra_syntax::SmolStr; 5use ra_syntax::SmolStr;
7use relative_path::RelativePathBuf; 6use relative_path::{RelativePath, RelativePathBuf};
8 7
9use crate::{db::DefDatabase, HirFileId, Name}; 8use crate::{db::DefDatabase, HirFileId, Name};
10 9
11#[derive(Clone, Copy)] 10#[derive(Clone, Debug)]
12pub(super) struct ParentModule<'a> { 11pub(super) struct ModDir {
13 pub(super) name: &'a Name, 12 /// `.` for `mod.rs`, `lib.rs`
14 pub(super) attr_path: Option<&'a SmolStr>, 13 /// `./foo` for `foo.rs`
14 /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
15 path: RelativePathBuf,
16 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
17 root_non_dir_owner: bool,
15} 18}
16 19
17impl<'a> ParentModule<'a> { 20impl ModDir {
18 fn attribute_path(&self) -> Option<&SmolStr> { 21 pub(super) fn root() -> ModDir {
19 self.attr_path.filter(|p| !p.is_empty()) 22 ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
20 } 23 }
21}
22
23pub(super) fn resolve_submodule(
24 db: &impl DefDatabase,
25 file_id: HirFileId,
26 mod_attr_path: Option<&SmolStr>,
27 name: &Name,
28 is_root: bool,
29 attr_path: Option<&SmolStr>,
30 parent_module: Option<ParentModule<'_>>,
31) -> Result<FileId, RelativePathBuf> {
32 let file_id = file_id.original_file(db);
33 let source_root_id = db.file_source_root(file_id);
34 let path = db.file_relative_path(file_id);
35 let root = RelativePathBuf::default();
36 let dir_path = path.parent().unwrap_or(&root);
37 let mod_name = path.file_stem().unwrap_or("unknown");
38 24
39 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { 25 pub(super) fn descend_into_definition(
40 (Some(file_path), Some(parent_module)) => { 26 &self,
41 let file_path = normalize_attribute_path(file_path); 27 name: &Name,
42 match parent_module.attribute_path() { 28 attr_path: Option<&SmolStr>,
43 Some(parent_module_attr_path) => { 29 ) -> ModDir {
44 let path = dir_path 30 let mut path = self.path.clone();
45 .join(format!( 31 match attr_path {
46 "{}/{}", 32 None => path.push(&name.to_string()),
47 normalize_attribute_path(parent_module_attr_path), 33 Some(attr_path) => {
48 file_path 34 if self.root_non_dir_owner {
49 )) 35 path = path
50 .normalize(); 36 .parent()
51 ResolutionMode::InlineModuleWithAttributePath( 37 .map(|it| it.to_relative_path_buf())
52 InsideInlineModuleMode::WithAttributePath(path), 38 .unwrap_or_else(RelativePathBuf::new);
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 } 39 }
40 let attr_path = &*normalize_attribute_path(attr_path);
41 path.push(RelativePath::new(attr_path));
62 } 42 }
63 } 43 }
64 (None, Some(parent_module)) => match parent_module.attribute_path() { 44 ModDir { path, root_non_dir_owner: false }
65 Some(parent_module_attr_path) => { 45 }
66 let path = dir_path.join(format!( 46
67 "{}/{}.rs", 47 pub(super) fn resolve_submodule(
68 normalize_attribute_path(parent_module_attr_path), 48 &self,
69 name 49 db: &impl DefDatabase,
70 )); 50 file_id: HirFileId,
71 ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path)) 51 name: &Name,
52 attr_path: Option<&SmolStr>,
53 ) -> Result<(FileId, ModDir), RelativePathBuf> {
54 let empty_path = RelativePathBuf::default();
55 let file_id = file_id.original_file(db);
56 let base_dir = {
57 let path = db.file_relative_path(file_id);
58 path.parent().unwrap_or(&empty_path).join(&self.path)
59 };
60
61 let mut candidate_files = Vec::new();
62 match attr_path {
63 Some(attr) => {
64 let base = if self.root_non_dir_owner {
65 base_dir.parent().unwrap_or(&empty_path)
66 } else {
67 &base_dir
68 };
69 candidate_files.push(base.join(&*normalize_attribute_path(attr)))
72 } 70 }
73 None => { 71 None => {
74 let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name)); 72 candidate_files.push(base_dir.join(&format!("{}.rs", name)));
75 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path)) 73 candidate_files.push(base_dir.join(&format!("{}/mod.rs", name)));
76 } 74 }
77 }, 75 };
78 (Some(file_path), None) => { 76
79 let file_path = normalize_attribute_path(file_path); 77 let source_root_id = db.file_source_root(file_id);
80 let path = dir_path.join(file_path.as_ref()).normalize(); 78 let source_root = db.source_root(source_root_id);
81 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) 79 for candidate in candidate_files.iter() {
82 } 80 let candidate = candidate.normalize();
83 (None, None) => { 81 if let Some(file_id) = source_root.file_by_relative_path(&candidate) {
84 let is_dir_owner = is_root || mod_name == "mod" || mod_attr_path.is_some(); 82 let mut root_non_dir_owner = false;
85 if is_dir_owner { 83 let mut mod_path = RelativePathBuf::new();
86 let file_mod = dir_path.join(format!("{}.rs", name)); 84 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
87 let dir_mod = dir_path.join(format!("{}/mod.rs", name)); 85 root_non_dir_owner = true;
88 ResolutionMode::OutOfLine(OutOfLineMode::RootOrModRs { 86 mod_path.push(&name.to_string());
89 file: file_mod, 87 }
90 directory: dir_mod, 88 return Ok((file_id, ModDir { path: mod_path, root_non_dir_owner }));
91 })
92 } else {
93 let path = dir_path.join(format!("{}/{}.rs", mod_name, name));
94 ResolutionMode::OutOfLine(OutOfLineMode::FileInDirectory(path))
95 } 89 }
96 } 90 }
97 }; 91 let suggestion = candidate_files.first().unwrap();
98 92 Err(base_dir.join(suggestion))
99 resolve_mode.resolve(db.source_root(source_root_id)) 93 }
100} 94}
101 95
102fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> { 96fn normalize_attribute_path(file_path: &str) -> Cow<str> {
103 let current_dir = "./"; 97 let current_dir = "./";
104 let windows_path_separator = r#"\"#; 98 let windows_path_separator = r#"\"#;
105 let current_dir_normalize = if file_path.starts_with(current_dir) { 99 let current_dir_normalize = if file_path.starts_with(current_dir) {
106 &file_path[current_dir.len()..] 100 &file_path[current_dir.len()..]
107 } else { 101 } else {
108 file_path.as_str() 102 file_path
109 }; 103 };
110 if current_dir_normalize.contains(windows_path_separator) { 104 if current_dir_normalize.contains(windows_path_separator) {
111 Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/")) 105 Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/"))
@@ -113,75 +107,3 @@ fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> {
113 Cow::Borrowed(current_dir_normalize) 107 Cow::Borrowed(current_dir_normalize)
114 } 108 }
115} 109}
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 }
131 }
132 OutOfLineMode::FileInDirectory(path) => resolve_simple_path(source_root, path),
133 OutOfLineMode::WithAttributePath(path) => resolve_simple_path(source_root, path),
134 }
135 }
136}
137
138enum InsideInlineModuleMode {
139 File(RelativePathBuf),
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}