diff options
Diffstat (limited to 'crates/ra_hir_def/src/item_tree.rs')
-rw-r--r-- | crates/ra_hir_def/src/item_tree.rs | 133 |
1 files changed, 116 insertions, 17 deletions
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs index aca2503a0..9a5dd701e 100644 --- a/crates/ra_hir_def/src/item_tree.rs +++ b/crates/ra_hir_def/src/item_tree.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! A simplified AST that only contains items. | 1 | //! A simplified AST that only contains items. |
2 | 2 | ||
3 | mod lower; | 3 | mod lower; |
4 | #[cfg(test)] | ||
5 | mod tests; | ||
4 | 6 | ||
5 | use std::{ | 7 | use std::{ |
6 | fmt::{self, Debug}, | 8 | fmt::{self, Debug}, |
@@ -31,16 +33,20 @@ use crate::{ | |||
31 | type_ref::{Mutability, TypeBound, TypeRef}, | 33 | type_ref::{Mutability, TypeBound, TypeRef}, |
32 | visibility::RawVisibility, | 34 | visibility::RawVisibility, |
33 | }; | 35 | }; |
36 | use smallvec::SmallVec; | ||
34 | 37 | ||
35 | /// The item tree of a source file. | 38 | /// The item tree of a source file. |
36 | #[derive(Debug, Default, Eq, PartialEq)] | 39 | #[derive(Debug, Eq, PartialEq)] |
37 | pub struct ItemTree { | 40 | pub struct ItemTree { |
41 | file_id: HirFileId, | ||
38 | top_level: Vec<ModItem>, | 42 | top_level: Vec<ModItem>, |
39 | top_attrs: Attrs, | 43 | top_attrs: Attrs, |
40 | attrs: FxHashMap<ModItem, Attrs>, | 44 | attrs: FxHashMap<ModItem, Attrs>, |
41 | empty_attrs: Attrs, | 45 | empty_attrs: Attrs, |
46 | inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>, | ||
42 | 47 | ||
43 | imports: Arena<Import>, | 48 | imports: Arena<Import>, |
49 | extern_crates: Arena<ExternCrate>, | ||
44 | functions: Arena<Function>, | 50 | functions: Arena<Function>, |
45 | structs: Arena<Struct>, | 51 | structs: Arena<Struct>, |
46 | fields: Arena<Field>, | 52 | fields: Arena<Field>, |
@@ -63,7 +69,7 @@ impl ItemTree { | |||
63 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | 69 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { |
64 | node | 70 | node |
65 | } else { | 71 | } else { |
66 | return Default::default(); | 72 | return Arc::new(Self::empty(file_id)); |
67 | }; | 73 | }; |
68 | 74 | ||
69 | let hygiene = Hygiene::new(db.upcast(), file_id); | 75 | let hygiene = Hygiene::new(db.upcast(), file_id); |
@@ -80,20 +86,41 @@ impl ItemTree { | |||
80 | file_storage = file; | 86 | file_storage = file; |
81 | &file_storage | 87 | &file_storage |
82 | }, | 88 | }, |
83 | _ => return Default::default(), | 89 | _ => return Arc::new(Self::empty(file_id)), |
84 | } | 90 | } |
85 | }; | 91 | }; |
86 | 92 | ||
87 | let map = db.ast_id_map(file_id); | 93 | let ctx = lower::Ctx::new(db, hygiene, file_id); |
88 | let mut ctx = lower::Ctx { | 94 | let mut item_tree = ctx.lower(item_owner); |
89 | tree: ItemTree::default(), | 95 | item_tree.top_attrs = top_attrs.unwrap_or_default(); |
90 | hygiene, | 96 | Arc::new(item_tree) |
91 | file: file_id, | 97 | } |
92 | source_ast_id_map: map, | 98 | |
93 | body_ctx: crate::body::LowerCtx::new(db, file_id), | 99 | fn empty(file_id: HirFileId) -> Self { |
94 | }; | 100 | Self { |
95 | ctx.tree.top_attrs = top_attrs.unwrap_or_default(); | 101 | file_id, |
96 | Arc::new(ctx.lower(item_owner)) | 102 | top_level: Default::default(), |
103 | top_attrs: Default::default(), | ||
104 | attrs: Default::default(), | ||
105 | empty_attrs: Default::default(), | ||
106 | inner_items: Default::default(), | ||
107 | imports: Default::default(), | ||
108 | extern_crates: Default::default(), | ||
109 | functions: Default::default(), | ||
110 | structs: Default::default(), | ||
111 | fields: Default::default(), | ||
112 | unions: Default::default(), | ||
113 | enums: Default::default(), | ||
114 | variants: Default::default(), | ||
115 | consts: Default::default(), | ||
116 | statics: Default::default(), | ||
117 | traits: Default::default(), | ||
118 | impls: Default::default(), | ||
119 | type_aliases: Default::default(), | ||
120 | mods: Default::default(), | ||
121 | macro_calls: Default::default(), | ||
122 | exprs: Default::default(), | ||
123 | } | ||
97 | } | 124 | } |
98 | 125 | ||
99 | /// Returns an iterator over all items located at the top level of the `HirFileId` this | 126 | /// Returns an iterator over all items located at the top level of the `HirFileId` this |
@@ -110,17 +137,49 @@ impl ItemTree { | |||
110 | pub fn attrs(&self, of: ModItem) -> &Attrs { | 137 | pub fn attrs(&self, of: ModItem) -> &Attrs { |
111 | self.attrs.get(&of).unwrap_or(&self.empty_attrs) | 138 | self.attrs.get(&of).unwrap_or(&self.empty_attrs) |
112 | } | 139 | } |
140 | |||
141 | /// Returns the lowered inner items that `ast` corresponds to. | ||
142 | /// | ||
143 | /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered | ||
144 | /// to multiple items in the `ItemTree`. | ||
145 | pub fn inner_items(&self, ast: FileAstId<ast::ModuleItem>) -> &[ModItem] { | ||
146 | &self.inner_items[&ast] | ||
147 | } | ||
148 | |||
149 | pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { | ||
150 | self.inner_items.values().flatten().copied() | ||
151 | } | ||
152 | |||
153 | pub fn source<S: ItemTreeSource>( | ||
154 | &self, | ||
155 | db: &dyn DefDatabase, | ||
156 | of: FileItemTreeId<S>, | ||
157 | ) -> S::Source { | ||
158 | // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty | ||
159 | // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with). | ||
160 | let root = db | ||
161 | .parse_or_expand(self.file_id) | ||
162 | .expect("parse_or_expand failed on constructed ItemTree"); | ||
163 | |||
164 | let id = self[of].ast_id(); | ||
165 | let map = db.ast_id_map(self.file_id); | ||
166 | let ptr = map.get(id); | ||
167 | ptr.to_node(&root) | ||
168 | } | ||
113 | } | 169 | } |
114 | 170 | ||
115 | /// Trait implemented by all nodes in the item tree. | 171 | /// Trait implemented by all nodes in the item tree. |
116 | pub trait ItemTreeNode: Clone { | 172 | pub trait ItemTreeNode: Clone { |
117 | /// Looks up an instance of `Self` in an item tree. | 173 | /// Looks up an instance of `Self` in an item tree. |
118 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self; | 174 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self; |
175 | |||
176 | /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type. | ||
177 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>; | ||
119 | } | 178 | } |
120 | 179 | ||
121 | /// Trait for item tree nodes that allow accessing the original AST node. | 180 | /// Trait for item tree nodes that allow accessing the original AST node. |
122 | pub trait ItemTreeSource: ItemTreeNode { | 181 | pub trait ItemTreeSource: ItemTreeNode { |
123 | type Source: AstNode; | 182 | type Source: AstNode + Into<ast::ModuleItem>; |
124 | 183 | ||
125 | fn ast_id(&self) -> FileAstId<Self::Source>; | 184 | fn ast_id(&self) -> FileAstId<Self::Source>; |
126 | } | 185 | } |
@@ -164,12 +223,22 @@ macro_rules! nodes { | |||
164 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { | 223 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { |
165 | &tree.$fld[index] | 224 | &tree.$fld[index] |
166 | } | 225 | } |
226 | |||
227 | |||
228 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> { | ||
229 | if let ModItem::$node(id) = mod_item { | ||
230 | Some(id) | ||
231 | } else { | ||
232 | None | ||
233 | } | ||
234 | } | ||
167 | } | 235 | } |
168 | )+ }; | 236 | )+ }; |
169 | } | 237 | } |
170 | 238 | ||
171 | nodes!( | 239 | nodes!( |
172 | Import in imports, | 240 | Import in imports, |
241 | ExternCrate in extern_crates, | ||
173 | Function in functions, | 242 | Function in functions, |
174 | Struct in structs, | 243 | Struct in structs, |
175 | Union in unions, | 244 | Union in unions, |
@@ -196,6 +265,8 @@ macro_rules! source { | |||
196 | } | 265 | } |
197 | 266 | ||
198 | source! { | 267 | source! { |
268 | Import -> ast::UseItem, | ||
269 | ExternCrate -> ast::ExternCrateItem, | ||
199 | Function -> ast::FnDef, | 270 | Function -> ast::FnDef, |
200 | Struct -> ast::StructDef, | 271 | Struct -> ast::StructDef, |
201 | Union -> ast::UnionDef, | 272 | Union -> ast::UnionDef, |
@@ -248,7 +319,7 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { | |||
248 | } | 319 | } |
249 | } | 320 | } |
250 | 321 | ||
251 | /// A desugared `extern crate` or `use` import. | 322 | /// A desugared `use` import. |
252 | #[derive(Debug, Clone, Eq, PartialEq)] | 323 | #[derive(Debug, Clone, Eq, PartialEq)] |
253 | pub struct Import { | 324 | pub struct Import { |
254 | pub path: ModPath, | 325 | pub path: ModPath, |
@@ -256,8 +327,19 @@ pub struct Import { | |||
256 | pub visibility: RawVisibility, | 327 | pub visibility: RawVisibility, |
257 | pub is_glob: bool, | 328 | pub is_glob: bool, |
258 | pub is_prelude: bool, | 329 | pub is_prelude: bool, |
259 | pub is_extern_crate: bool, | 330 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many |
331 | /// `Import`s can map to the same `use` item. | ||
332 | pub ast_id: FileAstId<ast::UseItem>, | ||
333 | } | ||
334 | |||
335 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
336 | pub struct ExternCrate { | ||
337 | pub path: ModPath, | ||
338 | pub alias: Option<ImportAlias>, | ||
339 | pub visibility: RawVisibility, | ||
340 | /// Whether this is a `#[macro_use] extern crate ...`. | ||
260 | pub is_macro_use: bool, | 341 | pub is_macro_use: bool, |
342 | pub ast_id: FileAstId<ast::ExternCrateItem>, | ||
261 | } | 343 | } |
262 | 344 | ||
263 | #[derive(Debug, Clone, Eq, PartialEq)] | 345 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -270,7 +352,6 @@ pub struct Function { | |||
270 | pub params: Vec<TypeRef>, | 352 | pub params: Vec<TypeRef>, |
271 | pub ret_type: TypeRef, | 353 | pub ret_type: TypeRef, |
272 | pub ast_id: FileAstId<ast::FnDef>, | 354 | pub ast_id: FileAstId<ast::FnDef>, |
273 | // FIXME inner items | ||
274 | } | 355 | } |
275 | 356 | ||
276 | #[derive(Debug, Clone, Eq, PartialEq)] | 357 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -412,6 +493,7 @@ macro_rules! impl_froms { | |||
412 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | 493 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
413 | pub enum ModItem { | 494 | pub enum ModItem { |
414 | Import(FileItemTreeId<Import>), | 495 | Import(FileItemTreeId<Import>), |
496 | ExternCrate(FileItemTreeId<ExternCrate>), | ||
415 | Function(FileItemTreeId<Function>), | 497 | Function(FileItemTreeId<Function>), |
416 | Struct(FileItemTreeId<Struct>), | 498 | Struct(FileItemTreeId<Struct>), |
417 | Union(FileItemTreeId<Union>), | 499 | Union(FileItemTreeId<Union>), |
@@ -429,6 +511,7 @@ impl ModItem { | |||
429 | pub fn as_assoc_item(&self) -> Option<AssocItem> { | 511 | pub fn as_assoc_item(&self) -> Option<AssocItem> { |
430 | match self { | 512 | match self { |
431 | ModItem::Import(_) | 513 | ModItem::Import(_) |
514 | | ModItem::ExternCrate(_) | ||
432 | | ModItem::Struct(_) | 515 | | ModItem::Struct(_) |
433 | | ModItem::Union(_) | 516 | | ModItem::Union(_) |
434 | | ModItem::Enum(_) | 517 | | ModItem::Enum(_) |
@@ -442,10 +525,15 @@ impl ModItem { | |||
442 | ModItem::Function(func) => Some(AssocItem::Function(*func)), | 525 | ModItem::Function(func) => Some(AssocItem::Function(*func)), |
443 | } | 526 | } |
444 | } | 527 | } |
528 | |||
529 | pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> { | ||
530 | N::id_from_mod_item(self) | ||
531 | } | ||
445 | } | 532 | } |
446 | 533 | ||
447 | impl_froms!(ModItem { | 534 | impl_froms!(ModItem { |
448 | Import(FileItemTreeId<Import>), | 535 | Import(FileItemTreeId<Import>), |
536 | ExternCrate(FileItemTreeId<ExternCrate>), | ||
449 | Function(FileItemTreeId<Function>), | 537 | Function(FileItemTreeId<Function>), |
450 | Struct(FileItemTreeId<Struct>), | 538 | Struct(FileItemTreeId<Struct>), |
451 | Union(FileItemTreeId<Union>), | 539 | Union(FileItemTreeId<Union>), |
@@ -474,6 +562,17 @@ impl_froms!(AssocItem { | |||
474 | MacroCall(FileItemTreeId<MacroCall>), | 562 | MacroCall(FileItemTreeId<MacroCall>), |
475 | }); | 563 | }); |
476 | 564 | ||
565 | impl From<AssocItem> for ModItem { | ||
566 | fn from(item: AssocItem) -> Self { | ||
567 | match item { | ||
568 | AssocItem::Function(it) => it.into(), | ||
569 | AssocItem::TypeAlias(it) => it.into(), | ||
570 | AssocItem::Const(it) => it.into(), | ||
571 | AssocItem::MacroCall(it) => it.into(), | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | |||
477 | #[derive(Debug, Eq, PartialEq)] | 576 | #[derive(Debug, Eq, PartialEq)] |
478 | pub struct Variant { | 577 | pub struct Variant { |
479 | pub name: Name, | 578 | pub name: Name, |