aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/adt.rs3
-rw-r--r--crates/ra_hir/src/db.rs19
-rw-r--r--crates/ra_hir/src/ids.rs287
-rw-r--r--crates/ra_hir/src/krate.rs5
-rw-r--r--crates/ra_hir/src/lib.rs169
-rw-r--r--crates/ra_hir/src/macros.rs199
-rw-r--r--crates/ra_hir/src/mock.rs10
-rw-r--r--crates/ra_hir/src/module.rs30
-rw-r--r--crates/ra_hir/src/module/imp.rs6
-rw-r--r--crates/ra_hir/src/module/nameres.rs91
-rw-r--r--crates/ra_hir/src/module/nameres/tests.rs84
-rw-r--r--crates/ra_hir/src/query_definitions.rs73
-rw-r--r--crates/ra_hir/src/source_binder.rs51
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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_db::Cancelable;
3use ra_syntax::ast::{self, NameOwner, StructFlavor}; 4use ra_syntax::ast::{self, NameOwner, StructFlavor};
4 5
5use crate::{ 6use 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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::SyntaxNode; 3use ra_syntax::{SyntaxNode, SourceFileNode};
4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; 4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable};
5 5
6use crate::{ 6use 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
19pub trait HirDatabase: SyntaxDatabase 20pub 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 @@
1use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
3
4use 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)]
29pub struct HirFileId(HirFileIdRepr);
30
31impl 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)]
73enum HirFileIdRepr {
74 File(FileId),
75 Macro(MacroCallId),
76}
77
78impl From<FileId> for HirFileId {
79 fn from(file_id: FileId) -> HirFileId {
80 HirFileId(HirFileIdRepr::File(file_id))
81 }
82}
83
84impl 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)]
93pub struct MacroCallId(u32);
94ra_db::impl_numeric_id!(MacroCallId);
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub struct MacroCallLoc {
98 pub(crate) source_root_id: SourceRootId,
99 pub(crate) module_id: ModuleId,
100 pub(crate) source_item_id: SourceItemId,
101}
102
103impl 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
112impl 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)]
125pub struct DefId(u32);
126ra_db::impl_numeric_id!(DefId);
127
128#[derive(Clone, Debug, PartialEq, Eq, Hash)]
129pub 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)]
137pub(crate) enum DefKind {
138 Module,
139 Function,
140 Struct,
141 Enum,
142 Item,
143
144 StructCtor,
145}
146
147impl 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
184impl DefLoc {
185 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
186 db.as_ref().loc2id(&self)
187 }
188}
189
190impl 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.
209pub(crate) type SourceFileItemId = Id<SyntaxNode>;
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
212pub 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)]
220pub struct SourceFileItems {
221 file_id: HirFileId,
222 arena: Arena<SyntaxNode>,
223}
224
225impl 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
282impl 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 @@
1pub use ra_db::CrateId; 1pub use ra_db::{CrateId, Cancelable};
2 2
3use crate::{HirDatabase, Module, Cancelable, Name, AsName}; 3use 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;
22mod arena; 22mod arena;
23pub mod source_binder; 23pub mod source_binder;
24 24
25mod ids;
26mod macros;
25mod name; 27mod name;
28// can't use `crate` or `r#crate` here :(
26mod krate; 29mod krate;
27mod module; 30mod module;
28mod function; 31mod function;
@@ -30,21 +33,18 @@ mod adt;
30mod type_ref; 33mod type_ref;
31mod ty; 34mod ty;
32 35
33use std::ops::Index;
34
35use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
36use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
37
38use crate::{ 36use 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
44pub use self::{ 42pub 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
54pub use self::function::FnSignatureInfo; 54pub 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)]
59pub struct DefId(u32);
60ra_db::impl_numeric_id!(DefId);
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63pub(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)]
74pub struct DefLoc {
75 pub(crate) kind: DefKind,
76 source_root_id: SourceRootId,
77 module_id: ModuleId,
78 source_item_id: SourceItemId,
79}
80
81impl 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
98impl DefId {
99 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
100 db.as_ref().id2loc(self)
101 }
102}
103
104impl DefLoc {
105 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
106 db.as_ref().loc2id(&self)
107 }
108}
109
110pub enum Def { 56pub 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
118impl 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.
153pub(crate) type SourceFileItemId = Id<SyntaxNode>;
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
156pub 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)]
164pub struct SourceFileItems {
165 file_id: FileId,
166 arena: Arena<SyntaxNode>,
167}
168
169impl 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
214impl 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!
10use std::sync::Arc;
11
12use ra_db::LocalSyntaxPtr;
13use ra_syntax::{
14 TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode,
15 ast::{self, NameOwner},
16};
17
18use crate::{HirDatabase, MacroCallId};
19
20// Hard-coded defs for now :-(
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub enum MacroDef {
23 CTry,
24 Vec,
25 QueryGroup,
26}
27
28impl 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)]
134pub struct MacroInput {
135 // Should be token trees
136 pub text: String,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub 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
152impl 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
188pub(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
6use relative_path::RelativePathBuf; 6use relative_path::RelativePathBuf;
7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; 7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
8 8
9use crate::{db, DefId, DefLoc}; 9use crate::{db, DefId, DefLoc, MacroCallId, MacroCallLoc};
10 10
11pub const WORKSPACE: SourceRootId = SourceRootId(0); 11pub const WORKSPACE: SourceRootId = SourceRootId(0);
12 12
@@ -95,6 +95,7 @@ impl MockDatabase {
95#[derive(Debug, Default)] 95#[derive(Debug, Default)]
96struct IdMaps { 96struct IdMaps {
97 defs: LocationIntener<DefLoc, DefId>, 97 defs: LocationIntener<DefLoc, DefId>,
98 macros: LocationIntener<MacroCallLoc, MacroCallId>,
98} 99}
99 100
100impl salsa::Database for MockDatabase { 101impl 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}
148impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>> for MockDatabase {
149 fn as_ref(&self) -> &LocationIntener<MacroCallLoc, MacroCallId> {
150 &self.id_maps.macros
151 }
152}
147 153
148impl MockDatabase { 154impl 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;
15use crate::{ 15use 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
287impl ModuleSource { 301impl 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};
25use ra_db::SourceRootId; 25use ra_db::{SourceRootId, Cancelable, FileId};
26 26
27use crate::{ 27use 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)]
66pub struct InputModuleItems { 66pub 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)]
72struct ModuleItem { 72pub(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
97impl NamedImport { 97impl 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
211impl InputModuleItems { 213impl 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
260impl ModuleItem { 272impl 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]
81fn 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]
81fn item_map_using_self() { 110fn 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]
225fn 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};
11use ra_db::{SourceRootId, FileId, Cancelable,}; 11use ra_db::{SourceRootId, Cancelable,};
12 12
13use crate::{ 13use 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
50pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { 51pub(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
65pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { 58pub(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
120pub(super) fn input_module_items( 113pub(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 @@
8use ra_db::{FileId, FilePosition, Cancelable}; 8use ra_db::{FileId, FilePosition, Cancelable};
9use ra_editor::find_node_at_offset; 9use ra_editor::find_node_at_offset;
10use ra_syntax::{ 10use ra_syntax::{
11 SmolStr, TextRange, SyntaxNodeRef,
11 ast::{self, AstNode, NameOwner}, 12 ast::{self, AstNode, NameOwner},
12 SyntaxNodeRef,
13}; 13};
14 14
15use crate::{ 15use 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.
22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { 22pub 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
130pub 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}