diff options
Diffstat (limited to 'crates/ra_hir/src/module')
-rw-r--r-- | crates/ra_hir/src/module/imp.rs | 190 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 509 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres/tests.rs | 273 |
3 files changed, 0 insertions, 972 deletions
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs deleted file mode 100644 index 3849026db..000000000 --- a/crates/ra_hir/src/module/imp.rs +++ /dev/null | |||
@@ -1,190 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::ast::{self, NameOwner}; | ||
4 | use relative_path::RelativePathBuf; | ||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
6 | use arrayvec::ArrayVec; | ||
7 | use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId}; | ||
8 | |||
9 | use crate::{ | ||
10 | HirDatabase, Name, AsName, | ||
11 | }; | ||
12 | |||
13 | use super::{ | ||
14 | LinkData, LinkId, ModuleData, ModuleId, ModuleSource, | ||
15 | ModuleTree, Problem, | ||
16 | }; | ||
17 | |||
18 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | ||
19 | pub enum Submodule { | ||
20 | Declaration(Name), | ||
21 | Definition(Name, ModuleSource), | ||
22 | } | ||
23 | |||
24 | impl Submodule { | ||
25 | fn name(&self) -> &Name { | ||
26 | match self { | ||
27 | Submodule::Declaration(name) => name, | ||
28 | Submodule::Definition(name, _) => name, | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | pub(crate) fn modules<'a>( | ||
34 | root: impl ast::ModuleItemOwner<'a>, | ||
35 | ) -> impl Iterator<Item = (Name, ast::Module<'a>)> { | ||
36 | root.items() | ||
37 | .filter_map(|item| match item { | ||
38 | ast::ModuleItem::Module(m) => Some(m), | ||
39 | _ => None, | ||
40 | }) | ||
41 | .filter_map(|module| { | ||
42 | let name = module.name()?.as_name(); | ||
43 | Some((name, module)) | ||
44 | }) | ||
45 | } | ||
46 | |||
47 | pub(crate) fn module_tree( | ||
48 | db: &impl HirDatabase, | ||
49 | source_root: SourceRootId, | ||
50 | ) -> Cancelable<Arc<ModuleTree>> { | ||
51 | db.check_canceled()?; | ||
52 | let res = create_module_tree(db, source_root)?; | ||
53 | Ok(Arc::new(res)) | ||
54 | } | ||
55 | |||
56 | fn create_module_tree<'a>( | ||
57 | db: &impl HirDatabase, | ||
58 | source_root: SourceRootId, | ||
59 | ) -> Cancelable<ModuleTree> { | ||
60 | let mut tree = ModuleTree::default(); | ||
61 | |||
62 | let mut roots = FxHashMap::default(); | ||
63 | let mut visited = FxHashSet::default(); | ||
64 | |||
65 | let source_root = db.source_root(source_root); | ||
66 | for &file_id in source_root.files.values() { | ||
67 | let source = ModuleSource::new_file(file_id.into()); | ||
68 | if visited.contains(&source) { | ||
69 | continue; // TODO: use explicit crate_roots here | ||
70 | } | ||
71 | assert!(!roots.contains_key(&file_id)); | ||
72 | let module_id = build_subtree( | ||
73 | db, | ||
74 | &source_root, | ||
75 | &mut tree, | ||
76 | &mut visited, | ||
77 | &mut roots, | ||
78 | None, | ||
79 | source, | ||
80 | )?; | ||
81 | roots.insert(file_id, module_id); | ||
82 | } | ||
83 | Ok(tree) | ||
84 | } | ||
85 | |||
86 | fn build_subtree( | ||
87 | db: &impl HirDatabase, | ||
88 | source_root: &SourceRoot, | ||
89 | tree: &mut ModuleTree, | ||
90 | visited: &mut FxHashSet<ModuleSource>, | ||
91 | roots: &mut FxHashMap<FileId, ModuleId>, | ||
92 | parent: Option<LinkId>, | ||
93 | source: ModuleSource, | ||
94 | ) -> Cancelable<ModuleId> { | ||
95 | visited.insert(source); | ||
96 | let id = tree.push_mod(ModuleData { | ||
97 | source, | ||
98 | parent, | ||
99 | children: Vec::new(), | ||
100 | }); | ||
101 | for sub in db.submodules(source)?.iter() { | ||
102 | let link = tree.push_link(LinkData { | ||
103 | name: sub.name().clone(), | ||
104 | owner: id, | ||
105 | points_to: Vec::new(), | ||
106 | problem: None, | ||
107 | }); | ||
108 | |||
109 | let (points_to, problem) = match sub { | ||
110 | Submodule::Declaration(name) => { | ||
111 | let (points_to, problem) = resolve_submodule(db, source, &name); | ||
112 | let points_to = points_to | ||
113 | .into_iter() | ||
114 | .map(|file_id| match roots.remove(&file_id) { | ||
115 | Some(module_id) => { | ||
116 | tree.mods[module_id].parent = Some(link); | ||
117 | Ok(module_id) | ||
118 | } | ||
119 | None => build_subtree( | ||
120 | db, | ||
121 | source_root, | ||
122 | tree, | ||
123 | visited, | ||
124 | roots, | ||
125 | Some(link), | ||
126 | ModuleSource::new_file(file_id.into()), | ||
127 | ), | ||
128 | }) | ||
129 | .collect::<Cancelable<Vec<_>>>()?; | ||
130 | (points_to, problem) | ||
131 | } | ||
132 | Submodule::Definition(_name, submodule_source) => { | ||
133 | let points_to = build_subtree( | ||
134 | db, | ||
135 | source_root, | ||
136 | tree, | ||
137 | visited, | ||
138 | roots, | ||
139 | Some(link), | ||
140 | *submodule_source, | ||
141 | )?; | ||
142 | (vec![points_to], None) | ||
143 | } | ||
144 | }; | ||
145 | |||
146 | tree.links[link].points_to = points_to; | ||
147 | tree.links[link].problem = problem; | ||
148 | } | ||
149 | Ok(id) | ||
150 | } | ||
151 | |||
152 | fn resolve_submodule( | ||
153 | db: &impl HirDatabase, | ||
154 | source: ModuleSource, | ||
155 | name: &Name, | ||
156 | ) -> (Vec<FileId>, Option<Problem>) { | ||
157 | // FIXME: handle submodules of inline modules properly | ||
158 | let file_id = source.file_id().original_file(db); | ||
159 | let source_root_id = db.file_source_root(file_id); | ||
160 | let path = db.file_relative_path(file_id); | ||
161 | let root = RelativePathBuf::default(); | ||
162 | let dir_path = path.parent().unwrap_or(&root); | ||
163 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
164 | let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main"; | ||
165 | |||
166 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
167 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
168 | let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
169 | let mut candidates = ArrayVec::<[_; 2]>::new(); | ||
170 | if is_dir_owner { | ||
171 | candidates.push(file_mod.clone()); | ||
172 | candidates.push(dir_mod); | ||
173 | } else { | ||
174 | candidates.push(file_dir_mod.clone()); | ||
175 | }; | ||
176 | let sr = db.source_root(source_root_id); | ||
177 | let points_to = candidates | ||
178 | .into_iter() | ||
179 | .filter_map(|path| sr.files.get(&path)) | ||
180 | .map(|&it| it) | ||
181 | .collect::<Vec<_>>(); | ||
182 | let problem = if points_to.is_empty() { | ||
183 | Some(Problem::UnresolvedModule { | ||
184 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
185 | }) | ||
186 | } else { | ||
187 | None | ||
188 | }; | ||
189 | (points_to, problem) | ||
190 | } | ||
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs deleted file mode 100644 index 7d5e86c89..000000000 --- a/crates/ra_hir/src/module/nameres.rs +++ /dev/null | |||
@@ -1,509 +0,0 @@ | |||
1 | //! Name resolution algorithm. The end result of the algorithm is `ItemMap`: a | ||
2 | //! map with maps each module to it's scope: the set of items, visible in the | ||
3 | //! module. That is, we only resolve imports here, name resolution of item | ||
4 | //! bodies will be done in a separate step. | ||
5 | //! | ||
6 | //! Like Rustc, we use an interative per-crate algorithm: we start with scopes | ||
7 | //! containing only directly defined items, and then iteratively resolve | ||
8 | //! imports. | ||
9 | //! | ||
10 | //! To make this work nicely in the IDE scenarios, we place `InputModuleItems` | ||
11 | //! in between raw syntax and name resolution. `InputModuleItems` are computed | ||
12 | //! using only the module's syntax, and it is all directly defined items plus | ||
13 | //! imports. The plain is to make `InputModuleItems` independent of local | ||
14 | //! modifications (that is, typing inside a function shold not change IMIs), | ||
15 | //! such that the results of name resolution can be preserved unless the module | ||
16 | //! structure itself is modified. | ||
17 | use std::sync::Arc; | ||
18 | |||
19 | use rustc_hash::FxHashMap; | ||
20 | use ra_syntax::{ | ||
21 | TextRange, | ||
22 | SyntaxKind::{self, *}, | ||
23 | ast::{self, AstNode} | ||
24 | }; | ||
25 | use ra_db::{SourceRootId, Cancelable, FileId}; | ||
26 | |||
27 | use crate::{ | ||
28 | HirFileId, | ||
29 | DefId, DefLoc, DefKind, | ||
30 | SourceItemId, SourceFileItemId, SourceFileItems, | ||
31 | Path, PathKind, | ||
32 | HirDatabase, Crate, | ||
33 | Name, AsName, | ||
34 | module::{ModuleId, ModuleTree}, | ||
35 | }; | ||
36 | |||
37 | /// Item map is the result of the name resolution. Item map contains, for each | ||
38 | /// module, the set of visible items. | ||
39 | // FIXME: currenty we compute item map per source-root. We should do it per crate instead. | ||
40 | #[derive(Default, Debug, PartialEq, Eq)] | ||
41 | pub struct ItemMap { | ||
42 | pub per_module: FxHashMap<ModuleId, ModuleScope>, | ||
43 | } | ||
44 | |||
45 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
46 | pub struct ModuleScope { | ||
47 | items: FxHashMap<Name, Resolution>, | ||
48 | } | ||
49 | |||
50 | impl ModuleScope { | ||
51 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a { | ||
52 | self.items.iter() | ||
53 | } | ||
54 | pub fn get(&self, name: &Name) -> Option<&Resolution> { | ||
55 | self.items.get(name) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /// A set of items and imports declared inside a module, without relation to | ||
60 | /// other modules. | ||
61 | /// | ||
62 | /// This stands in-between raw syntax and name resolution and alow us to avoid | ||
63 | /// recomputing name res: if `InputModuleItems` are the same, we can avoid | ||
64 | /// running name resolution. | ||
65 | #[derive(Debug, Default, PartialEq, Eq)] | ||
66 | pub struct InputModuleItems { | ||
67 | pub(crate) items: Vec<ModuleItem>, | ||
68 | imports: Vec<Import>, | ||
69 | } | ||
70 | |||
71 | #[derive(Debug, PartialEq, Eq)] | ||
72 | pub(crate) struct ModuleItem { | ||
73 | pub(crate) id: SourceItemId, | ||
74 | pub(crate) name: Name, | ||
75 | kind: SyntaxKind, | ||
76 | vis: Vis, | ||
77 | } | ||
78 | |||
79 | #[derive(Debug, PartialEq, Eq)] | ||
80 | enum Vis { | ||
81 | // Priv, | ||
82 | Other, | ||
83 | } | ||
84 | |||
85 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
86 | struct Import { | ||
87 | path: Path, | ||
88 | kind: ImportKind, | ||
89 | } | ||
90 | |||
91 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
92 | pub struct NamedImport { | ||
93 | pub file_item_id: SourceFileItemId, | ||
94 | pub relative_range: TextRange, | ||
95 | } | ||
96 | |||
97 | impl NamedImport { | ||
98 | // FIXME: this is only here for one use-case in completion. Seems like a | ||
99 | // pretty gross special case. | ||
100 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { | ||
101 | let source_item_id = SourceItemId { | ||
102 | file_id: file_id.into(), | ||
103 | item_id: Some(self.file_item_id), | ||
104 | }; | ||
105 | let syntax = db.file_item(source_item_id); | ||
106 | let offset = syntax.borrowed().range().start(); | ||
107 | self.relative_range + offset | ||
108 | } | ||
109 | } | ||
110 | |||
111 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
112 | enum ImportKind { | ||
113 | Glob, | ||
114 | Named(NamedImport), | ||
115 | } | ||
116 | |||
117 | /// Resolution is basically `DefId` atm, but it should account for stuff like | ||
118 | /// multiple namespaces, ambiguity and errors. | ||
119 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
120 | pub struct Resolution { | ||
121 | /// None for unresolved | ||
122 | pub def_id: PerNs<DefId>, | ||
123 | /// ident by whitch this is imported into local scope. | ||
124 | pub import: Option<NamedImport>, | ||
125 | } | ||
126 | |||
127 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
128 | pub enum Namespace { | ||
129 | Types, | ||
130 | Values, | ||
131 | } | ||
132 | |||
133 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
134 | pub struct PerNs<T> { | ||
135 | pub types: Option<T>, | ||
136 | pub values: Option<T>, | ||
137 | } | ||
138 | |||
139 | impl<T> PerNs<T> { | ||
140 | pub fn none() -> PerNs<T> { | ||
141 | PerNs { | ||
142 | types: None, | ||
143 | values: None, | ||
144 | } | ||
145 | } | ||
146 | |||
147 | pub fn values(t: T) -> PerNs<T> { | ||
148 | PerNs { | ||
149 | types: None, | ||
150 | values: Some(t), | ||
151 | } | ||
152 | } | ||
153 | |||
154 | pub fn types(t: T) -> PerNs<T> { | ||
155 | PerNs { | ||
156 | types: Some(t), | ||
157 | values: None, | ||
158 | } | ||
159 | } | ||
160 | |||
161 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
162 | PerNs { | ||
163 | types: Some(types), | ||
164 | values: Some(values), | ||
165 | } | ||
166 | } | ||
167 | |||
168 | pub fn is_none(&self) -> bool { | ||
169 | self.types.is_none() && self.values.is_none() | ||
170 | } | ||
171 | |||
172 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
173 | match namespace { | ||
174 | Namespace::Types => self.types, | ||
175 | Namespace::Values => self.values, | ||
176 | } | ||
177 | } | ||
178 | |||
179 | pub fn take_types(self) -> Option<T> { | ||
180 | self.take(Namespace::Types) | ||
181 | } | ||
182 | |||
183 | pub fn take_values(self) -> Option<T> { | ||
184 | self.take(Namespace::Values) | ||
185 | } | ||
186 | |||
187 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | ||
188 | self.as_ref().take(namespace) | ||
189 | } | ||
190 | |||
191 | pub fn as_ref(&self) -> PerNs<&T> { | ||
192 | PerNs { | ||
193 | types: self.types.as_ref(), | ||
194 | values: self.values.as_ref(), | ||
195 | } | ||
196 | } | ||
197 | |||
198 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
199 | PerNs { | ||
200 | types: self.types.and_then(&f), | ||
201 | values: self.values.and_then(&f), | ||
202 | } | ||
203 | } | ||
204 | |||
205 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
206 | PerNs { | ||
207 | types: self.types.map(&f), | ||
208 | values: self.values.map(&f), | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | impl InputModuleItems { | ||
214 | pub(crate) fn add_item( | ||
215 | &mut self, | ||
216 | file_id: HirFileId, | ||
217 | file_items: &SourceFileItems, | ||
218 | item: ast::ModuleItem, | ||
219 | ) -> Option<()> { | ||
220 | match item { | ||
221 | ast::ModuleItem::StructDef(it) => { | ||
222 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
223 | } | ||
224 | ast::ModuleItem::EnumDef(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 | } | ||
236 | ast::ModuleItem::ImplBlock(_) => { | ||
237 | // impls don't define items | ||
238 | } | ||
239 | ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it), | ||
240 | ast::ModuleItem::ExternCrateItem(_) => { | ||
241 | // TODO | ||
242 | } | ||
243 | ast::ModuleItem::ConstDef(it) => { | ||
244 | self.items.push(ModuleItem::new(file_id, 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 | } | ||
252 | } | ||
253 | Some(()) | ||
254 | } | ||
255 | |||
256 | fn add_use_item(&mut self, file_items: &SourceFileItems, item: ast::UseItem) { | ||
257 | let file_item_id = file_items.id_of_unchecked(item.syntax()); | ||
258 | let start_offset = item.syntax().range().start(); | ||
259 | Path::expand_use_item(item, |path, range| { | ||
260 | let kind = match range { | ||
261 | None => ImportKind::Glob, | ||
262 | Some(range) => ImportKind::Named(NamedImport { | ||
263 | file_item_id, | ||
264 | relative_range: range - start_offset, | ||
265 | }), | ||
266 | }; | ||
267 | self.imports.push(Import { kind, path }) | ||
268 | }) | ||
269 | } | ||
270 | } | ||
271 | |||
272 | impl ModuleItem { | ||
273 | fn new<'a>( | ||
274 | file_id: HirFileId, | ||
275 | file_items: &SourceFileItems, | ||
276 | item: impl ast::NameOwner<'a>, | ||
277 | ) -> Option<ModuleItem> { | ||
278 | let name = item.name()?.as_name(); | ||
279 | let kind = item.syntax().kind(); | ||
280 | let vis = Vis::Other; | ||
281 | let item_id = Some(file_items.id_of_unchecked(item.syntax())); | ||
282 | let id = SourceItemId { file_id, item_id }; | ||
283 | let res = ModuleItem { | ||
284 | id, | ||
285 | name, | ||
286 | kind, | ||
287 | vis, | ||
288 | }; | ||
289 | Some(res) | ||
290 | } | ||
291 | } | ||
292 | |||
293 | pub(crate) struct Resolver<'a, DB> { | ||
294 | db: &'a DB, | ||
295 | input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, | ||
296 | source_root: SourceRootId, | ||
297 | module_tree: Arc<ModuleTree>, | ||
298 | result: ItemMap, | ||
299 | } | ||
300 | |||
301 | impl<'a, DB> Resolver<'a, DB> | ||
302 | where | ||
303 | DB: HirDatabase, | ||
304 | { | ||
305 | pub(crate) fn new( | ||
306 | db: &'a DB, | ||
307 | input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, | ||
308 | source_root: SourceRootId, | ||
309 | module_tree: Arc<ModuleTree>, | ||
310 | ) -> Resolver<'a, DB> { | ||
311 | Resolver { | ||
312 | db, | ||
313 | input, | ||
314 | source_root, | ||
315 | module_tree, | ||
316 | result: ItemMap::default(), | ||
317 | } | ||
318 | } | ||
319 | |||
320 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { | ||
321 | for (&module_id, items) in self.input.iter() { | ||
322 | self.populate_module(module_id, Arc::clone(items))?; | ||
323 | } | ||
324 | |||
325 | for &module_id in self.input.keys() { | ||
326 | self.db.check_canceled()?; | ||
327 | self.resolve_imports(module_id)?; | ||
328 | } | ||
329 | Ok(self.result) | ||
330 | } | ||
331 | |||
332 | fn populate_module( | ||
333 | &mut self, | ||
334 | module_id: ModuleId, | ||
335 | input: Arc<InputModuleItems>, | ||
336 | ) -> Cancelable<()> { | ||
337 | let mut module_items = ModuleScope::default(); | ||
338 | |||
339 | // Populate extern crates prelude | ||
340 | { | ||
341 | let root_id = module_id.crate_root(&self.module_tree); | ||
342 | let file_id = root_id.source(&self.module_tree).file_id(); | ||
343 | let crate_graph = self.db.crate_graph(); | ||
344 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) | ||
345 | { | ||
346 | let krate = Crate::new(crate_id); | ||
347 | for dep in krate.dependencies(self.db)? { | ||
348 | if let Some(module) = dep.krate.root_module(self.db)? { | ||
349 | let def_id = module.def_id; | ||
350 | self.add_module_item( | ||
351 | &mut module_items, | ||
352 | dep.name.clone(), | ||
353 | PerNs::types(def_id), | ||
354 | ); | ||
355 | } | ||
356 | } | ||
357 | }; | ||
358 | } | ||
359 | for import in input.imports.iter() { | ||
360 | if let Some(name) = import.path.segments.iter().last() { | ||
361 | if let ImportKind::Named(import) = import.kind { | ||
362 | module_items.items.insert( | ||
363 | name.clone(), | ||
364 | Resolution { | ||
365 | def_id: PerNs::none(), | ||
366 | import: Some(import), | ||
367 | }, | ||
368 | ); | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | // Populate explicitly declared items, except modules | ||
373 | for item in input.items.iter() { | ||
374 | if item.kind == MODULE { | ||
375 | continue; | ||
376 | } | ||
377 | // depending on the item kind, the location can define something in | ||
378 | // the values namespace, the types namespace, or both | ||
379 | let kind = DefKind::for_syntax_kind(item.kind); | ||
380 | let def_id = kind.map(|k| { | ||
381 | let def_loc = DefLoc { | ||
382 | kind: k, | ||
383 | source_root_id: self.source_root, | ||
384 | module_id, | ||
385 | source_item_id: item.id, | ||
386 | }; | ||
387 | def_loc.id(self.db) | ||
388 | }); | ||
389 | let resolution = Resolution { | ||
390 | def_id, | ||
391 | import: None, | ||
392 | }; | ||
393 | module_items.items.insert(item.name.clone(), resolution); | ||
394 | } | ||
395 | |||
396 | // Populate modules | ||
397 | for (name, module_id) in module_id.children(&self.module_tree) { | ||
398 | let def_loc = DefLoc { | ||
399 | kind: DefKind::Module, | ||
400 | source_root_id: self.source_root, | ||
401 | module_id, | ||
402 | source_item_id: module_id.source(&self.module_tree).0, | ||
403 | }; | ||
404 | let def_id = def_loc.id(self.db); | ||
405 | self.add_module_item(&mut module_items, name, PerNs::types(def_id)); | ||
406 | } | ||
407 | |||
408 | self.result.per_module.insert(module_id, module_items); | ||
409 | Ok(()) | ||
410 | } | ||
411 | |||
412 | fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def_id: PerNs<DefId>) { | ||
413 | let resolution = Resolution { | ||
414 | def_id, | ||
415 | import: None, | ||
416 | }; | ||
417 | module_items.items.insert(name, resolution); | ||
418 | } | ||
419 | |||
420 | fn resolve_imports(&mut self, module_id: ModuleId) -> Cancelable<()> { | ||
421 | for import in self.input[&module_id].imports.iter() { | ||
422 | self.resolve_import(module_id, import)?; | ||
423 | } | ||
424 | Ok(()) | ||
425 | } | ||
426 | |||
427 | fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable<()> { | ||
428 | let ptr = match import.kind { | ||
429 | ImportKind::Glob => return Ok(()), | ||
430 | ImportKind::Named(ptr) => ptr, | ||
431 | }; | ||
432 | |||
433 | let mut curr: ModuleId = match import.path.kind { | ||
434 | PathKind::Plain | PathKind::Self_ => module_id, | ||
435 | PathKind::Super => { | ||
436 | match module_id.parent(&self.module_tree) { | ||
437 | Some(it) => it, | ||
438 | // TODO: error | ||
439 | None => return Ok(()), | ||
440 | } | ||
441 | } | ||
442 | PathKind::Crate => module_id.crate_root(&self.module_tree), | ||
443 | }; | ||
444 | |||
445 | for (i, name) in import.path.segments.iter().enumerate() { | ||
446 | let is_last = i == import.path.segments.len() - 1; | ||
447 | |||
448 | let def_id = match self.result.per_module[&curr].items.get(name) { | ||
449 | Some(res) if !res.def_id.is_none() => res.def_id, | ||
450 | _ => return Ok(()), | ||
451 | }; | ||
452 | |||
453 | if !is_last { | ||
454 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { | ||
455 | d | ||
456 | } else { | ||
457 | return Ok(()); | ||
458 | }; | ||
459 | curr = match type_def_id.loc(self.db) { | ||
460 | DefLoc { | ||
461 | kind: DefKind::Module, | ||
462 | module_id: target_module_id, | ||
463 | source_root_id, | ||
464 | .. | ||
465 | } => { | ||
466 | if source_root_id == self.source_root { | ||
467 | target_module_id | ||
468 | } else { | ||
469 | let module = crate::code_model_api::Module::new(type_def_id); | ||
470 | let path = Path { | ||
471 | segments: import.path.segments[i + 1..].iter().cloned().collect(), | ||
472 | kind: PathKind::Crate, | ||
473 | }; | ||
474 | let def_id = module.resolve_path(self.db, &path)?; | ||
475 | if !def_id.is_none() { | ||
476 | self.update(module_id, |items| { | ||
477 | let res = Resolution { | ||
478 | def_id: def_id, | ||
479 | import: Some(ptr), | ||
480 | }; | ||
481 | items.items.insert(name.clone(), res); | ||
482 | }) | ||
483 | } | ||
484 | return Ok(()); | ||
485 | } | ||
486 | } | ||
487 | _ => return Ok(()), | ||
488 | } | ||
489 | } else { | ||
490 | self.update(module_id, |items| { | ||
491 | let res = Resolution { | ||
492 | def_id: def_id, | ||
493 | import: Some(ptr), | ||
494 | }; | ||
495 | items.items.insert(name.clone(), res); | ||
496 | }) | ||
497 | } | ||
498 | } | ||
499 | Ok(()) | ||
500 | } | ||
501 | |||
502 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | ||
503 | let module_items = self.result.per_module.get_mut(&module_id).unwrap(); | ||
504 | f(module_items) | ||
505 | } | ||
506 | } | ||
507 | |||
508 | #[cfg(test)] | ||
509 | mod tests; | ||
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs deleted file mode 100644 index dcbe65aec..000000000 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ /dev/null | |||
@@ -1,273 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use salsa::Database; | ||
4 | use ra_db::{FilesDatabase, CrateGraph}; | ||
5 | use relative_path::RelativePath; | ||
6 | use test_utils::assert_eq_text; | ||
7 | |||
8 | use crate::{ | ||
9 | self as hir, | ||
10 | db::HirDatabase, | ||
11 | mock::MockDatabase, | ||
12 | }; | ||
13 | |||
14 | fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { | ||
15 | let (db, pos) = MockDatabase::with_position(fixture); | ||
16 | let source_root = db.file_source_root(pos.file_id); | ||
17 | let module = hir::source_binder::module_from_position(&db, pos) | ||
18 | .unwrap() | ||
19 | .unwrap(); | ||
20 | let module_id = module.def_id.loc(&db).module_id; | ||
21 | (db.item_map(source_root).unwrap(), module_id) | ||
22 | } | ||
23 | |||
24 | fn check_module_item_map(map: &hir::ItemMap, module_id: hir::ModuleId, expected: &str) { | ||
25 | let mut lines = map.per_module[&module_id] | ||
26 | .items | ||
27 | .iter() | ||
28 | .map(|(name, res)| format!("{}: {}", name, dump_resolution(res))) | ||
29 | .collect::<Vec<_>>(); | ||
30 | lines.sort(); | ||
31 | let actual = lines.join("\n"); | ||
32 | let expected = expected | ||
33 | .trim() | ||
34 | .lines() | ||
35 | .map(|it| it.trim()) | ||
36 | .collect::<Vec<_>>() | ||
37 | .join("\n"); | ||
38 | assert_eq_text!(&actual, &expected); | ||
39 | |||
40 | fn dump_resolution(resolution: &hir::Resolution) -> &'static str { | ||
41 | match ( | ||
42 | resolution.def_id.types.is_some(), | ||
43 | resolution.def_id.values.is_some(), | ||
44 | ) { | ||
45 | (true, true) => "t v", | ||
46 | (true, false) => "t", | ||
47 | (false, true) => "v", | ||
48 | (false, false) => "_", | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[test] | ||
54 | fn item_map_smoke_test() { | ||
55 | let (item_map, module_id) = item_map( | ||
56 | " | ||
57 | //- /lib.rs | ||
58 | mod foo; | ||
59 | |||
60 | use crate::foo::bar::Baz; | ||
61 | <|> | ||
62 | |||
63 | //- /foo/mod.rs | ||
64 | pub mod bar; | ||
65 | |||
66 | //- /foo/bar.rs | ||
67 | pub struct Baz; | ||
68 | ", | ||
69 | ); | ||
70 | check_module_item_map( | ||
71 | &item_map, | ||
72 | module_id, | ||
73 | " | ||
74 | Baz: t v | ||
75 | foo: t | ||
76 | ", | ||
77 | ); | ||
78 | } | ||
79 | |||
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] | ||
110 | fn item_map_using_self() { | ||
111 | let (item_map, module_id) = item_map( | ||
112 | " | ||
113 | //- /lib.rs | ||
114 | mod foo; | ||
115 | use crate::foo::bar::Baz::{self}; | ||
116 | <|> | ||
117 | //- /foo/mod.rs | ||
118 | pub mod bar; | ||
119 | //- /foo/bar.rs | ||
120 | pub struct Baz; | ||
121 | ", | ||
122 | ); | ||
123 | check_module_item_map( | ||
124 | &item_map, | ||
125 | module_id, | ||
126 | " | ||
127 | Baz: t v | ||
128 | foo: t | ||
129 | ", | ||
130 | ); | ||
131 | } | ||
132 | |||
133 | #[test] | ||
134 | fn item_map_across_crates() { | ||
135 | let (mut db, sr) = MockDatabase::with_files( | ||
136 | " | ||
137 | //- /main.rs | ||
138 | use test_crate::Baz; | ||
139 | |||
140 | //- /lib.rs | ||
141 | pub struct Baz; | ||
142 | ", | ||
143 | ); | ||
144 | let main_id = sr.files[RelativePath::new("/main.rs")]; | ||
145 | let lib_id = sr.files[RelativePath::new("/lib.rs")]; | ||
146 | |||
147 | let mut crate_graph = CrateGraph::default(); | ||
148 | let main_crate = crate_graph.add_crate_root(main_id); | ||
149 | let lib_crate = crate_graph.add_crate_root(lib_id); | ||
150 | crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate); | ||
151 | |||
152 | db.set_crate_graph(crate_graph); | ||
153 | |||
154 | let source_root = db.file_source_root(main_id); | ||
155 | let module = hir::source_binder::module_from_file_id(&db, main_id) | ||
156 | .unwrap() | ||
157 | .unwrap(); | ||
158 | let module_id = module.def_id.loc(&db).module_id; | ||
159 | let item_map = db.item_map(source_root).unwrap(); | ||
160 | |||
161 | check_module_item_map( | ||
162 | &item_map, | ||
163 | module_id, | ||
164 | " | ||
165 | Baz: t v | ||
166 | test_crate: t | ||
167 | ", | ||
168 | ); | ||
169 | } | ||
170 | |||
171 | #[test] | ||
172 | fn typing_inside_a_function_should_not_invalidate_item_map() { | ||
173 | let (mut db, pos) = MockDatabase::with_position( | ||
174 | " | ||
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 | ||
229 | mod foo;<|> | ||
230 | |||
231 | use crate::foo::bar::Baz; | ||
232 | |||
233 | fn foo() -> i32 { | ||
234 | 1 + 1 | ||
235 | } | ||
236 | //- /foo/mod.rs | ||
237 | pub mod bar; | ||
238 | |||
239 | //- /foo/bar.rs | ||
240 | pub struct Baz; | ||
241 | ", | ||
242 | ); | ||
243 | let source_root = db.file_source_root(pos.file_id); | ||
244 | { | ||
245 | let events = db.log_executed(|| { | ||
246 | db.item_map(source_root).unwrap(); | ||
247 | }); | ||
248 | assert!(format!("{:?}", events).contains("item_map")) | ||
249 | } | ||
250 | |||
251 | let new_text = " | ||
252 | mod foo; | ||
253 | |||
254 | use crate::foo::bar::Baz; | ||
255 | |||
256 | fn foo() -> i32 { 92 } | ||
257 | " | ||
258 | .to_string(); | ||
259 | |||
260 | db.query_mut(ra_db::FileTextQuery) | ||
261 | .set(pos.file_id, Arc::new(new_text)); | ||
262 | |||
263 | { | ||
264 | let events = db.log_executed(|| { | ||
265 | db.item_map(source_root).unwrap(); | ||
266 | }); | ||
267 | assert!( | ||
268 | !format!("{:?}", events).contains("item_map"), | ||
269 | "{:#?}", | ||
270 | events | ||
271 | ) | ||
272 | } | ||
273 | } | ||