aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-27 12:56:26 +0100
committerGitHub <[email protected]>2021-05-27 12:56:26 +0100
commitd0a4ba294ccf0c925a5ff1115c19a60c6a24b734 (patch)
tree5caf7619e3486f68516d81971abc3018ddee5323
parentbfb06e17acd4bcb623ad5656490a7a1971980441 (diff)
parent196cb65ead398f81340de431400103224d7de660 (diff)
Merge #8997
8997: internal: stop expanding UseTrees during ItemTree lowering r=jonas-schievink a=jonas-schievink Closes https://github.com/rust-analyzer/rust-analyzer/issues/8908 Messy diff, but `ItemTree` lowering got simpler, since we now have a strict 1-to-1 mapping between `ast::Item` and `ModItem`. The most messy part is mapping a single `UseTree` back to its `ast::UseTree` counterpart for diagnostics, but I think the ad-hoc source map built during lowering does the job. Co-authored-by: Jonas Schievink <[email protected]>
-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.rs184
-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, 351 insertions, 241 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..f84c4cf2b 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 /// ```
542 /// use path::to::Item;
543 /// use path::to::Item as Renamed;
544 /// use path::to::Trait as _;
545 /// ```
546 Single { path: Interned<ModPath>, alias: Option<ImportAlias> },
547
548 /// ```
549 /// use *; // (invalid, but can occur in nested tree)
550 /// use path::*;
551 /// ```
552 Glob { path: Option<Interned<ModPath>> },
553
554 /// ```
555 /// use prefix::{self, Item, ...};
556 /// ```
557 Prefixed { prefix: Option<Interned<ModPath>>, list: Box<[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..40f3428b7 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -3,7 +3,6 @@
3use std::{collections::hash_map::Entry, mem, sync::Arc}; 3use std::{collections::hash_map::Entry, mem, sync::Arc};
4 4
5use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}; 5use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId};
6use smallvec::SmallVec;
7use syntax::{ 6use syntax::{
8 ast::{self, ModuleItemOwner}, 7 ast::{self, ModuleItemOwner},
9 SyntaxNode, WalkEvent, 8 SyntaxNode, WalkEvent,
@@ -20,22 +19,10 @@ fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
20 FileItemTreeId { index, _p: PhantomData } 19 FileItemTreeId { index, _p: PhantomData }
21} 20}
22 21
23struct ModItems(SmallVec<[ModItem; 1]>);
24
25impl<T> From<T> for ModItems
26where
27 T: Into<ModItem>,
28{
29 fn from(t: T) -> Self {
30 ModItems(SmallVec::from_buf([t.into(); 1]))
31 }
32}
33
34pub(super) struct Ctx<'a> { 22pub(super) struct Ctx<'a> {
35 db: &'a dyn DefDatabase, 23 db: &'a dyn DefDatabase,
36 tree: ItemTree, 24 tree: ItemTree,
37 hygiene: Hygiene, 25 hygiene: Hygiene,
38 file: HirFileId,
39 source_ast_id_map: Arc<AstIdMap>, 26 source_ast_id_map: Arc<AstIdMap>,
40 body_ctx: crate::body::LowerCtx<'a>, 27 body_ctx: crate::body::LowerCtx<'a>,
41 forced_visibility: Option<RawVisibilityId>, 28 forced_visibility: Option<RawVisibilityId>,
@@ -47,7 +34,6 @@ impl<'a> Ctx<'a> {
47 db, 34 db,
48 tree: ItemTree::default(), 35 tree: ItemTree::default(),
49 hygiene, 36 hygiene,
50 file,
51 source_ast_id_map: db.ast_id_map(file), 37 source_ast_id_map: db.ast_id_map(file),
52 body_ctx: crate::body::LowerCtx::new(db, file), 38 body_ctx: crate::body::LowerCtx::new(db, file),
53 forced_visibility: None, 39 forced_visibility: None,
@@ -55,11 +41,8 @@ impl<'a> Ctx<'a> {
55 } 41 }
56 42
57 pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree { 43 pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
58 self.tree.top_level = item_owner 44 self.tree.top_level =
59 .items() 45 item_owner.items().flat_map(|item| self.lower_mod_item(&item, false)).collect();
60 .flat_map(|item| self.lower_mod_item(&item, false))
61 .flat_map(|items| items.0)
62 .collect();
63 self.tree 46 self.tree
64 } 47 }
65 48
@@ -71,7 +54,6 @@ impl<'a> Ctx<'a> {
71 _ => None, 54 _ => None,
72 }) 55 })
73 .flat_map(|item| self.lower_mod_item(&item, false)) 56 .flat_map(|item| self.lower_mod_item(&item, false))
74 .flat_map(|items| items.0)
75 .collect(); 57 .collect();
76 58
77 // Non-items need to have their inner items collected. 59 // Non-items need to have their inner items collected.
@@ -98,7 +80,7 @@ impl<'a> Ctx<'a> {
98 self.tree.data_mut() 80 self.tree.data_mut()
99 } 81 }
100 82
101 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> { 83 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItem> {
102 // Collect inner items for 1-to-1-lowered items. 84 // Collect inner items for 1-to-1-lowered items.
103 match item { 85 match item {
104 ast::Item::Struct(_) 86 ast::Item::Struct(_)
@@ -129,34 +111,28 @@ impl<'a> Ctx<'a> {
129 }; 111 };
130 112
131 let attrs = RawAttrs::new(self.db, item, &self.hygiene); 113 let attrs = RawAttrs::new(self.db, item, &self.hygiene);
132 let items = match item { 114 let item: ModItem = match item {
133 ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into), 115 ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
134 ast::Item::Union(ast) => self.lower_union(ast).map(Into::into), 116 ast::Item::Union(ast) => self.lower_union(ast)?.into(),
135 ast::Item::Enum(ast) => self.lower_enum(ast).map(Into::into), 117 ast::Item::Enum(ast) => self.lower_enum(ast)?.into(),
136 ast::Item::Fn(ast) => self.lower_function(ast).map(Into::into), 118 ast::Item::Fn(ast) => self.lower_function(ast)?.into(),
137 ast::Item::TypeAlias(ast) => self.lower_type_alias(ast).map(Into::into), 119 ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?.into(),
138 ast::Item::Static(ast) => self.lower_static(ast).map(Into::into), 120 ast::Item::Static(ast) => self.lower_static(ast)?.into(),
139 ast::Item::Const(ast) => Some(self.lower_const(ast).into()), 121 ast::Item::Const(ast) => self.lower_const(ast).into(),
140 ast::Item::Module(ast) => self.lower_module(ast).map(Into::into), 122 ast::Item::Module(ast) => self.lower_module(ast)?.into(),
141 ast::Item::Trait(ast) => self.lower_trait(ast).map(Into::into), 123 ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
142 ast::Item::Impl(ast) => self.lower_impl(ast).map(Into::into), 124 ast::Item::Impl(ast) => self.lower_impl(ast)?.into(),
143 ast::Item::Use(ast) => Some(ModItems( 125 ast::Item::Use(ast) => self.lower_use(ast)?.into(),
144 self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(), 126 ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
145 )), 127 ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(),
146 ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into), 128 ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
147 ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), 129 ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
148 ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), 130 ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
149 ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into),
150 ast::Item::ExternBlock(ast) => Some(self.lower_extern_block(ast).into()),
151 }; 131 };
152 132
153 if !attrs.is_empty() { 133 self.add_attrs(item.into(), attrs.clone());
154 for item in items.iter().flat_map(|items| &items.0) {
155 self.add_attrs((*item).into(), attrs.clone());
156 }
157 }
158 134
159 items 135 Some(item)
160 } 136 }
161 137
162 fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) { 138 fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) {
@@ -190,12 +166,10 @@ impl<'a> Ctx<'a> {
190 }, 166 },
191 ast::Item(item) => { 167 ast::Item(item) => {
192 // FIXME: This triggers for macro calls in expression/pattern/type position 168 // FIXME: This triggers for macro calls in expression/pattern/type position
193 let mod_items = self.lower_mod_item(&item, true); 169 let mod_item = self.lower_mod_item(&item, true);
194 let current_block = block_stack.last(); 170 let current_block = block_stack.last();
195 if let (Some(mod_items), Some(block)) = (mod_items, current_block) { 171 if let (Some(mod_item), Some(block)) = (mod_item, current_block) {
196 if !mod_items.0.is_empty() { 172 self.data().inner_items.entry(*block).or_default().push(mod_item);
197 self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied());
198 }
199 } 173 }
200 }, 174 },
201 _ => {} 175 _ => {}
@@ -480,10 +454,7 @@ impl<'a> Ctx<'a> {
480 items: module 454 items: module
481 .item_list() 455 .item_list()
482 .map(|list| { 456 .map(|list| {
483 list.items() 457 list.items().flat_map(|item| self.lower_mod_item(&item, false)).collect()
484 .flat_map(|item| self.lower_mod_item(&item, false))
485 .flat_map(|items| items.0)
486 .collect()
487 }) 458 })
488 .unwrap_or_else(|| { 459 .unwrap_or_else(|| {
489 cov_mark::hit!(name_res_works_for_broken_modules); 460 cov_mark::hit!(name_res_works_for_broken_modules);
@@ -561,30 +532,13 @@ impl<'a> Ctx<'a> {
561 Some(id(self.data().impls.alloc(res))) 532 Some(id(self.data().impls.alloc(res)))
562 } 533 }
563 534
564 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> { 535 fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Import>> {
565 let visibility = self.lower_visibility(use_item); 536 let visibility = self.lower_visibility(use_item);
566 let ast_id = self.source_ast_id_map.ast_id(use_item); 537 let ast_id = self.source_ast_id_map.ast_id(use_item);
538 let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?;
567 539
568 // Every use item can expand to many `Import`s. 540 let res = Import { visibility, ast_id, use_tree };
569 let mut imports = Vec::new(); 541 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 } 542 }
589 543
590 fn lower_extern_crate( 544 fn lower_extern_crate(
@@ -884,3 +838,81 @@ fn lower_abi(abi: ast::Abi) -> Interned<str> {
884 } 838 }
885 } 839 }
886} 840}
841
842struct UseTreeLowering<'a> {
843 db: &'a dyn DefDatabase,
844 hygiene: &'a Hygiene,
845 mapping: Arena<ast::UseTree>,
846}
847
848impl UseTreeLowering<'_> {
849 fn lower_use_tree(&mut self, tree: ast::UseTree) -> Option<UseTree> {
850 if let Some(use_tree_list) = tree.use_tree_list() {
851 let prefix = match tree.path() {
852 // E.g. use something::{{{inner}}};
853 None => None,
854 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
855 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
856 Some(path) => {
857 match ModPath::from_src(self.db, path, &self.hygiene) {
858 Some(it) => Some(it),
859 None => return None, // FIXME: report errors somewhere
860 }
861 }
862 };
863
864 let list =
865 use_tree_list.use_trees().filter_map(|tree| self.lower_use_tree(tree)).collect();
866
867 Some(
868 self.use_tree(
869 UseTreeKind::Prefixed { prefix: prefix.map(Interned::new), list },
870 tree,
871 ),
872 )
873 } else {
874 let is_glob = tree.star_token().is_some();
875 let path = match tree.path() {
876 Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?),
877 None => None,
878 };
879 let alias = tree.rename().map(|a| {
880 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
881 });
882 if alias.is_some() && is_glob {
883 return None;
884 }
885
886 match (path, alias, is_glob) {
887 (path, None, true) => {
888 if path.is_none() {
889 cov_mark::hit!(glob_enum_group);
890 }
891 Some(self.use_tree(UseTreeKind::Glob { path: path.map(Interned::new) }, tree))
892 }
893 // Globs can't be renamed
894 (_, Some(_), true) | (None, None, false) => None,
895 // `bla::{ as Name}` is invalid
896 (None, Some(_), false) => None,
897 (Some(path), alias, false) => Some(
898 self.use_tree(UseTreeKind::Single { path: Interned::new(path), alias }, tree),
899 ),
900 }
901 }
902 }
903
904 fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree {
905 let index = self.mapping.alloc(ast);
906 UseTree { index, kind }
907 }
908}
909
910pub(super) fn lower_use_tree(
911 db: &dyn DefDatabase,
912 hygiene: &Hygiene,
913 tree: ast::UseTree,
914) -> Option<(UseTree, Arena<ast::UseTree>)> {
915 let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
916 let tree = lowering.lower_use_tree(tree)?;
917 Some((tree, lowering.mapping))
918}
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