diff options
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 564 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/lower.rs | 222 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/per_ns.rs | 78 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/raw.rs | 322 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 784 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/globs.rs | 118 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/incremental.rs | 123 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/macros.rs | 94 |
8 files changed, 1584 insertions, 721 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs new file mode 100644 index 000000000..12ed49a0a --- /dev/null +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -0,0 +1,564 @@ | |||
1 | use arrayvec::ArrayVec; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | use relative_path::RelativePathBuf; | ||
4 | use test_utils::tested_by; | ||
5 | use ra_db::FileId; | ||
6 | |||
7 | use crate::{ | ||
8 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | ||
9 | PersistentHirDatabase, HirFileId, Name, Path, Problem, Crate, | ||
10 | KnownName, | ||
11 | nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw}, | ||
12 | ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, | ||
13 | }; | ||
14 | |||
15 | use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId}; | ||
16 | |||
17 | pub(super) fn collect_defs( | ||
18 | db: &impl PersistentHirDatabase, | ||
19 | mut def_map: CrateDefMap, | ||
20 | ) -> CrateDefMap { | ||
21 | // populate external prelude | ||
22 | for dep in def_map.krate.dependencies(db) { | ||
23 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); | ||
24 | if let Some(module) = dep.krate.root_module(db) { | ||
25 | def_map.extern_prelude.insert(dep.name.clone(), module.into()); | ||
26 | } | ||
27 | // look for the prelude | ||
28 | if def_map.prelude.is_none() { | ||
29 | let map = db.crate_def_map(dep.krate); | ||
30 | if map.prelude.is_some() { | ||
31 | def_map.prelude = map.prelude; | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | let mut collector = DefCollector { | ||
37 | db, | ||
38 | def_map, | ||
39 | glob_imports: FxHashMap::default(), | ||
40 | unresolved_imports: Vec::new(), | ||
41 | unexpanded_macros: Vec::new(), | ||
42 | global_macro_scope: FxHashMap::default(), | ||
43 | }; | ||
44 | collector.collect(); | ||
45 | collector.finish() | ||
46 | } | ||
47 | |||
48 | /// Walks the tree of module recursively | ||
49 | struct DefCollector<DB> { | ||
50 | db: DB, | ||
51 | def_map: CrateDefMap, | ||
52 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, | ||
53 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, | ||
54 | unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>, | ||
55 | global_macro_scope: FxHashMap<Name, CrateMacroId>, | ||
56 | } | ||
57 | |||
58 | impl<'a, DB> DefCollector<&'a DB> | ||
59 | where | ||
60 | DB: PersistentHirDatabase, | ||
61 | { | ||
62 | fn collect(&mut self) { | ||
63 | let crate_graph = self.db.crate_graph(); | ||
64 | let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); | ||
65 | let raw_items = self.db.raw_items(file_id); | ||
66 | let module_id = self.def_map.root; | ||
67 | self.def_map.modules[module_id].definition = Some(file_id); | ||
68 | ModCollector { | ||
69 | def_collector: &mut *self, | ||
70 | module_id, | ||
71 | file_id: file_id.into(), | ||
72 | raw_items: &raw_items, | ||
73 | } | ||
74 | .collect(raw_items.items()); | ||
75 | |||
76 | // main name resolution fixed-point loop. | ||
77 | let mut i = 0; | ||
78 | loop { | ||
79 | match (self.resolve_imports(), self.resolve_macros()) { | ||
80 | (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break, | ||
81 | _ => i += 1, | ||
82 | } | ||
83 | if i == 1000 { | ||
84 | log::error!("diverging name resolution"); | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
90 | // show unresolved imports in completion, etc | ||
91 | for (module_id, import, import_data) in unresolved_imports { | ||
92 | self.record_resolved_import(module_id, PerNs::none(), import, &import_data) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) { | ||
97 | if let Ok(rules) = mbe::MacroRules::parse(tt) { | ||
98 | let macro_id = self.def_map.macros.alloc(rules); | ||
99 | if export { | ||
100 | self.def_map.public_macros.insert(name.clone(), macro_id); | ||
101 | } | ||
102 | self.global_macro_scope.insert(name, macro_id); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn resolve_imports(&mut self) -> ReachedFixedPoint { | ||
107 | let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
108 | let mut resolved = Vec::new(); | ||
109 | imports.retain(|(module_id, import, import_data)| { | ||
110 | let (def, fp) = self.resolve_import(*module_id, import_data); | ||
111 | if fp == ReachedFixedPoint::Yes { | ||
112 | resolved.push((*module_id, def, *import, import_data.clone())) | ||
113 | } | ||
114 | fp == ReachedFixedPoint::No | ||
115 | }); | ||
116 | self.unresolved_imports = imports; | ||
117 | // Resolves imports, filling-in module scopes | ||
118 | let result = | ||
119 | if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; | ||
120 | for (module_id, def, import, import_data) in resolved { | ||
121 | self.record_resolved_import(module_id, def, import, &import_data) | ||
122 | } | ||
123 | result | ||
124 | } | ||
125 | |||
126 | fn resolve_import( | ||
127 | &mut self, | ||
128 | module_id: CrateModuleId, | ||
129 | import: &raw::ImportData, | ||
130 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | ||
131 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | ||
132 | if import.is_extern_crate { | ||
133 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
134 | &import | ||
135 | .path | ||
136 | .as_ident() | ||
137 | .expect("extern crate should have been desugared to one-element path"), | ||
138 | ); | ||
139 | (res, ReachedFixedPoint::Yes) | ||
140 | } else { | ||
141 | let res = | ||
142 | self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path); | ||
143 | |||
144 | (res.resolved_def, res.reached_fixedpoint) | ||
145 | } | ||
146 | } | ||
147 | |||
148 | fn record_resolved_import( | ||
149 | &mut self, | ||
150 | module_id: CrateModuleId, | ||
151 | def: PerNs<ModuleDef>, | ||
152 | import_id: raw::ImportId, | ||
153 | import: &raw::ImportData, | ||
154 | ) { | ||
155 | if import.is_glob { | ||
156 | log::debug!("glob import: {:?}", import); | ||
157 | match def.take_types() { | ||
158 | Some(ModuleDef::Module(m)) => { | ||
159 | if import.is_prelude { | ||
160 | tested_by!(std_prelude); | ||
161 | self.def_map.prelude = Some(m); | ||
162 | } else if m.krate != self.def_map.krate { | ||
163 | tested_by!(glob_across_crates); | ||
164 | // glob import from other crate => we can just import everything once | ||
165 | let item_map = self.db.crate_def_map(m.krate); | ||
166 | let scope = &item_map[m.module_id].scope; | ||
167 | let items = scope | ||
168 | .items | ||
169 | .iter() | ||
170 | .map(|(name, res)| (name.clone(), res.clone())) | ||
171 | .collect::<Vec<_>>(); | ||
172 | self.update(module_id, Some(import_id), &items); | ||
173 | } else { | ||
174 | // glob import from same crate => we do an initial | ||
175 | // import, and then need to propagate any further | ||
176 | // additions | ||
177 | let scope = &self.def_map[m.module_id].scope; | ||
178 | let items = scope | ||
179 | .items | ||
180 | .iter() | ||
181 | .map(|(name, res)| (name.clone(), res.clone())) | ||
182 | .collect::<Vec<_>>(); | ||
183 | self.update(module_id, Some(import_id), &items); | ||
184 | // record the glob import in case we add further items | ||
185 | self.glob_imports | ||
186 | .entry(m.module_id) | ||
187 | .or_default() | ||
188 | .push((module_id, import_id)); | ||
189 | } | ||
190 | } | ||
191 | Some(ModuleDef::Enum(e)) => { | ||
192 | tested_by!(glob_enum); | ||
193 | // glob import from enum => just import all the variants | ||
194 | let variants = e.variants(self.db); | ||
195 | let resolutions = variants | ||
196 | .into_iter() | ||
197 | .filter_map(|variant| { | ||
198 | let res = Resolution { | ||
199 | def: PerNs::both(variant.into(), variant.into()), | ||
200 | import: Some(import_id), | ||
201 | }; | ||
202 | let name = variant.name(self.db)?; | ||
203 | Some((name, res)) | ||
204 | }) | ||
205 | .collect::<Vec<_>>(); | ||
206 | self.update(module_id, Some(import_id), &resolutions); | ||
207 | } | ||
208 | Some(d) => { | ||
209 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
210 | } | ||
211 | None => { | ||
212 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
213 | } | ||
214 | } | ||
215 | } else { | ||
216 | match import.path.segments.last() { | ||
217 | Some(last_segment) => { | ||
218 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
219 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
220 | |||
221 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
222 | if import.is_extern_crate && module_id == self.def_map.root { | ||
223 | if let Some(def) = def.take_types() { | ||
224 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
225 | } | ||
226 | } | ||
227 | let resolution = Resolution { def, import: Some(import_id) }; | ||
228 | self.update(module_id, Some(import_id), &[(name, resolution)]); | ||
229 | } | ||
230 | None => tested_by!(bogus_paths), | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
235 | fn update( | ||
236 | &mut self, | ||
237 | module_id: CrateModuleId, | ||
238 | import: Option<raw::ImportId>, | ||
239 | resolutions: &[(Name, Resolution)], | ||
240 | ) { | ||
241 | self.update_recursive(module_id, import, resolutions, 0) | ||
242 | } | ||
243 | |||
244 | fn update_recursive( | ||
245 | &mut self, | ||
246 | module_id: CrateModuleId, | ||
247 | import: Option<raw::ImportId>, | ||
248 | resolutions: &[(Name, Resolution)], | ||
249 | depth: usize, | ||
250 | ) { | ||
251 | if depth > 100 { | ||
252 | // prevent stack overflows (but this shouldn't be possible) | ||
253 | panic!("infinite recursion in glob imports!"); | ||
254 | } | ||
255 | let module_items = &mut self.def_map.modules[module_id].scope; | ||
256 | let mut changed = false; | ||
257 | for (name, res) in resolutions { | ||
258 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
259 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
260 | existing.def.types = res.def.types; | ||
261 | existing.import = import.or(res.import); | ||
262 | changed = true; | ||
263 | } | ||
264 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
265 | existing.def.values = res.def.values; | ||
266 | existing.import = import.or(res.import); | ||
267 | changed = true; | ||
268 | } | ||
269 | if existing.def.is_none() | ||
270 | && res.def.is_none() | ||
271 | && existing.import.is_none() | ||
272 | && res.import.is_some() | ||
273 | { | ||
274 | existing.import = res.import; | ||
275 | } | ||
276 | } | ||
277 | if !changed { | ||
278 | return; | ||
279 | } | ||
280 | let glob_imports = self | ||
281 | .glob_imports | ||
282 | .get(&module_id) | ||
283 | .into_iter() | ||
284 | .flat_map(|v| v.iter()) | ||
285 | .cloned() | ||
286 | .collect::<Vec<_>>(); | ||
287 | for (glob_importing_module, glob_import) in glob_imports { | ||
288 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
289 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | // XXX: this is just a pile of hacks now, because `PerNs` does not handle | ||
294 | // macro namespace. | ||
295 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | ||
296 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | ||
297 | let mut resolved = Vec::new(); | ||
298 | let mut res = ReachedFixedPoint::Yes; | ||
299 | macros.retain(|(module_id, call_id, path, tt)| { | ||
300 | if path.segments.len() != 2 { | ||
301 | return true; | ||
302 | } | ||
303 | let crate_name = &path.segments[0].name; | ||
304 | let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() { | ||
305 | Some(ModuleDef::Module(m)) => m.krate(self.db), | ||
306 | _ => return true, | ||
307 | }; | ||
308 | let krate = match krate { | ||
309 | Some(it) => it, | ||
310 | _ => return true, | ||
311 | }; | ||
312 | res = ReachedFixedPoint::No; | ||
313 | let def_map = self.db.crate_def_map(krate); | ||
314 | if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { | ||
315 | resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone())); | ||
316 | } | ||
317 | false | ||
318 | }); | ||
319 | |||
320 | for (module_id, macro_call_id, macro_def_id, arg) in resolved { | ||
321 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg); | ||
322 | } | ||
323 | res | ||
324 | } | ||
325 | |||
326 | fn collect_macro_expansion( | ||
327 | &mut self, | ||
328 | module_id: CrateModuleId, | ||
329 | macro_call_id: MacroCallId, | ||
330 | macro_def_id: (Crate, CrateMacroId), | ||
331 | macro_arg: tt::Subtree, | ||
332 | ) { | ||
333 | let (macro_krate, macro_id) = macro_def_id; | ||
334 | let dm; | ||
335 | let rules = if macro_krate == self.def_map.krate { | ||
336 | &self.def_map[macro_id] | ||
337 | } else { | ||
338 | dm = self.db.crate_def_map(macro_krate); | ||
339 | &dm[macro_id] | ||
340 | }; | ||
341 | if let Ok(expansion) = rules.expand(¯o_arg) { | ||
342 | self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id); | ||
343 | // XXX: this **does not** go through a database, because we can't | ||
344 | // identify macro_call without adding the whole state of name resolution | ||
345 | // as a parameter to the query. | ||
346 | // | ||
347 | // So, we run the queries "manually" and we must ensure that | ||
348 | // `db.hir_parse(macro_call_id)` returns the same source_file. | ||
349 | let file_id: HirFileId = macro_call_id.into(); | ||
350 | let source_file = mbe::token_tree_to_ast_item_list(&expansion); | ||
351 | |||
352 | let raw_items = raw::RawItems::from_source_file(&source_file, file_id); | ||
353 | ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } | ||
354 | .collect(raw_items.items()) | ||
355 | } | ||
356 | } | ||
357 | |||
358 | fn finish(self) -> CrateDefMap { | ||
359 | self.def_map | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /// Walks a single module, populating defs, imports and macros | ||
364 | struct ModCollector<'a, D> { | ||
365 | def_collector: D, | ||
366 | module_id: CrateModuleId, | ||
367 | file_id: HirFileId, | ||
368 | raw_items: &'a raw::RawItems, | ||
369 | } | ||
370 | |||
371 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | ||
372 | where | ||
373 | DB: PersistentHirDatabase, | ||
374 | { | ||
375 | fn collect(&mut self, items: &[raw::RawItem]) { | ||
376 | for item in items { | ||
377 | match *item { | ||
378 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | ||
379 | raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( | ||
380 | self.module_id, | ||
381 | import, | ||
382 | self.raw_items[import].clone(), | ||
383 | )), | ||
384 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | ||
385 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | |||
390 | fn collect_module(&mut self, module: &raw::ModuleData) { | ||
391 | match module { | ||
392 | // inline module, just recurse | ||
393 | raw::ModuleData::Definition { name, items, source_item_id } => { | ||
394 | let module_id = self.push_child_module( | ||
395 | name.clone(), | ||
396 | source_item_id.with_file_id(self.file_id), | ||
397 | None, | ||
398 | ); | ||
399 | ModCollector { | ||
400 | def_collector: &mut *self.def_collector, | ||
401 | module_id, | ||
402 | file_id: self.file_id, | ||
403 | raw_items: self.raw_items, | ||
404 | } | ||
405 | .collect(&*items); | ||
406 | } | ||
407 | // out of line module, resovle, parse and recurse | ||
408 | raw::ModuleData::Declaration { name, source_item_id } => { | ||
409 | let source_item_id = source_item_id.with_file_id(self.file_id); | ||
410 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); | ||
411 | let (file_ids, problem) = | ||
412 | resolve_submodule(self.def_collector.db, self.file_id, name, is_root); | ||
413 | |||
414 | if let Some(problem) = problem { | ||
415 | self.def_collector.def_map.problems.add(source_item_id, problem) | ||
416 | } | ||
417 | |||
418 | if let Some(&file_id) = file_ids.first() { | ||
419 | let module_id = | ||
420 | self.push_child_module(name.clone(), source_item_id, Some(file_id)); | ||
421 | let raw_items = self.def_collector.db.raw_items(file_id); | ||
422 | ModCollector { | ||
423 | def_collector: &mut *self.def_collector, | ||
424 | module_id, | ||
425 | file_id: file_id.into(), | ||
426 | raw_items: &raw_items, | ||
427 | } | ||
428 | .collect(raw_items.items()) | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | |||
434 | fn push_child_module( | ||
435 | &mut self, | ||
436 | name: Name, | ||
437 | declaration: SourceItemId, | ||
438 | definition: Option<FileId>, | ||
439 | ) -> CrateModuleId { | ||
440 | let modules = &mut self.def_collector.def_map.modules; | ||
441 | let res = modules.alloc(ModuleData::default()); | ||
442 | modules[res].parent = Some(self.module_id); | ||
443 | modules[res].declaration = Some(declaration); | ||
444 | modules[res].definition = definition; | ||
445 | modules[self.module_id].children.insert(name.clone(), res); | ||
446 | let resolution = Resolution { | ||
447 | def: PerNs::types( | ||
448 | Module { krate: self.def_collector.def_map.krate, module_id: res }.into(), | ||
449 | ), | ||
450 | import: None, | ||
451 | }; | ||
452 | self.def_collector.update(self.module_id, None, &[(name, resolution)]); | ||
453 | res | ||
454 | } | ||
455 | |||
456 | fn define_def(&mut self, def: &raw::DefData) { | ||
457 | let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }; | ||
458 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into()); | ||
459 | macro_rules! id { | ||
460 | () => { | ||
461 | AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id) | ||
462 | }; | ||
463 | } | ||
464 | let name = def.name.clone(); | ||
465 | let def: PerNs<ModuleDef> = match def.kind { | ||
466 | raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()), | ||
467 | raw::DefKind::Struct => { | ||
468 | let s = Struct { id: id!() }.into(); | ||
469 | PerNs::both(s, s) | ||
470 | } | ||
471 | raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()), | ||
472 | raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()), | ||
473 | raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()), | ||
474 | raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()), | ||
475 | raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), | ||
476 | }; | ||
477 | let resolution = Resolution { def, import: None }; | ||
478 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) | ||
479 | } | ||
480 | |||
481 | fn collect_macro(&mut self, mac: &raw::MacroData) { | ||
482 | // Case 1: macro rules, define a macro in crate-global mutable scope | ||
483 | if is_macro_rules(&mac.path) { | ||
484 | if let Some(name) = &mac.name { | ||
485 | self.def_collector.define_macro(name.clone(), &mac.arg, mac.export) | ||
486 | } | ||
487 | return; | ||
488 | } | ||
489 | |||
490 | let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id }; | ||
491 | let macro_call_id = MacroCallLoc { | ||
492 | module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }, | ||
493 | source_item_id, | ||
494 | } | ||
495 | .id(self.def_collector.db); | ||
496 | |||
497 | // Case 2: try to expand macro_rules from this crate, triggering | ||
498 | // recursive item collection. | ||
499 | if let Some(¯o_id) = | ||
500 | mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) | ||
501 | { | ||
502 | self.def_collector.collect_macro_expansion( | ||
503 | self.module_id, | ||
504 | macro_call_id, | ||
505 | (self.def_collector.def_map.krate, macro_id), | ||
506 | mac.arg.clone(), | ||
507 | ); | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | // Case 3: path to a macro from another crate, expand during name resolution | ||
512 | self.def_collector.unexpanded_macros.push(( | ||
513 | self.module_id, | ||
514 | macro_call_id, | ||
515 | mac.path.clone(), | ||
516 | mac.arg.clone(), | ||
517 | )) | ||
518 | } | ||
519 | } | ||
520 | |||
521 | fn is_macro_rules(path: &Path) -> bool { | ||
522 | path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules) | ||
523 | } | ||
524 | |||
525 | fn resolve_submodule( | ||
526 | db: &impl PersistentHirDatabase, | ||
527 | file_id: HirFileId, | ||
528 | name: &Name, | ||
529 | is_root: bool, | ||
530 | ) -> (Vec<FileId>, Option<Problem>) { | ||
531 | // FIXME: handle submodules of inline modules properly | ||
532 | let file_id = file_id.original_file(db); | ||
533 | let source_root_id = db.file_source_root(file_id); | ||
534 | let path = db.file_relative_path(file_id); | ||
535 | let root = RelativePathBuf::default(); | ||
536 | let dir_path = path.parent().unwrap_or(&root); | ||
537 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
538 | let is_dir_owner = is_root || mod_name == "mod"; | ||
539 | |||
540 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
541 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
542 | let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
543 | let mut candidates = ArrayVec::<[_; 2]>::new(); | ||
544 | if is_dir_owner { | ||
545 | candidates.push(file_mod.clone()); | ||
546 | candidates.push(dir_mod); | ||
547 | } else { | ||
548 | candidates.push(file_dir_mod.clone()); | ||
549 | }; | ||
550 | let sr = db.source_root(source_root_id); | ||
551 | let points_to = candidates | ||
552 | .into_iter() | ||
553 | .filter_map(|path| sr.files.get(&path)) | ||
554 | .map(|&it| it) | ||
555 | .collect::<Vec<_>>(); | ||
556 | let problem = if points_to.is_empty() { | ||
557 | Some(Problem::UnresolvedModule { | ||
558 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
559 | }) | ||
560 | } else { | ||
561 | None | ||
562 | }; | ||
563 | (points_to, problem) | ||
564 | } | ||
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs deleted file mode 100644 index 56262ad6d..000000000 --- a/crates/ra_hir/src/nameres/lower.rs +++ /dev/null | |||
@@ -1,222 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | AstNode, SourceFile, TreeArc, AstPtr, | ||
5 | ast::{self, ModuleItemOwner, NameOwner, AttrsOwner}, | ||
6 | }; | ||
7 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | ||
8 | use rustc_hash::FxHashMap; | ||
9 | |||
10 | use crate::{ | ||
11 | SourceItemId, Path, ModuleSource, Name, | ||
12 | HirFileId, MacroCallLoc, AsName, PerNs, Function, | ||
13 | ModuleDef, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | ||
14 | ids::LocationCtx, PersistentHirDatabase, | ||
15 | }; | ||
16 | |||
17 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
18 | pub struct ImportId(RawId); | ||
19 | impl_arena_id!(ImportId); | ||
20 | |||
21 | #[derive(Debug, PartialEq, Eq)] | ||
22 | pub(super) struct ImportData { | ||
23 | pub(super) path: Path, | ||
24 | pub(super) alias: Option<Name>, | ||
25 | pub(super) is_glob: bool, | ||
26 | pub(super) is_prelude: bool, | ||
27 | pub(super) is_extern_crate: bool, | ||
28 | } | ||
29 | |||
30 | /// A set of items and imports declared inside a module, without relation to | ||
31 | /// other modules. | ||
32 | /// | ||
33 | /// This sits in-between raw syntax and name resolution and allows us to avoid | ||
34 | /// recomputing name res: if two instance of `InputModuleItems` are the same, we | ||
35 | /// can avoid redoing name resolution. | ||
36 | #[derive(Debug, Default, PartialEq, Eq)] | ||
37 | pub struct LoweredModule { | ||
38 | pub(crate) declarations: FxHashMap<Name, PerNs<ModuleDef>>, | ||
39 | pub(super) imports: Arena<ImportId, ImportData>, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Default, PartialEq, Eq)] | ||
43 | pub struct ImportSourceMap { | ||
44 | map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>, | ||
45 | } | ||
46 | |||
47 | impl ImportSourceMap { | ||
48 | fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { | ||
49 | self.map.insert(import, AstPtr::new(segment)) | ||
50 | } | ||
51 | |||
52 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { | ||
53 | let file = match source { | ||
54 | ModuleSource::SourceFile(file) => &*file, | ||
55 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | ||
56 | }; | ||
57 | |||
58 | self.map[import].to_node(file).to_owned() | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl LoweredModule { | ||
63 | pub(crate) fn lower_module_query( | ||
64 | db: &impl PersistentHirDatabase, | ||
65 | module: Module, | ||
66 | ) -> Arc<LoweredModule> { | ||
67 | db.lower_module_with_source_map(module).0 | ||
68 | } | ||
69 | |||
70 | pub(crate) fn lower_module_with_source_map_query( | ||
71 | db: &impl PersistentHirDatabase, | ||
72 | module: Module, | ||
73 | ) -> (Arc<LoweredModule>, Arc<ImportSourceMap>) { | ||
74 | let (file_id, source) = module.definition_source(db); | ||
75 | let file_id: HirFileId = file_id.into(); | ||
76 | let mut source_map = ImportSourceMap::default(); | ||
77 | let mut res = LoweredModule::default(); | ||
78 | match source { | ||
79 | ModuleSource::SourceFile(it) => { | ||
80 | res.fill(&mut source_map, db, module, file_id, &mut it.items_with_macros()) | ||
81 | } | ||
82 | ModuleSource::Module(it) => { | ||
83 | if let Some(item_list) = it.item_list() { | ||
84 | res.fill( | ||
85 | &mut source_map, | ||
86 | db, | ||
87 | module, | ||
88 | file_id, | ||
89 | &mut item_list.items_with_macros(), | ||
90 | ) | ||
91 | } | ||
92 | } | ||
93 | }; | ||
94 | (Arc::new(res), Arc::new(source_map)) | ||
95 | } | ||
96 | |||
97 | fn fill( | ||
98 | &mut self, | ||
99 | source_map: &mut ImportSourceMap, | ||
100 | db: &impl PersistentHirDatabase, | ||
101 | module: Module, | ||
102 | file_id: HirFileId, | ||
103 | items: &mut Iterator<Item = ast::ItemOrMacro>, | ||
104 | ) { | ||
105 | let file_items = db.file_items(file_id); | ||
106 | |||
107 | for item in items { | ||
108 | match item { | ||
109 | ast::ItemOrMacro::Item(it) => { | ||
110 | self.add_def_id(source_map, db, module, file_id, it); | ||
111 | } | ||
112 | ast::ItemOrMacro::Macro(macro_call) => { | ||
113 | let item_id = file_items.id_of_unchecked(macro_call.syntax()); | ||
114 | let loc = | ||
115 | MacroCallLoc { module, source_item_id: SourceItemId { file_id, item_id } }; | ||
116 | let id = loc.id(db); | ||
117 | let file_id = HirFileId::from(id); | ||
118 | //FIXME: expand recursively | ||
119 | for item in db.hir_parse(file_id).items() { | ||
120 | self.add_def_id(source_map, db, module, file_id, item); | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | fn add_def_id( | ||
128 | &mut self, | ||
129 | source_map: &mut ImportSourceMap, | ||
130 | db: &impl PersistentHirDatabase, | ||
131 | module: Module, | ||
132 | file_id: HirFileId, | ||
133 | item: &ast::ModuleItem, | ||
134 | ) { | ||
135 | let ctx = LocationCtx::new(db, module, file_id); | ||
136 | match item.kind() { | ||
137 | ast::ModuleItemKind::StructDef(it) => { | ||
138 | if let Some(name) = it.name() { | ||
139 | let s = Struct { id: ctx.to_def(it) }; | ||
140 | let s: ModuleDef = s.into(); | ||
141 | self.declarations.insert(name.as_name(), PerNs::both(s, s)); | ||
142 | } | ||
143 | } | ||
144 | ast::ModuleItemKind::EnumDef(it) => { | ||
145 | if let Some(name) = it.name() { | ||
146 | let e = Enum { id: ctx.to_def(it) }; | ||
147 | let e: ModuleDef = e.into(); | ||
148 | self.declarations.insert(name.as_name(), PerNs::types(e)); | ||
149 | } | ||
150 | } | ||
151 | ast::ModuleItemKind::FnDef(it) => { | ||
152 | if let Some(name) = it.name() { | ||
153 | let func = Function { id: ctx.to_def(it) }; | ||
154 | self.declarations.insert(name.as_name(), PerNs::values(func.into())); | ||
155 | } | ||
156 | } | ||
157 | ast::ModuleItemKind::TraitDef(it) => { | ||
158 | if let Some(name) = it.name() { | ||
159 | let t = Trait { id: ctx.to_def(it) }; | ||
160 | self.declarations.insert(name.as_name(), PerNs::types(t.into())); | ||
161 | } | ||
162 | } | ||
163 | ast::ModuleItemKind::TypeAliasDef(it) => { | ||
164 | if let Some(name) = it.name() { | ||
165 | let t = TypeAlias { id: ctx.to_def(it) }; | ||
166 | self.declarations.insert(name.as_name(), PerNs::types(t.into())); | ||
167 | } | ||
168 | } | ||
169 | ast::ModuleItemKind::ImplBlock(_) => { | ||
170 | // impls don't define items | ||
171 | } | ||
172 | ast::ModuleItemKind::UseItem(it) => { | ||
173 | self.add_use_item(source_map, it); | ||
174 | } | ||
175 | ast::ModuleItemKind::ExternCrateItem(it) => { | ||
176 | if let Some(name_ref) = it.name_ref() { | ||
177 | let path = Path::from_name_ref(name_ref); | ||
178 | let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name); | ||
179 | self.imports.alloc(ImportData { | ||
180 | path, | ||
181 | alias, | ||
182 | is_glob: false, | ||
183 | is_prelude: false, | ||
184 | is_extern_crate: true, | ||
185 | }); | ||
186 | } | ||
187 | } | ||
188 | ast::ModuleItemKind::ConstDef(it) => { | ||
189 | if let Some(name) = it.name() { | ||
190 | let c = Const { id: ctx.to_def(it) }; | ||
191 | self.declarations.insert(name.as_name(), PerNs::values(c.into())); | ||
192 | } | ||
193 | } | ||
194 | ast::ModuleItemKind::StaticDef(it) => { | ||
195 | if let Some(name) = it.name() { | ||
196 | let s = Static { id: ctx.to_def(it) }; | ||
197 | self.declarations.insert(name.as_name(), PerNs::values(s.into())); | ||
198 | } | ||
199 | } | ||
200 | ast::ModuleItemKind::Module(_) => { | ||
201 | // modules are handled separately directly by name res | ||
202 | } | ||
203 | }; | ||
204 | } | ||
205 | |||
206 | fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) { | ||
207 | let is_prelude = | ||
208 | item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false)); | ||
209 | Path::expand_use_item(item, |path, segment, alias| { | ||
210 | let import = self.imports.alloc(ImportData { | ||
211 | path, | ||
212 | alias, | ||
213 | is_glob: segment.is_none(), | ||
214 | is_prelude, | ||
215 | is_extern_crate: false, | ||
216 | }); | ||
217 | if let Some(segment) = segment { | ||
218 | source_map.insert(import, segment) | ||
219 | } | ||
220 | }) | ||
221 | } | ||
222 | } | ||
diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs new file mode 100644 index 000000000..c40a3ff9d --- /dev/null +++ b/crates/ra_hir/src/nameres/per_ns.rs | |||
@@ -0,0 +1,78 @@ | |||
1 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
2 | pub enum Namespace { | ||
3 | Types, | ||
4 | Values, | ||
5 | } | ||
6 | |||
7 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
8 | pub struct PerNs<T> { | ||
9 | pub types: Option<T>, | ||
10 | pub values: Option<T>, | ||
11 | } | ||
12 | |||
13 | impl<T> Default for PerNs<T> { | ||
14 | fn default() -> Self { | ||
15 | PerNs { types: None, values: None } | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl<T> PerNs<T> { | ||
20 | pub fn none() -> PerNs<T> { | ||
21 | PerNs { types: None, values: None } | ||
22 | } | ||
23 | |||
24 | pub fn values(t: T) -> PerNs<T> { | ||
25 | PerNs { types: None, values: Some(t) } | ||
26 | } | ||
27 | |||
28 | pub fn types(t: T) -> PerNs<T> { | ||
29 | PerNs { types: Some(t), values: None } | ||
30 | } | ||
31 | |||
32 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
33 | PerNs { types: Some(types), values: Some(values) } | ||
34 | } | ||
35 | |||
36 | pub fn is_none(&self) -> bool { | ||
37 | self.types.is_none() && self.values.is_none() | ||
38 | } | ||
39 | |||
40 | pub fn is_both(&self) -> bool { | ||
41 | self.types.is_some() && self.values.is_some() | ||
42 | } | ||
43 | |||
44 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
45 | match namespace { | ||
46 | Namespace::Types => self.types, | ||
47 | Namespace::Values => self.values, | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub fn take_types(self) -> Option<T> { | ||
52 | self.take(Namespace::Types) | ||
53 | } | ||
54 | |||
55 | pub fn take_values(self) -> Option<T> { | ||
56 | self.take(Namespace::Values) | ||
57 | } | ||
58 | |||
59 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | ||
60 | self.as_ref().take(namespace) | ||
61 | } | ||
62 | |||
63 | pub fn as_ref(&self) -> PerNs<&T> { | ||
64 | PerNs { types: self.types.as_ref(), values: self.values.as_ref() } | ||
65 | } | ||
66 | |||
67 | pub fn or(self, other: PerNs<T>) -> PerNs<T> { | ||
68 | PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } | ||
69 | } | ||
70 | |||
71 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
72 | PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } | ||
73 | } | ||
74 | |||
75 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
76 | PerNs { types: self.types.map(&f), values: self.values.map(&f) } | ||
77 | } | ||
78 | } | ||
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs new file mode 100644 index 000000000..3226bbf0d --- /dev/null +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -0,0 +1,322 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | ops::Index, | ||
4 | }; | ||
5 | |||
6 | use test_utils::tested_by; | ||
7 | use ra_db::FileId; | ||
8 | use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; | ||
9 | use ra_syntax::{ | ||
10 | AstNode, SourceFile, AstPtr, TreeArc, | ||
11 | ast::{self, NameOwner, AttrsOwner}, | ||
12 | }; | ||
13 | |||
14 | use crate::{ | ||
15 | PersistentHirDatabase, Name, AsName, Path, HirFileId, ModuleSource, | ||
16 | ids::{SourceFileItemId, SourceFileItems}, | ||
17 | }; | ||
18 | |||
19 | #[derive(Debug, Default, PartialEq, Eq)] | ||
20 | pub struct RawItems { | ||
21 | modules: Arena<Module, ModuleData>, | ||
22 | imports: Arena<ImportId, ImportData>, | ||
23 | defs: Arena<Def, DefData>, | ||
24 | macros: Arena<Macro, MacroData>, | ||
25 | /// items for top-level module | ||
26 | items: Vec<RawItem>, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Default, PartialEq, Eq)] | ||
30 | pub struct ImportSourceMap { | ||
31 | map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>, | ||
32 | } | ||
33 | |||
34 | impl ImportSourceMap { | ||
35 | pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { | ||
36 | self.map.insert(import, AstPtr::new(segment)) | ||
37 | } | ||
38 | |||
39 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { | ||
40 | let file = match source { | ||
41 | ModuleSource::SourceFile(file) => &*file, | ||
42 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | ||
43 | }; | ||
44 | |||
45 | self.map[import].to_node(file).to_owned() | ||
46 | } | ||
47 | } | ||
48 | |||
49 | impl RawItems { | ||
50 | pub(crate) fn raw_items_query( | ||
51 | db: &impl PersistentHirDatabase, | ||
52 | file_id: FileId, | ||
53 | ) -> Arc<RawItems> { | ||
54 | db.raw_items_with_source_map(file_id).0 | ||
55 | } | ||
56 | |||
57 | pub(crate) fn raw_items_with_source_map_query( | ||
58 | db: &impl PersistentHirDatabase, | ||
59 | file_id: FileId, | ||
60 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>) { | ||
61 | let mut collector = RawItemsCollector { | ||
62 | raw_items: RawItems::default(), | ||
63 | source_file_items: db.file_items(file_id.into()), | ||
64 | source_map: ImportSourceMap::default(), | ||
65 | }; | ||
66 | let source_file = db.parse(file_id); | ||
67 | collector.process_module(None, &*source_file); | ||
68 | (Arc::new(collector.raw_items), Arc::new(collector.source_map)) | ||
69 | } | ||
70 | |||
71 | pub(crate) fn items(&self) -> &[RawItem] { | ||
72 | &self.items | ||
73 | } | ||
74 | |||
75 | // We can't use queries during name resolution for fear of cycles, so this | ||
76 | // is a query-less variant of the above function. | ||
77 | pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems { | ||
78 | let source_file_items = SourceFileItems::from_source_file(source_file, file_id); | ||
79 | let mut collector = RawItemsCollector { | ||
80 | raw_items: RawItems::default(), | ||
81 | source_file_items: Arc::new(source_file_items), | ||
82 | source_map: ImportSourceMap::default(), | ||
83 | }; | ||
84 | collector.process_module(None, &*source_file); | ||
85 | collector.raw_items | ||
86 | } | ||
87 | } | ||
88 | |||
89 | impl Index<Module> for RawItems { | ||
90 | type Output = ModuleData; | ||
91 | fn index(&self, idx: Module) -> &ModuleData { | ||
92 | &self.modules[idx] | ||
93 | } | ||
94 | } | ||
95 | |||
96 | impl Index<ImportId> for RawItems { | ||
97 | type Output = ImportData; | ||
98 | fn index(&self, idx: ImportId) -> &ImportData { | ||
99 | &self.imports[idx] | ||
100 | } | ||
101 | } | ||
102 | |||
103 | impl Index<Def> for RawItems { | ||
104 | type Output = DefData; | ||
105 | fn index(&self, idx: Def) -> &DefData { | ||
106 | &self.defs[idx] | ||
107 | } | ||
108 | } | ||
109 | |||
110 | impl Index<Macro> for RawItems { | ||
111 | type Output = MacroData; | ||
112 | fn index(&self, idx: Macro) -> &MacroData { | ||
113 | &self.macros[idx] | ||
114 | } | ||
115 | } | ||
116 | |||
117 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
118 | pub(crate) enum RawItem { | ||
119 | Module(Module), | ||
120 | Import(ImportId), | ||
121 | Def(Def), | ||
122 | Macro(Macro), | ||
123 | } | ||
124 | |||
125 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
126 | pub(crate) struct Module(RawId); | ||
127 | impl_arena_id!(Module); | ||
128 | |||
129 | #[derive(Debug, PartialEq, Eq)] | ||
130 | pub(crate) enum ModuleData { | ||
131 | Declaration { name: Name, source_item_id: SourceFileItemId }, | ||
132 | Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> }, | ||
133 | } | ||
134 | |||
135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
136 | pub struct ImportId(RawId); | ||
137 | impl_arena_id!(ImportId); | ||
138 | |||
139 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
140 | pub struct ImportData { | ||
141 | pub(crate) path: Path, | ||
142 | pub(crate) alias: Option<Name>, | ||
143 | pub(crate) is_glob: bool, | ||
144 | pub(crate) is_prelude: bool, | ||
145 | pub(crate) is_extern_crate: bool, | ||
146 | } | ||
147 | |||
148 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
149 | pub(crate) struct Def(RawId); | ||
150 | impl_arena_id!(Def); | ||
151 | |||
152 | #[derive(Debug, PartialEq, Eq)] | ||
153 | pub(crate) struct DefData { | ||
154 | pub(crate) source_item_id: SourceFileItemId, | ||
155 | pub(crate) name: Name, | ||
156 | pub(crate) kind: DefKind, | ||
157 | } | ||
158 | |||
159 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
160 | pub(crate) enum DefKind { | ||
161 | Function, | ||
162 | Struct, | ||
163 | Enum, | ||
164 | Const, | ||
165 | Static, | ||
166 | Trait, | ||
167 | TypeAlias, | ||
168 | } | ||
169 | |||
170 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
171 | pub(crate) struct Macro(RawId); | ||
172 | impl_arena_id!(Macro); | ||
173 | |||
174 | #[derive(Debug, PartialEq, Eq)] | ||
175 | pub(crate) struct MacroData { | ||
176 | pub(crate) source_item_id: SourceFileItemId, | ||
177 | pub(crate) path: Path, | ||
178 | pub(crate) name: Option<Name>, | ||
179 | pub(crate) arg: tt::Subtree, | ||
180 | pub(crate) export: bool, | ||
181 | } | ||
182 | |||
183 | struct RawItemsCollector { | ||
184 | raw_items: RawItems, | ||
185 | source_file_items: Arc<SourceFileItems>, | ||
186 | source_map: ImportSourceMap, | ||
187 | } | ||
188 | |||
189 | impl RawItemsCollector { | ||
190 | fn process_module(&mut self, current_module: Option<Module>, body: &impl ast::ModuleItemOwner) { | ||
191 | for item_or_macro in body.items_with_macros() { | ||
192 | match item_or_macro { | ||
193 | ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m), | ||
194 | ast::ItemOrMacro::Item(item) => self.add_item(current_module, item), | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn add_item(&mut self, current_module: Option<Module>, item: &ast::ModuleItem) { | ||
200 | let (kind, name) = match item.kind() { | ||
201 | ast::ModuleItemKind::Module(module) => { | ||
202 | self.add_module(current_module, module); | ||
203 | return; | ||
204 | } | ||
205 | ast::ModuleItemKind::UseItem(use_item) => { | ||
206 | self.add_use_item(current_module, use_item); | ||
207 | return; | ||
208 | } | ||
209 | ast::ModuleItemKind::ExternCrateItem(extern_crate) => { | ||
210 | self.add_extern_crate_item(current_module, extern_crate); | ||
211 | return; | ||
212 | } | ||
213 | ast::ModuleItemKind::ImplBlock(_) => { | ||
214 | // impls don't participate in name resolution | ||
215 | return; | ||
216 | } | ||
217 | ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()), | ||
218 | ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()), | ||
219 | ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()), | ||
220 | ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()), | ||
221 | ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()), | ||
222 | ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()), | ||
223 | ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()), | ||
224 | }; | ||
225 | if let Some(name) = name { | ||
226 | let name = name.as_name(); | ||
227 | let source_item_id = self.source_file_items.id_of_unchecked(item.syntax()); | ||
228 | let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id }); | ||
229 | self.push_item(current_module, RawItem::Def(def)) | ||
230 | } | ||
231 | } | ||
232 | |||
233 | fn add_module(&mut self, current_module: Option<Module>, module: &ast::Module) { | ||
234 | let name = match module.name() { | ||
235 | Some(it) => it.as_name(), | ||
236 | None => return, | ||
237 | }; | ||
238 | let source_item_id = self.source_file_items.id_of_unchecked(module.syntax()); | ||
239 | if module.has_semi() { | ||
240 | let item = | ||
241 | self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id }); | ||
242 | self.push_item(current_module, RawItem::Module(item)); | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | if let Some(item_list) = module.item_list() { | ||
247 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | ||
248 | name, | ||
249 | source_item_id, | ||
250 | items: Vec::new(), | ||
251 | }); | ||
252 | self.process_module(Some(item), item_list); | ||
253 | self.push_item(current_module, RawItem::Module(item)); | ||
254 | return; | ||
255 | } | ||
256 | tested_by!(name_res_works_for_broken_modules); | ||
257 | } | ||
258 | |||
259 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) { | ||
260 | let is_prelude = use_item.has_atom_attr("prelude_import"); | ||
261 | |||
262 | Path::expand_use_item(use_item, |path, segment, alias| { | ||
263 | let import = self.raw_items.imports.alloc(ImportData { | ||
264 | path, | ||
265 | alias, | ||
266 | is_glob: segment.is_none(), | ||
267 | is_prelude, | ||
268 | is_extern_crate: false, | ||
269 | }); | ||
270 | if let Some(segment) = segment { | ||
271 | self.source_map.insert(import, segment) | ||
272 | } | ||
273 | self.push_item(current_module, RawItem::Import(import)) | ||
274 | }) | ||
275 | } | ||
276 | |||
277 | fn add_extern_crate_item( | ||
278 | &mut self, | ||
279 | current_module: Option<Module>, | ||
280 | extern_crate: &ast::ExternCrateItem, | ||
281 | ) { | ||
282 | if let Some(name_ref) = extern_crate.name_ref() { | ||
283 | let path = Path::from_name_ref(name_ref); | ||
284 | let alias = extern_crate.alias().and_then(|a| a.name()).map(AsName::as_name); | ||
285 | let import = self.raw_items.imports.alloc(ImportData { | ||
286 | path, | ||
287 | alias, | ||
288 | is_glob: false, | ||
289 | is_prelude: false, | ||
290 | is_extern_crate: true, | ||
291 | }); | ||
292 | self.push_item(current_module, RawItem::Import(import)) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) { | ||
297 | let (path, arg) = match ( | ||
298 | m.path().and_then(Path::from_ast), | ||
299 | m.token_tree().and_then(mbe::ast_to_token_tree), | ||
300 | ) { | ||
301 | (Some(path), Some((token_tree, _token_map))) => (path, token_tree), | ||
302 | _ => return, | ||
303 | }; | ||
304 | |||
305 | let name = m.name().map(|it| it.as_name()); | ||
306 | let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); | ||
307 | let export = m.has_atom_attr("macro_export"); | ||
308 | let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export }); | ||
309 | self.push_item(current_module, RawItem::Macro(m)); | ||
310 | } | ||
311 | |||
312 | fn push_item(&mut self, current_module: Option<Module>, item: RawItem) { | ||
313 | match current_module { | ||
314 | Some(module) => match &mut self.raw_items.modules[module] { | ||
315 | ModuleData::Definition { items, .. } => items, | ||
316 | ModuleData::Declaration { .. } => unreachable!(), | ||
317 | }, | ||
318 | None => &mut self.raw_items.items, | ||
319 | } | ||
320 | .push(item) | ||
321 | } | ||
322 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 9b151bb0c..ac9b88520 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -1,34 +1,43 @@ | |||
1 | mod macros; | ||
2 | mod globs; | ||
3 | mod incremental; | ||
4 | |||
1 | use std::sync::Arc; | 5 | use std::sync::Arc; |
2 | 6 | ||
3 | use ra_db::SourceDatabase; | 7 | use ra_db::SourceDatabase; |
4 | use test_utils::{assert_eq_text, covers}; | 8 | use test_utils::covers; |
5 | 9 | use insta::assert_snapshot_matches; | |
6 | use crate::{ | 10 | |
7 | ItemMap, | 11 | use crate::{Crate, mock::{MockDatabase, CrateGraphFixture}, nameres::Resolution}; |
8 | PersistentHirDatabase, | 12 | |
9 | mock::MockDatabase, | 13 | use super::*; |
10 | module_tree::ModuleId, | 14 | |
11 | }; | 15 | fn compute_crate_def_map(fixture: &str, graph: Option<CrateGraphFixture>) -> Arc<CrateDefMap> { |
12 | use super::Resolution; | 16 | let mut db = MockDatabase::with_files(fixture); |
13 | 17 | if let Some(graph) = graph { | |
14 | fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) { | 18 | db.set_crate_graph_from_fixture(graph); |
15 | let (db, pos) = MockDatabase::with_position(fixture); | 19 | } |
16 | let module = crate::source_binder::module_from_position(&db, pos).unwrap(); | 20 | let crate_id = db.crate_graph().iter().next().unwrap(); |
17 | let krate = module.krate(&db).unwrap(); | 21 | let krate = Crate { crate_id }; |
18 | let module_id = module.module_id; | 22 | db.crate_def_map(krate) |
19 | (db.item_map(krate), module_id) | ||
20 | } | 23 | } |
21 | 24 | ||
22 | fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) { | 25 | fn render_crate_def_map(map: &CrateDefMap) -> String { |
23 | let mut lines = map[module_id] | 26 | let mut buf = String::new(); |
24 | .items | 27 | go(&mut buf, map, "\ncrate", map.root); |
25 | .iter() | 28 | return buf; |
26 | .map(|(name, res)| format!("{}: {}", name, dump_resolution(res))) | 29 | |
27 | .collect::<Vec<_>>(); | 30 | fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { |
28 | lines.sort(); | 31 | *buf += path; |
29 | let actual = lines.join("\n"); | 32 | *buf += "\n"; |
30 | let expected = expected.trim().lines().map(|it| it.trim()).collect::<Vec<_>>().join("\n"); | 33 | for (name, res) in map.modules[module].scope.items.iter() { |
31 | assert_eq_text!(&expected, &actual); | 34 | *buf += &format!("{}: {}\n", name, dump_resolution(res)) |
35 | } | ||
36 | for (name, child) in map.modules[module].children.iter() { | ||
37 | let path = path.to_string() + &format!("::{}", name); | ||
38 | go(buf, map, &path, *child); | ||
39 | } | ||
40 | } | ||
32 | 41 | ||
33 | fn dump_resolution(resolution: &Resolution) -> &'static str { | 42 | fn dump_resolution(resolution: &Resolution) -> &'static str { |
34 | match (resolution.def.types.is_some(), resolution.def.values.is_some()) { | 43 | match (resolution.def.types.is_some(), resolution.def.values.is_some()) { |
@@ -40,66 +49,112 @@ fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) { | |||
40 | } | 49 | } |
41 | } | 50 | } |
42 | 51 | ||
52 | fn def_map(fixtute: &str) -> String { | ||
53 | let dm = compute_crate_def_map(fixtute, None); | ||
54 | render_crate_def_map(&dm) | ||
55 | } | ||
56 | |||
57 | fn def_map_with_crate_graph(fixtute: &str, graph: CrateGraphFixture) -> String { | ||
58 | let dm = compute_crate_def_map(fixtute, Some(graph)); | ||
59 | render_crate_def_map(&dm) | ||
60 | } | ||
61 | |||
43 | #[test] | 62 | #[test] |
44 | fn item_map_smoke_test() { | 63 | fn crate_def_map_smoke_test() { |
45 | let (item_map, module_id) = item_map( | 64 | let map = def_map( |
46 | " | 65 | " |
47 | //- /lib.rs | 66 | //- /lib.rs |
48 | mod foo; | 67 | mod foo; |
49 | 68 | struct S; | |
50 | use crate::foo::bar::Baz; | 69 | use crate::foo::bar::E; |
51 | <|> | 70 | use self::E::V; |
52 | 71 | ||
53 | //- /foo/mod.rs | 72 | //- /foo/mod.rs |
54 | pub mod bar; | 73 | pub mod bar; |
74 | fn f() {} | ||
55 | 75 | ||
56 | //- /foo/bar.rs | 76 | //- /foo/bar.rs |
57 | pub struct Baz; | 77 | pub struct Baz; |
58 | ", | 78 | enum E { V } |
79 | ", | ||
59 | ); | 80 | ); |
60 | check_module_item_map( | 81 | assert_snapshot_matches!(map, @r###" |
61 | &item_map, | 82 | crate |
62 | module_id, | 83 | V: t v |
84 | E: t | ||
85 | foo: t | ||
86 | S: t v | ||
87 | |||
88 | crate::foo | ||
89 | bar: t | ||
90 | f: v | ||
91 | |||
92 | crate::foo::bar | ||
93 | Baz: t v | ||
94 | E: t | ||
95 | "### | ||
96 | ) | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn bogus_paths() { | ||
101 | covers!(bogus_paths); | ||
102 | let map = def_map( | ||
63 | " | 103 | " |
64 | Baz: t v | 104 | //- /lib.rs |
65 | foo: t | 105 | mod foo; |
106 | struct S; | ||
107 | use self; | ||
108 | |||
109 | //- /foo/mod.rs | ||
110 | use super; | ||
111 | use crate; | ||
112 | |||
66 | ", | 113 | ", |
67 | ); | 114 | ); |
115 | assert_snapshot_matches!(map, @r###" | ||
116 | crate | ||
117 | foo: t | ||
118 | S: t v | ||
119 | |||
120 | crate::foo | ||
121 | "### | ||
122 | ) | ||
68 | } | 123 | } |
69 | 124 | ||
70 | #[test] | 125 | #[test] |
71 | fn use_as() { | 126 | fn use_as() { |
72 | let (item_map, module_id) = item_map( | 127 | let map = def_map( |
73 | " | 128 | " |
74 | //- /lib.rs | 129 | //- /lib.rs |
75 | mod foo; | 130 | mod foo; |
76 | 131 | ||
77 | use crate::foo::Baz as Foo; | 132 | use crate::foo::Baz as Foo; |
78 | <|> | ||
79 | 133 | ||
80 | //- /foo/mod.rs | 134 | //- /foo/mod.rs |
81 | pub struct Baz; | 135 | pub struct Baz; |
82 | ", | ||
83 | ); | ||
84 | check_module_item_map( | ||
85 | &item_map, | ||
86 | module_id, | ||
87 | " | ||
88 | Foo: t v | ||
89 | foo: t | ||
90 | ", | 136 | ", |
91 | ); | 137 | ); |
138 | assert_snapshot_matches!(map, | ||
139 | @r###" | ||
140 | crate | ||
141 | Foo: t v | ||
142 | foo: t | ||
143 | |||
144 | crate::foo | ||
145 | Baz: t v | ||
146 | "### | ||
147 | ); | ||
92 | } | 148 | } |
93 | 149 | ||
94 | #[test] | 150 | #[test] |
95 | fn use_trees() { | 151 | fn use_trees() { |
96 | let (item_map, module_id) = item_map( | 152 | let map = def_map( |
97 | " | 153 | " |
98 | //- /lib.rs | 154 | //- /lib.rs |
99 | mod foo; | 155 | mod foo; |
100 | 156 | ||
101 | use crate::foo::bar::{Baz, Quux}; | 157 | use crate::foo::bar::{Baz, Quux}; |
102 | <|> | ||
103 | 158 | ||
104 | //- /foo/mod.rs | 159 | //- /foo/mod.rs |
105 | pub mod bar; | 160 | pub mod bar; |
@@ -107,28 +162,33 @@ fn use_trees() { | |||
107 | //- /foo/bar.rs | 162 | //- /foo/bar.rs |
108 | pub struct Baz; | 163 | pub struct Baz; |
109 | pub enum Quux {}; | 164 | pub enum Quux {}; |
110 | ", | ||
111 | ); | ||
112 | check_module_item_map( | ||
113 | &item_map, | ||
114 | module_id, | ||
115 | " | ||
116 | Baz: t v | ||
117 | Quux: t | ||
118 | foo: t | ||
119 | ", | 165 | ", |
120 | ); | 166 | ); |
167 | assert_snapshot_matches!(map, | ||
168 | @r###" | ||
169 | crate | ||
170 | Quux: t | ||
171 | Baz: t v | ||
172 | foo: t | ||
173 | |||
174 | crate::foo | ||
175 | bar: t | ||
176 | |||
177 | crate::foo::bar | ||
178 | Quux: t | ||
179 | Baz: t v | ||
180 | "### | ||
181 | ); | ||
121 | } | 182 | } |
122 | 183 | ||
123 | #[test] | 184 | #[test] |
124 | fn re_exports() { | 185 | fn re_exports() { |
125 | let (item_map, module_id) = item_map( | 186 | let map = def_map( |
126 | " | 187 | " |
127 | //- /lib.rs | 188 | //- /lib.rs |
128 | mod foo; | 189 | mod foo; |
129 | 190 | ||
130 | use self::foo::Baz; | 191 | use self::foo::Baz; |
131 | <|> | ||
132 | 192 | ||
133 | //- /foo/mod.rs | 193 | //- /foo/mod.rs |
134 | pub mod bar; | 194 | pub mod bar; |
@@ -137,137 +197,73 @@ fn re_exports() { | |||
137 | 197 | ||
138 | //- /foo/bar.rs | 198 | //- /foo/bar.rs |
139 | pub struct Baz; | 199 | pub struct Baz; |
140 | ", | ||
141 | ); | ||
142 | check_module_item_map( | ||
143 | &item_map, | ||
144 | module_id, | ||
145 | " | ||
146 | Baz: t v | ||
147 | foo: t | ||
148 | ", | 200 | ", |
149 | ); | 201 | ); |
150 | } | 202 | assert_snapshot_matches!(map, |
203 | @r###" | ||
204 | crate | ||
205 | Baz: t v | ||
206 | foo: t | ||
151 | 207 | ||
152 | #[test] | 208 | crate::foo |
153 | fn glob_1() { | 209 | bar: t |
154 | let (item_map, module_id) = item_map( | 210 | Baz: t v |
155 | " | ||
156 | //- /lib.rs | ||
157 | mod foo; | ||
158 | use foo::*; | ||
159 | <|> | ||
160 | |||
161 | //- /foo/mod.rs | ||
162 | pub mod bar; | ||
163 | pub use self::bar::Baz; | ||
164 | pub struct Foo; | ||
165 | 211 | ||
166 | //- /foo/bar.rs | 212 | crate::foo::bar |
167 | pub struct Baz; | 213 | Baz: t v |
168 | ", | 214 | "### |
169 | ); | ||
170 | check_module_item_map( | ||
171 | &item_map, | ||
172 | module_id, | ||
173 | " | ||
174 | Baz: t v | ||
175 | Foo: t v | ||
176 | bar: t | ||
177 | foo: t | ||
178 | ", | ||
179 | ); | 215 | ); |
180 | } | 216 | } |
181 | 217 | ||
182 | #[test] | 218 | #[test] |
183 | fn glob_2() { | 219 | fn std_prelude() { |
184 | let (item_map, module_id) = item_map( | 220 | covers!(std_prelude); |
185 | " | 221 | let map = def_map_with_crate_graph( |
186 | //- /lib.rs | ||
187 | mod foo; | ||
188 | use foo::*; | ||
189 | <|> | ||
190 | |||
191 | //- /foo/mod.rs | ||
192 | pub mod bar; | ||
193 | pub use self::bar::*; | ||
194 | pub struct Foo; | ||
195 | |||
196 | //- /foo/bar.rs | ||
197 | pub struct Baz; | ||
198 | pub use super::*; | ||
199 | ", | ||
200 | ); | ||
201 | check_module_item_map( | ||
202 | &item_map, | ||
203 | module_id, | ||
204 | " | 222 | " |
205 | Baz: t v | 223 | //- /main.rs |
206 | Foo: t v | 224 | use Foo::*; |
207 | bar: t | ||
208 | foo: t | ||
209 | ", | ||
210 | ); | ||
211 | } | ||
212 | 225 | ||
213 | #[test] | ||
214 | fn glob_enum() { | ||
215 | covers!(glob_enum); | ||
216 | let (item_map, module_id) = item_map( | ||
217 | " | ||
218 | //- /lib.rs | 226 | //- /lib.rs |
219 | enum Foo { | 227 | mod prelude; |
220 | Bar, Baz | 228 | #[prelude_import] |
221 | } | 229 | use prelude::*; |
222 | use self::Foo::*; | 230 | |
223 | <|> | 231 | //- /prelude.rs |
224 | ", | 232 | pub enum Foo { Bar, Baz }; |
225 | ); | ||
226 | check_module_item_map( | ||
227 | &item_map, | ||
228 | module_id, | ||
229 | " | ||
230 | Bar: t v | ||
231 | Baz: t v | ||
232 | Foo: t | ||
233 | ", | 233 | ", |
234 | crate_graph! { | ||
235 | "main": ("/main.rs", ["test_crate"]), | ||
236 | "test_crate": ("/lib.rs", []), | ||
237 | }, | ||
234 | ); | 238 | ); |
239 | assert_snapshot_matches!(map, @r###" | ||
240 | crate | ||
241 | Bar: t v | ||
242 | Baz: t v | ||
243 | "###); | ||
235 | } | 244 | } |
236 | 245 | ||
237 | #[test] | 246 | #[test] |
238 | fn glob_across_crates() { | 247 | fn can_import_enum_variant() { |
239 | covers!(glob_across_crates); | 248 | covers!(can_import_enum_variant); |
240 | let mut db = MockDatabase::with_files( | 249 | let map = def_map( |
241 | " | 250 | " |
242 | //- /main.rs | ||
243 | use test_crate::*; | ||
244 | |||
245 | //- /lib.rs | 251 | //- /lib.rs |
246 | pub struct Baz; | 252 | enum E { V } |
253 | use self::E::V; | ||
247 | ", | 254 | ", |
248 | ); | 255 | ); |
249 | db.set_crate_graph_from_fixture(crate_graph! { | 256 | assert_snapshot_matches!(map, @r###" |
250 | "main": ("/main.rs", ["test_crate"]), | 257 | crate |
251 | "test_crate": ("/lib.rs", []), | 258 | V: t v |
252 | }); | 259 | E: t |
253 | let main_id = db.file_id_of("/main.rs"); | 260 | "### |
254 | |||
255 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
256 | let krate = module.krate(&db).unwrap(); | ||
257 | let item_map = db.item_map(krate); | ||
258 | |||
259 | check_module_item_map( | ||
260 | &item_map, | ||
261 | module.module_id, | ||
262 | " | ||
263 | Baz: t v | ||
264 | ", | ||
265 | ); | 261 | ); |
266 | } | 262 | } |
267 | 263 | ||
268 | #[test] | 264 | #[test] |
269 | fn edition_2015_imports() { | 265 | fn edition_2015_imports() { |
270 | let mut db = MockDatabase::with_files( | 266 | let map = def_map_with_crate_graph( |
271 | " | 267 | " |
272 | //- /main.rs | 268 | //- /main.rs |
273 | mod foo; | 269 | mod foo; |
@@ -282,31 +278,32 @@ fn edition_2015_imports() { | |||
282 | 278 | ||
283 | //- /lib.rs | 279 | //- /lib.rs |
284 | struct FromLib; | 280 | struct FromLib; |
285 | ", | ||
286 | ); | ||
287 | db.set_crate_graph_from_fixture(crate_graph! { | ||
288 | "main": ("/main.rs", "2015", ["other_crate"]), | ||
289 | "other_crate": ("/lib.rs", "2018", []), | ||
290 | }); | ||
291 | let foo_id = db.file_id_of("/foo.rs"); | ||
292 | |||
293 | let module = crate::source_binder::module_from_file_id(&db, foo_id).unwrap(); | ||
294 | let krate = module.krate(&db).unwrap(); | ||
295 | let item_map = db.item_map(krate); | ||
296 | |||
297 | check_module_item_map( | ||
298 | &item_map, | ||
299 | module.module_id, | ||
300 | " | ||
301 | Bar: t v | ||
302 | FromLib: t v | ||
303 | ", | 281 | ", |
282 | crate_graph! { | ||
283 | "main": ("/main.rs", "2015", ["other_crate"]), | ||
284 | "other_crate": ("/lib.rs", "2018", []), | ||
285 | }, | ||
286 | ); | ||
287 | |||
288 | assert_snapshot_matches!(map, | ||
289 | @r###" | ||
290 | crate | ||
291 | bar: t | ||
292 | foo: t | ||
293 | |||
294 | crate::bar | ||
295 | Bar: t v | ||
296 | |||
297 | crate::foo | ||
298 | FromLib: t v | ||
299 | Bar: t v | ||
300 | "### | ||
304 | ); | 301 | ); |
305 | } | 302 | } |
306 | 303 | ||
307 | #[test] | 304 | #[test] |
308 | fn module_resolution_works_for_non_standard_filenames() { | 305 | fn module_resolution_works_for_non_standard_filenames() { |
309 | let mut db = MockDatabase::with_files( | 306 | let map = def_map_with_crate_graph( |
310 | " | 307 | " |
311 | //- /my_library.rs | 308 | //- /my_library.rs |
312 | mod foo; | 309 | mod foo; |
@@ -315,73 +312,32 @@ fn module_resolution_works_for_non_standard_filenames() { | |||
315 | //- /foo/mod.rs | 312 | //- /foo/mod.rs |
316 | pub struct Bar; | 313 | pub struct Bar; |
317 | ", | 314 | ", |
315 | crate_graph! { | ||
316 | "my_library": ("/my_library.rs", []), | ||
317 | }, | ||
318 | ); | 318 | ); |
319 | db.set_crate_graph_from_fixture(crate_graph! { | ||
320 | "my_library": ("/my_library.rs", []), | ||
321 | }); | ||
322 | let file_id = db.file_id_of("/my_library.rs"); | ||
323 | |||
324 | let module = crate::source_binder::module_from_file_id(&db, file_id).unwrap(); | ||
325 | let krate = module.krate(&db).unwrap(); | ||
326 | let module_id = module.module_id; | ||
327 | let item_map = db.item_map(krate); | ||
328 | check_module_item_map( | ||
329 | &item_map, | ||
330 | module_id, | ||
331 | " | ||
332 | Bar: t v | ||
333 | foo: t | ||
334 | ", | ||
335 | ); | ||
336 | } | ||
337 | 319 | ||
338 | #[test] | 320 | assert_snapshot_matches!(map, |
339 | fn std_prelude() { | 321 | @r###" |
340 | covers!(std_prelude); | 322 | crate |
341 | let mut db = MockDatabase::with_files( | 323 | Bar: t v |
342 | " | 324 | foo: t |
343 | //- /main.rs | ||
344 | use Foo::*; | ||
345 | |||
346 | //- /lib.rs | ||
347 | mod prelude; | ||
348 | #[prelude_import] | ||
349 | use prelude::*; | ||
350 | 325 | ||
351 | //- /prelude.rs | 326 | crate::foo |
352 | pub enum Foo { Bar, Baz }; | 327 | Bar: t v |
353 | ", | 328 | "### |
354 | ); | ||
355 | db.set_crate_graph_from_fixture(crate_graph! { | ||
356 | "main": ("/main.rs", ["test_crate"]), | ||
357 | "test_crate": ("/lib.rs", []), | ||
358 | }); | ||
359 | let main_id = db.file_id_of("/main.rs"); | ||
360 | |||
361 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
362 | let krate = module.krate(&db).unwrap(); | ||
363 | let item_map = db.item_map(krate); | ||
364 | |||
365 | check_module_item_map( | ||
366 | &item_map, | ||
367 | module.module_id, | ||
368 | " | ||
369 | Bar: t v | ||
370 | Baz: t v | ||
371 | ", | ||
372 | ); | 329 | ); |
373 | } | 330 | } |
374 | 331 | ||
375 | #[test] | 332 | #[test] |
376 | fn name_res_works_for_broken_modules() { | 333 | fn name_res_works_for_broken_modules() { |
377 | covers!(name_res_works_for_broken_modules); | 334 | covers!(name_res_works_for_broken_modules); |
378 | let (item_map, module_id) = item_map( | 335 | let map = def_map( |
379 | " | 336 | " |
380 | //- /lib.rs | 337 | //- /lib.rs |
381 | mod foo // no `;`, no body | 338 | mod foo // no `;`, no body |
382 | 339 | ||
383 | use self::foo::Baz; | 340 | use self::foo::Baz; |
384 | <|> | ||
385 | 341 | ||
386 | //- /foo/mod.rs | 342 | //- /foo/mod.rs |
387 | pub mod bar; | 343 | pub mod bar; |
@@ -390,65 +346,47 @@ fn name_res_works_for_broken_modules() { | |||
390 | 346 | ||
391 | //- /foo/bar.rs | 347 | //- /foo/bar.rs |
392 | pub struct Baz; | 348 | pub struct Baz; |
393 | ", | ||
394 | ); | ||
395 | check_module_item_map( | ||
396 | &item_map, | ||
397 | module_id, | ||
398 | " | ||
399 | Baz: _ | ||
400 | ", | 349 | ", |
401 | ); | 350 | ); |
402 | } | 351 | assert_snapshot_matches!(map, |
403 | 352 | @r###" | |
404 | #[test] | 353 | crate |
405 | fn item_map_using_self() { | 354 | Baz: _ |
406 | let (item_map, module_id) = item_map( | 355 | "### |
407 | " | ||
408 | //- /lib.rs | ||
409 | mod foo; | ||
410 | use crate::foo::bar::Baz::{self}; | ||
411 | <|> | ||
412 | //- /foo/mod.rs | ||
413 | pub mod bar; | ||
414 | //- /foo/bar.rs | ||
415 | pub struct Baz; | ||
416 | ", | ||
417 | ); | ||
418 | check_module_item_map( | ||
419 | &item_map, | ||
420 | module_id, | ||
421 | " | ||
422 | Baz: t v | ||
423 | foo: t | ||
424 | ", | ||
425 | ); | 356 | ); |
426 | } | 357 | } |
427 | 358 | ||
428 | #[test] | 359 | #[test] |
429 | fn item_map_enum_importing() { | 360 | fn item_map_using_self() { |
430 | covers!(item_map_enum_importing); | 361 | let map = def_map( |
431 | let (item_map, module_id) = item_map( | ||
432 | " | 362 | " |
433 | //- /lib.rs | 363 | //- /lib.rs |
434 | enum E { V } | 364 | mod foo; |
435 | use self::E::V; | 365 | use crate::foo::bar::Baz::{self}; |
436 | <|> | 366 | //- /foo/mod.rs |
367 | pub mod bar; | ||
368 | //- /foo/bar.rs | ||
369 | pub struct Baz; | ||
437 | ", | 370 | ", |
438 | ); | 371 | ); |
439 | check_module_item_map( | 372 | assert_snapshot_matches!(map, |
440 | &item_map, | 373 | @r###" |
441 | module_id, | 374 | crate |
442 | " | 375 | Baz: t v |
443 | E: t | 376 | foo: t |
444 | V: t v | 377 | |
445 | ", | 378 | crate::foo |
379 | bar: t | ||
380 | |||
381 | crate::foo::bar | ||
382 | Baz: t v | ||
383 | "### | ||
446 | ); | 384 | ); |
447 | } | 385 | } |
448 | 386 | ||
449 | #[test] | 387 | #[test] |
450 | fn item_map_across_crates() { | 388 | fn item_map_across_crates() { |
451 | let mut db = MockDatabase::with_files( | 389 | let map = def_map_with_crate_graph( |
452 | " | 390 | " |
453 | //- /main.rs | 391 | //- /main.rs |
454 | use test_crate::Baz; | 392 | use test_crate::Baz; |
@@ -456,29 +394,23 @@ fn item_map_across_crates() { | |||
456 | //- /lib.rs | 394 | //- /lib.rs |
457 | pub struct Baz; | 395 | pub struct Baz; |
458 | ", | 396 | ", |
397 | crate_graph! { | ||
398 | "main": ("/main.rs", ["test_crate"]), | ||
399 | "test_crate": ("/lib.rs", []), | ||
400 | }, | ||
459 | ); | 401 | ); |
460 | db.set_crate_graph_from_fixture(crate_graph! { | 402 | |
461 | "main": ("/main.rs", ["test_crate"]), | 403 | assert_snapshot_matches!(map, |
462 | "test_crate": ("/lib.rs", []), | 404 | @r###" |
463 | }); | 405 | crate |
464 | let main_id = db.file_id_of("/main.rs"); | 406 | Baz: t v |
465 | 407 | "### | |
466 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
467 | let krate = module.krate(&db).unwrap(); | ||
468 | let item_map = db.item_map(krate); | ||
469 | |||
470 | check_module_item_map( | ||
471 | &item_map, | ||
472 | module.module_id, | ||
473 | " | ||
474 | Baz: t v | ||
475 | ", | ||
476 | ); | 408 | ); |
477 | } | 409 | } |
478 | 410 | ||
479 | #[test] | 411 | #[test] |
480 | fn extern_crate_rename() { | 412 | fn extern_crate_rename() { |
481 | let mut db = MockDatabase::with_files( | 413 | let map = def_map_with_crate_graph( |
482 | " | 414 | " |
483 | //- /main.rs | 415 | //- /main.rs |
484 | extern crate alloc as alloc_crate; | 416 | extern crate alloc as alloc_crate; |
@@ -492,29 +424,23 @@ fn extern_crate_rename() { | |||
492 | //- /lib.rs | 424 | //- /lib.rs |
493 | struct Arc; | 425 | struct Arc; |
494 | ", | 426 | ", |
427 | crate_graph! { | ||
428 | "main": ("/main.rs", ["alloc"]), | ||
429 | "alloc": ("/lib.rs", []), | ||
430 | }, | ||
495 | ); | 431 | ); |
496 | db.set_crate_graph_from_fixture(crate_graph! { | 432 | |
497 | "main": ("/main.rs", ["alloc"]), | 433 | assert_snapshot_matches!(map, |
498 | "alloc": ("/lib.rs", []), | 434 | @r###" |
499 | }); | 435 | crate |
500 | let sync_id = db.file_id_of("/sync.rs"); | 436 | Arc: t v |
501 | 437 | "### | |
502 | let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap(); | ||
503 | let krate = module.krate(&db).unwrap(); | ||
504 | let item_map = db.item_map(krate); | ||
505 | |||
506 | check_module_item_map( | ||
507 | &item_map, | ||
508 | module.module_id, | ||
509 | " | ||
510 | Arc: t v | ||
511 | ", | ||
512 | ); | 438 | ); |
513 | } | 439 | } |
514 | 440 | ||
515 | #[test] | 441 | #[test] |
516 | fn extern_crate_rename_2015_edition() { | 442 | fn extern_crate_rename_2015_edition() { |
517 | let mut db = MockDatabase::with_files( | 443 | let map = def_map_with_crate_graph( |
518 | " | 444 | " |
519 | //- /main.rs | 445 | //- /main.rs |
520 | extern crate alloc as alloc_crate; | 446 | extern crate alloc as alloc_crate; |
@@ -528,29 +454,23 @@ fn extern_crate_rename_2015_edition() { | |||
528 | //- /lib.rs | 454 | //- /lib.rs |
529 | struct Arc; | 455 | struct Arc; |
530 | ", | 456 | ", |
457 | crate_graph! { | ||
458 | "main": ("/main.rs", "2015", ["alloc"]), | ||
459 | "alloc": ("/lib.rs", []), | ||
460 | }, | ||
531 | ); | 461 | ); |
532 | db.set_crate_graph_from_fixture(crate_graph! { | 462 | |
533 | "main": ("/main.rs", "2015", ["alloc"]), | 463 | assert_snapshot_matches!(map, |
534 | "alloc": ("/lib.rs", []), | 464 | @r###" |
535 | }); | 465 | crate |
536 | let sync_id = db.file_id_of("/sync.rs"); | 466 | Arc: t v |
537 | 467 | "### | |
538 | let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap(); | ||
539 | let krate = module.krate(&db).unwrap(); | ||
540 | let item_map = db.item_map(krate); | ||
541 | |||
542 | check_module_item_map( | ||
543 | &item_map, | ||
544 | module.module_id, | ||
545 | " | ||
546 | Arc: t v | ||
547 | ", | ||
548 | ); | 468 | ); |
549 | } | 469 | } |
550 | 470 | ||
551 | #[test] | 471 | #[test] |
552 | fn import_across_source_roots() { | 472 | fn import_across_source_roots() { |
553 | let mut db = MockDatabase::with_files( | 473 | let map = def_map_with_crate_graph( |
554 | " | 474 | " |
555 | //- /lib.rs | 475 | //- /lib.rs |
556 | pub mod a { | 476 | pub mod a { |
@@ -564,29 +484,23 @@ fn import_across_source_roots() { | |||
564 | //- /main/main.rs | 484 | //- /main/main.rs |
565 | use test_crate::a::b::C; | 485 | use test_crate::a::b::C; |
566 | ", | 486 | ", |
487 | crate_graph! { | ||
488 | "main": ("/main/main.rs", ["test_crate"]), | ||
489 | "test_crate": ("/lib.rs", []), | ||
490 | }, | ||
567 | ); | 491 | ); |
568 | db.set_crate_graph_from_fixture(crate_graph! { | 492 | |
569 | "main": ("/main/main.rs", ["test_crate"]), | 493 | assert_snapshot_matches!(map, |
570 | "test_crate": ("/lib.rs", []), | 494 | @r###" |
571 | }); | 495 | crate |
572 | let main_id = db.file_id_of("/main/main.rs"); | 496 | C: t v |
573 | 497 | "### | |
574 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
575 | let krate = module.krate(&db).unwrap(); | ||
576 | let item_map = db.item_map(krate); | ||
577 | |||
578 | check_module_item_map( | ||
579 | &item_map, | ||
580 | module.module_id, | ||
581 | " | ||
582 | C: t v | ||
583 | ", | ||
584 | ); | 498 | ); |
585 | } | 499 | } |
586 | 500 | ||
587 | #[test] | 501 | #[test] |
588 | fn reexport_across_crates() { | 502 | fn reexport_across_crates() { |
589 | let mut db = MockDatabase::with_files( | 503 | let map = def_map_with_crate_graph( |
590 | " | 504 | " |
591 | //- /main.rs | 505 | //- /main.rs |
592 | use test_crate::Baz; | 506 | use test_crate::Baz; |
@@ -599,29 +513,23 @@ fn reexport_across_crates() { | |||
599 | //- /foo.rs | 513 | //- /foo.rs |
600 | pub struct Baz; | 514 | pub struct Baz; |
601 | ", | 515 | ", |
516 | crate_graph! { | ||
517 | "main": ("/main.rs", ["test_crate"]), | ||
518 | "test_crate": ("/lib.rs", []), | ||
519 | }, | ||
602 | ); | 520 | ); |
603 | db.set_crate_graph_from_fixture(crate_graph! { | 521 | |
604 | "main": ("/main.rs", ["test_crate"]), | 522 | assert_snapshot_matches!(map, |
605 | "test_crate": ("/lib.rs", []), | 523 | @r###" |
606 | }); | 524 | crate |
607 | let main_id = db.file_id_of("/main.rs"); | 525 | Baz: t v |
608 | 526 | "### | |
609 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
610 | let krate = module.krate(&db).unwrap(); | ||
611 | let item_map = db.item_map(krate); | ||
612 | |||
613 | check_module_item_map( | ||
614 | &item_map, | ||
615 | module.module_id, | ||
616 | " | ||
617 | Baz: t v | ||
618 | ", | ||
619 | ); | 527 | ); |
620 | } | 528 | } |
621 | 529 | ||
622 | #[test] | 530 | #[test] |
623 | fn values_dont_shadow_extern_crates() { | 531 | fn values_dont_shadow_extern_crates() { |
624 | let mut db = MockDatabase::with_files( | 532 | let map = def_map_with_crate_graph( |
625 | " | 533 | " |
626 | //- /main.rs | 534 | //- /main.rs |
627 | fn foo() {} | 535 | fn foo() {} |
@@ -630,139 +538,17 @@ fn values_dont_shadow_extern_crates() { | |||
630 | //- /foo/lib.rs | 538 | //- /foo/lib.rs |
631 | pub struct Bar; | 539 | pub struct Bar; |
632 | ", | 540 | ", |
541 | crate_graph! { | ||
542 | "main": ("/main.rs", ["foo"]), | ||
543 | "foo": ("/foo/lib.rs", []), | ||
544 | }, | ||
633 | ); | 545 | ); |
634 | db.set_crate_graph_from_fixture(crate_graph! { | ||
635 | "main": ("/main.rs", ["foo"]), | ||
636 | "foo": ("/foo/lib.rs", []), | ||
637 | }); | ||
638 | let main_id = db.file_id_of("/main.rs"); | ||
639 | |||
640 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
641 | let krate = module.krate(&db).unwrap(); | ||
642 | let item_map = db.item_map(krate); | ||
643 | |||
644 | check_module_item_map( | ||
645 | &item_map, | ||
646 | module.module_id, | ||
647 | " | ||
648 | Bar: t v | ||
649 | foo: v | ||
650 | ", | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) { | ||
655 | let (mut db, pos) = MockDatabase::with_position(initial); | ||
656 | let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap(); | ||
657 | let krate = module.krate(&db).unwrap(); | ||
658 | { | ||
659 | let events = db.log_executed(|| { | ||
660 | db.item_map(krate); | ||
661 | }); | ||
662 | assert!(format!("{:?}", events).contains("item_map")) | ||
663 | } | ||
664 | db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); | ||
665 | |||
666 | { | ||
667 | let events = db.log_executed(|| { | ||
668 | db.item_map(krate); | ||
669 | }); | ||
670 | assert!(!format!("{:?}", events).contains("item_map"), "{:#?}", events) | ||
671 | } | ||
672 | } | ||
673 | |||
674 | #[test] | ||
675 | fn typing_inside_a_function_should_not_invalidate_item_map() { | ||
676 | check_item_map_is_not_recomputed( | ||
677 | " | ||
678 | //- /lib.rs | ||
679 | mod foo;<|> | ||
680 | |||
681 | use crate::foo::bar::Baz; | ||
682 | |||
683 | fn foo() -> i32 { | ||
684 | 1 + 1 | ||
685 | } | ||
686 | //- /foo/mod.rs | ||
687 | pub mod bar; | ||
688 | |||
689 | //- /foo/bar.rs | ||
690 | pub struct Baz; | ||
691 | ", | ||
692 | " | ||
693 | mod foo; | ||
694 | |||
695 | use crate::foo::bar::Baz; | ||
696 | |||
697 | fn foo() -> i32 { 92 } | ||
698 | ", | ||
699 | ); | ||
700 | } | ||
701 | |||
702 | #[test] | ||
703 | fn adding_inner_items_should_not_invalidate_item_map() { | ||
704 | check_item_map_is_not_recomputed( | ||
705 | " | ||
706 | //- /lib.rs | ||
707 | struct S { a: i32} | ||
708 | enum E { A } | ||
709 | trait T { | ||
710 | fn a() {} | ||
711 | } | ||
712 | mod foo;<|> | ||
713 | impl S { | ||
714 | fn a() {} | ||
715 | } | ||
716 | use crate::foo::bar::Baz; | ||
717 | //- /foo/mod.rs | ||
718 | pub mod bar; | ||
719 | |||
720 | //- /foo/bar.rs | ||
721 | pub struct Baz; | ||
722 | ", | ||
723 | " | ||
724 | struct S { a: i32, b: () } | ||
725 | enum E { A, B } | ||
726 | trait T { | ||
727 | fn a() {} | ||
728 | fn b() {} | ||
729 | } | ||
730 | mod foo;<|> | ||
731 | impl S { | ||
732 | fn a() {} | ||
733 | fn b() {} | ||
734 | } | ||
735 | use crate::foo::bar::Baz; | ||
736 | ", | ||
737 | ); | ||
738 | } | ||
739 | 546 | ||
740 | #[test] | 547 | assert_snapshot_matches!(map, |
741 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() { | 548 | @r###" |
742 | check_item_map_is_not_recomputed( | 549 | crate |
743 | " | 550 | Bar: t v |
744 | //- /lib.rs | 551 | foo: v |
745 | mod foo; | 552 | "### |
746 | |||
747 | use crate::foo::bar::Baz; | ||
748 | |||
749 | //- /foo/mod.rs | ||
750 | pub mod bar; | ||
751 | |||
752 | //- /foo/bar.rs | ||
753 | <|> | ||
754 | salsa::query_group! { | ||
755 | trait Baz { | ||
756 | fn foo() -> i32 { 1 + 1 } | ||
757 | } | ||
758 | } | ||
759 | ", | ||
760 | " | ||
761 | salsa::query_group! { | ||
762 | trait Baz { | ||
763 | fn foo() -> i32 { 92 } | ||
764 | } | ||
765 | } | ||
766 | ", | ||
767 | ); | 553 | ); |
768 | } | 554 | } |
diff --git a/crates/ra_hir/src/nameres/tests/globs.rs b/crates/ra_hir/src/nameres/tests/globs.rs new file mode 100644 index 000000000..6e50c7ff6 --- /dev/null +++ b/crates/ra_hir/src/nameres/tests/globs.rs | |||
@@ -0,0 +1,118 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn glob_1() { | ||
5 | let map = def_map( | ||
6 | " | ||
7 | //- /lib.rs | ||
8 | mod foo; | ||
9 | use foo::*; | ||
10 | |||
11 | //- /foo/mod.rs | ||
12 | pub mod bar; | ||
13 | pub use self::bar::Baz; | ||
14 | pub struct Foo; | ||
15 | |||
16 | //- /foo/bar.rs | ||
17 | pub struct Baz; | ||
18 | ", | ||
19 | ); | ||
20 | assert_snapshot_matches!(map, @r###" | ||
21 | crate | ||
22 | bar: t | ||
23 | Foo: t v | ||
24 | Baz: t v | ||
25 | foo: t | ||
26 | |||
27 | crate::foo | ||
28 | bar: t | ||
29 | Foo: t v | ||
30 | Baz: t v | ||
31 | |||
32 | crate::foo::bar | ||
33 | Baz: t v | ||
34 | "### | ||
35 | ); | ||
36 | } | ||
37 | |||
38 | #[test] | ||
39 | fn glob_2() { | ||
40 | let map = def_map( | ||
41 | " | ||
42 | //- /lib.rs | ||
43 | mod foo; | ||
44 | use foo::*; | ||
45 | |||
46 | //- /foo/mod.rs | ||
47 | pub mod bar; | ||
48 | pub use self::bar::*; | ||
49 | pub struct Foo; | ||
50 | |||
51 | //- /foo/bar.rs | ||
52 | pub struct Baz; | ||
53 | pub use super::*; | ||
54 | ", | ||
55 | ); | ||
56 | assert_snapshot_matches!(map, @r###" | ||
57 | crate | ||
58 | bar: t | ||
59 | Foo: t v | ||
60 | Baz: t v | ||
61 | foo: t | ||
62 | |||
63 | crate::foo | ||
64 | bar: t | ||
65 | Foo: t v | ||
66 | Baz: t v | ||
67 | |||
68 | crate::foo::bar | ||
69 | bar: t | ||
70 | Foo: t v | ||
71 | Baz: t v | ||
72 | "### | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | #[test] | ||
77 | fn glob_across_crates() { | ||
78 | covers!(glob_across_crates); | ||
79 | let map = def_map_with_crate_graph( | ||
80 | " | ||
81 | //- /main.rs | ||
82 | use test_crate::*; | ||
83 | |||
84 | //- /lib.rs | ||
85 | pub struct Baz; | ||
86 | ", | ||
87 | crate_graph! { | ||
88 | "main": ("/main.rs", ["test_crate"]), | ||
89 | "test_crate": ("/lib.rs", []), | ||
90 | }, | ||
91 | ); | ||
92 | assert_snapshot_matches!(map, @r###" | ||
93 | crate | ||
94 | Baz: t v | ||
95 | "### | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn glob_enum() { | ||
101 | covers!(glob_enum); | ||
102 | let map = def_map( | ||
103 | " | ||
104 | //- /lib.rs | ||
105 | enum Foo { | ||
106 | Bar, Baz | ||
107 | } | ||
108 | use self::Foo::*; | ||
109 | ", | ||
110 | ); | ||
111 | assert_snapshot_matches!(map, @r###" | ||
112 | crate | ||
113 | Foo: t | ||
114 | Bar: t v | ||
115 | Baz: t v | ||
116 | "### | ||
117 | ); | ||
118 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs new file mode 100644 index 000000000..698781923 --- /dev/null +++ b/crates/ra_hir/src/nameres/tests/incremental.rs | |||
@@ -0,0 +1,123 @@ | |||
1 | use super::*; | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use ra_db::SourceDatabase; | ||
6 | |||
7 | fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) { | ||
8 | let (mut db, pos) = MockDatabase::with_position(initial); | ||
9 | let crate_id = db.crate_graph().iter().next().unwrap(); | ||
10 | let krate = Crate { crate_id }; | ||
11 | { | ||
12 | let events = db.log_executed(|| { | ||
13 | db.crate_def_map(krate); | ||
14 | }); | ||
15 | assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | ||
16 | } | ||
17 | db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); | ||
18 | |||
19 | { | ||
20 | let events = db.log_executed(|| { | ||
21 | db.crate_def_map(krate); | ||
22 | }); | ||
23 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | ||
24 | } | ||
25 | } | ||
26 | |||
27 | #[test] | ||
28 | fn typing_inside_a_function_should_not_invalidate_def_map() { | ||
29 | check_def_map_is_not_recomputed( | ||
30 | " | ||
31 | //- /lib.rs | ||
32 | mod foo;<|> | ||
33 | |||
34 | use crate::foo::bar::Baz; | ||
35 | |||
36 | fn foo() -> i32 { | ||
37 | 1 + 1 | ||
38 | } | ||
39 | //- /foo/mod.rs | ||
40 | pub mod bar; | ||
41 | |||
42 | //- /foo/bar.rs | ||
43 | pub struct Baz; | ||
44 | ", | ||
45 | " | ||
46 | mod foo; | ||
47 | |||
48 | use crate::foo::bar::Baz; | ||
49 | |||
50 | fn foo() -> i32 { 92 } | ||
51 | ", | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn adding_inner_items_should_not_invalidate_def_map() { | ||
57 | check_def_map_is_not_recomputed( | ||
58 | " | ||
59 | //- /lib.rs | ||
60 | struct S { a: i32} | ||
61 | enum E { A } | ||
62 | trait T { | ||
63 | fn a() {} | ||
64 | } | ||
65 | mod foo;<|> | ||
66 | impl S { | ||
67 | fn a() {} | ||
68 | } | ||
69 | use crate::foo::bar::Baz; | ||
70 | //- /foo/mod.rs | ||
71 | pub mod bar; | ||
72 | |||
73 | //- /foo/bar.rs | ||
74 | pub struct Baz; | ||
75 | ", | ||
76 | " | ||
77 | struct S { a: i32, b: () } | ||
78 | enum E { A, B } | ||
79 | trait T { | ||
80 | fn a() {} | ||
81 | fn b() {} | ||
82 | } | ||
83 | mod foo;<|> | ||
84 | impl S { | ||
85 | fn a() {} | ||
86 | fn b() {} | ||
87 | } | ||
88 | use crate::foo::bar::Baz; | ||
89 | ", | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | // It would be awesome to make this work, but it's unclear how | ||
94 | #[test] | ||
95 | #[ignore] | ||
96 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_def_map() { | ||
97 | check_def_map_is_not_recomputed( | ||
98 | " | ||
99 | //- /lib.rs | ||
100 | mod foo; | ||
101 | |||
102 | use crate::foo::bar::Baz; | ||
103 | |||
104 | //- /foo/mod.rs | ||
105 | pub mod bar; | ||
106 | |||
107 | //- /foo/bar.rs | ||
108 | <|> | ||
109 | salsa::query_group! { | ||
110 | trait Baz { | ||
111 | fn foo() -> i32 { 1 + 1 } | ||
112 | } | ||
113 | } | ||
114 | ", | ||
115 | " | ||
116 | salsa::query_group! { | ||
117 | trait Baz { | ||
118 | fn foo() -> i32 { 92 } | ||
119 | } | ||
120 | } | ||
121 | ", | ||
122 | ); | ||
123 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs new file mode 100644 index 000000000..8781b026b --- /dev/null +++ b/crates/ra_hir/src/nameres/tests/macros.rs | |||
@@ -0,0 +1,94 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn macro_rules_are_globally_visible() { | ||
5 | let map = def_map( | ||
6 | " | ||
7 | //- /lib.rs | ||
8 | macro_rules! structs { | ||
9 | ($($i:ident),*) => { | ||
10 | $(struct $i { field: u32 } )* | ||
11 | } | ||
12 | } | ||
13 | structs!(Foo); | ||
14 | mod nested; | ||
15 | |||
16 | //- /nested.rs | ||
17 | structs!(Bar, Baz); | ||
18 | ", | ||
19 | ); | ||
20 | assert_snapshot_matches!(map, @r###" | ||
21 | crate | ||
22 | nested: t | ||
23 | Foo: t v | ||
24 | |||
25 | crate::nested | ||
26 | Bar: t v | ||
27 | Baz: t v | ||
28 | "###); | ||
29 | } | ||
30 | |||
31 | #[test] | ||
32 | fn macro_rules_can_define_modules() { | ||
33 | let map = def_map( | ||
34 | " | ||
35 | //- /lib.rs | ||
36 | macro_rules! m { | ||
37 | ($name:ident) => { mod $name; } | ||
38 | } | ||
39 | m!(n1); | ||
40 | |||
41 | //- /n1.rs | ||
42 | m!(n2) | ||
43 | //- /n1/n2.rs | ||
44 | struct X; | ||
45 | ", | ||
46 | ); | ||
47 | assert_snapshot_matches!(map, @r###" | ||
48 | crate | ||
49 | n1: t | ||
50 | |||
51 | crate::n1 | ||
52 | n2: t | ||
53 | |||
54 | crate::n1::n2 | ||
55 | X: t v | ||
56 | "###); | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn macro_rules_from_other_crates_are_visible() { | ||
61 | let map = def_map_with_crate_graph( | ||
62 | " | ||
63 | //- /main.rs | ||
64 | foo::structs!(Foo, Bar) | ||
65 | mod bar; | ||
66 | |||
67 | //- /bar.rs | ||
68 | use crate::*; | ||
69 | |||
70 | //- /lib.rs | ||
71 | #[macro_export] | ||
72 | macro_rules! structs { | ||
73 | ($($i:ident),*) => { | ||
74 | $(struct $i { field: u32 } )* | ||
75 | } | ||
76 | } | ||
77 | ", | ||
78 | crate_graph! { | ||
79 | "main": ("/main.rs", ["foo"]), | ||
80 | "foo": ("/lib.rs", []), | ||
81 | }, | ||
82 | ); | ||
83 | assert_snapshot_matches!(map, @r###" | ||
84 | crate | ||
85 | bar: t | ||
86 | Foo: t v | ||
87 | Bar: t v | ||
88 | |||
89 | crate::bar | ||
90 | bar: t | ||
91 | Foo: t v | ||
92 | Bar: t v | ||
93 | "###); | ||
94 | } | ||