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.rs131
1 files changed, 119 insertions, 12 deletions
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 11767d100..c4d20c416 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)]
@@ -644,7 +661,6 @@ pub struct Trait {
644 pub generic_params: Interned<GenericParams>, 661 pub generic_params: Interned<GenericParams>,
645 pub is_auto: bool, 662 pub is_auto: bool,
646 pub is_unsafe: bool, 663 pub is_unsafe: bool,
647 pub bounds: Box<[Interned<TypeBound>]>,
648 pub items: Box<[AssocItem]>, 664 pub items: Box<[AssocItem]>,
649 pub ast_id: FileAstId<ast::Trait>, 665 pub ast_id: FileAstId<ast::Trait>,
650} 666}
@@ -711,6 +727,97 @@ pub struct MacroDef {
711 pub ast_id: FileAstId<ast::MacroDef>, 727 pub ast_id: FileAstId<ast::MacroDef>,
712} 728}
713 729
730impl Import {
731 /// Maps a `UseTree` contained in this import back to its AST node.
732 pub fn use_tree_to_ast(
733 &self,
734 db: &dyn DefDatabase,
735 file_id: HirFileId,
736 index: Idx<ast::UseTree>,
737 ) -> ast::UseTree {
738 // Re-lower the AST item and get the source map.
739 // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
740 let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
741 let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
742 let hygiene = Hygiene::new(db.upcast(), file_id);
743 let (_, source_map) =
744 lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
745 source_map[index].clone()
746 }
747}
748
749impl UseTree {
750 /// Expands the `UseTree` into individually imported `ModPath`s.
751 pub fn expand(
752 &self,
753 mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, /* is_glob */ bool, Option<ImportAlias>),
754 ) {
755 self.expand_impl(None, &mut cb)
756 }
757
758 fn expand_impl(
759 &self,
760 prefix: Option<ModPath>,
761 cb: &mut dyn FnMut(
762 Idx<ast::UseTree>,
763 ModPath,
764 /* is_glob */ bool,
765 Option<ImportAlias>,
766 ),
767 ) {
768 fn concat_mod_paths(prefix: Option<ModPath>, path: &ModPath) -> Option<ModPath> {
769 match (prefix, &path.kind) {
770 (None, _) => Some(path.clone()),
771 (Some(mut prefix), PathKind::Plain) => {
772 for segment in path.segments() {
773 prefix.push_segment(segment.clone());
774 }
775 Some(prefix)
776 }
777 (Some(prefix), PathKind::Super(0)) => {
778 // `some::path::self` == `some::path`
779 if path.segments().is_empty() {
780 Some(prefix)
781 } else {
782 None
783 }
784 }
785 (Some(_), _) => None,
786 }
787 }
788
789 match &self.kind {
790 UseTreeKind::Single { path, alias } => {
791 if let Some(path) = concat_mod_paths(prefix, path) {
792 cb(self.index, path, false, alias.clone());
793 }
794 }
795 UseTreeKind::Glob { path: Some(path) } => {
796 if let Some(path) = concat_mod_paths(prefix, path) {
797 cb(self.index, path, true, None);
798 }
799 }
800 UseTreeKind::Glob { path: None } => {
801 if let Some(prefix) = prefix {
802 cb(self.index, prefix, true, None);
803 }
804 }
805 UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
806 let prefix = match additional_prefix {
807 Some(path) => match concat_mod_paths(prefix, path) {
808 Some(path) => Some(path),
809 None => return,
810 },
811 None => prefix,
812 };
813 for tree in &**list {
814 tree.expand_impl(prefix.clone(), cb);
815 }
816 }
817 }
818 }
819}
820
714macro_rules! impl_froms { 821macro_rules! impl_froms {
715 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { 822 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
716 $( 823 $(