diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-09 11:00:56 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-09 11:00:56 +0000 |
commit | 19f6cdcc034a2a91e6112a4ceb32e26413b0aa0d (patch) | |
tree | 548400065966f715ae203e6f6e0fcfd9f12e4470 /crates | |
parent | 34956b7823d72467fbf4fa62bd4413dfb680f78d (diff) | |
parent | 7784c7a701ba944decf671f80dea581d68667663 (diff) |
Merge #268
268: WIP: resolve imports across crates r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_db/src/input.rs | 34 | ||||
-rw-r--r-- | crates/ra_db/src/mock.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/krate.rs | 48 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 24 | ||||
-rw-r--r-- | crates/ra_hir/src/module.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 199 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres/tests.rs | 127 | ||||
-rw-r--r-- | crates/ra_hir/src/query_definitions.rs | 9 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/project_model.rs | 36 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/server_world.rs | 39 |
11 files changed, 373 insertions, 168 deletions
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 7d9faa43c..ac144b991 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::{FxHashSet, FxHashMap}; |
4 | use rustc_hash::FxHashSet; | 4 | use ra_syntax::SmolStr; |
5 | use salsa; | 5 | use salsa; |
6 | 6 | ||
7 | use crate::file_resolver::FileResolverImp; | 7 | use crate::file_resolver::FileResolverImp; |
@@ -20,25 +20,32 @@ pub struct CrateGraph { | |||
20 | #[derive(Debug, Clone, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, PartialEq, Eq)] |
21 | struct CrateData { | 21 | struct CrateData { |
22 | file_id: FileId, | 22 | file_id: FileId, |
23 | deps: Vec<Dependency>, | 23 | dependencies: Vec<Dependency>, |
24 | } | 24 | } |
25 | 25 | ||
26 | impl CrateData { | 26 | impl CrateData { |
27 | fn new(file_id: FileId) -> CrateData { | 27 | fn new(file_id: FileId) -> CrateData { |
28 | CrateData { | 28 | CrateData { |
29 | file_id, | 29 | file_id, |
30 | deps: Vec::new(), | 30 | dependencies: Vec::new(), |
31 | } | 31 | } |
32 | } | 32 | } |
33 | 33 | ||
34 | fn add_dep(&mut self, dep: CrateId) { | 34 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { |
35 | self.deps.push(Dependency { crate_: dep }) | 35 | self.dependencies.push(Dependency { name, crate_id }) |
36 | } | 36 | } |
37 | } | 37 | } |
38 | 38 | ||
39 | #[derive(Debug, Clone, PartialEq, Eq)] | 39 | #[derive(Debug, Clone, PartialEq, Eq)] |
40 | pub struct Dependency { | 40 | pub struct Dependency { |
41 | crate_: CrateId, | 41 | pub crate_id: CrateId, |
42 | pub name: SmolStr, | ||
43 | } | ||
44 | |||
45 | impl Dependency { | ||
46 | pub fn crate_id(&self) -> CrateId { | ||
47 | self.crate_id | ||
48 | } | ||
42 | } | 49 | } |
43 | 50 | ||
44 | impl CrateGraph { | 51 | impl CrateGraph { |
@@ -48,8 +55,11 @@ impl CrateGraph { | |||
48 | assert!(prev.is_none()); | 55 | assert!(prev.is_none()); |
49 | crate_id | 56 | crate_id |
50 | } | 57 | } |
51 | pub fn add_dep(&mut self, from: CrateId, to: CrateId) { | 58 | //FIXME: check that we don't have cycles here. |
52 | self.arena.get_mut(&from).unwrap().add_dep(to) | 59 | // Just a simple depth first search from `to` should work, |
60 | // the graph is small. | ||
61 | pub fn add_dep(&mut self, from: CrateId, name: SmolStr, to: CrateId) { | ||
62 | self.arena.get_mut(&from).unwrap().add_dep(name, to) | ||
53 | } | 63 | } |
54 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | 64 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { |
55 | self.arena[&crate_id].file_id | 65 | self.arena[&crate_id].file_id |
@@ -61,6 +71,12 @@ impl CrateGraph { | |||
61 | .find(|(_crate_id, data)| data.file_id == file_id)?; | 71 | .find(|(_crate_id, data)| data.file_id == file_id)?; |
62 | Some(crate_id) | 72 | Some(crate_id) |
63 | } | 73 | } |
74 | pub fn dependencies<'a>( | ||
75 | &'a self, | ||
76 | crate_id: CrateId, | ||
77 | ) -> impl Iterator<Item = &'a Dependency> + 'a { | ||
78 | self.arena[&crate_id].dependencies.iter() | ||
79 | } | ||
64 | } | 80 | } |
65 | 81 | ||
66 | salsa::query_group! { | 82 | salsa::query_group! { |
diff --git a/crates/ra_db/src/mock.rs b/crates/ra_db/src/mock.rs index 2840f9655..2f7551597 100644 --- a/crates/ra_db/src/mock.rs +++ b/crates/ra_db/src/mock.rs | |||
@@ -5,7 +5,7 @@ use relative_path::{RelativePath, RelativePathBuf}; | |||
5 | 5 | ||
6 | use crate::{FileId, FileResolver, SourceRoot, FileResolverImp}; | 6 | use crate::{FileId, FileResolver, SourceRoot, FileResolverImp}; |
7 | 7 | ||
8 | #[derive(Default, Debug)] | 8 | #[derive(Default, Debug, Clone)] |
9 | pub struct FileMap(Vec<(FileId, RelativePathBuf)>); | 9 | pub struct FileMap(Vec<(FileId, RelativePathBuf)>); |
10 | 10 | ||
11 | impl FileMap { | 11 | impl FileMap { |
@@ -28,6 +28,11 @@ impl FileMap { | |||
28 | self.iter().map(|(id, _)| id).collect() | 28 | self.iter().map(|(id, _)| id).collect() |
29 | } | 29 | } |
30 | 30 | ||
31 | pub fn file_id(&self, path: &str) -> FileId { | ||
32 | assert!(path.starts_with('/')); | ||
33 | self.iter().find(|(_, p)| p == &path[1..]).unwrap().0 | ||
34 | } | ||
35 | |||
31 | fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a { | 36 | fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a { |
32 | self.0 | 37 | self.0 |
33 | .iter() | 38 | .iter() |
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs new file mode 100644 index 000000000..1196dcef1 --- /dev/null +++ b/crates/ra_hir/src/krate.rs | |||
@@ -0,0 +1,48 @@ | |||
1 | use ra_syntax::SmolStr; | ||
2 | pub use ra_db::CrateId; | ||
3 | |||
4 | use crate::{HirDatabase, Module, Cancelable}; | ||
5 | |||
6 | /// hir::Crate describes a single crate. It's the main inteface with which | ||
7 | /// crate's dependencies interact. Mostly, it should be just a proxy for the | ||
8 | /// root module. | ||
9 | #[derive(Debug)] | ||
10 | pub struct Crate { | ||
11 | crate_id: CrateId, | ||
12 | } | ||
13 | |||
14 | #[derive(Debug)] | ||
15 | pub struct CrateDependency { | ||
16 | pub krate: Crate, | ||
17 | pub name: SmolStr, | ||
18 | } | ||
19 | |||
20 | impl Crate { | ||
21 | pub(crate) fn new(crate_id: CrateId) -> Crate { | ||
22 | Crate { crate_id } | ||
23 | } | ||
24 | pub fn dependencies(&self, db: &impl HirDatabase) -> Vec<CrateDependency> { | ||
25 | let crate_graph = db.crate_graph(); | ||
26 | crate_graph | ||
27 | .dependencies(self.crate_id) | ||
28 | .map(|dep| { | ||
29 | let krate = Crate::new(dep.crate_id()); | ||
30 | let name = dep.name.clone(); | ||
31 | CrateDependency { krate, name } | ||
32 | }) | ||
33 | .collect() | ||
34 | } | ||
35 | pub fn root_module(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> { | ||
36 | let crate_graph = db.crate_graph(); | ||
37 | let file_id = crate_graph.crate_root(self.crate_id); | ||
38 | let source_root_id = db.file_source_root(file_id); | ||
39 | let module_tree = db.module_tree(source_root_id)?; | ||
40 | // FIXME: teach module tree about crate roots instead of guessing | ||
41 | let (module_id, _) = ctry!(module_tree | ||
42 | .modules_with_sources() | ||
43 | .find(|(_, src)| src.file_id() == file_id)); | ||
44 | |||
45 | let module = Module::new(db, source_root_id, module_id)?; | ||
46 | Ok(Some(module)) | ||
47 | } | ||
48 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f50b922af..578fde259 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -18,12 +18,14 @@ pub mod db; | |||
18 | #[cfg(test)] | 18 | #[cfg(test)] |
19 | mod mock; | 19 | mod mock; |
20 | mod query_definitions; | 20 | mod query_definitions; |
21 | mod function; | ||
22 | mod module; | ||
23 | mod path; | 21 | mod path; |
24 | mod arena; | 22 | mod arena; |
25 | pub mod source_binder; | 23 | pub mod source_binder; |
26 | 24 | ||
25 | mod krate; | ||
26 | mod module; | ||
27 | mod function; | ||
28 | |||
27 | use std::ops::Index; | 29 | use std::ops::Index; |
28 | 30 | ||
29 | use ra_syntax::{SyntaxNodeRef, SyntaxNode}; | 31 | use ra_syntax::{SyntaxNodeRef, SyntaxNode}; |
@@ -36,6 +38,7 @@ use crate::{ | |||
36 | 38 | ||
37 | pub use self::{ | 39 | pub use self::{ |
38 | path::{Path, PathKind}, | 40 | path::{Path, PathKind}, |
41 | krate::Crate, | ||
39 | module::{Module, ModuleId, Problem, nameres::ItemMap}, | 42 | module::{Module, ModuleId, Problem, nameres::ItemMap}, |
40 | function::{Function, FnScopes}, | 43 | function::{Function, FnScopes}, |
41 | }; | 44 | }; |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index e855df11d..b7193c4f3 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -2,7 +2,7 @@ use std::sync::Arc; | |||
2 | 2 | ||
3 | use parking_lot::Mutex; | 3 | use parking_lot::Mutex; |
4 | use salsa::{self, Database}; | 4 | use salsa::{self, Database}; |
5 | use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE}; | 5 | use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE, CrateGraph}; |
6 | use relative_path::RelativePathBuf; | 6 | use relative_path::RelativePathBuf; |
7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | 7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; |
8 | 8 | ||
@@ -16,7 +16,24 @@ pub(crate) struct MockDatabase { | |||
16 | } | 16 | } |
17 | 17 | ||
18 | impl MockDatabase { | 18 | impl MockDatabase { |
19 | pub(crate) fn with_files(fixture: &str) -> (MockDatabase, FileMap) { | ||
20 | let (db, file_map, position) = MockDatabase::from_fixture(fixture); | ||
21 | assert!(position.is_none()); | ||
22 | (db, file_map) | ||
23 | } | ||
24 | |||
19 | pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) { | 25 | pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) { |
26 | let (db, _, position) = MockDatabase::from_fixture(fixture); | ||
27 | let position = position.expect("expected a marker ( <|> )"); | ||
28 | (db, position) | ||
29 | } | ||
30 | |||
31 | pub(crate) fn set_crate_graph(&mut self, crate_graph: CrateGraph) { | ||
32 | self.query_mut(ra_db::CrateGraphQuery) | ||
33 | .set((), Arc::new(crate_graph)); | ||
34 | } | ||
35 | |||
36 | fn from_fixture(fixture: &str) -> (MockDatabase, FileMap, Option<FilePosition>) { | ||
20 | let mut db = MockDatabase::default(); | 37 | let mut db = MockDatabase::default(); |
21 | 38 | ||
22 | let mut position = None; | 39 | let mut position = None; |
@@ -32,11 +49,10 @@ impl MockDatabase { | |||
32 | db.add_file(&mut file_map, &entry.meta, &entry.text); | 49 | db.add_file(&mut file_map, &entry.meta, &entry.text); |
33 | } | 50 | } |
34 | } | 51 | } |
35 | let position = position.expect("expected a marker (<|>)"); | 52 | let source_root = file_map.clone().into_source_root(); |
36 | let source_root = file_map.into_source_root(); | ||
37 | db.query_mut(ra_db::SourceRootQuery) | 53 | db.query_mut(ra_db::SourceRootQuery) |
38 | .set(WORKSPACE, Arc::new(source_root)); | 54 | .set(WORKSPACE, Arc::new(source_root)); |
39 | (db, position) | 55 | (db, file_map, position) |
40 | } | 56 | } |
41 | 57 | ||
42 | fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId { | 58 | fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId { |
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index e7a49f83a..c6bb76d56 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs | |||
@@ -12,7 +12,7 @@ use ra_db::{SourceRootId, FileId, Cancelable}; | |||
12 | use relative_path::RelativePathBuf; | 12 | use relative_path::RelativePathBuf; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, | 15 | DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, |
16 | arena::{Arena, Id}, | 16 | arena::{Arena, Id}, |
17 | }; | 17 | }; |
18 | 18 | ||
@@ -64,6 +64,15 @@ impl Module { | |||
64 | }) | 64 | }) |
65 | } | 65 | } |
66 | 66 | ||
67 | /// Returns the crate this module is part of. | ||
68 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { | ||
69 | let root_id = self.module_id.crate_root(&self.tree); | ||
70 | let file_id = root_id.source(&self.tree).file_id(); | ||
71 | let crate_graph = db.crate_graph(); | ||
72 | let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; | ||
73 | Some(Crate::new(crate_id)) | ||
74 | } | ||
75 | |||
67 | /// The root of the tree this module is part of | 76 | /// The root of the tree this module is part of |
68 | pub fn crate_root(&self) -> Module { | 77 | pub fn crate_root(&self) -> Module { |
69 | let root_id = self.module_id.crate_root(&self.tree); | 78 | let root_id = self.module_id.crate_root(&self.tree); |
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 6511359d0..9afeade9e 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
@@ -31,7 +31,7 @@ use crate::{ | |||
31 | DefId, DefLoc, DefKind, | 31 | DefId, DefLoc, DefKind, |
32 | SourceItemId, SourceFileItemId, SourceFileItems, | 32 | SourceItemId, SourceFileItemId, SourceFileItems, |
33 | Path, PathKind, | 33 | Path, PathKind, |
34 | HirDatabase, | 34 | HirDatabase, Crate, |
35 | module::{ModuleId, ModuleTree}, | 35 | module::{ModuleId, ModuleTree}, |
36 | }; | 36 | }; |
37 | 37 | ||
@@ -200,34 +200,63 @@ impl ModuleItem { | |||
200 | } | 200 | } |
201 | 201 | ||
202 | pub(crate) struct Resolver<'a, DB> { | 202 | pub(crate) struct Resolver<'a, DB> { |
203 | pub(crate) db: &'a DB, | 203 | db: &'a DB, |
204 | pub(crate) input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, | 204 | input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, |
205 | pub(crate) source_root: SourceRootId, | 205 | source_root: SourceRootId, |
206 | pub(crate) module_tree: Arc<ModuleTree>, | 206 | module_tree: Arc<ModuleTree>, |
207 | pub(crate) result: ItemMap, | 207 | result: ItemMap, |
208 | } | 208 | } |
209 | 209 | ||
210 | impl<'a, DB> Resolver<'a, DB> | 210 | impl<'a, DB> Resolver<'a, DB> |
211 | where | 211 | where |
212 | DB: HirDatabase, | 212 | DB: HirDatabase, |
213 | { | 213 | { |
214 | pub(crate) fn new( | ||
215 | db: &'a DB, | ||
216 | input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, | ||
217 | source_root: SourceRootId, | ||
218 | module_tree: Arc<ModuleTree>, | ||
219 | ) -> Resolver<'a, DB> { | ||
220 | Resolver { | ||
221 | db, | ||
222 | input, | ||
223 | source_root, | ||
224 | module_tree, | ||
225 | result: ItemMap::default(), | ||
226 | } | ||
227 | } | ||
228 | |||
214 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { | 229 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { |
215 | for (&module_id, items) in self.input.iter() { | 230 | for (&module_id, items) in self.input.iter() { |
216 | self.populate_module(module_id, items) | 231 | self.populate_module(module_id, items)?; |
217 | } | 232 | } |
218 | 233 | ||
219 | for &module_id in self.input.keys() { | 234 | for &module_id in self.input.keys() { |
220 | self.db.check_canceled()?; | 235 | self.db.check_canceled()?; |
221 | self.resolve_imports(module_id); | 236 | self.resolve_imports(module_id)?; |
222 | } | 237 | } |
223 | Ok(self.result) | 238 | Ok(self.result) |
224 | } | 239 | } |
225 | 240 | ||
226 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { | 241 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> { |
227 | let file_id = module_id.source(&self.module_tree).file_id(); | 242 | let file_id = module_id.source(&self.module_tree).file_id(); |
228 | 243 | ||
229 | let mut module_items = ModuleScope::default(); | 244 | let mut module_items = ModuleScope::default(); |
230 | 245 | ||
246 | // Populate extern crates prelude | ||
247 | { | ||
248 | let root_id = module_id.crate_root(&self.module_tree); | ||
249 | let file_id = root_id.source(&self.module_tree).file_id(); | ||
250 | let crate_graph = self.db.crate_graph(); | ||
251 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) { | ||
252 | let krate = Crate::new(crate_id); | ||
253 | for dep in krate.dependencies(self.db) { | ||
254 | if let Some(module) = dep.krate.root_module(self.db)? { | ||
255 | self.add_module_item(&mut module_items, dep.name, module.module_id); | ||
256 | } | ||
257 | } | ||
258 | }; | ||
259 | } | ||
231 | for import in input.imports.iter() { | 260 | for import in input.imports.iter() { |
232 | if let Some(name) = import.path.segments.iter().last() { | 261 | if let Some(name) = import.path.segments.iter().last() { |
233 | if let ImportKind::Named(import) = import.kind { | 262 | if let ImportKind::Named(import) = import.kind { |
@@ -241,10 +270,9 @@ where | |||
241 | } | 270 | } |
242 | } | 271 | } |
243 | } | 272 | } |
244 | 273 | // Populate explicitelly declared items, except modules | |
245 | for item in input.items.iter() { | 274 | for item in input.items.iter() { |
246 | if item.kind == MODULE { | 275 | if item.kind == MODULE { |
247 | // handle submodules separatelly | ||
248 | continue; | 276 | continue; |
249 | } | 277 | } |
250 | let def_loc = DefLoc { | 278 | let def_loc = DefLoc { |
@@ -264,45 +292,50 @@ where | |||
264 | module_items.items.insert(item.name.clone(), resolution); | 292 | module_items.items.insert(item.name.clone(), resolution); |
265 | } | 293 | } |
266 | 294 | ||
295 | // Populate modules | ||
267 | for (name, module_id) in module_id.children(&self.module_tree) { | 296 | for (name, module_id) in module_id.children(&self.module_tree) { |
268 | let def_loc = DefLoc { | 297 | self.add_module_item(&mut module_items, name, module_id); |
269 | kind: DefKind::Module, | ||
270 | source_root_id: self.source_root, | ||
271 | module_id, | ||
272 | source_item_id: module_id.source(&self.module_tree).0, | ||
273 | }; | ||
274 | let def_id = def_loc.id(self.db); | ||
275 | let resolution = Resolution { | ||
276 | def_id: Some(def_id), | ||
277 | import: None, | ||
278 | }; | ||
279 | module_items.items.insert(name, resolution); | ||
280 | } | 298 | } |
281 | 299 | ||
282 | self.result.per_module.insert(module_id, module_items); | 300 | self.result.per_module.insert(module_id, module_items); |
301 | Ok(()) | ||
283 | } | 302 | } |
284 | 303 | ||
285 | fn resolve_imports(&mut self, module_id: ModuleId) { | 304 | fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, module_id: ModuleId) { |
305 | let def_loc = DefLoc { | ||
306 | kind: DefKind::Module, | ||
307 | source_root_id: self.source_root, | ||
308 | module_id, | ||
309 | source_item_id: module_id.source(&self.module_tree).0, | ||
310 | }; | ||
311 | let def_id = def_loc.id(self.db); | ||
312 | let resolution = Resolution { | ||
313 | def_id: Some(def_id), | ||
314 | import: None, | ||
315 | }; | ||
316 | module_items.items.insert(name, resolution); | ||
317 | } | ||
318 | |||
319 | fn resolve_imports(&mut self, module_id: ModuleId) -> Cancelable<()> { | ||
286 | for import in self.input[&module_id].imports.iter() { | 320 | for import in self.input[&module_id].imports.iter() { |
287 | self.resolve_import(module_id, import); | 321 | self.resolve_import(module_id, import)?; |
288 | } | 322 | } |
323 | Ok(()) | ||
289 | } | 324 | } |
290 | 325 | ||
291 | fn resolve_import(&mut self, module_id: ModuleId, import: &Import) { | 326 | fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable<()> { |
292 | let ptr = match import.kind { | 327 | let ptr = match import.kind { |
293 | ImportKind::Glob => return, | 328 | ImportKind::Glob => return Ok(()), |
294 | ImportKind::Named(ptr) => ptr, | 329 | ImportKind::Named(ptr) => ptr, |
295 | }; | 330 | }; |
296 | 331 | ||
297 | let mut curr = match import.path.kind { | 332 | let mut curr = match import.path.kind { |
298 | // TODO: handle extern crates | 333 | PathKind::Plain | PathKind::Self_ => module_id, |
299 | PathKind::Plain => return, | ||
300 | PathKind::Self_ => module_id, | ||
301 | PathKind::Super => { | 334 | PathKind::Super => { |
302 | match module_id.parent(&self.module_tree) { | 335 | match module_id.parent(&self.module_tree) { |
303 | Some(it) => it, | 336 | Some(it) => it, |
304 | // TODO: error | 337 | // TODO: error |
305 | None => return, | 338 | None => return Ok(()), |
306 | } | 339 | } |
307 | } | 340 | } |
308 | PathKind::Crate => module_id.crate_root(&self.module_tree), | 341 | PathKind::Crate => module_id.crate_root(&self.module_tree), |
@@ -312,10 +345,10 @@ where | |||
312 | let is_last = i == import.path.segments.len() - 1; | 345 | let is_last = i == import.path.segments.len() - 1; |
313 | 346 | ||
314 | let def_id = match self.result.per_module[&curr].items.get(name) { | 347 | let def_id = match self.result.per_module[&curr].items.get(name) { |
315 | None => return, | 348 | None => return Ok(()), |
316 | Some(res) => match res.def_id { | 349 | Some(res) => match res.def_id { |
317 | Some(it) => it, | 350 | Some(it) => it, |
318 | None => return, | 351 | None => return Ok(()), |
319 | }, | 352 | }, |
320 | }; | 353 | }; |
321 | 354 | ||
@@ -326,7 +359,7 @@ where | |||
326 | module_id, | 359 | module_id, |
327 | .. | 360 | .. |
328 | } => module_id, | 361 | } => module_id, |
329 | _ => return, | 362 | _ => return Ok(()), |
330 | } | 363 | } |
331 | } else { | 364 | } else { |
332 | self.update(module_id, |items| { | 365 | self.update(module_id, |items| { |
@@ -338,6 +371,7 @@ where | |||
338 | }) | 371 | }) |
339 | } | 372 | } |
340 | } | 373 | } |
374 | Ok(()) | ||
341 | } | 375 | } |
342 | 376 | ||
343 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | 377 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { |
@@ -347,99 +381,4 @@ where | |||
347 | } | 381 | } |
348 | 382 | ||
349 | #[cfg(test)] | 383 | #[cfg(test)] |
350 | mod tests { | 384 | mod tests; |
351 | use std::sync::Arc; | ||
352 | |||
353 | use salsa::Database; | ||
354 | use ra_db::FilesDatabase; | ||
355 | use ra_syntax::SmolStr; | ||
356 | |||
357 | use crate::{ | ||
358 | self as hir, | ||
359 | db::HirDatabase, | ||
360 | mock::MockDatabase, | ||
361 | }; | ||
362 | |||
363 | fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { | ||
364 | let (db, pos) = MockDatabase::with_position(fixture); | ||
365 | let source_root = db.file_source_root(pos.file_id); | ||
366 | let module = hir::source_binder::module_from_position(&db, pos) | ||
367 | .unwrap() | ||
368 | .unwrap(); | ||
369 | let module_id = module.module_id; | ||
370 | (db.item_map(source_root).unwrap(), module_id) | ||
371 | } | ||
372 | |||
373 | #[test] | ||
374 | fn test_item_map() { | ||
375 | let (item_map, module_id) = item_map( | ||
376 | " | ||
377 | //- /lib.rs | ||
378 | mod foo; | ||
379 | |||
380 | use crate::foo::bar::Baz; | ||
381 | <|> | ||
382 | |||
383 | //- /foo/mod.rs | ||
384 | pub mod bar; | ||
385 | |||
386 | //- /foo/bar.rs | ||
387 | pub struct Baz; | ||
388 | ", | ||
389 | ); | ||
390 | let name = SmolStr::from("Baz"); | ||
391 | let resolution = &item_map.per_module[&module_id].items[&name]; | ||
392 | assert!(resolution.def_id.is_some()); | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn typing_inside_a_function_should_not_invalidate_item_map() { | ||
397 | let (mut db, pos) = MockDatabase::with_position( | ||
398 | " | ||
399 | //- /lib.rs | ||
400 | mod foo;<|> | ||
401 | |||
402 | use crate::foo::bar::Baz; | ||
403 | |||
404 | fn foo() -> i32 { | ||
405 | 1 + 1 | ||
406 | } | ||
407 | //- /foo/mod.rs | ||
408 | pub mod bar; | ||
409 | |||
410 | //- /foo/bar.rs | ||
411 | pub struct Baz; | ||
412 | ", | ||
413 | ); | ||
414 | let source_root = db.file_source_root(pos.file_id); | ||
415 | { | ||
416 | let events = db.log_executed(|| { | ||
417 | db.item_map(source_root).unwrap(); | ||
418 | }); | ||
419 | assert!(format!("{:?}", events).contains("item_map")) | ||
420 | } | ||
421 | |||
422 | let new_text = " | ||
423 | mod foo; | ||
424 | |||
425 | use crate::foo::bar::Baz; | ||
426 | |||
427 | fn foo() -> i32 { 92 } | ||
428 | " | ||
429 | .to_string(); | ||
430 | |||
431 | db.query_mut(ra_db::FileTextQuery) | ||
432 | .set(pos.file_id, Arc::new(new_text)); | ||
433 | |||
434 | { | ||
435 | let events = db.log_executed(|| { | ||
436 | db.item_map(source_root).unwrap(); | ||
437 | }); | ||
438 | assert!( | ||
439 | !format!("{:?}", events).contains("_item_map"), | ||
440 | "{:#?}", | ||
441 | events | ||
442 | ) | ||
443 | } | ||
444 | } | ||
445 | } | ||
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs new file mode 100644 index 000000000..9ddc32dcd --- /dev/null +++ b/crates/ra_hir/src/module/nameres/tests.rs | |||
@@ -0,0 +1,127 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use salsa::Database; | ||
4 | use ra_db::{FilesDatabase, CrateGraph}; | ||
5 | use ra_syntax::SmolStr; | ||
6 | |||
7 | use crate::{ | ||
8 | self as hir, | ||
9 | db::HirDatabase, | ||
10 | mock::MockDatabase, | ||
11 | }; | ||
12 | |||
13 | fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { | ||
14 | let (db, pos) = MockDatabase::with_position(fixture); | ||
15 | let source_root = db.file_source_root(pos.file_id); | ||
16 | let module = hir::source_binder::module_from_position(&db, pos) | ||
17 | .unwrap() | ||
18 | .unwrap(); | ||
19 | let module_id = module.module_id; | ||
20 | (db.item_map(source_root).unwrap(), module_id) | ||
21 | } | ||
22 | |||
23 | #[test] | ||
24 | fn item_map_smoke_test() { | ||
25 | let (item_map, module_id) = item_map( | ||
26 | " | ||
27 | //- /lib.rs | ||
28 | mod foo; | ||
29 | |||
30 | use crate::foo::bar::Baz; | ||
31 | <|> | ||
32 | |||
33 | //- /foo/mod.rs | ||
34 | pub mod bar; | ||
35 | |||
36 | //- /foo/bar.rs | ||
37 | pub struct Baz; | ||
38 | ", | ||
39 | ); | ||
40 | let name = SmolStr::from("Baz"); | ||
41 | let resolution = &item_map.per_module[&module_id].items[&name]; | ||
42 | assert!(resolution.def_id.is_some()); | ||
43 | } | ||
44 | |||
45 | #[test] | ||
46 | fn item_map_across_crates() { | ||
47 | let (mut db, files) = MockDatabase::with_files( | ||
48 | " | ||
49 | //- /main.rs | ||
50 | use test_crate::Baz; | ||
51 | |||
52 | //- /lib.rs | ||
53 | pub struct Baz; | ||
54 | ", | ||
55 | ); | ||
56 | let main_id = files.file_id("/main.rs"); | ||
57 | let lib_id = files.file_id("/lib.rs"); | ||
58 | |||
59 | let mut crate_graph = CrateGraph::default(); | ||
60 | let main_crate = crate_graph.add_crate_root(main_id); | ||
61 | let lib_crate = crate_graph.add_crate_root(lib_id); | ||
62 | crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate); | ||
63 | |||
64 | db.set_crate_graph(crate_graph); | ||
65 | |||
66 | let source_root = db.file_source_root(main_id); | ||
67 | let module = hir::source_binder::module_from_file_id(&db, main_id) | ||
68 | .unwrap() | ||
69 | .unwrap(); | ||
70 | let module_id = module.module_id; | ||
71 | let item_map = db.item_map(source_root).unwrap(); | ||
72 | |||
73 | let name = SmolStr::from("Baz"); | ||
74 | let resolution = &item_map.per_module[&module_id].items[&name]; | ||
75 | assert!(resolution.def_id.is_some()); | ||
76 | } | ||
77 | |||
78 | #[test] | ||
79 | fn typing_inside_a_function_should_not_invalidate_item_map() { | ||
80 | let (mut db, pos) = MockDatabase::with_position( | ||
81 | " | ||
82 | //- /lib.rs | ||
83 | mod foo;<|> | ||
84 | |||
85 | use crate::foo::bar::Baz; | ||
86 | |||
87 | fn foo() -> i32 { | ||
88 | 1 + 1 | ||
89 | } | ||
90 | //- /foo/mod.rs | ||
91 | pub mod bar; | ||
92 | |||
93 | //- /foo/bar.rs | ||
94 | pub struct Baz; | ||
95 | ", | ||
96 | ); | ||
97 | let source_root = db.file_source_root(pos.file_id); | ||
98 | { | ||
99 | let events = db.log_executed(|| { | ||
100 | db.item_map(source_root).unwrap(); | ||
101 | }); | ||
102 | assert!(format!("{:?}", events).contains("item_map")) | ||
103 | } | ||
104 | |||
105 | let new_text = " | ||
106 | mod foo; | ||
107 | |||
108 | use crate::foo::bar::Baz; | ||
109 | |||
110 | fn foo() -> i32 { 92 } | ||
111 | " | ||
112 | .to_string(); | ||
113 | |||
114 | db.query_mut(ra_db::FileTextQuery) | ||
115 | .set(pos.file_id, Arc::new(new_text)); | ||
116 | |||
117 | { | ||
118 | let events = db.log_executed(|| { | ||
119 | db.item_map(source_root).unwrap(); | ||
120 | }); | ||
121 | assert!( | ||
122 | !format!("{:?}", events).contains("_item_map"), | ||
123 | "{:#?}", | ||
124 | events | ||
125 | ) | ||
126 | } | ||
127 | } | ||
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index bb4457d07..37c4f9e4f 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -141,13 +141,8 @@ pub(super) fn item_map( | |||
141 | Ok((id, items)) | 141 | Ok((id, items)) |
142 | }) | 142 | }) |
143 | .collect::<Cancelable<FxHashMap<_, _>>>()?; | 143 | .collect::<Cancelable<FxHashMap<_, _>>>()?; |
144 | let resolver = Resolver { | 144 | |
145 | db: db, | 145 | let resolver = Resolver::new(db, &input, source_root, module_tree); |
146 | input: &input, | ||
147 | source_root, | ||
148 | module_tree, | ||
149 | result: ItemMap::default(), | ||
150 | }; | ||
151 | let res = resolver.resolve()?; | 146 | let res = resolver.resolve()?; |
152 | let elapsed = start.elapsed(); | 147 | let elapsed = start.elapsed(); |
153 | log::info!("item_map: {:?}", elapsed); | 148 | log::info!("item_map: {:?}", elapsed); |
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs index 3305d468a..cb91ada90 100644 --- a/crates/ra_lsp_server/src/project_model.rs +++ b/crates/ra_lsp_server/src/project_model.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use std::path::{Path, PathBuf}; | 1 | use std::path::{Path, PathBuf}; |
2 | 2 | ||
3 | use serde_derive::Serialize; | ||
4 | use cargo_metadata::{metadata_run, CargoOpt}; | 3 | use cargo_metadata::{metadata_run, CargoOpt}; |
5 | use ra_syntax::SmolStr; | 4 | use ra_syntax::SmolStr; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -11,15 +10,22 @@ use crate::{ | |||
11 | thread_watcher::{ThreadWatcher, Worker}, | 10 | thread_watcher::{ThreadWatcher, Worker}, |
12 | }; | 11 | }; |
13 | 12 | ||
13 | /// `CargoWorksapce` represents the logical structure of, well, a Cargo | ||
14 | /// workspace. It pretty closely mirrors `cargo metadata` output. | ||
15 | /// | ||
16 | /// Note that internally, rust analyzer uses a differnet structure: | ||
17 | /// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, | ||
18 | /// while this knows about `Pacakges` & `Targets`: purely cargo-related | ||
19 | /// concepts. | ||
14 | #[derive(Debug, Clone)] | 20 | #[derive(Debug, Clone)] |
15 | pub struct CargoWorkspace { | 21 | pub struct CargoWorkspace { |
16 | packages: Vec<PackageData>, | 22 | packages: Vec<PackageData>, |
17 | targets: Vec<TargetData>, | 23 | targets: Vec<TargetData>, |
18 | } | 24 | } |
19 | 25 | ||
20 | #[derive(Clone, Copy, Debug, Serialize)] | 26 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
21 | pub struct Package(usize); | 27 | pub struct Package(usize); |
22 | #[derive(Clone, Copy, Debug, Serialize)] | 28 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
23 | pub struct Target(usize); | 29 | pub struct Target(usize); |
24 | 30 | ||
25 | #[derive(Debug, Clone)] | 31 | #[derive(Debug, Clone)] |
@@ -28,6 +34,13 @@ struct PackageData { | |||
28 | manifest: PathBuf, | 34 | manifest: PathBuf, |
29 | targets: Vec<Target>, | 35 | targets: Vec<Target>, |
30 | is_member: bool, | 36 | is_member: bool, |
37 | dependencies: Vec<PackageDependency>, | ||
38 | } | ||
39 | |||
40 | #[derive(Debug, Clone)] | ||
41 | pub struct PackageDependency { | ||
42 | pub pkg: Package, | ||
43 | pub name: SmolStr, | ||
31 | } | 44 | } |
32 | 45 | ||
33 | #[derive(Debug, Clone)] | 46 | #[derive(Debug, Clone)] |
@@ -61,6 +74,12 @@ impl Package { | |||
61 | pub fn is_member(self, ws: &CargoWorkspace) -> bool { | 74 | pub fn is_member(self, ws: &CargoWorkspace) -> bool { |
62 | ws.pkg(self).is_member | 75 | ws.pkg(self).is_member |
63 | } | 76 | } |
77 | pub fn dependencies<'a>( | ||
78 | self, | ||
79 | ws: &'a CargoWorkspace, | ||
80 | ) -> impl Iterator<Item = &'a PackageDependency> + 'a { | ||
81 | ws.pkg(self).dependencies.iter() | ||
82 | } | ||
64 | } | 83 | } |
65 | 84 | ||
66 | impl Target { | 85 | impl Target { |
@@ -106,6 +125,7 @@ impl CargoWorkspace { | |||
106 | manifest: PathBuf::from(meta_pkg.manifest_path), | 125 | manifest: PathBuf::from(meta_pkg.manifest_path), |
107 | targets: Vec::new(), | 126 | targets: Vec::new(), |
108 | is_member, | 127 | is_member, |
128 | dependencies: Vec::new(), | ||
109 | }; | 129 | }; |
110 | for meta_tgt in meta_pkg.targets { | 130 | for meta_tgt in meta_pkg.targets { |
111 | let tgt = Target(targets.len()); | 131 | let tgt = Target(targets.len()); |
@@ -119,6 +139,16 @@ impl CargoWorkspace { | |||
119 | } | 139 | } |
120 | packages.push(pkg_data) | 140 | packages.push(pkg_data) |
121 | } | 141 | } |
142 | let resolve = meta.resolve.expect("metadata executed with deps"); | ||
143 | for node in resolve.nodes { | ||
144 | let source = pkg_by_id[&node.id]; | ||
145 | for id in node.dependencies { | ||
146 | let target = pkg_by_id[&id]; | ||
147 | let name: SmolStr = packages[target.0].name.replace('-', "_").into(); | ||
148 | let dep = PackageDependency { name, pkg: target }; | ||
149 | packages[source.0].dependencies.push(dep); | ||
150 | } | ||
151 | } | ||
122 | 152 | ||
123 | Ok(CargoWorkspace { packages, targets }) | 153 | Ok(CargoWorkspace { packages, targets }) |
124 | } | 154 | } |
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index c3f89ad5f..ab4c2c8aa 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -13,7 +13,7 @@ use failure::{bail, format_err}; | |||
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | path_map::{PathMap, Root}, | 15 | path_map::{PathMap, Root}, |
16 | project_model::CargoWorkspace, | 16 | project_model::{CargoWorkspace, TargetKind}, |
17 | vfs::{FileEvent, FileEventKind}, | 17 | vfs::{FileEvent, FileEventKind}, |
18 | Result, | 18 | Result, |
19 | }; | 19 | }; |
@@ -142,17 +142,34 @@ impl ServerWorldState { | |||
142 | } | 142 | } |
143 | pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { | 143 | pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { |
144 | let mut crate_graph = CrateGraph::default(); | 144 | let mut crate_graph = CrateGraph::default(); |
145 | ws.iter() | 145 | let mut pkg_to_lib_crate = FxHashMap::default(); |
146 | .flat_map(|ws| { | 146 | let mut pkg_crates = FxHashMap::default(); |
147 | ws.packages() | 147 | for ws in ws.iter() { |
148 | .flat_map(move |pkg| pkg.targets(ws)) | 148 | for pkg in ws.packages() { |
149 | .map(move |tgt| tgt.root(ws)) | 149 | for tgt in pkg.targets(ws) { |
150 | }) | 150 | let root = tgt.root(ws); |
151 | .for_each(|root| { | 151 | if let Some(file_id) = self.path_map.get_id(root) { |
152 | if let Some(file_id) = self.path_map.get_id(root) { | 152 | let crate_id = crate_graph.add_crate_root(file_id); |
153 | crate_graph.add_crate_root(file_id); | 153 | if tgt.kind(ws) == TargetKind::Lib { |
154 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
155 | } | ||
156 | pkg_crates | ||
157 | .entry(pkg) | ||
158 | .or_insert_with(Vec::new) | ||
159 | .push(crate_id); | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | for pkg in ws.packages() { | ||
164 | for dep in pkg.dependencies(ws) { | ||
165 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | ||
166 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
167 | crate_graph.add_dep(from, dep.name.clone(), to); | ||
168 | } | ||
169 | } | ||
154 | } | 170 | } |
155 | }); | 171 | } |
172 | } | ||
156 | self.workspaces = Arc::new(ws); | 173 | self.workspaces = Arc::new(ws); |
157 | let mut change = AnalysisChange::new(); | 174 | let mut change = AnalysisChange::new(); |
158 | change.set_crate_graph(crate_graph); | 175 | change.set_crate_graph(crate_graph); |