diff options
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/diagnostics.rs | 49 | ||||
-rw-r--r-- | crates/hir_def/src/find_path.rs | 170 | ||||
-rw-r--r-- | crates/hir_def/src/item_scope.rs | 22 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 8 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/tests.rs | 6 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 93 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 508 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests.rs | 1 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 131 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/macros.rs | 73 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/mod_resolution.rs | 36 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 4 | ||||
-rw-r--r-- | crates/hir_def/src/test_db.rs | 42 |
14 files changed, 855 insertions, 295 deletions
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index 3e19d9117..001b3c5db 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -28,3 +28,52 @@ impl Diagnostic for UnresolvedModule { | |||
28 | self | 28 | self |
29 | } | 29 | } |
30 | } | 30 | } |
31 | |||
32 | #[derive(Debug)] | ||
33 | pub struct UnresolvedExternCrate { | ||
34 | pub file: HirFileId, | ||
35 | pub item: AstPtr<ast::ExternCrate>, | ||
36 | } | ||
37 | |||
38 | impl Diagnostic for UnresolvedExternCrate { | ||
39 | fn code(&self) -> DiagnosticCode { | ||
40 | DiagnosticCode("unresolved-extern-crate") | ||
41 | } | ||
42 | fn message(&self) -> String { | ||
43 | "unresolved extern crate".to_string() | ||
44 | } | ||
45 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
46 | InFile::new(self.file, self.item.clone().into()) | ||
47 | } | ||
48 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
49 | self | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | pub struct UnresolvedImport { | ||
55 | pub file: HirFileId, | ||
56 | pub node: AstPtr<ast::UseTree>, | ||
57 | } | ||
58 | |||
59 | impl Diagnostic for UnresolvedImport { | ||
60 | fn code(&self) -> DiagnosticCode { | ||
61 | DiagnosticCode("unresolved-import") | ||
62 | } | ||
63 | fn message(&self) -> String { | ||
64 | "unresolved import".to_string() | ||
65 | } | ||
66 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
67 | InFile::new(self.file, self.node.clone().into()) | ||
68 | } | ||
69 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
70 | self | ||
71 | } | ||
72 | fn is_experimental(&self) -> bool { | ||
73 | // This currently results in false positives in the following cases: | ||
74 | // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) | ||
75 | // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly) | ||
76 | // - proc macros and/or proc macro generated code | ||
77 | true | ||
78 | } | ||
79 | } | ||
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index ac2c54ac5..baf374144 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -4,6 +4,7 @@ use hir_expand::name::{known, AsName, Name}; | |||
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::nameres::CrateDefMap; | ||
7 | use crate::{ | 8 | use crate::{ |
8 | db::DefDatabase, | 9 | db::DefDatabase, |
9 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
@@ -18,7 +19,12 @@ use crate::{ | |||
18 | /// *from where* you're referring to the item, hence the `from` parameter. | 19 | /// *from where* you're referring to the item, hence the `from` parameter. |
19 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
20 | let _p = profile::span("find_path"); | 21 | let _p = profile::span("find_path"); |
21 | find_path_inner(db, item, from, MAX_PATH_LEN) | 22 | find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Not) |
23 | } | ||
24 | |||
25 | pub fn find_path_prefixed(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | ||
26 | let _p = profile::span("find_path_absolute"); | ||
27 | find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Plain) | ||
22 | } | 28 | } |
23 | 29 | ||
24 | const MAX_PATH_LEN: usize = 15; | 30 | const MAX_PATH_LEN: usize = 15; |
@@ -36,11 +42,67 @@ impl ModPath { | |||
36 | } | 42 | } |
37 | } | 43 | } |
38 | 44 | ||
45 | fn check_crate_self_super( | ||
46 | def_map: &CrateDefMap, | ||
47 | item: ItemInNs, | ||
48 | from: ModuleId, | ||
49 | ) -> Option<ModPath> { | ||
50 | // - if the item is the crate root, return `crate` | ||
51 | if item | ||
52 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
53 | krate: from.krate, | ||
54 | local_id: def_map.root, | ||
55 | })) | ||
56 | { | ||
57 | Some(ModPath::from_segments(PathKind::Crate, Vec::new())) | ||
58 | } else if item == ItemInNs::Types(from.into()) { | ||
59 | // - if the item is the module we're in, use `self` | ||
60 | Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) | ||
61 | } else { | ||
62 | if let Some(parent_id) = def_map.modules[from.local_id].parent { | ||
63 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) | ||
64 | if item | ||
65 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
66 | krate: from.krate, | ||
67 | local_id: parent_id, | ||
68 | })) | ||
69 | { | ||
70 | return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); | ||
71 | } | ||
72 | } | ||
73 | None | ||
74 | } | ||
75 | } | ||
76 | |||
77 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
78 | pub enum Prefixed { | ||
79 | Not, | ||
80 | BySelf, | ||
81 | Plain, | ||
82 | } | ||
83 | |||
84 | impl Prefixed { | ||
85 | #[inline] | ||
86 | fn prefix(self) -> Option<PathKind> { | ||
87 | match self { | ||
88 | Prefixed::Not => None, | ||
89 | Prefixed::BySelf => Some(PathKind::Super(0)), | ||
90 | Prefixed::Plain => Some(PathKind::Plain), | ||
91 | } | ||
92 | } | ||
93 | |||
94 | #[inline] | ||
95 | fn prefixed(self) -> bool { | ||
96 | self != Prefixed::Not | ||
97 | } | ||
98 | } | ||
99 | |||
39 | fn find_path_inner( | 100 | fn find_path_inner( |
40 | db: &dyn DefDatabase, | 101 | db: &dyn DefDatabase, |
41 | item: ItemInNs, | 102 | item: ItemInNs, |
42 | from: ModuleId, | 103 | from: ModuleId, |
43 | max_len: usize, | 104 | max_len: usize, |
105 | prefixed: Prefixed, | ||
44 | ) -> Option<ModPath> { | 106 | ) -> Option<ModPath> { |
45 | if max_len == 0 { | 107 | if max_len == 0 { |
46 | return None; | 108 | return None; |
@@ -51,41 +113,22 @@ fn find_path_inner( | |||
51 | // - if the item is already in scope, return the name under which it is | 113 | // - if the item is already in scope, return the name under which it is |
52 | let def_map = db.crate_def_map(from.krate); | 114 | let def_map = db.crate_def_map(from.krate); |
53 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; | 115 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; |
54 | if let Some((name, _)) = from_scope.name_of(item) { | 116 | let scope_name = |
55 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); | 117 | if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; |
118 | if !prefixed.prefixed() && scope_name.is_some() { | ||
119 | return scope_name | ||
120 | .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name])); | ||
56 | } | 121 | } |
57 | 122 | ||
58 | // - if the item is the crate root, return `crate` | 123 | if let modpath @ Some(_) = check_crate_self_super(&def_map, item, from) { |
59 | if item | 124 | return modpath; |
60 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
61 | krate: from.krate, | ||
62 | local_id: def_map.root, | ||
63 | })) | ||
64 | { | ||
65 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); | ||
66 | } | ||
67 | |||
68 | // - if the item is the module we're in, use `self` | ||
69 | if item == ItemInNs::Types(from.into()) { | ||
70 | return Some(ModPath::from_segments(PathKind::Super(0), Vec::new())); | ||
71 | } | ||
72 | |||
73 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) | ||
74 | if let Some(parent_id) = def_map.modules[from.local_id].parent { | ||
75 | if item | ||
76 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
77 | krate: from.krate, | ||
78 | local_id: parent_id, | ||
79 | })) | ||
80 | { | ||
81 | return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); | ||
82 | } | ||
83 | } | 125 | } |
84 | 126 | ||
85 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude | 127 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude |
86 | for (name, def_id) in &def_map.extern_prelude { | 128 | for (name, def_id) in &def_map.extern_prelude { |
87 | if item == ItemInNs::Types(*def_id) { | 129 | if item == ItemInNs::Types(*def_id) { |
88 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); | 130 | let name = scope_name.unwrap_or_else(|| name.clone()); |
131 | return Some(ModPath::from_segments(PathKind::Plain, vec![name])); | ||
89 | } | 132 | } |
90 | } | 133 | } |
91 | 134 | ||
@@ -138,6 +181,7 @@ fn find_path_inner( | |||
138 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | 181 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), |
139 | from, | 182 | from, |
140 | best_path_len - 1, | 183 | best_path_len - 1, |
184 | prefixed, | ||
141 | ) { | 185 | ) { |
142 | path.segments.push(name); | 186 | path.segments.push(name); |
143 | 187 | ||
@@ -165,6 +209,7 @@ fn find_path_inner( | |||
165 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), | 209 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), |
166 | from, | 210 | from, |
167 | best_path_len - 1, | 211 | best_path_len - 1, |
212 | prefixed, | ||
168 | )?; | 213 | )?; |
169 | path.segments.push(info.path.segments.last().unwrap().clone()); | 214 | path.segments.push(info.path.segments.last().unwrap().clone()); |
170 | Some(path) | 215 | Some(path) |
@@ -181,7 +226,13 @@ fn find_path_inner( | |||
181 | } | 226 | } |
182 | } | 227 | } |
183 | 228 | ||
184 | best_path | 229 | if let Some(prefix) = prefixed.prefix() { |
230 | best_path.or_else(|| { | ||
231 | scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) | ||
232 | }) | ||
233 | } else { | ||
234 | best_path | ||
235 | } | ||
185 | } | 236 | } |
186 | 237 | ||
187 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { | 238 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { |
@@ -304,7 +355,7 @@ mod tests { | |||
304 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 355 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
305 | /// item the `path` refers to returns that same path when called from the | 356 | /// item the `path` refers to returns that same path when called from the |
306 | /// module the cursor is in. | 357 | /// module the cursor is in. |
307 | fn check_found_path(ra_fixture: &str, path: &str) { | 358 | fn check_found_path_(ra_fixture: &str, path: &str, absolute: bool) { |
308 | let (db, pos) = TestDB::with_position(ra_fixture); | 359 | let (db, pos) = TestDB::with_position(ra_fixture); |
309 | let module = db.module_for_file(pos.file_id); | 360 | let module = db.module_for_file(pos.file_id); |
310 | let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); | 361 | let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); |
@@ -324,9 +375,20 @@ mod tests { | |||
324 | .take_types() | 375 | .take_types() |
325 | .unwrap(); | 376 | .unwrap(); |
326 | 377 | ||
327 | let found_path = find_path(&db, ItemInNs::Types(resolved), module); | 378 | let found_path = if absolute { find_path_prefixed } else { find_path }( |
379 | &db, | ||
380 | ItemInNs::Types(resolved), | ||
381 | module, | ||
382 | ); | ||
383 | assert_eq!(found_path, Some(mod_path), "absolute {}", absolute); | ||
384 | } | ||
385 | |||
386 | fn check_found_path(ra_fixture: &str, path: &str) { | ||
387 | check_found_path_(ra_fixture, path, false); | ||
388 | } | ||
328 | 389 | ||
329 | assert_eq!(found_path, Some(mod_path)); | 390 | fn check_found_path_abs(ra_fixture: &str, path: &str) { |
391 | check_found_path_(ra_fixture, path, true); | ||
330 | } | 392 | } |
331 | 393 | ||
332 | #[test] | 394 | #[test] |
@@ -337,6 +399,7 @@ mod tests { | |||
337 | <|> | 399 | <|> |
338 | "#; | 400 | "#; |
339 | check_found_path(code, "S"); | 401 | check_found_path(code, "S"); |
402 | check_found_path_abs(code, "S"); | ||
340 | } | 403 | } |
341 | 404 | ||
342 | #[test] | 405 | #[test] |
@@ -347,6 +410,7 @@ mod tests { | |||
347 | <|> | 410 | <|> |
348 | "#; | 411 | "#; |
349 | check_found_path(code, "E::A"); | 412 | check_found_path(code, "E::A"); |
413 | check_found_path_abs(code, "E::A"); | ||
350 | } | 414 | } |
351 | 415 | ||
352 | #[test] | 416 | #[test] |
@@ -359,6 +423,7 @@ mod tests { | |||
359 | <|> | 423 | <|> |
360 | "#; | 424 | "#; |
361 | check_found_path(code, "foo::S"); | 425 | check_found_path(code, "foo::S"); |
426 | check_found_path_abs(code, "foo::S"); | ||
362 | } | 427 | } |
363 | 428 | ||
364 | #[test] | 429 | #[test] |
@@ -373,6 +438,7 @@ mod tests { | |||
373 | <|> | 438 | <|> |
374 | "#; | 439 | "#; |
375 | check_found_path(code, "super::S"); | 440 | check_found_path(code, "super::S"); |
441 | check_found_path_abs(code, "super::S"); | ||
376 | } | 442 | } |
377 | 443 | ||
378 | #[test] | 444 | #[test] |
@@ -384,6 +450,7 @@ mod tests { | |||
384 | <|> | 450 | <|> |
385 | "#; | 451 | "#; |
386 | check_found_path(code, "self"); | 452 | check_found_path(code, "self"); |
453 | check_found_path_abs(code, "self"); | ||
387 | } | 454 | } |
388 | 455 | ||
389 | #[test] | 456 | #[test] |
@@ -395,6 +462,7 @@ mod tests { | |||
395 | <|> | 462 | <|> |
396 | "#; | 463 | "#; |
397 | check_found_path(code, "crate"); | 464 | check_found_path(code, "crate"); |
465 | check_found_path_abs(code, "crate"); | ||
398 | } | 466 | } |
399 | 467 | ||
400 | #[test] | 468 | #[test] |
@@ -407,6 +475,7 @@ mod tests { | |||
407 | <|> | 475 | <|> |
408 | "#; | 476 | "#; |
409 | check_found_path(code, "crate::S"); | 477 | check_found_path(code, "crate::S"); |
478 | check_found_path_abs(code, "crate::S"); | ||
410 | } | 479 | } |
411 | 480 | ||
412 | #[test] | 481 | #[test] |
@@ -418,6 +487,7 @@ mod tests { | |||
418 | pub struct S; | 487 | pub struct S; |
419 | "#; | 488 | "#; |
420 | check_found_path(code, "std::S"); | 489 | check_found_path(code, "std::S"); |
490 | check_found_path_abs(code, "std::S"); | ||
421 | } | 491 | } |
422 | 492 | ||
423 | #[test] | 493 | #[test] |
@@ -430,14 +500,14 @@ mod tests { | |||
430 | pub struct S; | 500 | pub struct S; |
431 | "#; | 501 | "#; |
432 | check_found_path(code, "std_renamed::S"); | 502 | check_found_path(code, "std_renamed::S"); |
503 | check_found_path_abs(code, "std_renamed::S"); | ||
433 | } | 504 | } |
434 | 505 | ||
435 | #[test] | 506 | #[test] |
436 | fn partially_imported() { | 507 | fn partially_imported() { |
437 | // Tests that short paths are used even for external items, when parts of the path are | 508 | // Tests that short paths are used even for external items, when parts of the path are |
438 | // already in scope. | 509 | // already in scope. |
439 | check_found_path( | 510 | let code = r#" |
440 | r#" | ||
441 | //- /main.rs crate:main deps:syntax | 511 | //- /main.rs crate:main deps:syntax |
442 | 512 | ||
443 | use syntax::ast; | 513 | use syntax::ast; |
@@ -449,12 +519,11 @@ mod tests { | |||
449 | A, B, C, | 519 | A, B, C, |
450 | } | 520 | } |
451 | } | 521 | } |
452 | "#, | 522 | "#; |
453 | "ast::ModuleItem", | 523 | check_found_path(code, "ast::ModuleItem"); |
454 | ); | 524 | check_found_path_abs(code, "syntax::ast::ModuleItem"); |
455 | 525 | ||
456 | check_found_path( | 526 | let code = r#" |
457 | r#" | ||
458 | //- /main.rs crate:main deps:syntax | 527 | //- /main.rs crate:main deps:syntax |
459 | 528 | ||
460 | <|> | 529 | <|> |
@@ -465,9 +534,9 @@ mod tests { | |||
465 | A, B, C, | 534 | A, B, C, |
466 | } | 535 | } |
467 | } | 536 | } |
468 | "#, | 537 | "#; |
469 | "syntax::ast::ModuleItem", | 538 | check_found_path(code, "syntax::ast::ModuleItem"); |
470 | ); | 539 | check_found_path_abs(code, "syntax::ast::ModuleItem"); |
471 | } | 540 | } |
472 | 541 | ||
473 | #[test] | 542 | #[test] |
@@ -481,6 +550,7 @@ mod tests { | |||
481 | <|> | 550 | <|> |
482 | "#; | 551 | "#; |
483 | check_found_path(code, "bar::S"); | 552 | check_found_path(code, "bar::S"); |
553 | check_found_path_abs(code, "bar::S"); | ||
484 | } | 554 | } |
485 | 555 | ||
486 | #[test] | 556 | #[test] |
@@ -494,6 +564,7 @@ mod tests { | |||
494 | <|> | 564 | <|> |
495 | "#; | 565 | "#; |
496 | check_found_path(code, "bar::U"); | 566 | check_found_path(code, "bar::U"); |
567 | check_found_path_abs(code, "bar::U"); | ||
497 | } | 568 | } |
498 | 569 | ||
499 | #[test] | 570 | #[test] |
@@ -507,6 +578,7 @@ mod tests { | |||
507 | pub struct S; | 578 | pub struct S; |
508 | "#; | 579 | "#; |
509 | check_found_path(code, "std::S"); | 580 | check_found_path(code, "std::S"); |
581 | check_found_path_abs(code, "std::S"); | ||
510 | } | 582 | } |
511 | 583 | ||
512 | #[test] | 584 | #[test] |
@@ -520,6 +592,7 @@ mod tests { | |||
520 | pub use prelude::*; | 592 | pub use prelude::*; |
521 | "#; | 593 | "#; |
522 | check_found_path(code, "S"); | 594 | check_found_path(code, "S"); |
595 | check_found_path_abs(code, "S"); | ||
523 | } | 596 | } |
524 | 597 | ||
525 | #[test] | 598 | #[test] |
@@ -537,6 +610,8 @@ mod tests { | |||
537 | "#; | 610 | "#; |
538 | check_found_path(code, "None"); | 611 | check_found_path(code, "None"); |
539 | check_found_path(code, "Some"); | 612 | check_found_path(code, "Some"); |
613 | check_found_path_abs(code, "None"); | ||
614 | check_found_path_abs(code, "Some"); | ||
540 | } | 615 | } |
541 | 616 | ||
542 | #[test] | 617 | #[test] |
@@ -553,6 +628,7 @@ mod tests { | |||
553 | pub use crate::foo::bar::S; | 628 | pub use crate::foo::bar::S; |
554 | "#; | 629 | "#; |
555 | check_found_path(code, "baz::S"); | 630 | check_found_path(code, "baz::S"); |
631 | check_found_path_abs(code, "baz::S"); | ||
556 | } | 632 | } |
557 | 633 | ||
558 | #[test] | 634 | #[test] |
@@ -567,6 +643,7 @@ mod tests { | |||
567 | "#; | 643 | "#; |
568 | // crate::S would be shorter, but using private imports seems wrong | 644 | // crate::S would be shorter, but using private imports seems wrong |
569 | check_found_path(code, "crate::bar::S"); | 645 | check_found_path(code, "crate::bar::S"); |
646 | check_found_path_abs(code, "crate::bar::S"); | ||
570 | } | 647 | } |
571 | 648 | ||
572 | #[test] | 649 | #[test] |
@@ -585,6 +662,7 @@ mod tests { | |||
585 | pub use super::foo; | 662 | pub use super::foo; |
586 | "#; | 663 | "#; |
587 | check_found_path(code, "crate::foo::S"); | 664 | check_found_path(code, "crate::foo::S"); |
665 | check_found_path_abs(code, "crate::foo::S"); | ||
588 | } | 666 | } |
589 | 667 | ||
590 | #[test] | 668 | #[test] |
@@ -605,6 +683,7 @@ mod tests { | |||
605 | } | 683 | } |
606 | "#; | 684 | "#; |
607 | check_found_path(code, "std::sync::Arc"); | 685 | check_found_path(code, "std::sync::Arc"); |
686 | check_found_path_abs(code, "std::sync::Arc"); | ||
608 | } | 687 | } |
609 | 688 | ||
610 | #[test] | 689 | #[test] |
@@ -629,6 +708,7 @@ mod tests { | |||
629 | } | 708 | } |
630 | "#; | 709 | "#; |
631 | check_found_path(code, "core::fmt::Error"); | 710 | check_found_path(code, "core::fmt::Error"); |
711 | check_found_path_abs(code, "core::fmt::Error"); | ||
632 | } | 712 | } |
633 | 713 | ||
634 | #[test] | 714 | #[test] |
@@ -652,6 +732,7 @@ mod tests { | |||
652 | } | 732 | } |
653 | "#; | 733 | "#; |
654 | check_found_path(code, "alloc::sync::Arc"); | 734 | check_found_path(code, "alloc::sync::Arc"); |
735 | check_found_path_abs(code, "alloc::sync::Arc"); | ||
655 | } | 736 | } |
656 | 737 | ||
657 | #[test] | 738 | #[test] |
@@ -669,6 +750,7 @@ mod tests { | |||
669 | pub struct Arc; | 750 | pub struct Arc; |
670 | "#; | 751 | "#; |
671 | check_found_path(code, "megaalloc::Arc"); | 752 | check_found_path(code, "megaalloc::Arc"); |
753 | check_found_path_abs(code, "megaalloc::Arc"); | ||
672 | } | 754 | } |
673 | 755 | ||
674 | #[test] | 756 | #[test] |
@@ -683,5 +765,7 @@ mod tests { | |||
683 | "#; | 765 | "#; |
684 | check_found_path(code, "u8"); | 766 | check_found_path(code, "u8"); |
685 | check_found_path(code, "u16"); | 767 | check_found_path(code, "u16"); |
768 | check_found_path_abs(code, "u8"); | ||
769 | check_found_path_abs(code, "u16"); | ||
686 | } | 770 | } |
687 | } | 771 | } |
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index f1e9dfd5b..12c24e1ca 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -5,10 +5,12 @@ use std::collections::hash_map::Entry; | |||
5 | 5 | ||
6 | use base_db::CrateId; | 6 | use base_db::CrateId; |
7 | use hir_expand::name::Name; | 7 | use hir_expand::name::Name; |
8 | use hir_expand::MacroDefKind; | ||
8 | use once_cell::sync::Lazy; | 9 | use once_cell::sync::Lazy; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
10 | use test_utils::mark; | 11 | use test_utils::mark; |
11 | 12 | ||
13 | use crate::ModuleId; | ||
12 | use crate::{ | 14 | use crate::{ |
13 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, | 15 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
14 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, | 16 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, |
@@ -265,6 +267,26 @@ impl ItemScope { | |||
265 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | 267 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { |
266 | self.legacy_macros.clone() | 268 | self.legacy_macros.clone() |
267 | } | 269 | } |
270 | |||
271 | /// Marks everything that is not a procedural macro as private to `this_module`. | ||
272 | pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { | ||
273 | self.types | ||
274 | .values_mut() | ||
275 | .chain(self.values.values_mut()) | ||
276 | .map(|(_, v)| v) | ||
277 | .chain(self.unnamed_trait_imports.values_mut()) | ||
278 | .for_each(|vis| *vis = Visibility::Module(this_module)); | ||
279 | |||
280 | for (mac, vis) in self.macros.values_mut() { | ||
281 | if let MacroDefKind::ProcMacro(_) = mac.kind { | ||
282 | // FIXME: Technically this is insufficient since reexports of proc macros are also | ||
283 | // forbidden. Practically nobody does that. | ||
284 | continue; | ||
285 | } | ||
286 | |||
287 | *vis = Visibility::Module(this_module); | ||
288 | } | ||
289 | } | ||
268 | } | 290 | } |
269 | 291 | ||
270 | impl PerNs { | 292 | impl PerNs { |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 52abb8e7f..0fd91b9d0 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -291,7 +291,6 @@ pub enum AttrOwner { | |||
291 | 291 | ||
292 | Variant(Idx<Variant>), | 292 | Variant(Idx<Variant>), |
293 | Field(Idx<Field>), | 293 | Field(Idx<Field>), |
294 | // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`. | ||
295 | } | 294 | } |
296 | 295 | ||
297 | macro_rules! from_attrs { | 296 | macro_rules! from_attrs { |
@@ -483,11 +482,16 @@ pub struct Import { | |||
483 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many | 482 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many |
484 | /// `Import`s can map to the same `use` item. | 483 | /// `Import`s can map to the same `use` item. |
485 | pub ast_id: FileAstId<ast::Use>, | 484 | pub ast_id: FileAstId<ast::Use>, |
485 | /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`. | ||
486 | /// | ||
487 | /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting | ||
488 | /// precise diagnostics. | ||
489 | pub index: usize, | ||
486 | } | 490 | } |
487 | 491 | ||
488 | #[derive(Debug, Clone, Eq, PartialEq)] | 492 | #[derive(Debug, Clone, Eq, PartialEq)] |
489 | pub struct ExternCrate { | 493 | pub struct ExternCrate { |
490 | pub path: ModPath, | 494 | pub name: Name, |
491 | pub alias: Option<ImportAlias>, | 495 | pub alias: Option<ImportAlias>, |
492 | pub visibility: RawVisibilityId, | 496 | pub visibility: RawVisibilityId, |
493 | /// Whether this is a `#[macro_use] extern crate ...`. | 497 | /// Whether this is a `#[macro_use] extern crate ...`. |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index d93377c3b..54814f141 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -483,7 +483,7 @@ impl Ctx { | |||
483 | ModPath::expand_use_item( | 483 | ModPath::expand_use_item( |
484 | InFile::new(self.file, use_item.clone()), | 484 | InFile::new(self.file, use_item.clone()), |
485 | &self.hygiene, | 485 | &self.hygiene, |
486 | |path, _tree, is_glob, alias| { | 486 | |path, _use_tree, is_glob, alias| { |
487 | imports.push(id(tree.imports.alloc(Import { | 487 | imports.push(id(tree.imports.alloc(Import { |
488 | path, | 488 | path, |
489 | alias, | 489 | alias, |
@@ -491,6 +491,7 @@ impl Ctx { | |||
491 | is_glob, | 491 | is_glob, |
492 | is_prelude, | 492 | is_prelude, |
493 | ast_id, | 493 | ast_id, |
494 | index: imports.len(), | ||
494 | }))); | 495 | }))); |
495 | }, | 496 | }, |
496 | ); | 497 | ); |
@@ -502,7 +503,7 @@ impl Ctx { | |||
502 | &mut self, | 503 | &mut self, |
503 | extern_crate: &ast::ExternCrate, | 504 | extern_crate: &ast::ExternCrate, |
504 | ) -> Option<FileItemTreeId<ExternCrate>> { | 505 | ) -> Option<FileItemTreeId<ExternCrate>> { |
505 | let path = ModPath::from_name_ref(&extern_crate.name_ref()?); | 506 | let name = extern_crate.name_ref()?.as_name(); |
506 | let alias = extern_crate.rename().map(|a| { | 507 | let alias = extern_crate.rename().map(|a| { |
507 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) | 508 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) |
508 | }); | 509 | }); |
@@ -511,7 +512,7 @@ impl Ctx { | |||
511 | // FIXME: cfg_attr | 512 | // FIXME: cfg_attr |
512 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | 513 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); |
513 | 514 | ||
514 | let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; | 515 | let res = ExternCrate { name, alias, visibility, is_macro_use, ast_id }; |
515 | Some(id(self.data().extern_crates.alloc(res))) | 516 | Some(id(self.data().extern_crates.alloc(res))) |
516 | } | 517 | } |
517 | 518 | ||
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index eed3d0d6f..1a806cda5 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -228,11 +228,11 @@ fn smoke() { | |||
228 | 228 | ||
229 | top-level items: | 229 | top-level items: |
230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | 230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] |
231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } | 231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 0 } |
232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | 232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] |
233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } | 233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 1 } |
234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] | 234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] |
235 | ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } | 235 | ExternCrate { name: Name(Text("krate")), alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } |
236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] | 236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] |
237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } | 237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } |
238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] | 238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index bf302172d..5e4d73c1f 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -288,31 +288,70 @@ pub enum ModuleSource { | |||
288 | 288 | ||
289 | mod diagnostics { | 289 | mod diagnostics { |
290 | use hir_expand::diagnostics::DiagnosticSink; | 290 | use hir_expand::diagnostics::DiagnosticSink; |
291 | use hir_expand::hygiene::Hygiene; | ||
292 | use hir_expand::InFile; | ||
291 | use syntax::{ast, AstPtr}; | 293 | use syntax::{ast, AstPtr}; |
292 | 294 | ||
293 | use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; | 295 | use crate::path::ModPath; |
296 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | ||
294 | 297 | ||
295 | #[derive(Debug, PartialEq, Eq)] | 298 | #[derive(Debug, PartialEq, Eq)] |
296 | pub(super) enum DefDiagnostic { | 299 | enum DiagnosticKind { |
297 | UnresolvedModule { | 300 | UnresolvedModule { declaration: AstId<ast::Module>, candidate: String }, |
298 | module: LocalModuleId, | 301 | |
299 | declaration: AstId<ast::Module>, | 302 | UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, |
300 | candidate: String, | 303 | |
301 | }, | 304 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
305 | } | ||
306 | |||
307 | #[derive(Debug, PartialEq, Eq)] | ||
308 | pub(super) struct DefDiagnostic { | ||
309 | in_module: LocalModuleId, | ||
310 | kind: DiagnosticKind, | ||
302 | } | 311 | } |
303 | 312 | ||
304 | impl DefDiagnostic { | 313 | impl DefDiagnostic { |
314 | pub(super) fn unresolved_module( | ||
315 | container: LocalModuleId, | ||
316 | declaration: AstId<ast::Module>, | ||
317 | candidate: String, | ||
318 | ) -> Self { | ||
319 | Self { | ||
320 | in_module: container, | ||
321 | kind: DiagnosticKind::UnresolvedModule { declaration, candidate }, | ||
322 | } | ||
323 | } | ||
324 | |||
325 | pub(super) fn unresolved_extern_crate( | ||
326 | container: LocalModuleId, | ||
327 | declaration: AstId<ast::ExternCrate>, | ||
328 | ) -> Self { | ||
329 | Self { | ||
330 | in_module: container, | ||
331 | kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration }, | ||
332 | } | ||
333 | } | ||
334 | |||
335 | pub(super) fn unresolved_import( | ||
336 | container: LocalModuleId, | ||
337 | ast: AstId<ast::Use>, | ||
338 | index: usize, | ||
339 | ) -> Self { | ||
340 | Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } | ||
341 | } | ||
342 | |||
305 | pub(super) fn add_to( | 343 | pub(super) fn add_to( |
306 | &self, | 344 | &self, |
307 | db: &dyn DefDatabase, | 345 | db: &dyn DefDatabase, |
308 | target_module: LocalModuleId, | 346 | target_module: LocalModuleId, |
309 | sink: &mut DiagnosticSink, | 347 | sink: &mut DiagnosticSink, |
310 | ) { | 348 | ) { |
311 | match self { | 349 | if self.in_module != target_module { |
312 | DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { | 350 | return; |
313 | if *module != target_module { | 351 | } |
314 | return; | 352 | |
315 | } | 353 | match &self.kind { |
354 | DiagnosticKind::UnresolvedModule { declaration, candidate } => { | ||
316 | let decl = declaration.to_node(db.upcast()); | 355 | let decl = declaration.to_node(db.upcast()); |
317 | sink.push(UnresolvedModule { | 356 | sink.push(UnresolvedModule { |
318 | file: declaration.file_id, | 357 | file: declaration.file_id, |
@@ -320,6 +359,36 @@ mod diagnostics { | |||
320 | candidate: candidate.clone(), | 359 | candidate: candidate.clone(), |
321 | }) | 360 | }) |
322 | } | 361 | } |
362 | |||
363 | DiagnosticKind::UnresolvedExternCrate { ast } => { | ||
364 | let item = ast.to_node(db.upcast()); | ||
365 | sink.push(UnresolvedExternCrate { | ||
366 | file: ast.file_id, | ||
367 | item: AstPtr::new(&item), | ||
368 | }); | ||
369 | } | ||
370 | |||
371 | DiagnosticKind::UnresolvedImport { ast, index } => { | ||
372 | let use_item = ast.to_node(db.upcast()); | ||
373 | let hygiene = Hygiene::new(db.upcast(), ast.file_id); | ||
374 | let mut cur = 0; | ||
375 | let mut tree = None; | ||
376 | ModPath::expand_use_item( | ||
377 | InFile::new(ast.file_id, use_item), | ||
378 | &hygiene, | ||
379 | |_mod_path, use_tree, _is_glob, _alias| { | ||
380 | if cur == *index { | ||
381 | tree = Some(use_tree.clone()); | ||
382 | } | ||
383 | |||
384 | cur += 1; | ||
385 | }, | ||
386 | ); | ||
387 | |||
388 | if let Some(tree) = tree { | ||
389 | sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); | ||
390 | } | ||
391 | } | ||
323 | } | 392 | } |
324 | } | 393 | } |
325 | } | 394 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 3e99c8773..100e25ffc 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -3,8 +3,11 @@ | |||
3 | //! `DefCollector::collect` contains the fixed-point iteration loop which | 3 | //! `DefCollector::collect` contains the fixed-point iteration loop which |
4 | //! resolves imports and expands macros. | 4 | //! resolves imports and expands macros. |
5 | 5 | ||
6 | use std::iter; | ||
7 | |||
6 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
7 | use cfg::CfgOptions; | 9 | use cfg::CfgOptions; |
10 | use hir_expand::InFile; | ||
8 | use hir_expand::{ | 11 | use hir_expand::{ |
9 | ast_id_map::FileAstId, | 12 | ast_id_map::FileAstId, |
10 | builtin_derive::find_builtin_derive, | 13 | builtin_derive::find_builtin_derive, |
@@ -13,17 +16,16 @@ use hir_expand::{ | |||
13 | proc_macro::ProcMacroExpander, | 16 | proc_macro::ProcMacroExpander, |
14 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 17 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
15 | }; | 18 | }; |
16 | use rustc_hash::FxHashMap; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
17 | use syntax::ast; | 20 | use syntax::ast; |
18 | use test_utils::mark; | 21 | use test_utils::mark; |
22 | use tt::{Leaf, TokenTree}; | ||
19 | 23 | ||
20 | use crate::{ | 24 | use crate::{ |
21 | attr::Attrs, | 25 | attr::Attrs, |
22 | db::DefDatabase, | 26 | db::DefDatabase, |
23 | item_scope::{ImportType, PerNsGlobImports}, | 27 | item_scope::{ImportType, PerNsGlobImports}, |
24 | item_tree::{ | 28 | item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind}, |
25 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind, | ||
26 | }, | ||
27 | nameres::{ | 29 | nameres::{ |
28 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | 30 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, |
29 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, | 31 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, |
@@ -85,6 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr | |||
85 | mod_dirs: FxHashMap::default(), | 87 | mod_dirs: FxHashMap::default(), |
86 | cfg_options, | 88 | cfg_options, |
87 | proc_macros, | 89 | proc_macros, |
90 | exports_proc_macros: false, | ||
88 | from_glob_import: Default::default(), | 91 | from_glob_import: Default::default(), |
89 | }; | 92 | }; |
90 | collector.collect(); | 93 | collector.collect(); |
@@ -112,6 +115,12 @@ impl PartialResolvedImport { | |||
112 | } | 115 | } |
113 | 116 | ||
114 | #[derive(Clone, Debug, Eq, PartialEq)] | 117 | #[derive(Clone, Debug, Eq, PartialEq)] |
118 | enum ImportSource { | ||
119 | Import(ItemTreeId<item_tree::Import>), | ||
120 | ExternCrate(ItemTreeId<item_tree::ExternCrate>), | ||
121 | } | ||
122 | |||
123 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
115 | struct Import { | 124 | struct Import { |
116 | pub path: ModPath, | 125 | pub path: ModPath, |
117 | pub alias: Option<ImportAlias>, | 126 | pub alias: Option<ImportAlias>, |
@@ -120,11 +129,12 @@ struct Import { | |||
120 | pub is_prelude: bool, | 129 | pub is_prelude: bool, |
121 | pub is_extern_crate: bool, | 130 | pub is_extern_crate: bool, |
122 | pub is_macro_use: bool, | 131 | pub is_macro_use: bool, |
132 | source: ImportSource, | ||
123 | } | 133 | } |
124 | 134 | ||
125 | impl Import { | 135 | impl Import { |
126 | fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { | 136 | fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self { |
127 | let it = &tree[id]; | 137 | let it = &tree[id.value]; |
128 | let visibility = &tree[it.visibility]; | 138 | let visibility = &tree[it.visibility]; |
129 | Self { | 139 | Self { |
130 | path: it.path.clone(), | 140 | path: it.path.clone(), |
@@ -134,20 +144,22 @@ impl Import { | |||
134 | is_prelude: it.is_prelude, | 144 | is_prelude: it.is_prelude, |
135 | is_extern_crate: false, | 145 | is_extern_crate: false, |
136 | is_macro_use: false, | 146 | is_macro_use: false, |
147 | source: ImportSource::Import(id), | ||
137 | } | 148 | } |
138 | } | 149 | } |
139 | 150 | ||
140 | fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { | 151 | fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self { |
141 | let it = &tree[id]; | 152 | let it = &tree[id.value]; |
142 | let visibility = &tree[it.visibility]; | 153 | let visibility = &tree[it.visibility]; |
143 | Self { | 154 | Self { |
144 | path: it.path.clone(), | 155 | path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), |
145 | alias: it.alias.clone(), | 156 | alias: it.alias.clone(), |
146 | visibility: visibility.clone(), | 157 | visibility: visibility.clone(), |
147 | is_glob: false, | 158 | is_glob: false, |
148 | is_prelude: false, | 159 | is_prelude: false, |
149 | is_extern_crate: true, | 160 | is_extern_crate: true, |
150 | is_macro_use: it.is_macro_use, | 161 | is_macro_use: it.is_macro_use, |
162 | source: ImportSource::ExternCrate(id), | ||
151 | } | 163 | } |
152 | } | 164 | } |
153 | } | 165 | } |
@@ -191,7 +203,12 @@ struct DefCollector<'a> { | |||
191 | unexpanded_attribute_macros: Vec<DeriveDirective>, | 203 | unexpanded_attribute_macros: Vec<DeriveDirective>, |
192 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 204 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
193 | cfg_options: &'a CfgOptions, | 205 | cfg_options: &'a CfgOptions, |
206 | /// List of procedural macros defined by this crate. This is read from the dynamic library | ||
207 | /// built by the build system, and is the list of proc. macros we can actually expand. It is | ||
208 | /// empty when proc. macro support is disabled (in which case we still do name resolution for | ||
209 | /// them). | ||
194 | proc_macros: Vec<(Name, ProcMacroExpander)>, | 210 | proc_macros: Vec<(Name, ProcMacroExpander)>, |
211 | exports_proc_macros: bool, | ||
195 | from_glob_import: PerNsGlobImports, | 212 | from_glob_import: PerNsGlobImports, |
196 | } | 213 | } |
197 | 214 | ||
@@ -245,28 +262,61 @@ impl DefCollector<'_> { | |||
245 | 262 | ||
246 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | 263 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
247 | // show unresolved imports in completion, etc | 264 | // show unresolved imports in completion, etc |
248 | for directive in unresolved_imports { | 265 | for directive in &unresolved_imports { |
249 | self.record_resolved_import(&directive) | 266 | self.record_resolved_import(directive) |
267 | } | ||
268 | self.unresolved_imports = unresolved_imports; | ||
269 | |||
270 | // FIXME: This condition should instead check if this is a `proc-macro` type crate. | ||
271 | if self.exports_proc_macros { | ||
272 | // A crate exporting procedural macros is not allowed to export anything else. | ||
273 | // | ||
274 | // Additionally, while the proc macro entry points must be `pub`, they are not publicly | ||
275 | // exported in type/value namespace. This function reduces the visibility of all items | ||
276 | // in the crate root that aren't proc macros. | ||
277 | let root = self.def_map.root; | ||
278 | let root = &mut self.def_map.modules[root]; | ||
279 | root.scope.censor_non_proc_macros(ModuleId { | ||
280 | krate: self.def_map.krate, | ||
281 | local_id: self.def_map.root, | ||
282 | }); | ||
250 | } | 283 | } |
251 | |||
252 | // Record proc-macros | ||
253 | self.collect_proc_macro(); | ||
254 | } | 284 | } |
255 | 285 | ||
256 | fn collect_proc_macro(&mut self) { | 286 | /// Adds a definition of procedural macro `name` to the root module. |
257 | let proc_macros = std::mem::take(&mut self.proc_macros); | 287 | /// |
258 | for (name, expander) in proc_macros { | 288 | /// # Notes on procedural macro resolution |
259 | let krate = self.def_map.krate; | 289 | /// |
260 | 290 | /// Procedural macro functionality is provided by the build system: It has to build the proc | |
261 | let macro_id = MacroDefId { | 291 | /// macro and pass the resulting dynamic library to rust-analyzer. |
292 | /// | ||
293 | /// When procedural macro support is enabled, the list of proc macros exported by a crate is | ||
294 | /// known before we resolve names in the crate. This list is stored in `self.proc_macros` and is | ||
295 | /// derived from the dynamic library. | ||
296 | /// | ||
297 | /// However, we *also* would like to be able to at least *resolve* macros on our own, without | ||
298 | /// help by the build system. So, when the macro isn't found in `self.proc_macros`, we instead | ||
299 | /// use a dummy expander that always errors. This comes with the drawback of macros potentially | ||
300 | /// going out of sync with what the build system sees (since we resolve using VFS state, but | ||
301 | /// Cargo builds only on-disk files). We could and probably should add diagnostics for that. | ||
302 | fn resolve_proc_macro(&mut self, name: &Name) { | ||
303 | self.exports_proc_macros = true; | ||
304 | let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) { | ||
305 | Some((_, expander)) => MacroDefId { | ||
262 | ast_id: None, | 306 | ast_id: None, |
263 | krate: Some(krate), | 307 | krate: Some(self.def_map.krate), |
264 | kind: MacroDefKind::CustomDerive(expander), | 308 | kind: MacroDefKind::ProcMacro(*expander), |
265 | local_inner: false, | 309 | local_inner: false, |
266 | }; | 310 | }, |
311 | None => MacroDefId { | ||
312 | ast_id: None, | ||
313 | krate: Some(self.def_map.krate), | ||
314 | kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)), | ||
315 | local_inner: false, | ||
316 | }, | ||
317 | }; | ||
267 | 318 | ||
268 | self.define_proc_macro(name.clone(), macro_id); | 319 | self.define_proc_macro(name.clone(), macro_def); |
269 | } | ||
270 | } | 320 | } |
271 | 321 | ||
272 | /// Define a macro with `macro_rules`. | 322 | /// Define a macro with `macro_rules`. |
@@ -346,20 +396,15 @@ impl DefCollector<'_> { | |||
346 | fn import_macros_from_extern_crate( | 396 | fn import_macros_from_extern_crate( |
347 | &mut self, | 397 | &mut self, |
348 | current_module_id: LocalModuleId, | 398 | current_module_id: LocalModuleId, |
349 | import: &item_tree::ExternCrate, | 399 | extern_crate: &item_tree::ExternCrate, |
350 | ) { | 400 | ) { |
351 | log::debug!( | 401 | log::debug!( |
352 | "importing macros from extern crate: {:?} ({:?})", | 402 | "importing macros from extern crate: {:?} ({:?})", |
353 | import, | 403 | extern_crate, |
354 | self.def_map.edition, | 404 | self.def_map.edition, |
355 | ); | 405 | ); |
356 | 406 | ||
357 | let res = self.def_map.resolve_name_in_extern_prelude( | 407 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); |
358 | &import | ||
359 | .path | ||
360 | .as_ident() | ||
361 | .expect("extern crate should have been desugared to one-element path"), | ||
362 | ); | ||
363 | 408 | ||
364 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 409 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
365 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | 410 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
@@ -420,7 +465,11 @@ impl DefCollector<'_> { | |||
420 | .as_ident() | 465 | .as_ident() |
421 | .expect("extern crate should have been desugared to one-element path"), | 466 | .expect("extern crate should have been desugared to one-element path"), |
422 | ); | 467 | ); |
423 | PartialResolvedImport::Resolved(res) | 468 | if res.is_none() { |
469 | PartialResolvedImport::Unresolved | ||
470 | } else { | ||
471 | PartialResolvedImport::Resolved(res) | ||
472 | } | ||
424 | } else { | 473 | } else { |
425 | let res = self.def_map.resolve_path_fp_with_macro( | 474 | let res = self.def_map.resolve_path_fp_with_macro( |
426 | self.db, | 475 | self.db, |
@@ -774,7 +823,51 @@ impl DefCollector<'_> { | |||
774 | .collect(item_tree.top_level_items()); | 823 | .collect(item_tree.top_level_items()); |
775 | } | 824 | } |
776 | 825 | ||
777 | fn finish(self) -> CrateDefMap { | 826 | fn finish(mut self) -> CrateDefMap { |
827 | // Emit diagnostics for all remaining unresolved imports. | ||
828 | |||
829 | // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't | ||
830 | // resolve. We first emit diagnostics for unresolved extern crates and collect the missing | ||
831 | // crate names. Then we emit diagnostics for unresolved imports, but only if the import | ||
832 | // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a | ||
833 | // heuristic, but it works in practice. | ||
834 | let mut diagnosed_extern_crates = FxHashSet::default(); | ||
835 | for directive in &self.unresolved_imports { | ||
836 | if let ImportSource::ExternCrate(krate) = directive.import.source { | ||
837 | let item_tree = self.db.item_tree(krate.file_id); | ||
838 | let extern_crate = &item_tree[krate.value]; | ||
839 | |||
840 | diagnosed_extern_crates.insert(extern_crate.name.clone()); | ||
841 | |||
842 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( | ||
843 | directive.module_id, | ||
844 | InFile::new(krate.file_id, extern_crate.ast_id), | ||
845 | )); | ||
846 | } | ||
847 | } | ||
848 | |||
849 | for directive in &self.unresolved_imports { | ||
850 | if let ImportSource::Import(import) = &directive.import.source { | ||
851 | let item_tree = self.db.item_tree(import.file_id); | ||
852 | let import_data = &item_tree[import.value]; | ||
853 | |||
854 | match (import_data.path.segments.first(), &import_data.path.kind) { | ||
855 | (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { | ||
856 | if diagnosed_extern_crates.contains(krate) { | ||
857 | continue; | ||
858 | } | ||
859 | } | ||
860 | _ => {} | ||
861 | } | ||
862 | |||
863 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( | ||
864 | directive.module_id, | ||
865 | InFile::new(import.file_id, import_data.ast_id), | ||
866 | import_data.index, | ||
867 | )); | ||
868 | } | ||
869 | } | ||
870 | |||
778 | self.def_map | 871 | self.def_map |
779 | } | 872 | } |
780 | } | 873 | } |
@@ -819,178 +912,186 @@ impl ModCollector<'_, '_> { | |||
819 | 912 | ||
820 | for &item in items { | 913 | for &item in items { |
821 | let attrs = self.item_tree.attrs(item.into()); | 914 | let attrs = self.item_tree.attrs(item.into()); |
822 | if self.is_cfg_enabled(attrs) { | 915 | if !self.is_cfg_enabled(attrs) { |
823 | let module = | 916 | continue; |
824 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | 917 | } |
825 | let container = ContainerId::ModuleId(module); | 918 | let module = |
826 | 919 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | |
827 | let mut def = None; | 920 | let container = ContainerId::ModuleId(module); |
828 | match item { | 921 | |
829 | ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), | 922 | let mut def = None; |
830 | ModItem::Import(import_id) => { | 923 | match item { |
831 | self.def_collector.unresolved_imports.push(ImportDirective { | 924 | ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), |
832 | module_id: self.module_id, | 925 | ModItem::Import(import_id) => { |
833 | import: Import::from_use(&self.item_tree, import_id), | 926 | self.def_collector.unresolved_imports.push(ImportDirective { |
834 | status: PartialResolvedImport::Unresolved, | 927 | module_id: self.module_id, |
835 | }) | 928 | import: Import::from_use( |
836 | } | 929 | &self.item_tree, |
837 | ModItem::ExternCrate(import_id) => { | 930 | InFile::new(self.file_id, import_id), |
838 | self.def_collector.unresolved_imports.push(ImportDirective { | 931 | ), |
839 | module_id: self.module_id, | 932 | status: PartialResolvedImport::Unresolved, |
840 | import: Import::from_extern_crate(&self.item_tree, import_id), | 933 | }) |
841 | status: PartialResolvedImport::Unresolved, | 934 | } |
842 | }) | 935 | ModItem::ExternCrate(import_id) => { |
843 | } | 936 | self.def_collector.unresolved_imports.push(ImportDirective { |
844 | ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), | 937 | module_id: self.module_id, |
845 | ModItem::Impl(imp) => { | 938 | import: Import::from_extern_crate( |
846 | let module = ModuleId { | 939 | &self.item_tree, |
847 | krate: self.def_collector.def_map.krate, | 940 | InFile::new(self.file_id, import_id), |
848 | local_id: self.module_id, | 941 | ), |
849 | }; | 942 | status: PartialResolvedImport::Unresolved, |
850 | let container = ContainerId::ModuleId(module); | 943 | }) |
851 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } | 944 | } |
852 | .intern(self.def_collector.db); | 945 | ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), |
853 | self.def_collector.def_map.modules[self.module_id] | 946 | ModItem::Impl(imp) => { |
854 | .scope | 947 | let module = ModuleId { |
855 | .define_impl(impl_id) | 948 | krate: self.def_collector.def_map.krate, |
856 | } | 949 | local_id: self.module_id, |
857 | ModItem::Function(id) => { | 950 | }; |
858 | let func = &self.item_tree[id]; | 951 | let container = ContainerId::ModuleId(module); |
859 | def = Some(DefData { | 952 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } |
860 | id: FunctionLoc { | 953 | .intern(self.def_collector.db); |
861 | container: container.into(), | 954 | self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) |
862 | id: ItemTreeId::new(self.file_id, id), | 955 | } |
863 | } | 956 | ModItem::Function(id) => { |
864 | .intern(self.def_collector.db) | 957 | let func = &self.item_tree[id]; |
865 | .into(), | ||
866 | name: &func.name, | ||
867 | visibility: &self.item_tree[func.visibility], | ||
868 | has_constructor: false, | ||
869 | }); | ||
870 | } | ||
871 | ModItem::Struct(id) => { | ||
872 | let it = &self.item_tree[id]; | ||
873 | 958 | ||
874 | // FIXME: check attrs to see if this is an attribute macro invocation; | 959 | self.collect_proc_macro_def(&func.name, attrs); |
875 | // in which case we don't add the invocation, just a single attribute | ||
876 | // macro invocation | ||
877 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
878 | 960 | ||
879 | def = Some(DefData { | 961 | def = Some(DefData { |
880 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } | 962 | id: FunctionLoc { |
881 | .intern(self.def_collector.db) | 963 | container: container.into(), |
882 | .into(), | 964 | id: ItemTreeId::new(self.file_id, id), |
883 | name: &it.name, | 965 | } |
884 | visibility: &self.item_tree[it.visibility], | 966 | .intern(self.def_collector.db) |
885 | has_constructor: it.kind != StructDefKind::Record, | 967 | .into(), |
886 | }); | 968 | name: &func.name, |
887 | } | 969 | visibility: &self.item_tree[func.visibility], |
888 | ModItem::Union(id) => { | 970 | has_constructor: false, |
889 | let it = &self.item_tree[id]; | 971 | }); |
972 | } | ||
973 | ModItem::Struct(id) => { | ||
974 | let it = &self.item_tree[id]; | ||
890 | 975 | ||
891 | // FIXME: check attrs to see if this is an attribute macro invocation; | 976 | // FIXME: check attrs to see if this is an attribute macro invocation; |
892 | // in which case we don't add the invocation, just a single attribute | 977 | // in which case we don't add the invocation, just a single attribute |
893 | // macro invocation | 978 | // macro invocation |
894 | self.collect_derives(attrs, it.ast_id.upcast()); | 979 | self.collect_derives(attrs, it.ast_id.upcast()); |
895 | 980 | ||
896 | def = Some(DefData { | 981 | def = Some(DefData { |
897 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } | 982 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } |
898 | .intern(self.def_collector.db) | 983 | .intern(self.def_collector.db) |
899 | .into(), | 984 | .into(), |
900 | name: &it.name, | 985 | name: &it.name, |
901 | visibility: &self.item_tree[it.visibility], | 986 | visibility: &self.item_tree[it.visibility], |
902 | has_constructor: false, | 987 | has_constructor: it.kind != StructDefKind::Record, |
903 | }); | 988 | }); |
904 | } | 989 | } |
905 | ModItem::Enum(id) => { | 990 | ModItem::Union(id) => { |
906 | let it = &self.item_tree[id]; | 991 | let it = &self.item_tree[id]; |
907 | 992 | ||
908 | // FIXME: check attrs to see if this is an attribute macro invocation; | 993 | // FIXME: check attrs to see if this is an attribute macro invocation; |
909 | // in which case we don't add the invocation, just a single attribute | 994 | // in which case we don't add the invocation, just a single attribute |
910 | // macro invocation | 995 | // macro invocation |
911 | self.collect_derives(attrs, it.ast_id.upcast()); | 996 | self.collect_derives(attrs, it.ast_id.upcast()); |
912 | 997 | ||
913 | def = Some(DefData { | 998 | def = Some(DefData { |
914 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } | 999 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } |
915 | .intern(self.def_collector.db) | 1000 | .intern(self.def_collector.db) |
916 | .into(), | 1001 | .into(), |
917 | name: &it.name, | 1002 | name: &it.name, |
918 | visibility: &self.item_tree[it.visibility], | 1003 | visibility: &self.item_tree[it.visibility], |
919 | has_constructor: false, | 1004 | has_constructor: false, |
920 | }); | 1005 | }); |
921 | } | 1006 | } |
922 | ModItem::Const(id) => { | 1007 | ModItem::Enum(id) => { |
923 | let it = &self.item_tree[id]; | 1008 | let it = &self.item_tree[id]; |
924 | |||
925 | if let Some(name) = &it.name { | ||
926 | def = Some(DefData { | ||
927 | id: ConstLoc { | ||
928 | container: container.into(), | ||
929 | id: ItemTreeId::new(self.file_id, id), | ||
930 | } | ||
931 | .intern(self.def_collector.db) | ||
932 | .into(), | ||
933 | name, | ||
934 | visibility: &self.item_tree[it.visibility], | ||
935 | has_constructor: false, | ||
936 | }); | ||
937 | } | ||
938 | } | ||
939 | ModItem::Static(id) => { | ||
940 | let it = &self.item_tree[id]; | ||
941 | 1009 | ||
942 | def = Some(DefData { | 1010 | // FIXME: check attrs to see if this is an attribute macro invocation; |
943 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1011 | // in which case we don't add the invocation, just a single attribute |
944 | .intern(self.def_collector.db) | 1012 | // macro invocation |
945 | .into(), | 1013 | self.collect_derives(attrs, it.ast_id.upcast()); |
946 | name: &it.name, | ||
947 | visibility: &self.item_tree[it.visibility], | ||
948 | has_constructor: false, | ||
949 | }); | ||
950 | } | ||
951 | ModItem::Trait(id) => { | ||
952 | let it = &self.item_tree[id]; | ||
953 | 1014 | ||
954 | def = Some(DefData { | 1015 | def = Some(DefData { |
955 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1016 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } |
956 | .intern(self.def_collector.db) | 1017 | .intern(self.def_collector.db) |
957 | .into(), | 1018 | .into(), |
958 | name: &it.name, | 1019 | name: &it.name, |
959 | visibility: &self.item_tree[it.visibility], | 1020 | visibility: &self.item_tree[it.visibility], |
960 | has_constructor: false, | 1021 | has_constructor: false, |
961 | }); | 1022 | }); |
962 | } | 1023 | } |
963 | ModItem::TypeAlias(id) => { | 1024 | ModItem::Const(id) => { |
964 | let it = &self.item_tree[id]; | 1025 | let it = &self.item_tree[id]; |
965 | 1026 | ||
1027 | if let Some(name) = &it.name { | ||
966 | def = Some(DefData { | 1028 | def = Some(DefData { |
967 | id: TypeAliasLoc { | 1029 | id: ConstLoc { |
968 | container: container.into(), | 1030 | container: container.into(), |
969 | id: ItemTreeId::new(self.file_id, id), | 1031 | id: ItemTreeId::new(self.file_id, id), |
970 | } | 1032 | } |
971 | .intern(self.def_collector.db) | 1033 | .intern(self.def_collector.db) |
972 | .into(), | 1034 | .into(), |
973 | name: &it.name, | 1035 | name, |
974 | visibility: &self.item_tree[it.visibility], | 1036 | visibility: &self.item_tree[it.visibility], |
975 | has_constructor: false, | 1037 | has_constructor: false, |
976 | }); | 1038 | }); |
977 | } | 1039 | } |
978 | } | 1040 | } |
1041 | ModItem::Static(id) => { | ||
1042 | let it = &self.item_tree[id]; | ||
979 | 1043 | ||
980 | if let Some(DefData { id, name, visibility, has_constructor }) = def { | 1044 | def = Some(DefData { |
981 | self.def_collector.def_map.modules[self.module_id].scope.define_def(id); | 1045 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } |
982 | let vis = self | 1046 | .intern(self.def_collector.db) |
983 | .def_collector | 1047 | .into(), |
984 | .def_map | 1048 | name: &it.name, |
985 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | 1049 | visibility: &self.item_tree[it.visibility], |
986 | .unwrap_or(Visibility::Public); | 1050 | has_constructor: false, |
987 | self.def_collector.update( | 1051 | }); |
988 | self.module_id, | 1052 | } |
989 | &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], | 1053 | ModItem::Trait(id) => { |
990 | vis, | 1054 | let it = &self.item_tree[id]; |
991 | ImportType::Named, | 1055 | |
992 | ) | 1056 | def = Some(DefData { |
1057 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
1058 | .intern(self.def_collector.db) | ||
1059 | .into(), | ||
1060 | name: &it.name, | ||
1061 | visibility: &self.item_tree[it.visibility], | ||
1062 | has_constructor: false, | ||
1063 | }); | ||
993 | } | 1064 | } |
1065 | ModItem::TypeAlias(id) => { | ||
1066 | let it = &self.item_tree[id]; | ||
1067 | |||
1068 | def = Some(DefData { | ||
1069 | id: TypeAliasLoc { | ||
1070 | container: container.into(), | ||
1071 | id: ItemTreeId::new(self.file_id, id), | ||
1072 | } | ||
1073 | .intern(self.def_collector.db) | ||
1074 | .into(), | ||
1075 | name: &it.name, | ||
1076 | visibility: &self.item_tree[it.visibility], | ||
1077 | has_constructor: false, | ||
1078 | }); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | if let Some(DefData { id, name, visibility, has_constructor }) = def { | ||
1083 | self.def_collector.def_map.modules[self.module_id].scope.define_def(id); | ||
1084 | let vis = self | ||
1085 | .def_collector | ||
1086 | .def_map | ||
1087 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | ||
1088 | .unwrap_or(Visibility::Public); | ||
1089 | self.def_collector.update( | ||
1090 | self.module_id, | ||
1091 | &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], | ||
1092 | vis, | ||
1093 | ImportType::Named, | ||
1094 | ) | ||
994 | } | 1095 | } |
995 | } | 1096 | } |
996 | } | 1097 | } |
@@ -1051,13 +1152,11 @@ impl ModCollector<'_, '_> { | |||
1051 | self.import_all_legacy_macros(module_id); | 1152 | self.import_all_legacy_macros(module_id); |
1052 | } | 1153 | } |
1053 | } | 1154 | } |
1054 | Err(candidate) => self.def_collector.def_map.diagnostics.push( | 1155 | Err(candidate) => { |
1055 | DefDiagnostic::UnresolvedModule { | 1156 | self.def_collector.def_map.diagnostics.push( |
1056 | module: self.module_id, | 1157 | DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate), |
1057 | declaration: ast_id, | 1158 | ); |
1058 | candidate, | 1159 | } |
1059 | }, | ||
1060 | ), | ||
1061 | }; | 1160 | }; |
1062 | } | 1161 | } |
1063 | } | 1162 | } |
@@ -1119,6 +1218,30 @@ impl ModCollector<'_, '_> { | |||
1119 | } | 1218 | } |
1120 | } | 1219 | } |
1121 | 1220 | ||
1221 | /// If `attrs` registers a procedural macro, collects its definition. | ||
1222 | fn collect_proc_macro_def(&mut self, func_name: &Name, attrs: &Attrs) { | ||
1223 | // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere | ||
1224 | // FIXME: distinguish the type of macro | ||
1225 | let macro_name = if attrs.by_key("proc_macro").exists() | ||
1226 | || attrs.by_key("proc_macro_attribute").exists() | ||
1227 | { | ||
1228 | func_name.clone() | ||
1229 | } else { | ||
1230 | let derive = attrs.by_key("proc_macro_derive"); | ||
1231 | if let Some(arg) = derive.tt_values().next() { | ||
1232 | if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees { | ||
1233 | trait_name.as_name() | ||
1234 | } else { | ||
1235 | return; | ||
1236 | } | ||
1237 | } else { | ||
1238 | return; | ||
1239 | } | ||
1240 | }; | ||
1241 | |||
1242 | self.def_collector.resolve_proc_macro(¯o_name); | ||
1243 | } | ||
1244 | |||
1122 | fn collect_macro(&mut self, mac: &MacroCall) { | 1245 | fn collect_macro(&mut self, mac: &MacroCall) { |
1123 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); | 1246 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); |
1124 | 1247 | ||
@@ -1225,6 +1348,7 @@ mod tests { | |||
1225 | mod_dirs: FxHashMap::default(), | 1348 | mod_dirs: FxHashMap::default(), |
1226 | cfg_options: &CfgOptions::default(), | 1349 | cfg_options: &CfgOptions::default(), |
1227 | proc_macros: Default::default(), | 1350 | proc_macros: Default::default(), |
1351 | exports_proc_macros: false, | ||
1228 | from_glob_import: Default::default(), | 1352 | from_glob_import: Default::default(), |
1229 | }; | 1353 | }; |
1230 | collector.collect(); | 1354 | collector.collect(); |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 5ca30dac9..11d84f808 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -2,6 +2,7 @@ mod globs; | |||
2 | mod incremental; | 2 | mod incremental; |
3 | mod macros; | 3 | mod macros; |
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | ||
5 | mod primitives; | 6 | mod primitives; |
6 | 7 | ||
7 | use std::sync::Arc; | 8 | use std::sync::Arc; |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs new file mode 100644 index 000000000..576b813d2 --- /dev/null +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -0,0 +1,131 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use base_db::FileId; | ||
3 | use base_db::SourceDatabaseExt; | ||
4 | use hir_expand::db::AstDatabase; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | use syntax::TextRange; | ||
7 | use syntax::TextSize; | ||
8 | |||
9 | use crate::test_db::TestDB; | ||
10 | |||
11 | fn check_diagnostics(ra_fixture: &str) { | ||
12 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
13 | let annotations = db.extract_annotations(); | ||
14 | assert!(!annotations.is_empty()); | ||
15 | |||
16 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
17 | db.diagnostics(|d| { | ||
18 | let src = d.display_source(); | ||
19 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
20 | // FIXME: macros... | ||
21 | let file_id = src.file_id.original_file(&db); | ||
22 | let range = src.value.to_node(&root).text_range(); | ||
23 | let message = d.message().to_owned(); | ||
24 | actual.entry(file_id).or_default().push((range, message)); | ||
25 | }); | ||
26 | |||
27 | for (file_id, diags) in actual.iter_mut() { | ||
28 | diags.sort_by_key(|it| it.0.start()); | ||
29 | let text = db.file_text(*file_id); | ||
30 | // For multiline spans, place them on line start | ||
31 | for (range, content) in diags { | ||
32 | if text[*range].contains('\n') { | ||
33 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
34 | *content = format!("... {}", content); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | assert_eq!(annotations, actual); | ||
40 | } | ||
41 | |||
42 | #[test] | ||
43 | fn unresolved_import() { | ||
44 | check_diagnostics( | ||
45 | r" | ||
46 | use does_exist; | ||
47 | use does_not_exist; | ||
48 | //^^^^^^^^^^^^^^ unresolved import | ||
49 | |||
50 | mod does_exist {} | ||
51 | ", | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn unresolved_import_in_use_tree() { | ||
57 | // Only the relevant part of a nested `use` item should be highlighted. | ||
58 | check_diagnostics( | ||
59 | r" | ||
60 | use does_exist::{Exists, DoesntExist}; | ||
61 | //^^^^^^^^^^^ unresolved import | ||
62 | |||
63 | use {does_not_exist::*, does_exist}; | ||
64 | //^^^^^^^^^^^^^^^^^ unresolved import | ||
65 | |||
66 | use does_not_exist::{ | ||
67 | a, | ||
68 | //^ unresolved import | ||
69 | b, | ||
70 | //^ unresolved import | ||
71 | c, | ||
72 | //^ unresolved import | ||
73 | }; | ||
74 | |||
75 | mod does_exist { | ||
76 | pub struct Exists; | ||
77 | } | ||
78 | ", | ||
79 | ); | ||
80 | } | ||
81 | |||
82 | #[test] | ||
83 | fn unresolved_extern_crate() { | ||
84 | check_diagnostics( | ||
85 | r" | ||
86 | //- /main.rs crate:main deps:core | ||
87 | extern crate core; | ||
88 | extern crate doesnotexist; | ||
89 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
90 | //- /lib.rs crate:core | ||
91 | ", | ||
92 | ); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn dedup_unresolved_import_from_unresolved_crate() { | ||
97 | check_diagnostics( | ||
98 | r" | ||
99 | //- /main.rs crate:main | ||
100 | mod a { | ||
101 | extern crate doesnotexist; | ||
102 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
103 | |||
104 | // Should not error, since we already errored for the missing crate. | ||
105 | use doesnotexist::{self, bla, *}; | ||
106 | |||
107 | use crate::doesnotexist; | ||
108 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
109 | } | ||
110 | |||
111 | mod m { | ||
112 | use super::doesnotexist; | ||
113 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
114 | } | ||
115 | ", | ||
116 | ); | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn unresolved_module() { | ||
121 | check_diagnostics( | ||
122 | r" | ||
123 | //- /lib.rs | ||
124 | mod foo; | ||
125 | mod bar; | ||
126 | //^^^^^^^^ unresolved module | ||
127 | mod baz {} | ||
128 | //- /foo.rs | ||
129 | ", | ||
130 | ); | ||
131 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index e0fb8bdef..0851c3b7d 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -667,3 +667,76 @@ b! { static = #[] (); } | |||
667 | "#]], | 667 | "#]], |
668 | ); | 668 | ); |
669 | } | 669 | } |
670 | |||
671 | #[test] | ||
672 | fn resolves_proc_macros() { | ||
673 | check( | ||
674 | r" | ||
675 | struct TokenStream; | ||
676 | |||
677 | #[proc_macro] | ||
678 | pub fn function_like_macro(args: TokenStream) -> TokenStream { | ||
679 | args | ||
680 | } | ||
681 | |||
682 | #[proc_macro_attribute] | ||
683 | pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream { | ||
684 | item | ||
685 | } | ||
686 | |||
687 | #[proc_macro_derive(DummyTrait)] | ||
688 | pub fn derive_macro(_item: TokenStream) -> TokenStream { | ||
689 | TokenStream | ||
690 | } | ||
691 | ", | ||
692 | expect![[r#" | ||
693 | crate | ||
694 | DummyTrait: m | ||
695 | TokenStream: t v | ||
696 | attribute_macro: v m | ||
697 | derive_macro: v | ||
698 | function_like_macro: v m | ||
699 | "#]], | ||
700 | ); | ||
701 | } | ||
702 | |||
703 | #[test] | ||
704 | fn proc_macro_censoring() { | ||
705 | // Make sure that only proc macros are publicly exported from proc-macro crates. | ||
706 | |||
707 | check( | ||
708 | r" | ||
709 | //- /main.rs crate:main deps:macros | ||
710 | pub use macros::*; | ||
711 | |||
712 | //- /macros.rs crate:macros | ||
713 | pub struct TokenStream; | ||
714 | |||
715 | #[proc_macro] | ||
716 | pub fn function_like_macro(args: TokenStream) -> TokenStream { | ||
717 | args | ||
718 | } | ||
719 | |||
720 | #[proc_macro_attribute] | ||
721 | pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream { | ||
722 | item | ||
723 | } | ||
724 | |||
725 | #[proc_macro_derive(DummyTrait)] | ||
726 | pub fn derive_macro(_item: TokenStream) -> TokenStream { | ||
727 | TokenStream | ||
728 | } | ||
729 | |||
730 | #[macro_export] | ||
731 | macro_rules! mbe { | ||
732 | () => {}; | ||
733 | } | ||
734 | ", | ||
735 | expect![[r#" | ||
736 | crate | ||
737 | DummyTrait: m | ||
738 | attribute_macro: m | ||
739 | function_like_macro: m | ||
740 | "#]], | ||
741 | ); | ||
742 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index 1f619787e..f93337a6e 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -672,42 +672,6 @@ pub struct Baz; | |||
672 | } | 672 | } |
673 | 673 | ||
674 | #[test] | 674 | #[test] |
675 | fn unresolved_module_diagnostics() { | ||
676 | let db = TestDB::with_files( | ||
677 | r" | ||
678 | //- /lib.rs | ||
679 | mod foo; | ||
680 | mod bar; | ||
681 | mod baz {} | ||
682 | //- /foo.rs | ||
683 | ", | ||
684 | ); | ||
685 | let krate = db.test_crate(); | ||
686 | |||
687 | let crate_def_map = db.crate_def_map(krate); | ||
688 | |||
689 | expect![[r#" | ||
690 | [ | ||
691 | UnresolvedModule { | ||
692 | module: Idx::<ModuleData>(0), | ||
693 | declaration: InFile { | ||
694 | file_id: HirFileId( | ||
695 | FileId( | ||
696 | FileId( | ||
697 | 0, | ||
698 | ), | ||
699 | ), | ||
700 | ), | ||
701 | value: FileAstId::<syntax::ast::generated::nodes::Module>(1), | ||
702 | }, | ||
703 | candidate: "bar.rs", | ||
704 | }, | ||
705 | ] | ||
706 | "#]] | ||
707 | .assert_debug_eq(&crate_def_map.diagnostics); | ||
708 | } | ||
709 | |||
710 | #[test] | ||
711 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { | 675 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { |
712 | check( | 676 | check( |
713 | r#" | 677 | r#" |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 40ec38f56..209b18e78 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -56,10 +56,6 @@ impl ModPath { | |||
56 | ModPath { kind, segments } | 56 | ModPath { kind, segments } |
57 | } | 57 | } |
58 | 58 | ||
59 | pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath { | ||
60 | name_ref.as_name().into() | ||
61 | } | ||
62 | |||
63 | /// Converts an `tt::Ident` into a single-identifier `Path`. | 59 | /// Converts an `tt::Ident` into a single-identifier `Path`. |
64 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { | 60 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { |
65 | ident.as_name().into() | 61 | ident.as_name().into() |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 42a762936..fb1d3c974 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -5,9 +5,15 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use base_db::SourceDatabase; | ||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | 9 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; |
9 | use hir_expand::db::AstDatabase; | 10 | use hir_expand::db::AstDatabase; |
11 | use hir_expand::diagnostics::Diagnostic; | ||
12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | ||
13 | use rustc_hash::FxHashMap; | ||
10 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::TextRange; | ||
16 | use test_utils::extract_annotations; | ||
11 | 17 | ||
12 | use crate::db::DefDatabase; | 18 | use crate::db::DefDatabase; |
13 | 19 | ||
@@ -98,4 +104,40 @@ impl TestDB { | |||
98 | }) | 104 | }) |
99 | .collect() | 105 | .collect() |
100 | } | 106 | } |
107 | |||
108 | pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
109 | let mut files = Vec::new(); | ||
110 | let crate_graph = self.crate_graph(); | ||
111 | for krate in crate_graph.iter() { | ||
112 | let crate_def_map = self.crate_def_map(krate); | ||
113 | for (module_id, _) in crate_def_map.modules.iter() { | ||
114 | let file_id = crate_def_map[module_id].origin.file_id(); | ||
115 | files.extend(file_id) | ||
116 | } | ||
117 | } | ||
118 | assert!(!files.is_empty()); | ||
119 | files | ||
120 | .into_iter() | ||
121 | .filter_map(|file_id| { | ||
122 | let text = self.file_text(file_id); | ||
123 | let annotations = extract_annotations(&text); | ||
124 | if annotations.is_empty() { | ||
125 | return None; | ||
126 | } | ||
127 | Some((file_id, annotations)) | ||
128 | }) | ||
129 | .collect() | ||
130 | } | ||
131 | |||
132 | pub fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
133 | let crate_graph = self.crate_graph(); | ||
134 | for krate in crate_graph.iter() { | ||
135 | let crate_def_map = self.crate_def_map(krate); | ||
136 | |||
137 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
138 | for (module_id, _) in crate_def_map.modules.iter() { | ||
139 | crate_def_map.add_diagnostics(self, module_id, &mut sink); | ||
140 | } | ||
141 | } | ||
142 | } | ||
101 | } | 143 | } |