]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir_def/src/nameres/mod_resolution.rs
Merge #2139
[rust.git] / crates / ra_hir_def / src / nameres / mod_resolution.rs
1 //! This module resolves `mod foo;` declaration to file.
2 use hir_expand::name::Name;
3 use ra_db::FileId;
4 use ra_syntax::SmolStr;
5 use relative_path::RelativePathBuf;
6
7 use crate::{db::DefDatabase2, HirFileId};
8
9 #[derive(Clone, Debug)]
10 pub struct ModDir {
11     /// `.` for `mod.rs`, `lib.rs`
12     /// `./foo` for `foo.rs`
13     /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
14     path: RelativePathBuf,
15     /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
16     root_non_dir_owner: bool,
17 }
18
19 impl ModDir {
20     pub fn root() -> ModDir {
21         ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
22     }
23
24     pub fn descend_into_definition(&self, name: &Name, attr_path: Option<&SmolStr>) -> ModDir {
25         let mut path = self.path.clone();
26         match attr_to_path(attr_path) {
27             None => path.push(&name.to_string()),
28             Some(attr_path) => {
29                 if self.root_non_dir_owner {
30                     assert!(path.pop());
31                 }
32                 path.push(attr_path);
33             }
34         }
35         ModDir { path, root_non_dir_owner: false }
36     }
37
38     pub fn resolve_declaration(
39         &self,
40         db: &impl DefDatabase2,
41         file_id: HirFileId,
42         name: &Name,
43         attr_path: Option<&SmolStr>,
44     ) -> Result<(FileId, ModDir), RelativePathBuf> {
45         let file_id = file_id.original_file(db);
46
47         let mut candidate_files = Vec::new();
48         match attr_to_path(attr_path) {
49             Some(attr_path) => {
50                 let base =
51                     if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
52                 candidate_files.push(base.join(attr_path))
53             }
54             None => {
55                 candidate_files.push(self.path.join(&format!("{}.rs", name)));
56                 candidate_files.push(self.path.join(&format!("{}/mod.rs", name)));
57             }
58         };
59
60         for candidate in candidate_files.iter() {
61             if let Some(file_id) = db.resolve_relative_path(file_id, candidate) {
62                 let mut root_non_dir_owner = false;
63                 let mut mod_path = RelativePathBuf::new();
64                 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
65                     root_non_dir_owner = true;
66                     mod_path.push(&name.to_string());
67                 }
68                 return Ok((file_id, ModDir { path: mod_path, root_non_dir_owner }));
69             }
70         }
71         Err(candidate_files.remove(0))
72     }
73 }
74
75 fn attr_to_path(attr: Option<&SmolStr>) -> Option<RelativePathBuf> {
76     attr.and_then(|it| RelativePathBuf::from_path(&it.replace("\\", "/")).ok())
77 }