diff options
Diffstat (limited to 'crates/hir_def/src/item_tree.rs')
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 164 |
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 | ||
3 | mod lower; | 33 | mod lower; |
4 | mod pretty; | 34 | mod 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)] |
498 | pub struct Import { | 527 | pub 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, | 534 | pub struct UseTree { |
535 | pub index: Idx<ast::UseTree>, | ||
536 | kind: UseTreeKind, | ||
537 | } | ||
538 | |||
539 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
540 | pub 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 | ||
731 | impl 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 | |||
750 | impl 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 | |||
684 | macro_rules! impl_froms { | 822 | macro_rules! impl_froms { |
685 | ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { | 823 | ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { |
686 | $( | 824 | $( |