diff options
author | Jonas Schievink <[email protected]> | 2020-06-22 14:07:06 +0100 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-06-24 15:53:16 +0100 |
commit | 4b03b39d5b4b00daffb120a4d2d9ea4a55a9a7ac (patch) | |
tree | 85431e53ce86bbcf16ba9b38fcc5f2ad27378722 /crates/ra_hir_def/src/item_tree | |
parent | b94caeb88b4aab7219d4b2f5c8c6c668199247fb (diff) |
draw the rest of the owl
Diffstat (limited to 'crates/ra_hir_def/src/item_tree')
-rw-r--r-- | crates/ra_hir_def/src/item_tree/lower.rs | 271 | ||||
-rw-r--r-- | crates/ra_hir_def/src/item_tree/tests.rs | 254 |
2 files changed, 409 insertions, 116 deletions
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs index 737a69c30..f2b8a9418 100644 --- a/crates/ra_hir_def/src/item_tree/lower.rs +++ b/crates/ra_hir_def/src/item_tree/lower.rs | |||
@@ -7,9 +7,12 @@ use crate::{ | |||
7 | }; | 7 | }; |
8 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; | 8 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; |
9 | use ra_arena::map::ArenaMap; | 9 | use ra_arena::map::ArenaMap; |
10 | use ra_syntax::ast::{self, ModuleItemOwner}; | 10 | use ra_syntax::{ |
11 | ast::{self, ModuleItemOwner}, | ||
12 | SyntaxNode, | ||
13 | }; | ||
11 | use smallvec::SmallVec; | 14 | use smallvec::SmallVec; |
12 | use std::sync::Arc; | 15 | use std::{mem, sync::Arc}; |
13 | 16 | ||
14 | fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> { | 17 | fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> { |
15 | FileItemTreeId { index, _p: PhantomData } | 18 | FileItemTreeId { index, _p: PhantomData } |
@@ -27,78 +30,81 @@ where | |||
27 | } | 30 | } |
28 | 31 | ||
29 | pub(super) struct Ctx { | 32 | pub(super) struct Ctx { |
30 | pub tree: ItemTree, | 33 | tree: ItemTree, |
31 | pub hygiene: Hygiene, | 34 | hygiene: Hygiene, |
32 | pub file: HirFileId, | 35 | file: HirFileId, |
33 | pub source_ast_id_map: Arc<AstIdMap>, | 36 | source_ast_id_map: Arc<AstIdMap>, |
34 | pub body_ctx: crate::body::LowerCtx, | 37 | body_ctx: crate::body::LowerCtx, |
38 | inner_items: Vec<ModItem>, | ||
35 | } | 39 | } |
36 | 40 | ||
37 | impl Ctx { | 41 | impl Ctx { |
42 | pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { | ||
43 | Self { | ||
44 | tree: ItemTree::empty(file), | ||
45 | hygiene, | ||
46 | file, | ||
47 | source_ast_id_map: db.ast_id_map(file), | ||
48 | body_ctx: crate::body::LowerCtx::new(db, file), | ||
49 | inner_items: Vec::new(), | ||
50 | } | ||
51 | } | ||
52 | |||
38 | pub(super) fn lower(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree { | 53 | pub(super) fn lower(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree { |
39 | self.tree.top_level = item_owner | 54 | self.tree.top_level = item_owner |
40 | .items() | 55 | .items() |
41 | .flat_map(|item| self.lower_mod_item(&item)) | 56 | .flat_map(|item| self.lower_mod_item(&item, false)) |
42 | .flat_map(|items| items.0) | 57 | .flat_map(|items| items.0) |
43 | .collect(); | 58 | .collect(); |
44 | self.tree | 59 | self.tree |
45 | } | 60 | } |
46 | 61 | ||
47 | fn lower_mod_item(&mut self, item: &ast::ModuleItem) -> Option<ModItems> { | 62 | fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> { |
63 | assert!(inner || self.inner_items.is_empty()); | ||
64 | |||
65 | // Collect inner items for 1-to-1-lowered items. | ||
66 | match item { | ||
67 | ast::ModuleItem::StructDef(_) | ||
68 | | ast::ModuleItem::UnionDef(_) | ||
69 | | ast::ModuleItem::EnumDef(_) | ||
70 | | ast::ModuleItem::FnDef(_) | ||
71 | | ast::ModuleItem::TypeAliasDef(_) | ||
72 | | ast::ModuleItem::ConstDef(_) | ||
73 | | ast::ModuleItem::StaticDef(_) | ||
74 | | ast::ModuleItem::MacroCall(_) => self.collect_inner_items(item.syntax()), | ||
75 | |||
76 | // These are handled in their respective `lower_X` method (since we can't just blindly | ||
77 | // walk them). | ||
78 | ast::ModuleItem::TraitDef(_) | ||
79 | | ast::ModuleItem::ImplDef(_) | ||
80 | | ast::ModuleItem::ExternBlock(_) => {} | ||
81 | |||
82 | // These don't have inner items. | ||
83 | ast::ModuleItem::Module(_) | ||
84 | | ast::ModuleItem::ExternCrateItem(_) | ||
85 | | ast::ModuleItem::UseItem(_) => {} | ||
86 | }; | ||
87 | |||
48 | let attrs = Attrs::new(item, &self.hygiene); | 88 | let attrs = Attrs::new(item, &self.hygiene); |
49 | let items = match item { | 89 | let items = match item { |
50 | ast::ModuleItem::StructDef(ast) => { | 90 | ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into), |
51 | self.lower_struct(ast).map(|data| id(self.tree.structs.alloc(data)).into()) | 91 | ast::ModuleItem::UnionDef(ast) => self.lower_union(ast).map(Into::into), |
52 | } | 92 | ast::ModuleItem::EnumDef(ast) => self.lower_enum(ast).map(Into::into), |
53 | ast::ModuleItem::UnionDef(ast) => { | 93 | ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into), |
54 | self.lower_union(ast).map(|data| id(self.tree.unions.alloc(data)).into()) | 94 | ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into), |
55 | } | 95 | ast::ModuleItem::StaticDef(ast) => self.lower_static(ast).map(Into::into), |
56 | ast::ModuleItem::EnumDef(ast) => { | 96 | ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()), |
57 | self.lower_enum(ast).map(|data| id(self.tree.enums.alloc(data)).into()) | 97 | ast::ModuleItem::Module(ast) => self.lower_module(ast).map(Into::into), |
58 | } | 98 | ast::ModuleItem::TraitDef(ast) => self.lower_trait(ast).map(Into::into), |
59 | ast::ModuleItem::FnDef(ast) => { | 99 | ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into), |
60 | self.lower_function(ast).map(|data| id(self.tree.functions.alloc(data)).into()) | ||
61 | } | ||
62 | ast::ModuleItem::TypeAliasDef(ast) => { | ||
63 | self.lower_type_alias(ast).map(|data| id(self.tree.type_aliases.alloc(data)).into()) | ||
64 | } | ||
65 | ast::ModuleItem::StaticDef(ast) => { | ||
66 | self.lower_static(ast).map(|data| id(self.tree.statics.alloc(data)).into()) | ||
67 | } | ||
68 | ast::ModuleItem::ConstDef(ast) => { | ||
69 | let data = self.lower_const(ast); | ||
70 | Some(id(self.tree.consts.alloc(data)).into()) | ||
71 | } | ||
72 | ast::ModuleItem::Module(ast) => { | ||
73 | self.lower_module(ast).map(|data| id(self.tree.mods.alloc(data)).into()) | ||
74 | } | ||
75 | ast::ModuleItem::TraitDef(ast) => { | ||
76 | self.lower_trait(ast).map(|data| id(self.tree.traits.alloc(data)).into()) | ||
77 | } | ||
78 | ast::ModuleItem::ImplDef(ast) => { | ||
79 | self.lower_impl(ast).map(|data| id(self.tree.impls.alloc(data)).into()) | ||
80 | } | ||
81 | ast::ModuleItem::UseItem(ast) => Some(ModItems( | 100 | ast::ModuleItem::UseItem(ast) => Some(ModItems( |
82 | self.lower_use(ast) | 101 | self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(), |
83 | .into_iter() | ||
84 | .map(|data| id(self.tree.imports.alloc(data)).into()) | ||
85 | .collect::<SmallVec<_>>(), | ||
86 | )), | 102 | )), |
87 | ast::ModuleItem::ExternCrateItem(ast) => { | 103 | ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into), |
88 | self.lower_extern_crate(ast).map(|data| id(self.tree.imports.alloc(data)).into()) | 104 | ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), |
105 | ast::ModuleItem::ExternBlock(ast) => { | ||
106 | Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>())) | ||
89 | } | 107 | } |
90 | ast::ModuleItem::MacroCall(ast) => { | ||
91 | self.lower_macro_call(ast).map(|data| id(self.tree.macro_calls.alloc(data)).into()) | ||
92 | } | ||
93 | ast::ModuleItem::ExternBlock(ast) => Some(ModItems( | ||
94 | self.lower_extern_block(ast) | ||
95 | .into_iter() | ||
96 | .map(|item| match item { | ||
97 | Either::Left(func) => id(self.tree.functions.alloc(func)).into(), | ||
98 | Either::Right(statik) => id(self.tree.statics.alloc(statik)).into(), | ||
99 | }) | ||
100 | .collect::<SmallVec<_>>(), | ||
101 | )), | ||
102 | }; | 108 | }; |
103 | 109 | ||
104 | if !attrs.is_empty() { | 110 | if !attrs.is_empty() { |
@@ -110,22 +116,28 @@ impl Ctx { | |||
110 | items | 116 | items |
111 | } | 117 | } |
112 | 118 | ||
113 | fn lower_assoc_item(&mut self, item: &ast::AssocItem) -> Option<AssocItem> { | 119 | fn collect_inner_items(&mut self, container: &SyntaxNode) { |
120 | let mut inner_items = mem::replace(&mut self.tree.inner_items, FxHashMap::default()); | ||
121 | inner_items.extend( | ||
122 | container.descendants().skip(1).filter_map(ast::ModuleItem::cast).filter_map(|item| { | ||
123 | let ast_id = self.source_ast_id_map.ast_id(&item); | ||
124 | Some((ast_id, self.lower_mod_item(&item, true)?.0)) | ||
125 | }), | ||
126 | ); | ||
127 | self.tree.inner_items = inner_items; | ||
128 | } | ||
129 | |||
130 | fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> { | ||
114 | match item { | 131 | match item { |
115 | ast::AssocItem::FnDef(ast) => { | 132 | ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into), |
116 | self.lower_function(ast).map(|data| id(self.tree.functions.alloc(data)).into()) | 133 | ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into), |
117 | } | 134 | ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()), |
118 | ast::AssocItem::TypeAliasDef(ast) => { | 135 | ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), |
119 | self.lower_type_alias(ast).map(|data| id(self.tree.type_aliases.alloc(data)).into()) | 136 | _ => None, |
120 | } | ||
121 | ast::AssocItem::ConstDef(ast) => { | ||
122 | let data = self.lower_const(ast); | ||
123 | Some(id(self.tree.consts.alloc(data)).into()) | ||
124 | } | ||
125 | } | 137 | } |
126 | } | 138 | } |
127 | 139 | ||
128 | fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<Struct> { | 140 | fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> { |
129 | let attrs = self.lower_attrs(strukt); | 141 | let attrs = self.lower_attrs(strukt); |
130 | let visibility = self.lower_visibility(strukt); | 142 | let visibility = self.lower_visibility(strukt); |
131 | let name = strukt.name()?.as_name(); | 143 | let name = strukt.name()?.as_name(); |
@@ -138,7 +150,7 @@ impl Ctx { | |||
138 | ast::StructKind::Unit => StructDefKind::Unit, | 150 | ast::StructKind::Unit => StructDefKind::Unit, |
139 | }; | 151 | }; |
140 | let res = Struct { name, attrs, visibility, generic_params, fields, ast_id, kind }; | 152 | let res = Struct { name, attrs, visibility, generic_params, fields, ast_id, kind }; |
141 | Some(res) | 153 | Some(id(self.tree.structs.alloc(res))) |
142 | } | 154 | } |
143 | 155 | ||
144 | fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { | 156 | fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { |
@@ -193,7 +205,7 @@ impl Ctx { | |||
193 | Some(res) | 205 | Some(res) |
194 | } | 206 | } |
195 | 207 | ||
196 | fn lower_union(&mut self, union: &ast::UnionDef) -> Option<Union> { | 208 | fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> { |
197 | let attrs = self.lower_attrs(union); | 209 | let attrs = self.lower_attrs(union); |
198 | let visibility = self.lower_visibility(union); | 210 | let visibility = self.lower_visibility(union); |
199 | let name = union.name()?.as_name(); | 211 | let name = union.name()?.as_name(); |
@@ -206,10 +218,10 @@ impl Ctx { | |||
206 | }; | 218 | }; |
207 | let ast_id = self.source_ast_id_map.ast_id(union); | 219 | let ast_id = self.source_ast_id_map.ast_id(union); |
208 | let res = Union { name, attrs, visibility, generic_params, fields, ast_id }; | 220 | let res = Union { name, attrs, visibility, generic_params, fields, ast_id }; |
209 | Some(res) | 221 | Some(id(self.tree.unions.alloc(res))) |
210 | } | 222 | } |
211 | 223 | ||
212 | fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<Enum> { | 224 | fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> { |
213 | let attrs = self.lower_attrs(enum_); | 225 | let attrs = self.lower_attrs(enum_); |
214 | let visibility = self.lower_visibility(enum_); | 226 | let visibility = self.lower_visibility(enum_); |
215 | let name = enum_.name()?.as_name(); | 227 | let name = enum_.name()?.as_name(); |
@@ -220,7 +232,7 @@ impl Ctx { | |||
220 | }; | 232 | }; |
221 | let ast_id = self.source_ast_id_map.ast_id(enum_); | 233 | let ast_id = self.source_ast_id_map.ast_id(enum_); |
222 | let res = Enum { name, attrs, visibility, generic_params, variants, ast_id }; | 234 | let res = Enum { name, attrs, visibility, generic_params, variants, ast_id }; |
223 | Some(res) | 235 | Some(id(self.tree.enums.alloc(res))) |
224 | } | 236 | } |
225 | 237 | ||
226 | fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> { | 238 | fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> { |
@@ -241,7 +253,7 @@ impl Ctx { | |||
241 | Some(res) | 253 | Some(res) |
242 | } | 254 | } |
243 | 255 | ||
244 | fn lower_function(&mut self, func: &ast::FnDef) -> Option<Function> { | 256 | fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> { |
245 | let attrs = self.lower_attrs(func); | 257 | let attrs = self.lower_attrs(func); |
246 | let visibility = self.lower_visibility(func); | 258 | let visibility = self.lower_visibility(func); |
247 | let name = func.name()?.as_name(); | 259 | let name = func.name()?.as_name(); |
@@ -297,37 +309,42 @@ impl Ctx { | |||
297 | ast_id, | 309 | ast_id, |
298 | }; | 310 | }; |
299 | res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); | 311 | res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); |
300 | Some(res) | 312 | |
313 | Some(id(self.tree.functions.alloc(res))) | ||
301 | } | 314 | } |
302 | 315 | ||
303 | fn lower_type_alias(&mut self, type_alias: &ast::TypeAliasDef) -> Option<TypeAlias> { | 316 | fn lower_type_alias( |
317 | &mut self, | ||
318 | type_alias: &ast::TypeAliasDef, | ||
319 | ) -> Option<FileItemTreeId<TypeAlias>> { | ||
304 | let name = type_alias.name()?.as_name(); | 320 | let name = type_alias.name()?.as_name(); |
305 | let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it)); | 321 | let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it)); |
306 | let visibility = self.lower_visibility(type_alias); | 322 | let visibility = self.lower_visibility(type_alias); |
307 | let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias); | 323 | let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias); |
308 | let ast_id = self.source_ast_id_map.ast_id(type_alias); | 324 | let ast_id = self.source_ast_id_map.ast_id(type_alias); |
309 | let res = TypeAlias { name, visibility, generic_params, type_ref, ast_id }; | 325 | let res = TypeAlias { name, visibility, generic_params, type_ref, ast_id }; |
310 | Some(res) | 326 | Some(id(self.tree.type_aliases.alloc(res))) |
311 | } | 327 | } |
312 | 328 | ||
313 | fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<Static> { | 329 | fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> { |
314 | let name = static_.name()?.as_name(); | 330 | let name = static_.name()?.as_name(); |
315 | let type_ref = self.lower_type_ref_opt(static_.ascribed_type()); | 331 | let type_ref = self.lower_type_ref_opt(static_.ascribed_type()); |
316 | let visibility = self.lower_visibility(static_); | 332 | let visibility = self.lower_visibility(static_); |
317 | let ast_id = self.source_ast_id_map.ast_id(static_); | 333 | let ast_id = self.source_ast_id_map.ast_id(static_); |
318 | let res = Static { name, visibility, type_ref, ast_id }; | 334 | let res = Static { name, visibility, type_ref, ast_id }; |
319 | Some(res) | 335 | Some(id(self.tree.statics.alloc(res))) |
320 | } | 336 | } |
321 | 337 | ||
322 | fn lower_const(&mut self, konst: &ast::ConstDef) -> Const { | 338 | fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> { |
323 | let name = konst.name().map(|it| it.as_name()); | 339 | let name = konst.name().map(|it| it.as_name()); |
324 | let type_ref = self.lower_type_ref_opt(konst.ascribed_type()); | 340 | let type_ref = self.lower_type_ref_opt(konst.ascribed_type()); |
325 | let visibility = self.lower_visibility(konst); | 341 | let visibility = self.lower_visibility(konst); |
326 | let ast_id = self.source_ast_id_map.ast_id(konst); | 342 | let ast_id = self.source_ast_id_map.ast_id(konst); |
327 | Const { name, visibility, type_ref, ast_id } | 343 | let res = Const { name, visibility, type_ref, ast_id }; |
344 | id(self.tree.consts.alloc(res)) | ||
328 | } | 345 | } |
329 | 346 | ||
330 | fn lower_module(&mut self, module: &ast::Module) -> Option<Mod> { | 347 | fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> { |
331 | let name = module.name()?.as_name(); | 348 | let name = module.name()?.as_name(); |
332 | let visibility = self.lower_visibility(module); | 349 | let visibility = self.lower_visibility(module); |
333 | let kind = if module.semicolon_token().is_some() { | 350 | let kind = if module.semicolon_token().is_some() { |
@@ -338,7 +355,7 @@ impl Ctx { | |||
338 | .item_list() | 355 | .item_list() |
339 | .map(|list| { | 356 | .map(|list| { |
340 | list.items() | 357 | list.items() |
341 | .flat_map(|item| self.lower_mod_item(&item)) | 358 | .flat_map(|item| self.lower_mod_item(&item, false)) |
342 | .flat_map(|items| items.0) | 359 | .flat_map(|items| items.0) |
343 | .collect() | 360 | .collect() |
344 | }) | 361 | }) |
@@ -349,90 +366,101 @@ impl Ctx { | |||
349 | } | 366 | } |
350 | }; | 367 | }; |
351 | let ast_id = self.source_ast_id_map.ast_id(module); | 368 | let ast_id = self.source_ast_id_map.ast_id(module); |
352 | Some(Mod { name, visibility, kind, ast_id }) | 369 | let res = Mod { name, visibility, kind, ast_id }; |
370 | Some(id(self.tree.mods.alloc(res))) | ||
353 | } | 371 | } |
354 | 372 | ||
355 | fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<Trait> { | 373 | fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> { |
356 | let name = trait_def.name()?.as_name(); | 374 | let name = trait_def.name()?.as_name(); |
357 | let visibility = self.lower_visibility(trait_def); | 375 | let visibility = self.lower_visibility(trait_def); |
358 | let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def); | 376 | let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def); |
359 | let auto = trait_def.auto_token().is_some(); | 377 | let auto = trait_def.auto_token().is_some(); |
360 | let items = trait_def.item_list().map(|list| { | 378 | let items = trait_def.item_list().map(|list| { |
361 | // FIXME: Does not handle macros | 379 | list.items() |
362 | list.assoc_items().flat_map(|item| self.lower_assoc_item(&item)).collect() | 380 | .flat_map(|item| { |
381 | self.collect_inner_items(item.syntax()); | ||
382 | self.lower_assoc_item(&item) | ||
383 | }) | ||
384 | .collect() | ||
363 | }); | 385 | }); |
364 | let ast_id = self.source_ast_id_map.ast_id(trait_def); | 386 | let ast_id = self.source_ast_id_map.ast_id(trait_def); |
365 | Some(Trait { | 387 | let res = Trait { |
366 | name, | 388 | name, |
367 | visibility, | 389 | visibility, |
368 | generic_params, | 390 | generic_params, |
369 | auto, | 391 | auto, |
370 | items: items.unwrap_or_default(), | 392 | items: items.unwrap_or_default(), |
371 | ast_id, | 393 | ast_id, |
372 | }) | 394 | }; |
395 | Some(id(self.tree.traits.alloc(res))) | ||
373 | } | 396 | } |
374 | 397 | ||
375 | fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<Impl> { | 398 | fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> { |
376 | let generic_params = self.lower_generic_params(GenericsOwner::Impl, impl_def); | 399 | let generic_params = self.lower_generic_params(GenericsOwner::Impl, impl_def); |
377 | let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr)); | 400 | let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr)); |
378 | let target_type = self.lower_type_ref(&impl_def.target_type()?); | 401 | let target_type = self.lower_type_ref(&impl_def.target_type()?); |
379 | let is_negative = impl_def.excl_token().is_some(); | 402 | let is_negative = impl_def.excl_token().is_some(); |
403 | |||
404 | // We cannot use `assoc_items()` here as that does not include macro calls. | ||
380 | let items = impl_def | 405 | let items = impl_def |
381 | .item_list()? | 406 | .item_list()? |
382 | .assoc_items() | 407 | .items() |
383 | .filter_map(|item| self.lower_assoc_item(&item)) | 408 | .filter_map(|item| { |
409 | self.collect_inner_items(item.syntax()); | ||
410 | let assoc = self.lower_assoc_item(&item)?; | ||
411 | Some(assoc) | ||
412 | }) | ||
384 | .collect(); | 413 | .collect(); |
385 | let ast_id = self.source_ast_id_map.ast_id(impl_def); | 414 | let ast_id = self.source_ast_id_map.ast_id(impl_def); |
386 | Some(Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }) | 415 | let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }; |
416 | Some(id(self.tree.impls.alloc(res))) | ||
387 | } | 417 | } |
388 | 418 | ||
389 | fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<Import> { | 419 | fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> { |
390 | // FIXME: cfg_attr | 420 | // FIXME: cfg_attr |
391 | let is_prelude = use_item.has_atom_attr("prelude_import"); | 421 | let is_prelude = use_item.has_atom_attr("prelude_import"); |
392 | let visibility = self.lower_visibility(use_item); | 422 | let visibility = self.lower_visibility(use_item); |
423 | let ast_id = self.source_ast_id_map.ast_id(use_item); | ||
393 | 424 | ||
394 | // Every use item can expand to many `Import`s. | 425 | // Every use item can expand to many `Import`s. |
395 | let mut imports = Vec::new(); | 426 | let mut imports = Vec::new(); |
427 | let tree = &mut self.tree; | ||
396 | ModPath::expand_use_item( | 428 | ModPath::expand_use_item( |
397 | InFile::new(self.file, use_item.clone()), | 429 | InFile::new(self.file, use_item.clone()), |
398 | &self.hygiene, | 430 | &self.hygiene, |
399 | |path, _tree, is_glob, alias| { | 431 | |path, _tree, is_glob, alias| { |
400 | imports.push(Import { | 432 | imports.push(id(tree.imports.alloc(Import { |
401 | path, | 433 | path, |
402 | alias, | 434 | alias, |
403 | visibility: visibility.clone(), | 435 | visibility: visibility.clone(), |
404 | is_glob, | 436 | is_glob, |
405 | is_prelude, | 437 | is_prelude, |
406 | is_extern_crate: false, | 438 | ast_id, |
407 | is_macro_use: false, | 439 | }))); |
408 | }); | ||
409 | }, | 440 | }, |
410 | ); | 441 | ); |
411 | 442 | ||
412 | imports | 443 | imports |
413 | } | 444 | } |
414 | 445 | ||
415 | fn lower_extern_crate(&mut self, extern_crate: &ast::ExternCrateItem) -> Option<Import> { | 446 | fn lower_extern_crate( |
447 | &mut self, | ||
448 | extern_crate: &ast::ExternCrateItem, | ||
449 | ) -> Option<FileItemTreeId<ExternCrate>> { | ||
416 | let path = ModPath::from_name_ref(&extern_crate.name_ref()?); | 450 | let path = ModPath::from_name_ref(&extern_crate.name_ref()?); |
417 | let alias = extern_crate.alias().map(|a| { | 451 | let alias = extern_crate.alias().map(|a| { |
418 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) | 452 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) |
419 | }); | 453 | }); |
420 | let visibility = self.lower_visibility(extern_crate); | 454 | let visibility = self.lower_visibility(extern_crate); |
455 | let ast_id = self.source_ast_id_map.ast_id(extern_crate); | ||
421 | // FIXME: cfg_attr | 456 | // FIXME: cfg_attr |
422 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | 457 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); |
423 | 458 | ||
424 | Some(Import { | 459 | let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; |
425 | path, | 460 | Some(id(self.tree.extern_crates.alloc(res))) |
426 | alias, | ||
427 | visibility, | ||
428 | is_glob: false, | ||
429 | is_prelude: false, | ||
430 | is_extern_crate: true, | ||
431 | is_macro_use, | ||
432 | }) | ||
433 | } | 461 | } |
434 | 462 | ||
435 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<MacroCall> { | 463 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { |
436 | let name = m.name().map(|it| it.as_name()); | 464 | let name = m.name().map(|it| it.as_name()); |
437 | let attrs = Attrs::new(m, &self.hygiene); | 465 | let attrs = Attrs::new(m, &self.hygiene); |
438 | let path = ModPath::from_src(m.path()?, &self.hygiene)?; | 466 | let path = ModPath::from_src(m.path()?, &self.hygiene)?; |
@@ -455,15 +483,26 @@ impl Ctx { | |||
455 | }; | 483 | }; |
456 | 484 | ||
457 | let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); | 485 | let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); |
458 | Some(MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }) | 486 | let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }; |
487 | Some(id(self.tree.macro_calls.alloc(res))) | ||
459 | } | 488 | } |
460 | 489 | ||
461 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<Either<Function, Static>> { | 490 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { |
462 | block.extern_item_list().map_or(Vec::new(), |list| { | 491 | block.extern_item_list().map_or(Vec::new(), |list| { |
463 | list.extern_items() | 492 | list.extern_items() |
464 | .filter_map(|item| match item { | 493 | .filter_map(|item| { |
465 | ast::ExternItem::FnDef(ast) => self.lower_function(&ast).map(Either::Left), | 494 | self.collect_inner_items(item.syntax()); |
466 | ast::ExternItem::StaticDef(ast) => self.lower_static(&ast).map(Either::Right), | 495 | let id = match item { |
496 | ast::ExternItem::FnDef(ast) => { | ||
497 | let func = self.lower_function(&ast)?; | ||
498 | func.into() | ||
499 | } | ||
500 | ast::ExternItem::StaticDef(ast) => { | ||
501 | let statik = self.lower_static(&ast)?; | ||
502 | statik.into() | ||
503 | } | ||
504 | }; | ||
505 | Some(id) | ||
467 | }) | 506 | }) |
468 | .collect() | 507 | .collect() |
469 | }) | 508 | }) |
diff --git a/crates/ra_hir_def/src/item_tree/tests.rs b/crates/ra_hir_def/src/item_tree/tests.rs new file mode 100644 index 000000000..b60e6cbb0 --- /dev/null +++ b/crates/ra_hir_def/src/item_tree/tests.rs | |||
@@ -0,0 +1,254 @@ | |||
1 | use super::{ItemTree, ModItem, ModKind}; | ||
2 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
3 | use hir_expand::db::AstDatabase; | ||
4 | use insta::assert_snapshot; | ||
5 | use ra_db::fixture::WithFixture; | ||
6 | use ra_syntax::{ast, AstNode}; | ||
7 | use rustc_hash::FxHashSet; | ||
8 | use std::sync::Arc; | ||
9 | use stdx::format_to; | ||
10 | |||
11 | fn test_inner_items(ra_fixture: &str) { | ||
12 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
13 | let tree = db.item_tree(file_id.into()); | ||
14 | let root = db.parse_or_expand(file_id.into()).unwrap(); | ||
15 | let ast_id_map = db.ast_id_map(file_id.into()); | ||
16 | |||
17 | // Traverse the item tree and collect all module/impl/trait-level items as AST nodes. | ||
18 | let mut outer_items = FxHashSet::default(); | ||
19 | let mut worklist = tree.top_level_items().to_vec(); | ||
20 | while let Some(item) = worklist.pop() { | ||
21 | let node: ast::ModuleItem = match item { | ||
22 | ModItem::Import(it) => tree.source(&db, it).into(), | ||
23 | ModItem::ExternCrate(it) => tree.source(&db, it).into(), | ||
24 | ModItem::Function(it) => tree.source(&db, it).into(), | ||
25 | ModItem::Struct(it) => tree.source(&db, it).into(), | ||
26 | ModItem::Union(it) => tree.source(&db, it).into(), | ||
27 | ModItem::Enum(it) => tree.source(&db, it).into(), | ||
28 | ModItem::Const(it) => tree.source(&db, it).into(), | ||
29 | ModItem::Static(it) => tree.source(&db, it).into(), | ||
30 | ModItem::TypeAlias(it) => tree.source(&db, it).into(), | ||
31 | ModItem::Mod(it) => { | ||
32 | if let ModKind::Inline { items } = &tree[it].kind { | ||
33 | worklist.extend(items); | ||
34 | } | ||
35 | tree.source(&db, it).into() | ||
36 | } | ||
37 | ModItem::Trait(it) => { | ||
38 | worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item))); | ||
39 | tree.source(&db, it).into() | ||
40 | } | ||
41 | ModItem::Impl(it) => { | ||
42 | worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item))); | ||
43 | tree.source(&db, it).into() | ||
44 | } | ||
45 | ModItem::MacroCall(_) => continue, | ||
46 | }; | ||
47 | |||
48 | outer_items.insert(node); | ||
49 | } | ||
50 | |||
51 | // Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or | ||
52 | // registered as inner items. | ||
53 | for item in root.descendants().skip(1).filter_map(ast::ModuleItem::cast) { | ||
54 | if outer_items.contains(&item) { | ||
55 | continue; | ||
56 | } | ||
57 | |||
58 | let ast_id = ast_id_map.ast_id(&item); | ||
59 | assert!(!tree.inner_items(ast_id).is_empty()); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | fn item_tree(ra_fixture: &str) -> Arc<ItemTree> { | ||
64 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
65 | db.item_tree(file_id.into()) | ||
66 | } | ||
67 | |||
68 | fn print_item_tree(ra_fixture: &str) -> String { | ||
69 | let tree = item_tree(ra_fixture); | ||
70 | let mut out = String::new(); | ||
71 | |||
72 | format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs()); | ||
73 | format_to!(out, "top-level items:\n"); | ||
74 | for item in tree.top_level_items() { | ||
75 | fmt_mod_item(&mut out, &tree, *item); | ||
76 | format_to!(out, "\n"); | ||
77 | } | ||
78 | |||
79 | if !tree.inner_items.is_empty() { | ||
80 | format_to!(out, "\ninner items:\n"); | ||
81 | for (ast_id, items) in &tree.inner_items { | ||
82 | format_to!(out, "{:?}:\n", ast_id); | ||
83 | for inner in items { | ||
84 | format_to!(out, "- "); | ||
85 | fmt_mod_item(&mut out, &tree, *inner); | ||
86 | format_to!(out, "\n"); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | out | ||
92 | } | ||
93 | |||
94 | fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) { | ||
95 | match item { | ||
96 | ModItem::ExternCrate(it) => { | ||
97 | format_to!(out, "{:?}", tree[it]); | ||
98 | } | ||
99 | ModItem::Import(it) => { | ||
100 | format_to!(out, "{:?}", tree[it]); | ||
101 | } | ||
102 | ModItem::Function(it) => { | ||
103 | format_to!(out, "{:?}", tree[it]); | ||
104 | } | ||
105 | ModItem::Struct(it) => { | ||
106 | format_to!(out, "{:?}", tree[it]); | ||
107 | } | ||
108 | ModItem::Union(it) => { | ||
109 | format_to!(out, "{:?}", tree[it]); | ||
110 | } | ||
111 | ModItem::Enum(it) => { | ||
112 | format_to!(out, "{:?}", tree[it]); | ||
113 | } | ||
114 | ModItem::Const(it) => { | ||
115 | format_to!(out, "{:?}", tree[it]); | ||
116 | } | ||
117 | ModItem::Static(it) => { | ||
118 | format_to!(out, "{:?}", tree[it]); | ||
119 | } | ||
120 | ModItem::Trait(it) => { | ||
121 | format_to!(out, "{:?}", tree[it]); | ||
122 | } | ||
123 | ModItem::Impl(it) => { | ||
124 | format_to!(out, "{:?}", tree[it]); | ||
125 | } | ||
126 | ModItem::TypeAlias(it) => { | ||
127 | format_to!(out, "{:?}", tree[it]); | ||
128 | } | ||
129 | ModItem::Mod(it) => { | ||
130 | format_to!(out, "{:?}", tree[it]); | ||
131 | } | ||
132 | ModItem::MacroCall(it) => { | ||
133 | format_to!(out, "{:?}", tree[it]); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | #[test] | ||
139 | fn smoke() { | ||
140 | assert_snapshot!(print_item_tree(r" | ||
141 | #![attr] | ||
142 | |||
143 | use {a, b::*}; | ||
144 | extern crate krate; | ||
145 | |||
146 | trait Tr<U> { | ||
147 | type AssocTy: Tr<()>; | ||
148 | const CONST: u8; | ||
149 | fn method(&self); | ||
150 | fn dfl_method(&mut self) {} | ||
151 | } | ||
152 | |||
153 | struct Struct0<T = ()>; | ||
154 | struct Struct1<T>(u8); | ||
155 | struct Struct2<T> { | ||
156 | fld: (T, ), | ||
157 | } | ||
158 | |||
159 | enum En { | ||
160 | Variant { | ||
161 | field: u8, | ||
162 | }, | ||
163 | } | ||
164 | |||
165 | union Un { | ||
166 | fld: u16, | ||
167 | } | ||
168 | "), @r###" | ||
169 | inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) } | ||
170 | |||
171 | top-level items: | ||
172 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: Module(ModPath { kind: Super(0), segments: [] }), is_glob: false, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) } | ||
173 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: Module(ModPath { kind: Super(0), segments: [] }), is_glob: true, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) } | ||
174 | ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: Module(ModPath { kind: Super(0), segments: [] }), is_macro_use: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ExternCrateItem>(1) } | ||
175 | Trait { name: Name(Text("Tr")), visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 2, data: [TypeParamData { name: Some(Name(Text("Self"))), default: None, provenance: TraitSelf }, TypeParamData { name: Some(Name(Text("U"))), default: None, provenance: TypeParamList }] }, where_predicates: [] }, auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(2) } | ||
176 | Struct { name: Name(Text("Struct0")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("T"))), default: Some(Tuple([])), provenance: TypeParamList }] }, where_predicates: [] }, fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit } | ||
177 | Struct { name: Name(Text("Struct1")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("T"))), default: None, provenance: TypeParamList }] }, where_predicates: [] }, fields: Tuple(Idx::<Field>(0)..Idx::<Field>(1)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(4), kind: Tuple } | ||
178 | Struct { name: Name(Text("Struct2")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("T"))), default: None, provenance: TypeParamList }] }, where_predicates: [] }, fields: Record(Idx::<Field>(1)..Idx::<Field>(2)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(5), kind: Record } | ||
179 | Enum { name: Name(Text("En")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, variants: Idx::<Variant>(0)..Idx::<Variant>(1), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::EnumDef>(6) } | ||
180 | Union { name: Name(Text("Un")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, fields: Record(Idx::<Field>(3)..Idx::<Field>(4)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UnionDef>(7) } | ||
181 | "###); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn simple_inner_items() { | ||
186 | let tree = print_item_tree( | ||
187 | r" | ||
188 | impl<T:A> D for Response<T> { | ||
189 | fn foo() { | ||
190 | end(); | ||
191 | fn end<W: Write>() { | ||
192 | let _x: T = loop {}; | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | ", | ||
197 | ); | ||
198 | |||
199 | assert_snapshot!(tree, @r###" | ||
200 | inner attrs: Attrs { entries: None } | ||
201 | |||
202 | top-level items: | ||
203 | Impl { generic_params: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) } | ||
204 | |||
205 | inner items: | ||
206 | FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2): | ||
207 | - Function { name: Name(Text("end")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("W"))), default: None, provenance: TypeParamList }] }, where_predicates: [WherePredicate { target: TypeRef(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("W"))] }, generic_args: [None] })), bound: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Write"))] }, generic_args: [None] }) }] }, has_self_param: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } | ||
208 | "###); | ||
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn cursed_inner_items() { | ||
213 | test_inner_items( | ||
214 | r" | ||
215 | struct S<T: Trait = [u8; { fn f() {} 0 }]>(T); | ||
216 | |||
217 | enum En { | ||
218 | Var1 { | ||
219 | t: [(); { trait Inner {} 0 }], | ||
220 | }, | ||
221 | |||
222 | Var2([u16; { enum Inner {} 0 }]), | ||
223 | } | ||
224 | |||
225 | type Ty = [En; { struct Inner; 0 }]; | ||
226 | |||
227 | impl En { | ||
228 | fn assoc() { | ||
229 | trait InnerTrait {} | ||
230 | struct InnerStruct {} | ||
231 | impl InnerTrait for InnerStruct {} | ||
232 | } | ||
233 | } | ||
234 | ", | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn assoc_item_macros() { | ||
240 | let tree = print_item_tree( | ||
241 | r" | ||
242 | impl S { | ||
243 | items!(); | ||
244 | } | ||
245 | ", | ||
246 | ); | ||
247 | |||
248 | assert_snapshot!(tree, @r###" | ||
249 | inner attrs: Attrs { entries: None } | ||
250 | |||
251 | top-level items: | ||
252 | Impl { generic_params: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) } | ||
253 | "###); | ||
254 | } | ||