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.rs164
1 files changed, 151 insertions, 13 deletions
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 528270d49..227337a8d 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -1,4 +1,34 @@
1//! A simplified AST that only contains items. 1//! A simplified AST that only contains items.
2//!
3//! This is the primary IR used throughout `hir_def`. It is the input to the name resolution
4//! algorithm, as well as to the queries defined in `adt.rs`, `data.rs`, and most things in
5//! `attr.rs`.
6//!
7//! `ItemTree`s are built per `HirFileId`, from the syntax tree of the parsed file. This means that
8//! they are crate-independent: they don't know which `#[cfg]`s are active or which module they
9//! belong to, since those concepts don't exist at this level (a single `ItemTree` might be part of
10//! multiple crates, or might be included into the same crate twice via `#[path]`).
11//!
12//! One important purpose of this layer is to provide an "invalidation barrier" for incremental
13//! computations: when typing inside an item body, the `ItemTree` of the modified file is typically
14//! unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`).
15//!
16//! The `ItemTree` for the currently open file can be displayed by using the VS Code command
17//! "Rust Analyzer: Debug ItemTree".
18//!
19//! Compared to rustc's architecture, `ItemTree` has properties from both rustc's AST and HIR: many
20//! syntax-level Rust features are already desugared to simpler forms in the `ItemTree`, but name
21//! resolution has not yet been performed. `ItemTree`s are per-file, while rustc's AST and HIR are
22//! per-crate, because we are interested in incrementally computing it.
23//!
24//! The representation of items in the `ItemTree` should generally mirror the surface syntax: it is
25//! usually a bad idea to desugar a syntax-level construct to something that is structurally
26//! different here. Name resolution needs to be able to process attributes and expand macros
27//! (including attribute macros), and having a 1-to-1 mapping between syntax and the `ItemTree`
28//! avoids introducing subtle bugs.
29//!
30//! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its
31//! surface syntax.
2 32
3mod lower; 33mod lower;
4mod pretty; 34mod pretty;
@@ -493,21 +523,38 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
493 } 523 }
494} 524}
495 525
496/// A desugared `use` import.
497#[derive(Debug, Clone, Eq, PartialEq)] 526#[derive(Debug, Clone, Eq, PartialEq)]
498pub struct Import { 527pub struct Import {
499 pub path: Interned<ModPath>,
500 pub alias: Option<ImportAlias>,
501 pub visibility: RawVisibilityId, 528 pub visibility: RawVisibilityId,
502 pub is_glob: bool,
503 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
504 /// `Import`s can map to the same `use` item.
505 pub ast_id: FileAstId<ast::Use>, 529 pub ast_id: FileAstId<ast::Use>,
506 /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`. 530 pub use_tree: UseTree,
507 /// 531}
508 /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting 532
509 /// precise diagnostics. 533#[derive(Debug, Clone, Eq, PartialEq)]
510 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]> },
511} 558}
512 559
513#[derive(Debug, Clone, Eq, PartialEq)] 560#[derive(Debug, Clone, Eq, PartialEq)]
@@ -533,6 +580,7 @@ pub struct Function {
533 pub abi: Option<Interned<str>>, 580 pub abi: Option<Interned<str>>,
534 pub params: IdRange<Param>, 581 pub params: IdRange<Param>,
535 pub ret_type: Interned<TypeRef>, 582 pub ret_type: Interned<TypeRef>,
583 pub async_ret_type: Option<Interned<TypeRef>>,
536 pub ast_id: FileAstId<ast::Fn>, 584 pub ast_id: FileAstId<ast::Fn>,
537 pub(crate) flags: FnFlags, 585 pub(crate) flags: FnFlags,
538} 586}
@@ -614,7 +662,6 @@ pub struct Trait {
614 pub generic_params: Interned<GenericParams>, 662 pub generic_params: Interned<GenericParams>,
615 pub is_auto: bool, 663 pub is_auto: bool,
616 pub is_unsafe: bool, 664 pub is_unsafe: bool,
617 pub bounds: Box<[TypeBound]>,
618 pub items: Box<[AssocItem]>, 665 pub items: Box<[AssocItem]>,
619 pub ast_id: FileAstId<ast::Trait>, 666 pub ast_id: FileAstId<ast::Trait>,
620} 667}
@@ -634,7 +681,7 @@ pub struct TypeAlias {
634 pub name: Name, 681 pub name: Name,
635 pub visibility: RawVisibilityId, 682 pub visibility: RawVisibilityId,
636 /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. 683 /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
637 pub bounds: Box<[TypeBound]>, 684 pub bounds: Box<[Interned<TypeBound>]>,
638 pub generic_params: Interned<GenericParams>, 685 pub generic_params: Interned<GenericParams>,
639 pub type_ref: Option<Interned<TypeRef>>, 686 pub type_ref: Option<Interned<TypeRef>>,
640 pub is_extern: bool, 687 pub is_extern: bool,
@@ -681,6 +728,97 @@ pub struct MacroDef {
681 pub ast_id: FileAstId<ast::MacroDef>, 728 pub ast_id: FileAstId<ast::MacroDef>,
682} 729}
683 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
684macro_rules! impl_froms { 822macro_rules! impl_froms {
685 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { 823 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
686 $( 824 $(