diff options
author | Florian Diebold <[email protected]> | 2018-12-28 13:34:00 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-04 18:10:47 +0000 |
commit | ae9530addc4c5e9bbfd5c0287d3c3adb2de95e40 (patch) | |
tree | 42919bcc1ef1d439a04718aefe2fdc2fe3456afd /crates/ra_hir | |
parent | 226e31dae94f2c72f5cf650564e521b792793629 (diff) |
Add HIR for impl blocks
Since we need to be able to go from def to containing impl block, as well as the
other direction, and to find all impls for a certain type, a design similar to
the one for modules, where we collect all impls for the whole crate and keep
them in an arena, seemed fitting. The ImplBlock type, which provides the public
interface, then consists only of an Arc to the arena containing all impls, and
the index into it.
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/db.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/function.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir/src/ids.rs | 13 | ||||
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 172 | ||||
-rw-r--r-- | crates/ra_hir/src/krate.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/module.rs | 15 |
8 files changed, 225 insertions, 5 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 73a4cdc5c..6d5235ba4 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, SourceFileNode}; | |||
4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; | 4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, | 7 | Crate, DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, |
8 | SourceFileItems, SourceItemId, | 8 | SourceFileItems, SourceItemId, |
9 | query_definitions, | 9 | query_definitions, |
10 | FnScopes, | 10 | FnScopes, |
@@ -13,6 +13,7 @@ use crate::{ | |||
13 | nameres::{ItemMap, InputModuleItems}}, | 13 | nameres::{ItemMap, InputModuleItems}}, |
14 | ty::{InferenceResult, Ty}, | 14 | ty::{InferenceResult, Ty}, |
15 | adt::{StructData, EnumData}, | 15 | adt::{StructData, EnumData}, |
16 | impl_block::CrateImplBlocks, | ||
16 | }; | 17 | }; |
17 | 18 | ||
18 | salsa::query_group! { | 19 | salsa::query_group! { |
@@ -87,6 +88,11 @@ pub trait HirDatabase: SyntaxDatabase | |||
87 | type ModuleTreeQuery; | 88 | type ModuleTreeQuery; |
88 | use fn crate::module::imp::module_tree; | 89 | use fn crate::module::imp::module_tree; |
89 | } | 90 | } |
91 | |||
92 | fn impls_in_crate(krate: Crate) -> Cancelable<Arc<CrateImplBlocks>> { | ||
93 | type ImplsInCrateQuery; | ||
94 | use fn crate::impl_block::impls_in_crate; | ||
95 | } | ||
90 | } | 96 | } |
91 | 97 | ||
92 | } | 98 | } |
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index 5a44132fc..75ef308ae 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs | |||
@@ -11,11 +11,11 @@ use ra_syntax::{ | |||
11 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, | 11 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module}; | 14 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; |
15 | 15 | ||
16 | pub use self::scope::FnScopes; | 16 | pub use self::scope::FnScopes; |
17 | 17 | ||
18 | #[derive(Debug)] | 18 | #[derive(Debug, Clone, PartialEq, Eq)] |
19 | pub struct Function { | 19 | pub struct Function { |
20 | def_id: DefId, | 20 | def_id: DefId, |
21 | } | 21 | } |
@@ -25,6 +25,10 @@ impl Function { | |||
25 | Function { def_id } | 25 | Function { def_id } |
26 | } | 26 | } |
27 | 27 | ||
28 | pub fn def_id(&self) -> DefId { | ||
29 | self.def_id | ||
30 | } | ||
31 | |||
28 | pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode { | 32 | pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode { |
29 | let def_loc = self.def_id.loc(db); | 33 | let def_loc = self.def_id.loc(db); |
30 | assert!(def_loc.kind == DefKind::Function); | 34 | assert!(def_loc.kind == DefKind::Function); |
@@ -48,6 +52,15 @@ impl Function { | |||
48 | pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { | 52 | pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { |
49 | self.def_id.module(db) | 53 | self.def_id.module(db) |
50 | } | 54 | } |
55 | |||
56 | pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> { | ||
57 | self.def_id.krate(db) | ||
58 | } | ||
59 | |||
60 | /// The containing impl block, if this is a method. | ||
61 | pub fn impl_block(&self, db: &impl HirDatabase) -> Cancelable<Option<ImplBlock>> { | ||
62 | self.def_id.impl_block(db) | ||
63 | } | ||
51 | } | 64 | } |
52 | 65 | ||
53 | #[derive(Debug, Clone)] | 66 | #[derive(Debug, Clone)] |
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 66adacc7d..c98be66f9 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs | |||
@@ -2,7 +2,7 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; | |||
2 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; | 2 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; |
3 | use ra_arena::{Arena, RawId, impl_arena_id}; | 3 | use ra_arena::{Arena, RawId, impl_arena_id}; |
4 | 4 | ||
5 | use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum}; | 5 | use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; |
6 | 6 | ||
7 | /// hir makes a heavy use of ids: integer (u32) handlers to various things. You | 7 | /// hir makes a heavy use of ids: integer (u32) handlers to various things. You |
8 | /// can think of id as a pointer (but without a lifetime) or a file descriptor | 8 | /// can think of id as a pointer (but without a lifetime) or a file descriptor |
@@ -177,6 +177,17 @@ impl DefId { | |||
177 | let loc = self.loc(db); | 177 | let loc = self.loc(db); |
178 | Module::new(db, loc.source_root_id, loc.module_id) | 178 | Module::new(db, loc.source_root_id, loc.module_id) |
179 | } | 179 | } |
180 | |||
181 | /// Returns the containing crate. | ||
182 | pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> { | ||
183 | Ok(self.module(db)?.krate(db)) | ||
184 | } | ||
185 | |||
186 | /// Returns the containing impl block, if this is an impl item. | ||
187 | pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable<Option<ImplBlock>> { | ||
188 | let crate_impls = db.impls_in_crate(ctry!(self.krate(db)?))?; | ||
189 | Ok(ImplBlock::containing(crate_impls, self)) | ||
190 | } | ||
180 | } | 191 | } |
181 | 192 | ||
182 | impl DefLoc { | 193 | impl DefLoc { |
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs new file mode 100644 index 000000000..22f0a4461 --- /dev/null +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -0,0 +1,172 @@ | |||
1 | use std::sync::Arc; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | |||
4 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
5 | use ra_syntax::ast::{self, AstNode}; | ||
6 | use ra_db::{LocationIntener, Cancelable}; | ||
7 | |||
8 | use crate::{ | ||
9 | Crate, DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, | ||
10 | Module, Function, | ||
11 | db::HirDatabase, | ||
12 | type_ref::TypeRef, | ||
13 | module::{ModuleSourceNode}, | ||
14 | }; | ||
15 | |||
16 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
17 | pub struct ImplBlock { | ||
18 | crate_impl_blocks: Arc<CrateImplBlocks>, | ||
19 | impl_id: ImplId, | ||
20 | } | ||
21 | |||
22 | impl ImplBlock { | ||
23 | pub(crate) fn containing( | ||
24 | crate_impl_blocks: Arc<CrateImplBlocks>, | ||
25 | def_id: DefId, | ||
26 | ) -> Option<ImplBlock> { | ||
27 | let impl_id = *crate_impl_blocks.impls_by_def.get(&def_id)?; | ||
28 | Some(ImplBlock { | ||
29 | crate_impl_blocks, | ||
30 | impl_id, | ||
31 | }) | ||
32 | } | ||
33 | |||
34 | fn impl_data(&self) -> &ImplData { | ||
35 | &self.crate_impl_blocks.impls[self.impl_id] | ||
36 | } | ||
37 | |||
38 | pub fn target(&self) -> &TypeRef { | ||
39 | &self.impl_data().impl_for | ||
40 | } | ||
41 | |||
42 | pub fn items(&self) -> &[ImplItem] { | ||
43 | &self.impl_data().items | ||
44 | } | ||
45 | } | ||
46 | |||
47 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
48 | pub struct ImplData { | ||
49 | impl_for: TypeRef, | ||
50 | items: Vec<ImplItem>, | ||
51 | } | ||
52 | |||
53 | impl ImplData { | ||
54 | pub(crate) fn from_ast( | ||
55 | db: &impl AsRef<LocationIntener<DefLoc, DefId>>, | ||
56 | file_items: &SourceFileItems, | ||
57 | module: &Module, | ||
58 | node: ast::ImplBlock, | ||
59 | ) -> Self { | ||
60 | let impl_for = TypeRef::from_ast_opt(node.target_type()); | ||
61 | let file_id = module.source().file_id(); | ||
62 | let items = if let Some(item_list) = node.item_list() { | ||
63 | item_list | ||
64 | .impl_items() | ||
65 | .map(|item_node| { | ||
66 | let kind = match item_node { | ||
67 | ast::ImplItem::FnDef(..) => DefKind::Function, | ||
68 | ast::ImplItem::ConstDef(..) => DefKind::Item, | ||
69 | ast::ImplItem::TypeDef(..) => DefKind::Item, | ||
70 | }; | ||
71 | let item_id = file_items.id_of_unchecked(item_node.syntax()); | ||
72 | let def_loc = DefLoc { | ||
73 | kind, | ||
74 | source_root_id: module.source_root_id, | ||
75 | module_id: module.module_id, | ||
76 | source_item_id: SourceItemId { | ||
77 | file_id, | ||
78 | item_id: Some(item_id), | ||
79 | }, | ||
80 | }; | ||
81 | let def_id = def_loc.id(db); | ||
82 | match item_node { | ||
83 | ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)), | ||
84 | ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id), | ||
85 | ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id), | ||
86 | } | ||
87 | }) | ||
88 | .collect() | ||
89 | } else { | ||
90 | Vec::new() | ||
91 | }; | ||
92 | ImplData { impl_for, items } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
97 | pub enum ImplItem { | ||
98 | Method(Function), | ||
99 | // these don't have their own types yet | ||
100 | Const(DefId), | ||
101 | Type(DefId), | ||
102 | // Existential | ||
103 | } | ||
104 | |||
105 | impl ImplItem { | ||
106 | pub fn def_id(&self) -> DefId { | ||
107 | match self { | ||
108 | ImplItem::Method(f) => f.def_id(), | ||
109 | ImplItem::Const(def_id) => *def_id, | ||
110 | ImplItem::Type(def_id) => *def_id, | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
116 | pub struct ImplId(pub RawId); | ||
117 | impl_arena_id!(ImplId); | ||
118 | |||
119 | /// We have to collect all impl blocks in a crate, to later be able to find | ||
120 | /// impls for specific types. | ||
121 | #[derive(Debug, PartialEq, Eq)] | ||
122 | pub struct CrateImplBlocks { | ||
123 | impls: Arena<ImplId, ImplData>, | ||
124 | impls_by_def: FxHashMap<DefId, ImplId>, | ||
125 | } | ||
126 | |||
127 | impl CrateImplBlocks { | ||
128 | fn new() -> Self { | ||
129 | CrateImplBlocks { | ||
130 | impls: Arena::default(), | ||
131 | impls_by_def: FxHashMap::default(), | ||
132 | } | ||
133 | } | ||
134 | |||
135 | fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> { | ||
136 | let module_source_node = module.source().resolve(db); | ||
137 | let node = match &module_source_node { | ||
138 | ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(), | ||
139 | ModuleSourceNode::Module(node) => node.borrowed().syntax(), | ||
140 | }; | ||
141 | |||
142 | let source_file_items = db.file_items(module.source().file_id()); | ||
143 | |||
144 | for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { | ||
145 | let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast); | ||
146 | let id = self.impls.alloc(impl_block); | ||
147 | for impl_item in &self.impls[id].items { | ||
148 | self.impls_by_def.insert(impl_item.def_id(), id); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | for (_, child) in module.children() { | ||
153 | self.collect(db, child)?; | ||
154 | } | ||
155 | |||
156 | Ok(()) | ||
157 | } | ||
158 | } | ||
159 | |||
160 | pub(crate) fn impls_in_crate( | ||
161 | db: &impl HirDatabase, | ||
162 | krate: Crate, | ||
163 | ) -> Cancelable<Arc<CrateImplBlocks>> { | ||
164 | let mut result = CrateImplBlocks::new(); | ||
165 | let root_module = if let Some(root) = krate.root_module(db)? { | ||
166 | root | ||
167 | } else { | ||
168 | return Ok(Arc::new(result)); | ||
169 | }; | ||
170 | result.collect(db, root_module)?; | ||
171 | Ok(Arc::new(result)) | ||
172 | } | ||
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs index a0821d15d..5194e280b 100644 --- a/crates/ra_hir/src/krate.rs +++ b/crates/ra_hir/src/krate.rs | |||
@@ -5,7 +5,7 @@ use crate::{HirDatabase, Module, Name, AsName, HirFileId}; | |||
5 | /// hir::Crate describes a single crate. It's the main inteface with which | 5 | /// hir::Crate describes a single crate. It's the main inteface with which |
6 | /// crate's dependencies interact. Mostly, it should be just a proxy for the | 6 | /// crate's dependencies interact. Mostly, it should be just a proxy for the |
7 | /// root module. | 7 | /// root module. |
8 | #[derive(Debug)] | 8 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
9 | pub struct Crate { | 9 | pub struct Crate { |
10 | crate_id: CrateId, | 10 | crate_id: CrateId, |
11 | } | 11 | } |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 344b543b6..2abcec441 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -31,6 +31,7 @@ mod function; | |||
31 | mod adt; | 31 | mod adt; |
32 | mod type_ref; | 32 | mod type_ref; |
33 | mod ty; | 33 | mod ty; |
34 | mod impl_block; | ||
34 | 35 | ||
35 | use crate::{ | 36 | use crate::{ |
36 | db::HirDatabase, | 37 | db::HirDatabase, |
@@ -48,6 +49,7 @@ pub use self::{ | |||
48 | function::{Function, FnScopes}, | 49 | function::{Function, FnScopes}, |
49 | adt::{Struct, Enum}, | 50 | adt::{Struct, Enum}, |
50 | ty::Ty, | 51 | ty::Ty, |
52 | impl_block::{ImplBlock, ImplItem}, | ||
51 | }; | 53 | }; |
52 | 54 | ||
53 | pub use self::function::FnSignatureInfo; | 55 | pub use self::function::FnSignatureInfo; |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 89b18194a..ef245ec7a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -203,6 +203,7 @@ salsa::database_storage! { | |||
203 | fn type_for_field() for db::TypeForFieldQuery; | 203 | fn type_for_field() for db::TypeForFieldQuery; |
204 | fn struct_data() for db::StructDataQuery; | 204 | fn struct_data() for db::StructDataQuery; |
205 | fn enum_data() for db::EnumDataQuery; | 205 | fn enum_data() for db::EnumDataQuery; |
206 | fn impls_in_crate() for db::ImplsInCrateQuery; | ||
206 | } | 207 | } |
207 | } | 208 | } |
208 | } | 209 | } |
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index c70dc54dd..b9821115c 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs | |||
@@ -71,6 +71,21 @@ impl Module { | |||
71 | }) | 71 | }) |
72 | } | 72 | } |
73 | 73 | ||
74 | /// Returns an iterator of all children of this module. | ||
75 | pub fn children<'a>(&'a self) -> impl Iterator<Item = (Name, Module)> + 'a { | ||
76 | self.module_id | ||
77 | .children(&self.tree) | ||
78 | .map(move |(name, module_id)| { | ||
79 | ( | ||
80 | name, | ||
81 | Module { | ||
82 | module_id, | ||
83 | ..self.clone() | ||
84 | }, | ||
85 | ) | ||
86 | }) | ||
87 | } | ||
88 | |||
74 | /// Returns the crate this module is part of. | 89 | /// Returns the crate this module is part of. |
75 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { | 90 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { |
76 | let root_id = self.module_id.crate_root(&self.tree); | 91 | let root_id = self.module_id.crate_root(&self.tree); |