diff options
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/adt.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 19 | ||||
-rw-r--r-- | crates/ra_hir/src/ids.rs | 287 | ||||
-rw-r--r-- | crates/ra_hir/src/krate.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 169 | ||||
-rw-r--r-- | crates/ra_hir/src/macros.rs | 199 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir/src/module.rs | 30 | ||||
-rw-r--r-- | crates/ra_hir/src/module/imp.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 91 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres/tests.rs | 84 | ||||
-rw-r--r-- | crates/ra_hir/src/query_definitions.rs | 73 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 51 |
13 files changed, 772 insertions, 255 deletions
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index e839a5a90..c6463235c 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_db::Cancelable; | ||
3 | use ra_syntax::ast::{self, NameOwner, StructFlavor}; | 4 | use ra_syntax::ast::{self, NameOwner, StructFlavor}; |
4 | 5 | ||
5 | use crate::{ | 6 | use crate::{ |
6 | DefId, Cancelable, Name, AsName, | 7 | DefId, Name, AsName, |
7 | db::HirDatabase, | 8 | db::HirDatabase, |
8 | type_ref::TypeRef, | 9 | type_ref::TypeRef, |
9 | }; | 10 | }; |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 5a8ca3b47..73a4cdc5c 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,13 +1,14 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::SyntaxNode; | 3 | use ra_syntax::{SyntaxNode, SourceFileNode}; |
4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; | 4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | DefLoc, DefId, Name, | 7 | DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, |
8 | SourceFileItems, SourceItemId, | 8 | SourceFileItems, SourceItemId, |
9 | query_definitions, | 9 | query_definitions, |
10 | FnScopes, | 10 | FnScopes, |
11 | macros::MacroExpansion, | ||
11 | module::{ModuleId, ModuleTree, ModuleSource, | 12 | module::{ModuleId, ModuleTree, ModuleSource, |
12 | nameres::{ItemMap, InputModuleItems}}, | 13 | nameres::{ItemMap, InputModuleItems}}, |
13 | ty::{InferenceResult, Ty}, | 14 | ty::{InferenceResult, Ty}, |
@@ -18,7 +19,17 @@ salsa::query_group! { | |||
18 | 19 | ||
19 | pub trait HirDatabase: SyntaxDatabase | 20 | pub trait HirDatabase: SyntaxDatabase |
20 | + AsRef<LocationIntener<DefLoc, DefId>> | 21 | + AsRef<LocationIntener<DefLoc, DefId>> |
22 | + AsRef<LocationIntener<MacroCallLoc, MacroCallId>> | ||
21 | { | 23 | { |
24 | fn hir_source_file(file_id: HirFileId) -> SourceFileNode { | ||
25 | type HirSourceFileQuery; | ||
26 | use fn HirFileId::hir_source_file; | ||
27 | } | ||
28 | fn expand_macro_invocation(invoc: MacroCallId) -> Option<Arc<MacroExpansion>> { | ||
29 | type ExpandMacroCallQuery; | ||
30 | use fn crate::macros::expand_macro_invocation; | ||
31 | } | ||
32 | |||
22 | fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { | 33 | fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { |
23 | type FnScopesQuery; | 34 | type FnScopesQuery; |
24 | use fn query_definitions::fn_scopes; | 35 | use fn query_definitions::fn_scopes; |
@@ -49,7 +60,7 @@ pub trait HirDatabase: SyntaxDatabase | |||
49 | use fn crate::ty::type_for_field; | 60 | use fn crate::ty::type_for_field; |
50 | } | 61 | } |
51 | 62 | ||
52 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { | 63 | fn file_items(file_id: HirFileId) -> Arc<SourceFileItems> { |
53 | type SourceFileItemsQuery; | 64 | type SourceFileItemsQuery; |
54 | use fn query_definitions::file_items; | 65 | use fn query_definitions::file_items; |
55 | } | 66 | } |
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs new file mode 100644 index 000000000..4c7ebe3ea --- /dev/null +++ b/crates/ra_hir/src/ids.rs | |||
@@ -0,0 +1,287 @@ | |||
1 | use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; | ||
2 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; | ||
3 | |||
4 | use crate::{ | ||
5 | HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, | ||
6 | arena::{Arena, Id}, | ||
7 | }; | ||
8 | |||
9 | /// hir makes a heavy use of ids: integer (u32) handlers to various things. You | ||
10 | /// can think of id as a pointer (but without a lifetime) or a file descriptor | ||
11 | /// (but for hir objects). | ||
12 | /// | ||
13 | /// This module defines a bunch of ids we are using. The most important ones are | ||
14 | /// probably `HirFileId` and `DefId`. | ||
15 | |||
16 | /// Input to the analyzer is a set of file, where each file is indetified by | ||
17 | /// `FileId` and contains source code. However, another source of source code in | ||
18 | /// Rust are macros: each macro can be thought of as producing a "temporary | ||
19 | /// file". To assign id to such file, we use the id of a macro call that | ||
20 | /// produced the file. So, a `HirFileId` is either a `FileId` (source code | ||
21 | /// written by user), or a `MacroCallId` (source code produced by macro). | ||
22 | /// | ||
23 | /// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin | ||
24 | /// the call plus the offset of the macro call in the file. Note that this is a | ||
25 | /// recursive definition! Nethetheless, size_of of `HirFileId` is finite | ||
26 | /// (because everything bottoms out at the real `FileId`) and small | ||
27 | /// (`MacroCallId` uses location interner). | ||
28 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
29 | pub struct HirFileId(HirFileIdRepr); | ||
30 | |||
31 | impl HirFileId { | ||
32 | /// For macro-expansion files, returns the file original source file the | ||
33 | /// expansionoriginated from. | ||
34 | pub(crate) fn original_file(self, db: &impl HirDatabase) -> FileId { | ||
35 | match self.0 { | ||
36 | HirFileIdRepr::File(file_id) => file_id, | ||
37 | HirFileIdRepr::Macro(macro_call_id) => { | ||
38 | let loc = macro_call_id.loc(db); | ||
39 | loc.source_item_id.file_id.original_file(db) | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | pub(crate) fn as_original_file(self) -> FileId { | ||
45 | match self.0 { | ||
46 | HirFileIdRepr::File(file_id) => file_id, | ||
47 | HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self), | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub(crate) fn as_macro_call_id(self) -> Option<MacroCallId> { | ||
52 | match self.0 { | ||
53 | HirFileIdRepr::Macro(it) => Some(it), | ||
54 | _ => None, | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub(crate) fn hir_source_file(db: &impl HirDatabase, file_id: HirFileId) -> SourceFileNode { | ||
59 | match file_id.0 { | ||
60 | HirFileIdRepr::File(file_id) => db.source_file(file_id), | ||
61 | HirFileIdRepr::Macro(m) => { | ||
62 | if let Some(exp) = db.expand_macro_invocation(m) { | ||
63 | return exp.file(); | ||
64 | } | ||
65 | // returning an empty string looks fishy... | ||
66 | SourceFileNode::parse("") | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
73 | enum HirFileIdRepr { | ||
74 | File(FileId), | ||
75 | Macro(MacroCallId), | ||
76 | } | ||
77 | |||
78 | impl From<FileId> for HirFileId { | ||
79 | fn from(file_id: FileId) -> HirFileId { | ||
80 | HirFileId(HirFileIdRepr::File(file_id)) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl From<MacroCallId> for HirFileId { | ||
85 | fn from(macro_call_id: MacroCallId) -> HirFileId { | ||
86 | HirFileId(HirFileIdRepr::Macro(macro_call_id)) | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /// `MacroCallId` identifies a particular macro invocation, like | ||
91 | /// `println!("Hello, {}", world)`. | ||
92 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
93 | pub struct MacroCallId(u32); | ||
94 | ra_db::impl_numeric_id!(MacroCallId); | ||
95 | |||
96 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
97 | pub struct MacroCallLoc { | ||
98 | pub(crate) source_root_id: SourceRootId, | ||
99 | pub(crate) module_id: ModuleId, | ||
100 | pub(crate) source_item_id: SourceItemId, | ||
101 | } | ||
102 | |||
103 | impl MacroCallId { | ||
104 | pub(crate) fn loc( | ||
105 | self, | ||
106 | db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>, | ||
107 | ) -> MacroCallLoc { | ||
108 | db.as_ref().id2loc(self) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | impl MacroCallLoc { | ||
113 | #[allow(unused)] | ||
114 | pub(crate) fn id( | ||
115 | &self, | ||
116 | db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>, | ||
117 | ) -> MacroCallId { | ||
118 | db.as_ref().loc2id(&self) | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /// Def's are a core concept of hir. A `Def` is an Item (function, module, etc) | ||
123 | /// in a specific module. | ||
124 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
125 | pub struct DefId(u32); | ||
126 | ra_db::impl_numeric_id!(DefId); | ||
127 | |||
128 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
129 | pub struct DefLoc { | ||
130 | pub(crate) kind: DefKind, | ||
131 | pub(crate) source_root_id: SourceRootId, | ||
132 | pub(crate) module_id: ModuleId, | ||
133 | pub(crate) source_item_id: SourceItemId, | ||
134 | } | ||
135 | |||
136 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
137 | pub(crate) enum DefKind { | ||
138 | Module, | ||
139 | Function, | ||
140 | Struct, | ||
141 | Enum, | ||
142 | Item, | ||
143 | |||
144 | StructCtor, | ||
145 | } | ||
146 | |||
147 | impl DefId { | ||
148 | pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc { | ||
149 | db.as_ref().id2loc(self) | ||
150 | } | ||
151 | |||
152 | pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> { | ||
153 | let loc = self.loc(db); | ||
154 | let res = match loc.kind { | ||
155 | DefKind::Module => { | ||
156 | let module = Module::new(db, loc.source_root_id, loc.module_id)?; | ||
157 | Def::Module(module) | ||
158 | } | ||
159 | DefKind::Function => { | ||
160 | let function = Function::new(self); | ||
161 | Def::Function(function) | ||
162 | } | ||
163 | DefKind::Struct => { | ||
164 | let struct_def = Struct::new(self); | ||
165 | Def::Struct(struct_def) | ||
166 | } | ||
167 | DefKind::Enum => { | ||
168 | let enum_def = Enum::new(self); | ||
169 | Def::Enum(enum_def) | ||
170 | } | ||
171 | DefKind::StructCtor => Def::Item, | ||
172 | DefKind::Item => Def::Item, | ||
173 | }; | ||
174 | Ok(res) | ||
175 | } | ||
176 | |||
177 | /// For a module, returns that module; for any other def, returns the containing module. | ||
178 | pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { | ||
179 | let loc = self.loc(db); | ||
180 | Module::new(db, loc.source_root_id, loc.module_id) | ||
181 | } | ||
182 | } | ||
183 | |||
184 | impl DefLoc { | ||
185 | pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId { | ||
186 | db.as_ref().loc2id(&self) | ||
187 | } | ||
188 | } | ||
189 | |||
190 | impl DefKind { | ||
191 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> { | ||
192 | match kind { | ||
193 | SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), | ||
194 | SyntaxKind::MODULE => PerNs::types(DefKind::Module), | ||
195 | SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), | ||
196 | SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), | ||
197 | // These define items, but don't have their own DefKinds yet: | ||
198 | SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), | ||
199 | SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), | ||
200 | SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), | ||
201 | SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), | ||
202 | _ => PerNs::none(), | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
208 | /// it's OK to use it as a salsa key/value. | ||
209 | pub(crate) type SourceFileItemId = Id<SyntaxNode>; | ||
210 | |||
211 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
212 | pub struct SourceItemId { | ||
213 | pub(crate) file_id: HirFileId, | ||
214 | /// None for the whole file. | ||
215 | pub(crate) item_id: Option<SourceFileItemId>, | ||
216 | } | ||
217 | |||
218 | /// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. | ||
219 | #[derive(Debug, PartialEq, Eq)] | ||
220 | pub struct SourceFileItems { | ||
221 | file_id: HirFileId, | ||
222 | arena: Arena<SyntaxNode>, | ||
223 | } | ||
224 | |||
225 | impl SourceFileItems { | ||
226 | pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems { | ||
227 | let mut res = SourceFileItems { | ||
228 | file_id, | ||
229 | arena: Arena::default(), | ||
230 | }; | ||
231 | res.init(source_file); | ||
232 | res | ||
233 | } | ||
234 | |||
235 | fn init(&mut self, source_file: SourceFile) { | ||
236 | source_file.syntax().descendants().for_each(|it| { | ||
237 | if let Some(module_item) = ast::ModuleItem::cast(it) { | ||
238 | self.alloc(module_item.syntax().owned()); | ||
239 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | ||
240 | self.alloc(macro_call.syntax().owned()); | ||
241 | } | ||
242 | }); | ||
243 | } | ||
244 | |||
245 | fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId { | ||
246 | self.arena.alloc(item) | ||
247 | } | ||
248 | pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId { | ||
249 | assert_eq!( | ||
250 | self.file_id, file_id, | ||
251 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
252 | self.file_id, file_id | ||
253 | ); | ||
254 | self.id_of_unchecked(item) | ||
255 | } | ||
256 | pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId { | ||
257 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) { | ||
258 | return id; | ||
259 | } | ||
260 | // This should not happen. Let's try to give a sensible diagnostics. | ||
261 | if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) { | ||
262 | // FIXME(#288): whyyy are we getting here? | ||
263 | log::error!( | ||
264 | "unequal syntax nodes with the same range:\n{:?}\n{:?}", | ||
265 | item, | ||
266 | i | ||
267 | ); | ||
268 | return id; | ||
269 | } | ||
270 | panic!( | ||
271 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
272 | item, | ||
273 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
274 | ); | ||
275 | } | ||
276 | pub fn id_of_source_file(&self) -> SourceFileItemId { | ||
277 | let (id, _syntax) = self.arena.iter().next().unwrap(); | ||
278 | id | ||
279 | } | ||
280 | } | ||
281 | |||
282 | impl std::ops::Index<SourceFileItemId> for SourceFileItems { | ||
283 | type Output = SyntaxNode; | ||
284 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNode { | ||
285 | &self.arena[idx] | ||
286 | } | ||
287 | } | ||
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs index 89b1e639e..a0821d15d 100644 --- a/crates/ra_hir/src/krate.rs +++ b/crates/ra_hir/src/krate.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | pub use ra_db::CrateId; | 1 | pub use ra_db::{CrateId, Cancelable}; |
2 | 2 | ||
3 | use crate::{HirDatabase, Module, Cancelable, Name, AsName}; | 3 | use crate::{HirDatabase, Module, Name, AsName, HirFileId}; |
4 | 4 | ||
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 |
@@ -35,6 +35,7 @@ impl Crate { | |||
35 | let crate_graph = db.crate_graph(); | 35 | let crate_graph = db.crate_graph(); |
36 | let file_id = crate_graph.crate_root(self.crate_id); | 36 | let file_id = crate_graph.crate_root(self.crate_id); |
37 | let source_root_id = db.file_source_root(file_id); | 37 | let source_root_id = db.file_source_root(file_id); |
38 | let file_id = HirFileId::from(file_id); | ||
38 | let module_tree = db.module_tree(source_root_id)?; | 39 | let module_tree = db.module_tree(source_root_id)?; |
39 | // FIXME: teach module tree about crate roots instead of guessing | 40 | // FIXME: teach module tree about crate roots instead of guessing |
40 | let (module_id, _) = ctry!(module_tree | 41 | let (module_id, _) = ctry!(module_tree |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 5bbb09c01..8ee52a466 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -22,7 +22,10 @@ mod path; | |||
22 | mod arena; | 22 | mod arena; |
23 | pub mod source_binder; | 23 | pub mod source_binder; |
24 | 24 | ||
25 | mod ids; | ||
26 | mod macros; | ||
25 | mod name; | 27 | mod name; |
28 | // can't use `crate` or `r#crate` here :( | ||
26 | mod krate; | 29 | mod krate; |
27 | mod module; | 30 | mod module; |
28 | mod function; | 31 | mod function; |
@@ -30,21 +33,18 @@ mod adt; | |||
30 | mod type_ref; | 33 | mod type_ref; |
31 | mod ty; | 34 | mod ty; |
32 | 35 | ||
33 | use std::ops::Index; | ||
34 | |||
35 | use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind}; | ||
36 | use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; | ||
37 | |||
38 | use crate::{ | 36 | use crate::{ |
39 | db::HirDatabase, | 37 | db::HirDatabase, |
40 | arena::{Arena, Id}, | ||
41 | name::{AsName, KnownName}, | 38 | name::{AsName, KnownName}, |
39 | ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems}, | ||
42 | }; | 40 | }; |
43 | 41 | ||
44 | pub use self::{ | 42 | pub use self::{ |
45 | path::{Path, PathKind}, | 43 | path::{Path, PathKind}, |
46 | name::Name, | 44 | name::Name, |
47 | krate::Crate, | 45 | krate::Crate, |
46 | ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc}, | ||
47 | macros::{MacroDef, MacroInput, MacroExpansion}, | ||
48 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, | 48 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, |
49 | function::{Function, FnScopes}, | 49 | function::{Function, FnScopes}, |
50 | adt::{Struct, Enum}, | 50 | adt::{Struct, Enum}, |
@@ -53,60 +53,6 @@ pub use self::{ | |||
53 | 53 | ||
54 | pub use self::function::FnSignatureInfo; | 54 | pub use self::function::FnSignatureInfo; |
55 | 55 | ||
56 | /// Def's are a core concept of hir. A `Def` is an Item (function, module, etc) | ||
57 | /// in a specific module. | ||
58 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
59 | pub struct DefId(u32); | ||
60 | ra_db::impl_numeric_id!(DefId); | ||
61 | |||
62 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
63 | pub(crate) enum DefKind { | ||
64 | Module, | ||
65 | Function, | ||
66 | Struct, | ||
67 | Enum, | ||
68 | Item, | ||
69 | |||
70 | StructCtor, | ||
71 | } | ||
72 | |||
73 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
74 | pub struct DefLoc { | ||
75 | pub(crate) kind: DefKind, | ||
76 | source_root_id: SourceRootId, | ||
77 | module_id: ModuleId, | ||
78 | source_item_id: SourceItemId, | ||
79 | } | ||
80 | |||
81 | impl DefKind { | ||
82 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> { | ||
83 | match kind { | ||
84 | SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), | ||
85 | SyntaxKind::MODULE => PerNs::types(DefKind::Module), | ||
86 | SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), | ||
87 | SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), | ||
88 | // These define items, but don't have their own DefKinds yet: | ||
89 | SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), | ||
90 | SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), | ||
91 | SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), | ||
92 | SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), | ||
93 | _ => PerNs::none(), | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl DefId { | ||
99 | pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc { | ||
100 | db.as_ref().id2loc(self) | ||
101 | } | ||
102 | } | ||
103 | |||
104 | impl DefLoc { | ||
105 | pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId { | ||
106 | db.as_ref().loc2id(&self) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub enum Def { | 56 | pub enum Def { |
111 | Module(Module), | 57 | Module(Module), |
112 | Function(Function), | 58 | Function(Function), |
@@ -114,106 +60,3 @@ pub enum Def { | |||
114 | Enum(Enum), | 60 | Enum(Enum), |
115 | Item, | 61 | Item, |
116 | } | 62 | } |
117 | |||
118 | impl DefId { | ||
119 | pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> { | ||
120 | let loc = self.loc(db); | ||
121 | let res = match loc.kind { | ||
122 | DefKind::Module => { | ||
123 | let module = Module::new(db, loc.source_root_id, loc.module_id)?; | ||
124 | Def::Module(module) | ||
125 | } | ||
126 | DefKind::Function => { | ||
127 | let function = Function::new(self); | ||
128 | Def::Function(function) | ||
129 | } | ||
130 | DefKind::Struct => { | ||
131 | let struct_def = Struct::new(self); | ||
132 | Def::Struct(struct_def) | ||
133 | } | ||
134 | DefKind::Enum => { | ||
135 | let enum_def = Enum::new(self); | ||
136 | Def::Enum(enum_def) | ||
137 | } | ||
138 | DefKind::StructCtor => Def::Item, | ||
139 | DefKind::Item => Def::Item, | ||
140 | }; | ||
141 | Ok(res) | ||
142 | } | ||
143 | |||
144 | /// For a module, returns that module; for any other def, returns the containing module. | ||
145 | pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { | ||
146 | let loc = self.loc(db); | ||
147 | Module::new(db, loc.source_root_id, loc.module_id) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
152 | /// it's OK to use it as a salsa key/value. | ||
153 | pub(crate) type SourceFileItemId = Id<SyntaxNode>; | ||
154 | |||
155 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
156 | pub struct SourceItemId { | ||
157 | file_id: FileId, | ||
158 | /// None for the whole file. | ||
159 | item_id: Option<SourceFileItemId>, | ||
160 | } | ||
161 | |||
162 | /// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. | ||
163 | #[derive(Debug, PartialEq, Eq)] | ||
164 | pub struct SourceFileItems { | ||
165 | file_id: FileId, | ||
166 | arena: Arena<SyntaxNode>, | ||
167 | } | ||
168 | |||
169 | impl SourceFileItems { | ||
170 | fn new(file_id: FileId) -> SourceFileItems { | ||
171 | SourceFileItems { | ||
172 | file_id, | ||
173 | arena: Arena::default(), | ||
174 | } | ||
175 | } | ||
176 | |||
177 | fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId { | ||
178 | self.arena.alloc(item) | ||
179 | } | ||
180 | pub fn id_of(&self, file_id: FileId, item: SyntaxNodeRef) -> SourceFileItemId { | ||
181 | assert_eq!( | ||
182 | self.file_id, file_id, | ||
183 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
184 | self.file_id, file_id | ||
185 | ); | ||
186 | self.id_of_unchecked(item) | ||
187 | } | ||
188 | fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId { | ||
189 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) { | ||
190 | return id; | ||
191 | } | ||
192 | // This should not happen. Let's try to give a sensible diagnostics. | ||
193 | if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) { | ||
194 | // FIXME(#288): whyyy are we getting here? | ||
195 | log::error!( | ||
196 | "unequal syntax nodes with the same range:\n{:?}\n{:?}", | ||
197 | item, | ||
198 | i | ||
199 | ); | ||
200 | return id; | ||
201 | } | ||
202 | panic!( | ||
203 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
204 | item, | ||
205 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
206 | ); | ||
207 | } | ||
208 | pub fn id_of_source_file(&self) -> SourceFileItemId { | ||
209 | let (id, _syntax) = self.arena.iter().next().unwrap(); | ||
210 | id | ||
211 | } | ||
212 | } | ||
213 | |||
214 | impl Index<SourceFileItemId> for SourceFileItems { | ||
215 | type Output = SyntaxNode; | ||
216 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNode { | ||
217 | &self.arena[idx] | ||
218 | } | ||
219 | } | ||
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs new file mode 100644 index 000000000..1b378c977 --- /dev/null +++ b/crates/ra_hir/src/macros.rs | |||
@@ -0,0 +1,199 @@ | |||
1 | /// Machinery for macro expansion. | ||
2 | /// | ||
3 | /// One of the more complicated things about macros is managing the source code | ||
4 | /// that is produced after expansion. See `HirFileId` and `MacroCallId` for how | ||
5 | /// do we do that. | ||
6 | /// | ||
7 | /// When file-management question is resolved, all that is left is a token tree | ||
8 | /// to token tree transformation plus hygent. We don't have either of thouse | ||
9 | /// yet, so all macros are string based at the moment! | ||
10 | use std::sync::Arc; | ||
11 | |||
12 | use ra_db::LocalSyntaxPtr; | ||
13 | use ra_syntax::{ | ||
14 | TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode, | ||
15 | ast::{self, NameOwner}, | ||
16 | }; | ||
17 | |||
18 | use crate::{HirDatabase, MacroCallId}; | ||
19 | |||
20 | // Hard-coded defs for now :-( | ||
21 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
22 | pub enum MacroDef { | ||
23 | CTry, | ||
24 | Vec, | ||
25 | QueryGroup, | ||
26 | } | ||
27 | |||
28 | impl MacroDef { | ||
29 | /// Expands macro call, returning the expansion and offset to be used to | ||
30 | /// convert ranges between expansion and original source. | ||
31 | pub fn ast_expand(macro_call: ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> { | ||
32 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
33 | let exp = def.expand(input)?; | ||
34 | let off = macro_call.token_tree()?.syntax().range().start(); | ||
35 | Some((off, exp)) | ||
36 | } | ||
37 | |||
38 | fn from_call(macro_call: ast::MacroCall) -> Option<(MacroDef, MacroInput)> { | ||
39 | let def = { | ||
40 | let path = macro_call.path()?; | ||
41 | let name_ref = path.segment()?.name_ref()?; | ||
42 | if name_ref.text() == "ctry" { | ||
43 | MacroDef::CTry | ||
44 | } else if name_ref.text() == "vec" { | ||
45 | MacroDef::Vec | ||
46 | } else if name_ref.text() == "query_group" { | ||
47 | MacroDef::QueryGroup | ||
48 | } else { | ||
49 | return None; | ||
50 | } | ||
51 | }; | ||
52 | |||
53 | let input = { | ||
54 | let arg = macro_call.token_tree()?.syntax(); | ||
55 | MacroInput { | ||
56 | text: arg.text().to_string(), | ||
57 | } | ||
58 | }; | ||
59 | Some((def, input)) | ||
60 | } | ||
61 | |||
62 | fn expand(self, input: MacroInput) -> Option<MacroExpansion> { | ||
63 | match self { | ||
64 | MacroDef::CTry => self.expand_ctry(input), | ||
65 | MacroDef::Vec => self.expand_vec(input), | ||
66 | MacroDef::QueryGroup => self.expand_query_group(input), | ||
67 | } | ||
68 | } | ||
69 | fn expand_ctry(self, input: MacroInput) -> Option<MacroExpansion> { | ||
70 | let text = format!( | ||
71 | r" | ||
72 | fn dummy() {{ | ||
73 | match {} {{ | ||
74 | None => return Ok(None), | ||
75 | Some(it) => it, | ||
76 | }} | ||
77 | }}", | ||
78 | input.text | ||
79 | ); | ||
80 | let file = SourceFileNode::parse(&text); | ||
81 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
82 | let match_arg = match_expr.expr()?; | ||
83 | let ptr = LocalSyntaxPtr::new(match_arg.syntax()); | ||
84 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
85 | let ranges_map = vec![(src_range, match_arg.syntax().range())]; | ||
86 | let res = MacroExpansion { | ||
87 | text, | ||
88 | ranges_map, | ||
89 | ptr, | ||
90 | }; | ||
91 | Some(res) | ||
92 | } | ||
93 | fn expand_vec(self, input: MacroInput) -> Option<MacroExpansion> { | ||
94 | let text = format!(r"fn dummy() {{ {}; }}", input.text); | ||
95 | let file = SourceFileNode::parse(&text); | ||
96 | let array_expr = file.syntax().descendants().find_map(ast::ArrayExpr::cast)?; | ||
97 | let ptr = LocalSyntaxPtr::new(array_expr.syntax()); | ||
98 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
99 | let ranges_map = vec![(src_range, array_expr.syntax().range())]; | ||
100 | let res = MacroExpansion { | ||
101 | text, | ||
102 | ranges_map, | ||
103 | ptr, | ||
104 | }; | ||
105 | Some(res) | ||
106 | } | ||
107 | fn expand_query_group(self, input: MacroInput) -> Option<MacroExpansion> { | ||
108 | let anchor = "trait "; | ||
109 | let pos = input.text.find(anchor)? + anchor.len(); | ||
110 | let trait_name = input.text[pos..] | ||
111 | .chars() | ||
112 | .take_while(|c| c.is_alphabetic()) | ||
113 | .collect::<String>(); | ||
114 | if trait_name.is_empty() { | ||
115 | return None; | ||
116 | } | ||
117 | let src_range = TextRange::offset_len((pos as u32).into(), TextUnit::of_str(&trait_name)); | ||
118 | let text = format!(r"trait {} {{ }}", trait_name); | ||
119 | let file = SourceFileNode::parse(&text); | ||
120 | let trait_def = file.syntax().descendants().find_map(ast::TraitDef::cast)?; | ||
121 | let name = trait_def.name()?; | ||
122 | let ptr = LocalSyntaxPtr::new(trait_def.syntax()); | ||
123 | let ranges_map = vec![(src_range, name.syntax().range())]; | ||
124 | let res = MacroExpansion { | ||
125 | text, | ||
126 | ranges_map, | ||
127 | ptr, | ||
128 | }; | ||
129 | Some(res) | ||
130 | } | ||
131 | } | ||
132 | |||
133 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
134 | pub struct MacroInput { | ||
135 | // Should be token trees | ||
136 | pub text: String, | ||
137 | } | ||
138 | |||
139 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
140 | pub struct MacroExpansion { | ||
141 | /// The result of macro expansion. Should be token tree as well. | ||
142 | text: String, | ||
143 | /// Correspondence between ranges in the original source code and ranges in | ||
144 | /// the macro. | ||
145 | ranges_map: Vec<(TextRange, TextRange)>, | ||
146 | /// Implementation detail: internally, a macro is expanded to the whole file, | ||
147 | /// even if it is an expression. This `ptr` selects the actual expansion from | ||
148 | /// the expanded file. | ||
149 | ptr: LocalSyntaxPtr, | ||
150 | } | ||
151 | |||
152 | impl MacroExpansion { | ||
153 | // FIXME: does not really make sense, macro expansion is not neccessary a | ||
154 | // whole file. See `MacroExpansion::ptr` as well. | ||
155 | pub(crate) fn file(&self) -> SourceFileNode { | ||
156 | SourceFileNode::parse(&self.text) | ||
157 | } | ||
158 | |||
159 | pub fn syntax(&self) -> SyntaxNode { | ||
160 | self.ptr.resolve(&self.file()) | ||
161 | } | ||
162 | /// Maps range in the source code to the range in the expanded code. | ||
163 | pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
164 | for (s_range, t_range) in self.ranges_map.iter() { | ||
165 | if src_range.is_subrange(&s_range) { | ||
166 | let src_at_zero_range = src_range - src_range.start(); | ||
167 | let src_range_offset = src_range.start() - s_range.start(); | ||
168 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
169 | return Some(src_range); | ||
170 | } | ||
171 | } | ||
172 | None | ||
173 | } | ||
174 | /// Maps range in the expanded code to the range in the source code. | ||
175 | pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
176 | for (s_range, t_range) in self.ranges_map.iter() { | ||
177 | if tgt_range.is_subrange(&t_range) { | ||
178 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
179 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
180 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
181 | return Some(src_range); | ||
182 | } | ||
183 | } | ||
184 | None | ||
185 | } | ||
186 | } | ||
187 | |||
188 | pub(crate) fn expand_macro_invocation( | ||
189 | db: &impl HirDatabase, | ||
190 | invoc: MacroCallId, | ||
191 | ) -> Option<Arc<MacroExpansion>> { | ||
192 | let loc = invoc.loc(db); | ||
193 | let syntax = db.file_item(loc.source_item_id); | ||
194 | let syntax = syntax.borrowed(); | ||
195 | let macro_call = ast::MacroCall::cast(syntax).unwrap(); | ||
196 | |||
197 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
198 | def.expand(input).map(Arc::new) | ||
199 | } | ||
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index a2507c9b5..89b18194a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -6,7 +6,7 @@ use ra_db::{LocationIntener, BaseDatabase, FilePosition, FileId, CrateGraph, Sou | |||
6 | use relative_path::RelativePathBuf; | 6 | use relative_path::RelativePathBuf; |
7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | 7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; |
8 | 8 | ||
9 | use crate::{db, DefId, DefLoc}; | 9 | use crate::{db, DefId, DefLoc, MacroCallId, MacroCallLoc}; |
10 | 10 | ||
11 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 11 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
12 | 12 | ||
@@ -95,6 +95,7 @@ impl MockDatabase { | |||
95 | #[derive(Debug, Default)] | 95 | #[derive(Debug, Default)] |
96 | struct IdMaps { | 96 | struct IdMaps { |
97 | defs: LocationIntener<DefLoc, DefId>, | 97 | defs: LocationIntener<DefLoc, DefId>, |
98 | macros: LocationIntener<MacroCallLoc, MacroCallId>, | ||
98 | } | 99 | } |
99 | 100 | ||
100 | impl salsa::Database for MockDatabase { | 101 | impl salsa::Database for MockDatabase { |
@@ -144,6 +145,11 @@ impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase { | |||
144 | &self.id_maps.defs | 145 | &self.id_maps.defs |
145 | } | 146 | } |
146 | } | 147 | } |
148 | impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>> for MockDatabase { | ||
149 | fn as_ref(&self) -> &LocationIntener<MacroCallLoc, MacroCallId> { | ||
150 | &self.id_maps.macros | ||
151 | } | ||
152 | } | ||
147 | 153 | ||
148 | impl MockDatabase { | 154 | impl MockDatabase { |
149 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> { | 155 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> { |
@@ -183,6 +189,8 @@ salsa::database_storage! { | |||
183 | fn file_lines() for ra_db::FileLinesQuery; | 189 | fn file_lines() for ra_db::FileLinesQuery; |
184 | } | 190 | } |
185 | impl db::HirDatabase { | 191 | impl db::HirDatabase { |
192 | fn hir_source_file() for db::HirSourceFileQuery; | ||
193 | fn expand_macro_invocation() for db::ExpandMacroCallQuery; | ||
186 | fn module_tree() for db::ModuleTreeQuery; | 194 | fn module_tree() for db::ModuleTreeQuery; |
187 | fn fn_scopes() for db::FnScopesQuery; | 195 | fn fn_scopes() for db::FnScopesQuery; |
188 | fn file_items() for db::SourceFileItemsQuery; | 196 | fn file_items() for db::SourceFileItemsQuery; |
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 24c346984..a53b69d20 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs | |||
@@ -15,6 +15,7 @@ use relative_path::RelativePathBuf; | |||
15 | use crate::{ | 15 | use crate::{ |
16 | Def, DefKind, DefLoc, DefId, | 16 | Def, DefKind, DefLoc, DefId, |
17 | Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, | 17 | Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, |
18 | HirFileId, | ||
18 | arena::{Arena, Id}, | 19 | arena::{Arena, Id}, |
19 | }; | 20 | }; |
20 | 21 | ||
@@ -48,13 +49,17 @@ impl Module { | |||
48 | /// Returns `None` for the root module | 49 | /// Returns `None` for the root module |
49 | pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { | 50 | pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { |
50 | let link = self.module_id.parent_link(&self.tree)?; | 51 | let link = self.module_id.parent_link(&self.tree)?; |
51 | let file_id = link.owner(&self.tree).source(&self.tree).file_id(); | 52 | let file_id = link |
53 | .owner(&self.tree) | ||
54 | .source(&self.tree) | ||
55 | .file_id() | ||
56 | .as_original_file(); | ||
52 | let src = link.bind_source(&self.tree, db); | 57 | let src = link.bind_source(&self.tree, db); |
53 | Some((file_id, src)) | 58 | Some((file_id, src)) |
54 | } | 59 | } |
55 | 60 | ||
56 | pub fn source(&self) -> ModuleSource { | 61 | pub fn file_id(&self) -> FileId { |
57 | self.module_id.source(&self.tree) | 62 | self.source().file_id().as_original_file() |
58 | } | 63 | } |
59 | 64 | ||
60 | /// Parent module. Returns `None` if this is a root module. | 65 | /// Parent module. Returns `None` if this is a root module. |
@@ -69,12 +74,17 @@ impl Module { | |||
69 | /// Returns the crate this module is part of. | 74 | /// Returns the crate this module is part of. |
70 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { | 75 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { |
71 | let root_id = self.module_id.crate_root(&self.tree); | 76 | let root_id = self.module_id.crate_root(&self.tree); |
72 | let file_id = root_id.source(&self.tree).file_id(); | 77 | let file_id = root_id.source(&self.tree).file_id().as_original_file(); |
73 | let crate_graph = db.crate_graph(); | 78 | let crate_graph = db.crate_graph(); |
74 | let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; | 79 | let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; |
75 | Some(Crate::new(crate_id)) | 80 | Some(Crate::new(crate_id)) |
76 | } | 81 | } |
77 | 82 | ||
83 | /// Returns the all modules on the way to the root. | ||
84 | pub fn path_to_root(&self) -> Vec<Module> { | ||
85 | generate(Some(self.clone()), move |it| it.parent()).collect::<Vec<Module>>() | ||
86 | } | ||
87 | |||
78 | /// The root of the tree this module is part of | 88 | /// The root of the tree this module is part of |
79 | pub fn crate_root(&self) -> Module { | 89 | pub fn crate_root(&self) -> Module { |
80 | let root_id = self.module_id.crate_root(&self.tree); | 90 | let root_id = self.module_id.crate_root(&self.tree); |
@@ -157,6 +167,10 @@ impl Module { | |||
157 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { | 167 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { |
158 | self.module_id.problems(&self.tree, db) | 168 | self.module_id.problems(&self.tree, db) |
159 | } | 169 | } |
170 | |||
171 | pub(crate) fn source(&self) -> ModuleSource { | ||
172 | self.module_id.source(&self.tree) | ||
173 | } | ||
160 | } | 174 | } |
161 | 175 | ||
162 | /// Physically, rust source is organized as a set of files, but logically it is | 176 | /// Physically, rust source is organized as a set of files, but logically it is |
@@ -286,18 +300,18 @@ pub struct ModuleData { | |||
286 | 300 | ||
287 | impl ModuleSource { | 301 | impl ModuleSource { |
288 | // precondition: item_id **must** point to module | 302 | // precondition: item_id **must** point to module |
289 | fn new(file_id: FileId, item_id: Option<SourceFileItemId>) -> ModuleSource { | 303 | fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource { |
290 | let source_item_id = SourceItemId { file_id, item_id }; | 304 | let source_item_id = SourceItemId { file_id, item_id }; |
291 | ModuleSource(source_item_id) | 305 | ModuleSource(source_item_id) |
292 | } | 306 | } |
293 | 307 | ||
294 | pub(crate) fn new_file(file_id: FileId) -> ModuleSource { | 308 | pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource { |
295 | ModuleSource::new(file_id, None) | 309 | ModuleSource::new(file_id, None) |
296 | } | 310 | } |
297 | 311 | ||
298 | pub(crate) fn new_inline( | 312 | pub(crate) fn new_inline( |
299 | db: &impl HirDatabase, | 313 | db: &impl HirDatabase, |
300 | file_id: FileId, | 314 | file_id: HirFileId, |
301 | m: ast::Module, | 315 | m: ast::Module, |
302 | ) -> ModuleSource { | 316 | ) -> ModuleSource { |
303 | assert!(!m.has_semi()); | 317 | assert!(!m.has_semi()); |
@@ -306,7 +320,7 @@ impl ModuleSource { | |||
306 | ModuleSource::new(file_id, Some(item_id)) | 320 | ModuleSource::new(file_id, Some(item_id)) |
307 | } | 321 | } |
308 | 322 | ||
309 | pub fn file_id(self) -> FileId { | 323 | pub(crate) fn file_id(self) -> HirFileId { |
310 | self.0.file_id | 324 | self.0.file_id |
311 | } | 325 | } |
312 | 326 | ||
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs index eded85a63..3849026db 100644 --- a/crates/ra_hir/src/module/imp.rs +++ b/crates/ra_hir/src/module/imp.rs | |||
@@ -64,7 +64,7 @@ fn create_module_tree<'a>( | |||
64 | 64 | ||
65 | let source_root = db.source_root(source_root); | 65 | let source_root = db.source_root(source_root); |
66 | for &file_id in source_root.files.values() { | 66 | for &file_id in source_root.files.values() { |
67 | let source = ModuleSource::new_file(file_id); | 67 | let source = ModuleSource::new_file(file_id.into()); |
68 | if visited.contains(&source) { | 68 | if visited.contains(&source) { |
69 | continue; // TODO: use explicit crate_roots here | 69 | continue; // TODO: use explicit crate_roots here |
70 | } | 70 | } |
@@ -123,7 +123,7 @@ fn build_subtree( | |||
123 | visited, | 123 | visited, |
124 | roots, | 124 | roots, |
125 | Some(link), | 125 | Some(link), |
126 | ModuleSource::new_file(file_id), | 126 | ModuleSource::new_file(file_id.into()), |
127 | ), | 127 | ), |
128 | }) | 128 | }) |
129 | .collect::<Cancelable<Vec<_>>>()?; | 129 | .collect::<Cancelable<Vec<_>>>()?; |
@@ -155,7 +155,7 @@ fn resolve_submodule( | |||
155 | name: &Name, | 155 | name: &Name, |
156 | ) -> (Vec<FileId>, Option<Problem>) { | 156 | ) -> (Vec<FileId>, Option<Problem>) { |
157 | // FIXME: handle submodules of inline modules properly | 157 | // FIXME: handle submodules of inline modules properly |
158 | let file_id = source.file_id(); | 158 | let file_id = source.file_id().original_file(db); |
159 | let source_root_id = db.file_source_root(file_id); | 159 | let source_root_id = db.file_source_root(file_id); |
160 | let path = db.file_relative_path(file_id); | 160 | let path = db.file_relative_path(file_id); |
161 | let root = RelativePathBuf::default(); | 161 | let root = RelativePathBuf::default(); |
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 68eb02a98..8d1209626 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
@@ -22,10 +22,10 @@ use ra_syntax::{ | |||
22 | SyntaxKind::{self, *}, | 22 | SyntaxKind::{self, *}, |
23 | ast::{self, AstNode} | 23 | ast::{self, AstNode} |
24 | }; | 24 | }; |
25 | use ra_db::SourceRootId; | 25 | use ra_db::{SourceRootId, Cancelable, FileId}; |
26 | 26 | ||
27 | use crate::{ | 27 | use crate::{ |
28 | Cancelable, FileId, | 28 | HirFileId, |
29 | DefId, DefLoc, DefKind, | 29 | DefId, DefLoc, DefKind, |
30 | SourceItemId, SourceFileItemId, SourceFileItems, | 30 | SourceItemId, SourceFileItemId, SourceFileItems, |
31 | Path, PathKind, | 31 | Path, PathKind, |
@@ -64,14 +64,14 @@ impl ModuleScope { | |||
64 | /// running name resolution. | 64 | /// running name resolution. |
65 | #[derive(Debug, Default, PartialEq, Eq)] | 65 | #[derive(Debug, Default, PartialEq, Eq)] |
66 | pub struct InputModuleItems { | 66 | pub struct InputModuleItems { |
67 | items: Vec<ModuleItem>, | 67 | pub(crate) items: Vec<ModuleItem>, |
68 | imports: Vec<Import>, | 68 | imports: Vec<Import>, |
69 | } | 69 | } |
70 | 70 | ||
71 | #[derive(Debug, PartialEq, Eq)] | 71 | #[derive(Debug, PartialEq, Eq)] |
72 | struct ModuleItem { | 72 | pub(crate) struct ModuleItem { |
73 | id: SourceFileItemId, | 73 | pub(crate) id: SourceItemId, |
74 | name: Name, | 74 | pub(crate) name: Name, |
75 | kind: SyntaxKind, | 75 | kind: SyntaxKind, |
76 | vis: Vis, | 76 | vis: Vis, |
77 | } | 77 | } |
@@ -95,9 +95,11 @@ pub struct NamedImport { | |||
95 | } | 95 | } |
96 | 96 | ||
97 | impl NamedImport { | 97 | impl NamedImport { |
98 | // FIXME: this is only here for one use-case in completion. Seems like a | ||
99 | // pretty gross special case. | ||
98 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { | 100 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { |
99 | let source_item_id = SourceItemId { | 101 | let source_item_id = SourceItemId { |
100 | file_id, | 102 | file_id: file_id.into(), |
101 | item_id: Some(self.file_item_id), | 103 | item_id: Some(self.file_item_id), |
102 | }; | 104 | }; |
103 | let syntax = db.file_item(source_item_id); | 105 | let syntax = db.file_item(source_item_id); |
@@ -209,24 +211,28 @@ impl<T> PerNs<T> { | |||
209 | } | 211 | } |
210 | 212 | ||
211 | impl InputModuleItems { | 213 | impl InputModuleItems { |
212 | pub(crate) fn new<'a>( | 214 | pub(crate) fn add_item( |
215 | &mut self, | ||
216 | file_id: HirFileId, | ||
213 | file_items: &SourceFileItems, | 217 | file_items: &SourceFileItems, |
214 | items: impl Iterator<Item = ast::ModuleItem<'a>>, | 218 | item: ast::ModuleItem, |
215 | ) -> InputModuleItems { | 219 | ) -> Option<()> { |
216 | let mut res = InputModuleItems::default(); | ||
217 | for item in items { | ||
218 | res.add_item(file_items, item); | ||
219 | } | ||
220 | res | ||
221 | } | ||
222 | |||
223 | fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> { | ||
224 | match item { | 220 | match item { |
225 | ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 221 | ast::ModuleItem::StructDef(it) => { |
226 | ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 222 | self.items.push(ModuleItem::new(file_id, file_items, it)?) |
227 | ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 223 | } |
228 | ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 224 | ast::ModuleItem::EnumDef(it) => { |
229 | ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 225 | self.items.push(ModuleItem::new(file_id, file_items, it)?) |
226 | } | ||
227 | ast::ModuleItem::FnDef(it) => { | ||
228 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
229 | } | ||
230 | ast::ModuleItem::TraitDef(it) => { | ||
231 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
232 | } | ||
233 | ast::ModuleItem::TypeDef(it) => { | ||
234 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
235 | } | ||
230 | ast::ModuleItem::ImplItem(_) => { | 236 | ast::ModuleItem::ImplItem(_) => { |
231 | // impls don't define items | 237 | // impls don't define items |
232 | } | 238 | } |
@@ -234,9 +240,15 @@ impl InputModuleItems { | |||
234 | ast::ModuleItem::ExternCrateItem(_) => { | 240 | ast::ModuleItem::ExternCrateItem(_) => { |
235 | // TODO | 241 | // TODO |
236 | } | 242 | } |
237 | ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 243 | ast::ModuleItem::ConstDef(it) => { |
238 | ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 244 | self.items.push(ModuleItem::new(file_id, file_items, it)?) |
239 | ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?), | 245 | } |
246 | ast::ModuleItem::StaticDef(it) => { | ||
247 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
248 | } | ||
249 | ast::ModuleItem::Module(it) => { | ||
250 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
251 | } | ||
240 | } | 252 | } |
241 | Some(()) | 253 | Some(()) |
242 | } | 254 | } |
@@ -258,11 +270,16 @@ impl InputModuleItems { | |||
258 | } | 270 | } |
259 | 271 | ||
260 | impl ModuleItem { | 272 | impl ModuleItem { |
261 | fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { | 273 | fn new<'a>( |
274 | file_id: HirFileId, | ||
275 | file_items: &SourceFileItems, | ||
276 | item: impl ast::NameOwner<'a>, | ||
277 | ) -> Option<ModuleItem> { | ||
262 | let name = item.name()?.as_name(); | 278 | let name = item.name()?.as_name(); |
263 | let kind = item.syntax().kind(); | 279 | let kind = item.syntax().kind(); |
264 | let vis = Vis::Other; | 280 | let vis = Vis::Other; |
265 | let id = file_items.id_of_unchecked(item.syntax()); | 281 | let item_id = Some(file_items.id_of_unchecked(item.syntax())); |
282 | let id = SourceItemId { file_id, item_id }; | ||
266 | let res = ModuleItem { | 283 | let res = ModuleItem { |
267 | id, | 284 | id, |
268 | name, | 285 | name, |
@@ -302,7 +319,7 @@ where | |||
302 | 319 | ||
303 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { | 320 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { |
304 | for (&module_id, items) in self.input.iter() { | 321 | for (&module_id, items) in self.input.iter() { |
305 | self.populate_module(module_id, items)?; | 322 | self.populate_module(module_id, Arc::clone(items))?; |
306 | } | 323 | } |
307 | 324 | ||
308 | for &module_id in self.input.keys() { | 325 | for &module_id in self.input.keys() { |
@@ -312,9 +329,11 @@ where | |||
312 | Ok(self.result) | 329 | Ok(self.result) |
313 | } | 330 | } |
314 | 331 | ||
315 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> { | 332 | fn populate_module( |
316 | let file_id = module_id.source(&self.module_tree).file_id(); | 333 | &mut self, |
317 | 334 | module_id: ModuleId, | |
335 | input: Arc<InputModuleItems>, | ||
336 | ) -> Cancelable<()> { | ||
318 | let mut module_items = ModuleScope::default(); | 337 | let mut module_items = ModuleScope::default(); |
319 | 338 | ||
320 | // Populate extern crates prelude | 339 | // Populate extern crates prelude |
@@ -322,7 +341,8 @@ where | |||
322 | let root_id = module_id.crate_root(&self.module_tree); | 341 | let root_id = module_id.crate_root(&self.module_tree); |
323 | let file_id = root_id.source(&self.module_tree).file_id(); | 342 | let file_id = root_id.source(&self.module_tree).file_id(); |
324 | let crate_graph = self.db.crate_graph(); | 343 | let crate_graph = self.db.crate_graph(); |
325 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) { | 344 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) |
345 | { | ||
326 | let krate = Crate::new(crate_id); | 346 | let krate = Crate::new(crate_id); |
327 | for dep in krate.dependencies(self.db) { | 347 | for dep in krate.dependencies(self.db) { |
328 | if let Some(module) = dep.krate.root_module(self.db)? { | 348 | if let Some(module) = dep.krate.root_module(self.db)? { |
@@ -362,10 +382,7 @@ where | |||
362 | kind: k, | 382 | kind: k, |
363 | source_root_id: self.source_root, | 383 | source_root_id: self.source_root, |
364 | module_id, | 384 | module_id, |
365 | source_item_id: SourceItemId { | 385 | source_item_id: item.id, |
366 | file_id, | ||
367 | item_id: Some(item.id), | ||
368 | }, | ||
369 | }; | 386 | }; |
370 | def_loc.id(self.db) | 387 | def_loc.id(self.db) |
371 | }); | 388 | }); |
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index ca20f064f..a6a0bea31 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs | |||
@@ -78,6 +78,35 @@ fn item_map_smoke_test() { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | #[test] | 80 | #[test] |
81 | fn item_map_contains_items_from_expansions() { | ||
82 | let (item_map, module_id) = item_map( | ||
83 | " | ||
84 | //- /lib.rs | ||
85 | mod foo; | ||
86 | |||
87 | use crate::foo::bar::Baz; | ||
88 | <|> | ||
89 | |||
90 | //- /foo/mod.rs | ||
91 | pub mod bar; | ||
92 | |||
93 | //- /foo/bar.rs | ||
94 | salsa::query_group! { | ||
95 | trait Baz {} | ||
96 | } | ||
97 | ", | ||
98 | ); | ||
99 | check_module_item_map( | ||
100 | &item_map, | ||
101 | module_id, | ||
102 | " | ||
103 | Baz: t | ||
104 | foo: t | ||
105 | ", | ||
106 | ); | ||
107 | } | ||
108 | |||
109 | #[test] | ||
81 | fn item_map_using_self() { | 110 | fn item_map_using_self() { |
82 | let (item_map, module_id) = item_map( | 111 | let (item_map, module_id) = item_map( |
83 | " | 112 | " |
@@ -144,6 +173,59 @@ fn typing_inside_a_function_should_not_invalidate_item_map() { | |||
144 | let (mut db, pos) = MockDatabase::with_position( | 173 | let (mut db, pos) = MockDatabase::with_position( |
145 | " | 174 | " |
146 | //- /lib.rs | 175 | //- /lib.rs |
176 | mod foo; | ||
177 | |||
178 | use crate::foo::bar::Baz; | ||
179 | |||
180 | //- /foo/mod.rs | ||
181 | pub mod bar; | ||
182 | |||
183 | //- /foo/bar.rs | ||
184 | <|> | ||
185 | salsa::query_group! { | ||
186 | trait Baz { | ||
187 | fn foo() -> i32 { 1 + 1 } | ||
188 | } | ||
189 | } | ||
190 | ", | ||
191 | ); | ||
192 | let source_root = db.file_source_root(pos.file_id); | ||
193 | { | ||
194 | let events = db.log_executed(|| { | ||
195 | db.item_map(source_root).unwrap(); | ||
196 | }); | ||
197 | assert!(format!("{:?}", events).contains("item_map")) | ||
198 | } | ||
199 | |||
200 | let new_text = " | ||
201 | salsa::query_group! { | ||
202 | trait Baz { | ||
203 | fn foo() -> i32 { 92 } | ||
204 | } | ||
205 | } | ||
206 | " | ||
207 | .to_string(); | ||
208 | |||
209 | db.query_mut(ra_db::FileTextQuery) | ||
210 | .set(pos.file_id, Arc::new(new_text)); | ||
211 | |||
212 | { | ||
213 | let events = db.log_executed(|| { | ||
214 | db.item_map(source_root).unwrap(); | ||
215 | }); | ||
216 | assert!( | ||
217 | !format!("{:?}", events).contains("item_map"), | ||
218 | "{:#?}", | ||
219 | events | ||
220 | ) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() { | ||
226 | let (mut db, pos) = MockDatabase::with_position( | ||
227 | " | ||
228 | //- /lib.rs | ||
147 | mod foo;<|> | 229 | mod foo;<|> |
148 | 230 | ||
149 | use crate::foo::bar::Baz; | 231 | use crate::foo::bar::Baz; |
@@ -183,7 +265,7 @@ fn typing_inside_a_function_should_not_invalidate_item_map() { | |||
183 | db.item_map(source_root).unwrap(); | 265 | db.item_map(source_root).unwrap(); |
184 | }); | 266 | }); |
185 | assert!( | 267 | assert!( |
186 | !format!("{:?}", events).contains("_item_map"), | 268 | !format!("{:?}", events).contains("item_map"), |
187 | "{:#?}", | 269 | "{:#?}", |
188 | events | 270 | events |
189 | ) | 271 | ) |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index 721bd4195..a5d99beda 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -8,10 +8,11 @@ use ra_syntax::{ | |||
8 | AstNode, SyntaxNode, | 8 | AstNode, SyntaxNode, |
9 | ast::{self, NameOwner, ModuleItemOwner} | 9 | ast::{self, NameOwner, ModuleItemOwner} |
10 | }; | 10 | }; |
11 | use ra_db::{SourceRootId, FileId, Cancelable,}; | 11 | use ra_db::{SourceRootId, Cancelable,}; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, | 14 | SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId, |
15 | MacroCallLoc, | ||
15 | db::HirDatabase, | 16 | db::HirDatabase, |
16 | function::FnScopes, | 17 | function::FnScopes, |
17 | module::{ | 18 | module::{ |
@@ -47,25 +48,17 @@ pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc< | |||
47 | Ok(Arc::new(EnumData::new(enum_def.borrowed()))) | 48 | Ok(Arc::new(EnumData::new(enum_def.borrowed()))) |
48 | } | 49 | } |
49 | 50 | ||
50 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | 51 | pub(super) fn file_items(db: &impl HirDatabase, file_id: HirFileId) -> Arc<SourceFileItems> { |
51 | let mut res = SourceFileItems::new(file_id); | 52 | let source_file = db.hir_source_file(file_id); |
52 | let source_file = db.source_file(file_id); | ||
53 | let source_file = source_file.borrowed(); | 53 | let source_file = source_file.borrowed(); |
54 | source_file | 54 | let res = SourceFileItems::new(file_id, source_file); |
55 | .syntax() | ||
56 | .descendants() | ||
57 | .filter_map(ast::ModuleItem::cast) | ||
58 | .map(|it| it.syntax().owned()) | ||
59 | .for_each(|it| { | ||
60 | res.alloc(it); | ||
61 | }); | ||
62 | Arc::new(res) | 55 | Arc::new(res) |
63 | } | 56 | } |
64 | 57 | ||
65 | pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { | 58 | pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { |
66 | match source_item_id.item_id { | 59 | match source_item_id.item_id { |
67 | Some(id) => db.file_items(source_item_id.file_id)[id].clone(), | 60 | Some(id) => db.file_items(source_item_id.file_id)[id].clone(), |
68 | None => db.source_file(source_item_id.file_id).syntax().owned(), | 61 | None => db.hir_source_file(source_item_id.file_id).syntax().owned(), |
69 | } | 62 | } |
70 | } | 63 | } |
71 | 64 | ||
@@ -87,7 +80,7 @@ pub(crate) fn submodules( | |||
87 | 80 | ||
88 | fn collect_submodules<'a>( | 81 | fn collect_submodules<'a>( |
89 | db: &impl HirDatabase, | 82 | db: &impl HirDatabase, |
90 | file_id: FileId, | 83 | file_id: HirFileId, |
91 | root: impl ast::ModuleItemOwner<'a>, | 84 | root: impl ast::ModuleItemOwner<'a>, |
92 | ) -> Vec<Submodule> { | 85 | ) -> Vec<Submodule> { |
93 | modules(root) | 86 | modules(root) |
@@ -119,24 +112,48 @@ pub(crate) fn modules<'a>( | |||
119 | 112 | ||
120 | pub(super) fn input_module_items( | 113 | pub(super) fn input_module_items( |
121 | db: &impl HirDatabase, | 114 | db: &impl HirDatabase, |
122 | source_root: SourceRootId, | 115 | source_root_id: SourceRootId, |
123 | module_id: ModuleId, | 116 | module_id: ModuleId, |
124 | ) -> Cancelable<Arc<InputModuleItems>> { | 117 | ) -> Cancelable<Arc<InputModuleItems>> { |
125 | let module_tree = db.module_tree(source_root)?; | 118 | let module_tree = db.module_tree(source_root_id)?; |
126 | let source = module_id.source(&module_tree); | 119 | let source = module_id.source(&module_tree); |
127 | let file_items = db.file_items(source.file_id()); | 120 | let file_id = source.file_id(); |
128 | let res = match source.resolve(db) { | 121 | let file_items = db.file_items(file_id); |
129 | ModuleSourceNode::SourceFile(it) => { | 122 | let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| { |
130 | let items = it.borrowed().items(); | 123 | for item in items { |
131 | InputModuleItems::new(&file_items, items) | 124 | match item { |
125 | ast::ItemOrMacro::Item(it) => { | ||
126 | acc.add_item(file_id, &file_items, it); | ||
127 | } | ||
128 | ast::ItemOrMacro::Macro(macro_call) => { | ||
129 | let item_id = file_items.id_of_unchecked(macro_call.syntax()); | ||
130 | let loc = MacroCallLoc { | ||
131 | source_root_id, | ||
132 | module_id, | ||
133 | source_item_id: SourceItemId { | ||
134 | file_id, | ||
135 | item_id: Some(item_id), | ||
136 | }, | ||
137 | }; | ||
138 | let id = loc.id(db); | ||
139 | let file_id = HirFileId::from(id); | ||
140 | let file_items = db.file_items(file_id); | ||
141 | //FIXME: expand recursively | ||
142 | for item in db.hir_source_file(file_id).borrowed().items() { | ||
143 | acc.add_item(file_id, &file_items, item); | ||
144 | } | ||
145 | } | ||
146 | } | ||
132 | } | 147 | } |
148 | }; | ||
149 | |||
150 | let mut res = InputModuleItems::default(); | ||
151 | match source.resolve(db) { | ||
152 | ModuleSourceNode::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()), | ||
133 | ModuleSourceNode::Module(it) => { | 153 | ModuleSourceNode::Module(it) => { |
134 | let items = it | 154 | if let Some(item_list) = it.borrowed().item_list() { |
135 | .borrowed() | 155 | fill(&mut res, &mut item_list.items_with_macros()) |
136 | .item_list() | 156 | } |
137 | .into_iter() | ||
138 | .flat_map(|it| it.items()); | ||
139 | InputModuleItems::new(&file_items, items) | ||
140 | } | 157 | } |
141 | }; | 158 | }; |
142 | Ok(Arc::new(res)) | 159 | Ok(Arc::new(res)) |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a0d1daf71..85bd84469 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -8,8 +8,8 @@ | |||
8 | use ra_db::{FileId, FilePosition, Cancelable}; | 8 | use ra_db::{FileId, FilePosition, Cancelable}; |
9 | use ra_editor::find_node_at_offset; | 9 | use ra_editor::find_node_at_offset; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | SmolStr, TextRange, SyntaxNodeRef, | ||
11 | ast::{self, AstNode, NameOwner}, | 12 | ast::{self, AstNode, NameOwner}, |
12 | SyntaxNodeRef, | ||
13 | }; | 13 | }; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
@@ -20,7 +20,7 @@ use crate::{ | |||
20 | 20 | ||
21 | /// Locates the module by `FileId`. Picks topmost module in the file. | 21 | /// Locates the module by `FileId`. Picks topmost module in the file. |
22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { | 22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { |
23 | let module_source = ModuleSource::new_file(file_id); | 23 | let module_source = ModuleSource::new_file(file_id.into()); |
24 | module_from_source(db, module_source) | 24 | module_from_source(db, module_source) |
25 | } | 25 | } |
26 | 26 | ||
@@ -50,8 +50,8 @@ pub fn module_from_position( | |||
50 | ) -> Cancelable<Option<Module>> { | 50 | ) -> Cancelable<Option<Module>> { |
51 | let file = db.source_file(position.file_id); | 51 | let file = db.source_file(position.file_id); |
52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { | 52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { |
53 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), | 53 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id.into(), m), |
54 | _ => ModuleSource::new_file(position.file_id), | 54 | _ => ModuleSource::new_file(position.file_id.into()), |
55 | }; | 55 | }; |
56 | module_from_source(db, module_source) | 56 | module_from_source(db, module_source) |
57 | } | 57 | } |
@@ -67,9 +67,9 @@ pub fn module_from_child_node( | |||
67 | .filter_map(ast::Module::cast) | 67 | .filter_map(ast::Module::cast) |
68 | .find(|it| !it.has_semi()) | 68 | .find(|it| !it.has_semi()) |
69 | { | 69 | { |
70 | ModuleSource::new_inline(db, file_id, m) | 70 | ModuleSource::new_inline(db, file_id.into(), m) |
71 | } else { | 71 | } else { |
72 | ModuleSource::new_file(file_id) | 72 | ModuleSource::new_file(file_id.into()) |
73 | }; | 73 | }; |
74 | module_from_source(db, module_source) | 74 | module_from_source(db, module_source) |
75 | } | 75 | } |
@@ -78,7 +78,7 @@ fn module_from_source( | |||
78 | db: &impl HirDatabase, | 78 | db: &impl HirDatabase, |
79 | module_source: ModuleSource, | 79 | module_source: ModuleSource, |
80 | ) -> Cancelable<Option<Module>> { | 80 | ) -> Cancelable<Option<Module>> { |
81 | let source_root_id = db.file_source_root(module_source.file_id()); | 81 | let source_root_id = db.file_source_root(module_source.file_id().as_original_file()); |
82 | let module_tree = db.module_tree(source_root_id)?; | 82 | let module_tree = db.module_tree(source_root_id)?; |
83 | let m = module_tree | 83 | let m = module_tree |
84 | .modules_with_sources() | 84 | .modules_with_sources() |
@@ -126,3 +126,40 @@ pub fn function_from_child_node( | |||
126 | let fn_def = ctry!(node.ancestors().find_map(ast::FnDef::cast)); | 126 | let fn_def = ctry!(node.ancestors().find_map(ast::FnDef::cast)); |
127 | function_from_source(db, file_id, fn_def) | 127 | function_from_source(db, file_id, fn_def) |
128 | } | 128 | } |
129 | |||
130 | pub fn macro_symbols( | ||
131 | db: &impl HirDatabase, | ||
132 | file_id: FileId, | ||
133 | ) -> Cancelable<Vec<(SmolStr, TextRange)>> { | ||
134 | let module = match module_from_file_id(db, file_id)? { | ||
135 | Some(it) => it, | ||
136 | None => return Ok(Vec::new()), | ||
137 | }; | ||
138 | let items = db.input_module_items(module.source_root_id, module.module_id)?; | ||
139 | let mut res = Vec::new(); | ||
140 | |||
141 | for macro_call_id in items | ||
142 | .items | ||
143 | .iter() | ||
144 | .filter_map(|it| it.id.file_id.as_macro_call_id()) | ||
145 | { | ||
146 | if let Some(exp) = db.expand_macro_invocation(macro_call_id) { | ||
147 | let loc = macro_call_id.loc(db); | ||
148 | let syntax = db.file_item(loc.source_item_id); | ||
149 | let syntax = syntax.borrowed(); | ||
150 | let macro_call = ast::MacroCall::cast(syntax).unwrap(); | ||
151 | let off = macro_call.token_tree().unwrap().syntax().range().start(); | ||
152 | let file = exp.file(); | ||
153 | for trait_def in file.syntax().descendants().filter_map(ast::TraitDef::cast) { | ||
154 | if let Some(name) = trait_def.name() { | ||
155 | let dst_range = name.syntax().range(); | ||
156 | if let Some(src_range) = exp.map_range_back(dst_range) { | ||
157 | res.push((name.text(), src_range + off)) | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | Ok(res) | ||
165 | } | ||