diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-10-10 12:51:56 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-10 12:51:56 +0100 |
commit | 8bcf40115fbb802c8853dcf3c70fc626f6d1a404 (patch) | |
tree | af7a30f05592bddec30c203cc24d804138b2f038 /crates/ra_hir/src/nameres/mod_resolution.rs | |
parent | 72b8cfb5aa99d85cd8cc936232cbbf24d1995547 (diff) | |
parent | 51402832774d876cdae8ac7042717e61d466b527 (diff) |
Merge #1979
1979: Refactor and fix some more edge cases around name resolution r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/nameres/mod_resolution.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/mod_resolution.rs | 232 |
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. |
2 | use std::borrow::Cow; | ||
2 | 3 | ||
3 | use std::{borrow::Cow, sync::Arc}; | 4 | use ra_db::FileId; |
4 | |||
5 | use ra_db::{FileId, SourceRoot}; | ||
6 | use ra_syntax::SmolStr; | 5 | use ra_syntax::SmolStr; |
7 | use relative_path::RelativePathBuf; | 6 | use relative_path::{RelativePath, RelativePathBuf}; |
8 | 7 | ||
9 | use crate::{db::DefDatabase, HirFileId, Name}; | 8 | use crate::{db::DefDatabase, HirFileId, Name}; |
10 | 9 | ||
11 | #[derive(Clone, Copy)] | 10 | #[derive(Clone, Debug)] |
12 | pub(super) struct ParentModule<'a> { | 11 | pub(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 | ||
17 | impl<'a> ParentModule<'a> { | 20 | impl 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 | |||
23 | pub(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 | ||
102 | fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> { | 96 | fn 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 | |||
117 | enum OutOfLineMode { | ||
118 | RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf }, | ||
119 | FileInDirectory(RelativePathBuf), | ||
120 | WithAttributePath(RelativePathBuf), | ||
121 | } | ||
122 | |||
123 | impl 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 | |||
138 | enum InsideInlineModuleMode { | ||
139 | File(RelativePathBuf), | ||
140 | WithAttributePath(RelativePathBuf), | ||
141 | } | ||
142 | |||
143 | impl 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 | |||
154 | enum ResolutionMode { | ||
155 | OutOfLine(OutOfLineMode), | ||
156 | InsideInlineModule(InsideInlineModuleMode), | ||
157 | InlineModuleWithAttributePath(InsideInlineModuleMode), | ||
158 | } | ||
159 | |||
160 | impl 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 | |||
172 | fn 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 | |||
179 | fn 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 | } | ||