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/ra_hir/src/module | |
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/ra_hir/src/module')
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 199 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres/tests.rs | 127 |
2 files changed, 196 insertions, 130 deletions
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 | } | ||