aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-05-26 00:01:58 +0100
committerJonas Schievink <[email protected]>2021-05-26 00:01:58 +0100
commitb52df9187730abbcd9cbb132f7d184c74b9a3b7f (patch)
tree06cf9ab8dcd938db5390b7da40f7e9a6db262d0d
parent5587d0a3e3599063a8993e9a44a7628abbabae8b (diff)
Stop expanding UseTrees during ItemTree lowering
-rw-r--r--crates/hir/src/lib.rs26
-rw-r--r--crates/hir_def/src/item_tree.rs130
-rw-r--r--crates/hir_def/src/item_tree/lower.rs100
-rw-r--r--crates/hir_def/src/item_tree/pretty.rs43
-rw-r--r--crates/hir_def/src/item_tree/tests.rs17
-rw-r--r--crates/hir_def/src/nameres/collector.rs66
-rw-r--r--crates/hir_def/src/nameres/diagnostics.rs16
-rw-r--r--crates/hir_def/src/path.rs20
-rw-r--r--crates/hir_def/src/path/lower.rs2
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs78
-rw-r--r--crates/hir_def/src/test_db.rs8
-rw-r--r--crates/ide/src/diagnostics.rs2
12 files changed, 320 insertions, 188 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 1ecd2391b..01b2de515 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -472,27 +472,13 @@ impl Module {
472 }); 472 });
473 } 473 }
474 474
475 DefDiagnosticKind::UnresolvedImport { ast, index } => { 475 DefDiagnosticKind::UnresolvedImport { id, index } => {
476 let use_item = ast.to_node(db.upcast()); 476 let file_id = id.file_id();
477 let hygiene = Hygiene::new(db.upcast(), ast.file_id); 477 let item_tree = id.item_tree(db.upcast());
478 let mut cur = 0; 478 let import = &item_tree[id.value];
479 let mut tree = None;
480 ModPath::expand_use_item(
481 db.upcast(),
482 InFile::new(ast.file_id, use_item),
483 &hygiene,
484 |_mod_path, use_tree, _is_glob, _alias| {
485 if cur == *index {
486 tree = Some(use_tree.clone());
487 }
488
489 cur += 1;
490 },
491 );
492 479
493 if let Some(tree) = tree { 480 let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
494 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); 481 sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
495 }
496 } 482 }
497 483
498 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { 484 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 11767d100..508736885 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -523,21 +523,38 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
523 } 523 }
524} 524}
525 525
526/// A desugared `use` import.
527#[derive(Debug, Clone, Eq, PartialEq)] 526#[derive(Debug, Clone, Eq, PartialEq)]
528pub struct Import { 527pub struct Import {
529 pub path: Interned<ModPath>,
530 pub alias: Option<ImportAlias>,
531 pub visibility: RawVisibilityId, 528 pub visibility: RawVisibilityId,
532 pub is_glob: bool,
533 /// AST ID of the `use` item this import was derived from. Note that many `Import`s can map to
534 /// the same `use` item.
535 pub ast_id: FileAstId<ast::Use>, 529 pub ast_id: FileAstId<ast::Use>,
536 /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`. 530 pub use_tree: UseTree,
537 /// 531}
538 /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting 532
539 /// precise diagnostics. 533#[derive(Debug, Clone, Eq, PartialEq)]
540 pub index: usize, 534pub struct UseTree {
535 pub index: Idx<ast::UseTree>,
536 kind: UseTreeKind,
537}
538
539#[derive(Debug, Clone, Eq, PartialEq)]
540pub enum UseTreeKind {
541 /// ```ignore
542 /// use path::to::Item;
543 /// use path::to::Item as Renamed;
544 /// use path::to::Trait as _;
545 /// ```
546 Single { path: ModPath, alias: Option<ImportAlias> },
547
548 /// ```ignore
549 /// use *; // (invalid, but can occur in nested tree)
550 /// use path::*;
551 /// ```
552 Glob { path: Option<ModPath> },
553
554 /// ```ignore
555 /// use prefix::{self, Item, ...};
556 /// ```
557 Prefixed { prefix: Option<ModPath>, list: Vec<UseTree> },
541} 558}
542 559
543#[derive(Debug, Clone, Eq, PartialEq)] 560#[derive(Debug, Clone, Eq, PartialEq)]
@@ -711,6 +728,97 @@ pub struct MacroDef {
711 pub ast_id: FileAstId<ast::MacroDef>, 728 pub ast_id: FileAstId<ast::MacroDef>,
712} 729}
713 730
731impl Import {
732 /// Maps a `UseTree` contained in this import back to its AST node.
733 pub fn use_tree_to_ast(
734 &self,
735 db: &dyn DefDatabase,
736 file_id: HirFileId,
737 index: Idx<ast::UseTree>,
738 ) -> ast::UseTree {
739 // Re-lower the AST item and get the source map.
740 // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
741 let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
742 let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
743 let hygiene = Hygiene::new(db.upcast(), file_id);
744 let (_, source_map) =
745 lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
746 source_map[index].clone()
747 }
748}
749
750impl UseTree {
751 /// Expands the `UseTree` into individually imported `ModPath`s.
752 pub fn expand(
753 &self,
754 mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, /* is_glob */ bool, Option<ImportAlias>),
755 ) {
756 self.expand_impl(None, &mut cb)
757 }
758
759 fn expand_impl(
760 &self,
761 prefix: Option<ModPath>,
762 cb: &mut dyn FnMut(
763 Idx<ast::UseTree>,
764 ModPath,
765 /* is_glob */ bool,
766 Option<ImportAlias>,
767 ),
768 ) {
769 fn concat_mod_paths(prefix: Option<ModPath>, path: &ModPath) -> Option<ModPath> {
770 match (prefix, &path.kind) {
771 (None, _) => Some(path.clone()),
772 (Some(mut prefix), PathKind::Plain) => {
773 for segment in path.segments() {
774 prefix.push_segment(segment.clone());
775 }
776 Some(prefix)
777 }
778 (Some(prefix), PathKind::Super(0)) => {
779 // `some::path::self` == `some::path`
780 if path.segments().is_empty() {
781 Some(prefix)
782 } else {
783 None
784 }
785 }
786 (Some(_), _) => None,
787 }
788 }
789
790 match &self.kind {
791 UseTreeKind::Single { path, alias } => {
792 if let Some(path) = concat_mod_paths(prefix, path) {
793 cb(self.index, path, false, alias.clone());
794 }
795 }
796 UseTreeKind::Glob { path: Some(path) } => {
797 if let Some(path) = concat_mod_paths(prefix, path) {
798 cb(self.index, path, true, None);
799 }
800 }
801 UseTreeKind::Glob { path: None } => {
802 if let Some(prefix) = prefix {
803 cb(self.index, prefix, true, None);
804 }
805 }
806 UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
807 let prefix = match additional_prefix {
808 Some(path) => match concat_mod_paths(prefix, path) {
809 Some(path) => Some(path),
810 None => return,
811 },
812 None => prefix,
813 };
814 for tree in list {
815 tree.expand_impl(prefix.clone(), cb);
816 }
817 }
818 }
819 }
820}
821
714macro_rules! impl_froms { 822macro_rules! impl_froms {
715 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { 823 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
716 $( 824 $(
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index b4389371f..a59a3dc37 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -35,7 +35,6 @@ pub(super) struct Ctx<'a> {
35 db: &'a dyn DefDatabase, 35 db: &'a dyn DefDatabase,
36 tree: ItemTree, 36 tree: ItemTree,
37 hygiene: Hygiene, 37 hygiene: Hygiene,
38 file: HirFileId,
39 source_ast_id_map: Arc<AstIdMap>, 38 source_ast_id_map: Arc<AstIdMap>,
40 body_ctx: crate::body::LowerCtx<'a>, 39 body_ctx: crate::body::LowerCtx<'a>,
41 forced_visibility: Option<RawVisibilityId>, 40 forced_visibility: Option<RawVisibilityId>,
@@ -47,7 +46,6 @@ impl<'a> Ctx<'a> {
47 db, 46 db,
48 tree: ItemTree::default(), 47 tree: ItemTree::default(),
49 hygiene, 48 hygiene,
50 file,
51 source_ast_id_map: db.ast_id_map(file), 49 source_ast_id_map: db.ast_id_map(file),
52 body_ctx: crate::body::LowerCtx::new(db, file), 50 body_ctx: crate::body::LowerCtx::new(db, file),
53 forced_visibility: None, 51 forced_visibility: None,
@@ -561,30 +559,13 @@ impl<'a> Ctx<'a> {
561 Some(id(self.data().impls.alloc(res))) 559 Some(id(self.data().impls.alloc(res)))
562 } 560 }
563 561
564 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> { 562 fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Import>> {
565 let visibility = self.lower_visibility(use_item); 563 let visibility = self.lower_visibility(use_item);
566 let ast_id = self.source_ast_id_map.ast_id(use_item); 564 let ast_id = self.source_ast_id_map.ast_id(use_item);
565 let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?;
567 566
568 // Every use item can expand to many `Import`s. 567 let res = Import { visibility, ast_id, use_tree };
569 let mut imports = Vec::new(); 568 Some(id(self.data().imports.alloc(res)))
570 let tree = self.tree.data_mut();
571 ModPath::expand_use_item(
572 self.db,
573 InFile::new(self.file, use_item.clone()),
574 &self.hygiene,
575 |path, _use_tree, is_glob, alias| {
576 imports.push(id(tree.imports.alloc(Import {
577 path: Interned::new(path),
578 alias,
579 visibility,
580 is_glob,
581 ast_id,
582 index: imports.len(),
583 })));
584 },
585 );
586
587 imports
588 } 569 }
589 570
590 fn lower_extern_crate( 571 fn lower_extern_crate(
@@ -884,3 +865,76 @@ fn lower_abi(abi: ast::Abi) -> Interned<str> {
884 } 865 }
885 } 866 }
886} 867}
868
869struct UseTreeLowering<'a> {
870 db: &'a dyn DefDatabase,
871 hygiene: &'a Hygiene,
872 mapping: Arena<ast::UseTree>,
873}
874
875impl UseTreeLowering<'_> {
876 fn lower_use_tree(&mut self, tree: ast::UseTree) -> Option<UseTree> {
877 if let Some(use_tree_list) = tree.use_tree_list() {
878 let prefix = match tree.path() {
879 // E.g. use something::{{{inner}}};
880 None => None,
881 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
882 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
883 Some(path) => {
884 match ModPath::from_src(self.db, path, &self.hygiene) {
885 Some(it) => Some(it),
886 None => return None, // FIXME: report errors somewhere
887 }
888 }
889 };
890
891 let list =
892 use_tree_list.use_trees().filter_map(|tree| self.lower_use_tree(tree)).collect();
893
894 Some(self.use_tree(UseTreeKind::Prefixed { prefix, list }, tree))
895 } else {
896 let is_glob = tree.star_token().is_some();
897 let path = match tree.path() {
898 Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?),
899 None => None,
900 };
901 let alias = tree.rename().map(|a| {
902 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
903 });
904 if alias.is_some() && is_glob {
905 return None;
906 }
907
908 match (path, alias, is_glob) {
909 (path, None, true) => {
910 if path.is_none() {
911 cov_mark::hit!(glob_enum_group);
912 }
913 Some(self.use_tree(UseTreeKind::Glob { path }, tree))
914 }
915 // Globs can't be renamed
916 (_, Some(_), true) | (None, None, false) => None,
917 // `bla::{ as Name}` is invalid
918 (None, Some(_), false) => None,
919 (Some(path), alias, false) => {
920 Some(self.use_tree(UseTreeKind::Single { path, alias }, tree))
921 }
922 }
923 }
924 }
925
926 fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree {
927 let index = self.mapping.alloc(ast);
928 UseTree { index, kind }
929 }
930}
931
932pub(super) fn lower_use_tree(
933 db: &dyn DefDatabase,
934 hygiene: &Hygiene,
935 tree: ast::UseTree,
936) -> Option<(UseTree, Arena<ast::UseTree>)> {
937 let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
938 let tree = lowering.lower_use_tree(tree)?;
939 Some((tree, lowering.mapping))
940}
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index 9394a5de6..53631ab19 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -163,21 +163,46 @@ impl<'a> Printer<'a> {
163 } 163 }
164 } 164 }
165 165
166 fn print_use_tree(&mut self, use_tree: &UseTree) {
167 match &use_tree.kind {
168 UseTreeKind::Single { path, alias } => {
169 w!(self, "{}", path);
170 if let Some(alias) = alias {
171 w!(self, " as {}", alias);
172 }
173 }
174 UseTreeKind::Glob { path } => {
175 if let Some(path) = path {
176 w!(self, "{}::", path);
177 }
178 w!(self, "*");
179 }
180 UseTreeKind::Prefixed { prefix, list } => {
181 if let Some(prefix) = prefix {
182 w!(self, "{}::", prefix);
183 }
184 w!(self, "{{");
185 for (i, tree) in list.iter().enumerate() {
186 if i != 0 {
187 w!(self, ", ");
188 }
189 self.print_use_tree(tree);
190 }
191 w!(self, "}}");
192 }
193 }
194 }
195
166 fn print_mod_item(&mut self, item: ModItem) { 196 fn print_mod_item(&mut self, item: ModItem) {
167 self.print_attrs_of(item); 197 self.print_attrs_of(item);
168 198
169 match item { 199 match item {
170 ModItem::Import(it) => { 200 ModItem::Import(it) => {
171 let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it]; 201 let Import { visibility, use_tree, ast_id: _ } = &self.tree[it];
172 self.print_visibility(*visibility); 202 self.print_visibility(*visibility);
173 w!(self, "use {}", path); 203 w!(self, "use ");
174 if *is_glob { 204 self.print_use_tree(use_tree);
175 w!(self, "::*"); 205 wln!(self, ";");
176 }
177 if let Some(alias) = alias {
178 w!(self, " as {}", alias);
179 }
180 wln!(self, "; // {}", index);
181 } 206 }
182 ModItem::ExternCrate(it) => { 207 ModItem::ExternCrate(it) => {
183 let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; 208 let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index 6407871b5..20773aa69 100644
--- a/crates/hir_def/src/item_tree/tests.rs
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -26,6 +26,8 @@ use globs::*;
26 26
27/// docs on import 27/// docs on import
28use crate::{A, B}; 28use crate::{A, B};
29
30use a::{c, d::{e}};
29 "#, 31 "#,
30 expect![[r##" 32 expect![[r##"
31 #![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 } 33 #![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 }
@@ -36,19 +38,14 @@ use crate::{A, B};
36 38
37 pub(super) extern crate bli; 39 pub(super) extern crate bli;
38 40
39 pub use crate::path::nested; // 0 41 pub use crate::path::{nested, items as renamed, Trait as _};
40
41 pub use crate::path::items as renamed; // 1
42 42
43 pub use crate::path::Trait as _; // 2 43 pub(self) use globs::*;
44
45 pub(self) use globs::*; // 0
46 44
47 #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } 45 #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 }
48 pub(self) use crate::A; // 0 46 pub(self) use crate::{A, B};
49 47
50 #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } 48 pub(self) use a::{c, d::{e}};
51 pub(self) use crate::B; // 1
52 "##]], 49 "##]],
53 ); 50 );
54} 51}
@@ -218,7 +215,7 @@ mod outline;
218 #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 } 215 #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 }
219 #[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 } 216 #[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 }
220 pub(self) mod inline { 217 pub(self) mod inline {
221 pub(self) use super::*; // 0 218 pub(self) use super::*;
222 219
223 // flags = 0x2 220 // flags = 0x2
224 pub(self) fn fn_in_module() -> (); 221 pub(self) fn fn_in_module() -> ();
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3ea472908..4296c6304 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -17,6 +17,7 @@ use hir_expand::{
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use itertools::Itertools; 19use itertools::Itertools;
20use la_arena::Idx;
20use rustc_hash::{FxHashMap, FxHashSet}; 21use rustc_hash::{FxHashMap, FxHashSet};
21use syntax::ast; 22use syntax::ast;
22 23
@@ -143,7 +144,7 @@ impl PartialResolvedImport {
143 144
144#[derive(Clone, Debug, Eq, PartialEq)] 145#[derive(Clone, Debug, Eq, PartialEq)]
145enum ImportSource { 146enum ImportSource {
146 Import(ItemTreeId<item_tree::Import>), 147 Import { id: ItemTreeId<item_tree::Import>, use_tree: Idx<ast::UseTree> },
147 ExternCrate(ItemTreeId<item_tree::ExternCrate>), 148 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
148} 149}
149 150
@@ -165,20 +166,26 @@ impl Import {
165 krate: CrateId, 166 krate: CrateId,
166 tree: &ItemTree, 167 tree: &ItemTree,
167 id: ItemTreeId<item_tree::Import>, 168 id: ItemTreeId<item_tree::Import>,
168 ) -> Self { 169 ) -> Vec<Self> {
169 let it = &tree[id.value]; 170 let it = &tree[id.value];
170 let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); 171 let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
171 let visibility = &tree[it.visibility]; 172 let visibility = &tree[it.visibility];
172 Self { 173 let is_prelude = attrs.by_key("prelude_import").exists();
173 path: it.path.clone(), 174
174 alias: it.alias.clone(), 175 let mut res = Vec::new();
175 visibility: visibility.clone(), 176 it.use_tree.expand(|idx, path, is_glob, alias| {
176 is_glob: it.is_glob, 177 res.push(Self {
177 is_prelude: attrs.by_key("prelude_import").exists(), 178 path: Interned::new(path), // FIXME this makes little sense
178 is_extern_crate: false, 179 alias,
179 is_macro_use: false, 180 visibility: visibility.clone(),
180 source: ImportSource::Import(id), 181 is_glob,
181 } 182 is_prelude,
183 is_extern_crate: false,
184 is_macro_use: false,
185 source: ImportSource::Import { id, use_tree: idx },
186 });
187 });
188 res
182 } 189 }
183 190
184 fn from_extern_crate( 191 fn from_extern_crate(
@@ -1130,11 +1137,8 @@ impl DefCollector<'_> {
1130 } 1137 }
1131 1138
1132 for directive in &self.unresolved_imports { 1139 for directive in &self.unresolved_imports {
1133 if let ImportSource::Import(import) = &directive.import.source { 1140 if let ImportSource::Import { id: import, use_tree } = &directive.import.source {
1134 let item_tree = import.item_tree(self.db); 1141 match (directive.import.path.segments().first(), &directive.import.path.kind) {
1135 let import_data = &item_tree[import.value];
1136
1137 match (import_data.path.segments().first(), &import_data.path.kind) {
1138 (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { 1142 (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
1139 if diagnosed_extern_crates.contains(krate) { 1143 if diagnosed_extern_crates.contains(krate) {
1140 continue; 1144 continue;
@@ -1145,8 +1149,8 @@ impl DefCollector<'_> {
1145 1149
1146 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( 1150 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
1147 directive.module_id, 1151 directive.module_id,
1148 InFile::new(import.file_id(), import_data.ast_id), 1152 *import,
1149 import_data.index, 1153 *use_tree,
1150 )); 1154 ));
1151 } 1155 }
1152 } 1156 }
@@ -1222,16 +1226,20 @@ impl ModCollector<'_, '_> {
1222 match item { 1226 match item {
1223 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], &attrs), 1227 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], &attrs),
1224 ModItem::Import(import_id) => { 1228 ModItem::Import(import_id) => {
1225 self.def_collector.unresolved_imports.push(ImportDirective { 1229 let module_id = self.module_id;
1226 module_id: self.module_id, 1230 let imports = Import::from_use(
1227 import: Import::from_use( 1231 self.def_collector.db,
1228 self.def_collector.db, 1232 krate,
1229 krate, 1233 &self.item_tree,
1230 &self.item_tree, 1234 ItemTreeId::new(self.file_id, import_id),
1231 ItemTreeId::new(self.file_id, import_id), 1235 );
1232 ), 1236 self.def_collector.unresolved_imports.extend(imports.into_iter().map(
1233 status: PartialResolvedImport::Unresolved, 1237 |import| ImportDirective {
1234 }) 1238 module_id,
1239 import,
1240 status: PartialResolvedImport::Unresolved,
1241 },
1242 ));
1235 } 1243 }
1236 ModItem::ExternCrate(import_id) => { 1244 ModItem::ExternCrate(import_id) => {
1237 self.def_collector.unresolved_imports.push(ImportDirective { 1245 self.def_collector.unresolved_imports.push(ImportDirective {
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs
index 8f2f0ff9f..57c36c3c6 100644
--- a/crates/hir_def/src/nameres/diagnostics.rs
+++ b/crates/hir_def/src/nameres/diagnostics.rs
@@ -2,9 +2,15 @@
2 2
3use cfg::{CfgExpr, CfgOptions}; 3use cfg::{CfgExpr, CfgOptions};
4use hir_expand::MacroCallKind; 4use hir_expand::MacroCallKind;
5use la_arena::Idx;
5use syntax::ast; 6use syntax::ast;
6 7
7use crate::{nameres::LocalModuleId, path::ModPath, AstId}; 8use crate::{
9 item_tree::{self, ItemTreeId},
10 nameres::LocalModuleId,
11 path::ModPath,
12 AstId,
13};
8 14
9#[derive(Debug, PartialEq, Eq)] 15#[derive(Debug, PartialEq, Eq)]
10pub enum DefDiagnosticKind { 16pub enum DefDiagnosticKind {
@@ -12,7 +18,7 @@ pub enum DefDiagnosticKind {
12 18
13 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, 19 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
14 20
15 UnresolvedImport { ast: AstId<ast::Use>, index: usize }, 21 UnresolvedImport { id: ItemTreeId<item_tree::Import>, index: Idx<ast::UseTree> },
16 22
17 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, 23 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
18 24
@@ -53,10 +59,10 @@ impl DefDiagnostic {
53 59
54 pub(super) fn unresolved_import( 60 pub(super) fn unresolved_import(
55 container: LocalModuleId, 61 container: LocalModuleId,
56 ast: AstId<ast::Use>, 62 id: ItemTreeId<item_tree::Import>,
57 index: usize, 63 index: Idx<ast::UseTree>,
58 ) -> Self { 64 ) -> Self {
59 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } } 65 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
60 } 66 }
61 67
62 pub(super) fn unconfigured_code( 68 pub(super) fn unconfigured_code(
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index d9ec03d2d..16440041d 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -14,10 +14,7 @@ use hir_expand::{
14}; 14};
15use syntax::ast; 15use syntax::ast;
16 16
17use crate::{ 17use crate::type_ref::{TypeBound, TypeRef};
18 type_ref::{TypeBound, TypeRef},
19 InFile,
20};
21 18
22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 19#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct ModPath { 20pub struct ModPath {
@@ -56,8 +53,7 @@ impl Display for ImportAlias {
56 53
57impl ModPath { 54impl ModPath {
58 pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { 55 pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
59 let ctx = LowerCtx::with_hygiene(db, hygiene); 56 lower::convert_path(db, None, path, hygiene)
60 lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
61 } 57 }
62 58
63 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { 59 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
@@ -70,18 +66,6 @@ impl ModPath {
70 ModPath { kind, segments: Vec::new() } 66 ModPath { kind, segments: Vec::new() }
71 } 67 }
72 68
73 /// Calls `cb` with all paths, represented by this use item.
74 pub fn expand_use_item(
75 db: &dyn DefDatabase,
76 item_src: InFile<ast::Use>,
77 hygiene: &Hygiene,
78 mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
79 ) {
80 if let Some(tree) = item_src.value.use_tree() {
81 lower::lower_use_tree(db, None, tree, hygiene, &mut cb);
82 }
83 }
84
85 pub fn segments(&self) -> &[Name] { 69 pub fn segments(&self) -> &[Name] {
86 &self.segments 70 &self.segments
87 } 71 }
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 54ede7393..f6220aa92 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -15,7 +15,7 @@ use crate::{
15 type_ref::{LifetimeRef, TypeBound, TypeRef}, 15 type_ref::{LifetimeRef, TypeBound, TypeRef},
16}; 16};
17 17
18pub(super) use lower_use::lower_use_tree; 18pub(super) use lower_use::convert_path;
19 19
20/// Converts an `ast::Path` to `Path`. Works with use trees. 20/// Converts an `ast::Path` to `Path`. Works with use trees.
21/// It correctly handles `$crate` based path from macro call. 21/// It correctly handles `$crate` based path from macro call.
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
index ee80e3df3..0ee406f63 100644
--- a/crates/hir_def/src/path/lower/lower_use.rs
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -4,68 +4,15 @@
4use std::iter; 4use std::iter;
5 5
6use either::Either; 6use either::Either;
7use hir_expand::{hygiene::Hygiene, name::AsName}; 7use hir_expand::hygiene::Hygiene;
8use syntax::ast::{self, NameOwner}; 8use syntax::{ast, AstNode};
9 9
10use crate::{ 10use crate::{
11 db::DefDatabase, 11 db::DefDatabase,
12 path::{ImportAlias, ModPath, PathKind}, 12 path::{ModPath, PathKind},
13}; 13};
14 14
15pub(crate) fn lower_use_tree( 15pub(crate) fn convert_path(
16 db: &dyn DefDatabase,
17 prefix: Option<ModPath>,
18 tree: ast::UseTree,
19 hygiene: &Hygiene,
20 cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<ImportAlias>),
21) {
22 if let Some(use_tree_list) = tree.use_tree_list() {
23 let prefix = match tree.path() {
24 // E.g. use something::{{{inner}}};
25 None => prefix,
26 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
27 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
28 Some(path) => match convert_path(db, prefix, path, hygiene) {
29 Some(it) => Some(it),
30 None => return, // FIXME: report errors somewhere
31 },
32 };
33 for child_tree in use_tree_list.use_trees() {
34 lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb);
35 }
36 } else {
37 let alias = tree.rename().map(|a| {
38 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
39 });
40 let is_glob = tree.star_token().is_some();
41 if let Some(ast_path) = tree.path() {
42 // Handle self in a path.
43 // E.g. `use something::{self, <...>}`
44 if ast_path.qualifier().is_none() {
45 if let Some(segment) = ast_path.segment() {
46 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
47 if let Some(prefix) = prefix {
48 cb(prefix, &tree, false, alias);
49 return;
50 }
51 }
52 }
53 }
54 if let Some(path) = convert_path(db, prefix, ast_path, hygiene) {
55 cb(path, &tree, is_glob, alias)
56 }
57 // FIXME: report errors somewhere
58 // We get here if we do
59 } else if is_glob {
60 cov_mark::hit!(glob_enum_group);
61 if let Some(prefix) = prefix {
62 cb(prefix, &tree, is_glob, None)
63 }
64 }
65 }
66}
67
68fn convert_path(
69 db: &dyn DefDatabase, 16 db: &dyn DefDatabase,
70 prefix: Option<ModPath>, 17 prefix: Option<ModPath>,
71 path: ast::Path, 18 path: ast::Path,
@@ -78,7 +25,7 @@ fn convert_path(
78 }; 25 };
79 26
80 let segment = path.segment()?; 27 let segment = path.segment()?;
81 let res = match segment.kind()? { 28 let mut mod_path = match segment.kind()? {
82 ast::PathSegmentKind::Name(name_ref) => { 29 ast::PathSegmentKind::Name(name_ref) => {
83 match hygiene.name_ref_to_name(db.upcast(), name_ref) { 30 match hygiene.name_ref_to_name(db.upcast(), name_ref) {
84 Either::Left(name) => { 31 Either::Left(name) => {
@@ -125,5 +72,18 @@ fn convert_path(
125 return None; 72 return None;
126 } 73 }
127 }; 74 };
128 Some(res) 75
76 // handle local_inner_macros :
77 // Basically, even in rustc it is quite hacky:
78 // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
79 // We follow what it did anyway :)
80 if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
81 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
82 if let Some(crate_id) = hygiene.local_inner_macros(db.upcast(), path) {
83 mod_path.kind = PathKind::DollarCrate(crate_id);
84 }
85 }
86 }
87
88 Some(mod_path)
129} 89}
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index 6c357c915..a9c1e13e2 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -278,9 +278,11 @@ impl TestDB {
278 let node = ast.to_node(self.upcast()); 278 let node = ast.to_node(self.upcast());
279 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate") 279 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
280 } 280 }
281 DefDiagnosticKind::UnresolvedImport { ast, .. } => { 281 DefDiagnosticKind::UnresolvedImport { id, .. } => {
282 let node = ast.to_node(self.upcast()); 282 let item_tree = id.item_tree(self.upcast());
283 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport") 283 let import = &item_tree[id.value];
284 let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast());
285 (InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport")
284 } 286 }
285 DefDiagnosticKind::UnconfiguredCode { ast, .. } => { 287 DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
286 let node = ast.to_node(self.upcast()); 288 let node = ast.to_node(self.upcast());
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index dcac7c76d..6cf5810fa 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -311,6 +311,7 @@ mod tests {
311 /// * a diagnostic is produced 311 /// * a diagnostic is produced
312 /// * the first diagnostic fix trigger range touches the input cursor position 312 /// * the first diagnostic fix trigger range touches the input cursor position
313 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 313 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
314 #[track_caller]
314 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 315 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
315 check_nth_fix(0, ra_fixture_before, ra_fixture_after); 316 check_nth_fix(0, ra_fixture_before, ra_fixture_after);
316 } 317 }
@@ -325,6 +326,7 @@ mod tests {
325 } 326 }
326 } 327 }
327 328
329 #[track_caller]
328 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { 330 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
329 let after = trim_indent(ra_fixture_after); 331 let after = trim_indent(ra_fixture_after);
330 332