aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/item_tree.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/item_tree.rs')
-rw-r--r--crates/hir_def/src/item_tree.rs130
1 files changed, 119 insertions, 11 deletions
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 $(