aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r--crates/hir_def/src/diagnostics.rs49
-rw-r--r--crates/hir_def/src/find_path.rs170
-rw-r--r--crates/hir_def/src/item_scope.rs22
-rw-r--r--crates/hir_def/src/item_tree.rs8
-rw-r--r--crates/hir_def/src/item_tree/lower.rs7
-rw-r--r--crates/hir_def/src/item_tree/tests.rs6
-rw-r--r--crates/hir_def/src/nameres.rs93
-rw-r--r--crates/hir_def/src/nameres/collector.rs508
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs73
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs36
-rw-r--r--crates/hir_def/src/path.rs4
-rw-r--r--crates/hir_def/src/test_db.rs42
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)]
33pub struct UnresolvedExternCrate {
34 pub file: HirFileId,
35 pub item: AstPtr<ast::ExternCrate>,
36}
37
38impl 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)]
54pub struct UnresolvedImport {
55 pub file: HirFileId,
56 pub node: AstPtr<ast::UseTree>,
57}
58
59impl 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};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::nameres::CrateDefMap;
7use crate::{ 8use 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.
19pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub 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
25pub 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
24const MAX_PATH_LEN: usize = 15; 30const MAX_PATH_LEN: usize = 15;
@@ -36,11 +42,67 @@ impl ModPath {
36 } 42 }
37} 43}
38 44
45fn 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)]
78pub enum Prefixed {
79 Not,
80 BySelf,
81 Plain,
82}
83
84impl 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
39fn find_path_inner( 100fn 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
187fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 238fn 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
6use base_db::CrateId; 6use base_db::CrateId;
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use hir_expand::MacroDefKind;
8use once_cell::sync::Lazy; 9use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark; 11use test_utils::mark;
11 12
13use crate::ModuleId;
12use crate::{ 14use 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
270impl PerNs { 292impl 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
297macro_rules! from_attrs { 296macro_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)]
489pub struct ExternCrate { 493pub 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
289mod diagnostics { 289mod 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
6use std::iter;
7
6use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions; 9use cfg::CfgOptions;
10use hir_expand::InFile;
8use hir_expand::{ 11use 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};
16use rustc_hash::FxHashMap; 19use rustc_hash::{FxHashMap, FxHashSet};
17use syntax::ast; 20use syntax::ast;
18use test_utils::mark; 21use test_utils::mark;
22use tt::{Leaf, TokenTree};
19 23
20use crate::{ 24use 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)]
118enum ImportSource {
119 Import(ItemTreeId<item_tree::Import>),
120 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 124struct 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
125impl Import { 135impl 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(&macro_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;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
5mod primitives; 6mod primitives;
6 7
7use std::sync::Arc; 8use 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 @@
1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8
9use crate::test_db::TestDB;
10
11fn 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]
43fn 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]
56fn 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]
83fn 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]
96fn 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]
120fn 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]
672fn 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]
704fn 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]
675fn 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]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() { 675fn 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
8use base_db::SourceDatabase;
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
9use hir_expand::db::AstDatabase; 10use hir_expand::db::AstDatabase;
11use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder;
13use rustc_hash::FxHashMap;
10use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::TextRange;
16use test_utils::extract_annotations;
11 17
12use crate::db::DefDatabase; 18use 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}