diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-10 20:15:41 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-10 20:15:41 +0000 |
commit | ffd407affff967884fd51a977335469e819c7194 (patch) | |
tree | de0f95ec9e49ee97148047622e333353f24c1433 /crates/ra_hir | |
parent | b952c270ee87cc36500b3bb03bab9268f2184d1a (diff) | |
parent | 2f24e740db3365afac56aad3e8a533340369ef7d (diff) |
Merge #778
778: Glob imports r=matklad a=flodiebold
This implements glob imports, completing #231 :)
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/marks.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 127 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 120 |
4 files changed, 240 insertions, 13 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 19f103855..94a08aa63 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -327,7 +327,7 @@ impl Enum { | |||
327 | db.enum_data(*self).name.clone() | 327 | db.enum_data(*self).name.clone() |
328 | } | 328 | } |
329 | 329 | ||
330 | pub fn variants(&self, db: &impl HirDatabase) -> Vec<EnumVariant> { | 330 | pub fn variants(&self, db: &impl PersistentHirDatabase) -> Vec<EnumVariant> { |
331 | db.enum_data(*self) | 331 | db.enum_data(*self) |
332 | .variants | 332 | .variants |
333 | .iter() | 333 | .iter() |
@@ -389,7 +389,7 @@ impl EnumVariant { | |||
389 | self.parent | 389 | self.parent |
390 | } | 390 | } |
391 | 391 | ||
392 | pub fn name(&self, db: &impl HirDatabase) -> Option<Name> { | 392 | pub fn name(&self, db: &impl PersistentHirDatabase) -> Option<Name> { |
393 | db.enum_data(self.parent).variants[self.id].name.clone() | 393 | db.enum_data(self.parent).variants[self.id].name.clone() |
394 | } | 394 | } |
395 | 395 | ||
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index aba0c9968..50d4e824c 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs | |||
@@ -4,4 +4,6 @@ test_utils::marks!( | |||
4 | type_var_cycles_resolve_completely | 4 | type_var_cycles_resolve_completely |
5 | type_var_cycles_resolve_as_possible | 5 | type_var_cycles_resolve_as_possible |
6 | type_var_resolves_to_int_var | 6 | type_var_resolves_to_int_var |
7 | glob_enum | ||
8 | glob_across_crates | ||
7 | ); | 9 | ); |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index b7382d9c3..94f7db024 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -61,7 +61,7 @@ impl ModuleScope { | |||
61 | 61 | ||
62 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like | 62 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like |
63 | /// multiple namespaces, ambiguity and errors. | 63 | /// multiple namespaces, ambiguity and errors. |
64 | #[derive(Debug, Clone, PartialEq, Eq)] | 64 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
65 | pub struct Resolution { | 65 | pub struct Resolution { |
66 | /// None for unresolved | 66 | /// None for unresolved |
67 | pub def: PerNs<ModuleDef>, | 67 | pub def: PerNs<ModuleDef>, |
@@ -154,6 +154,8 @@ struct Resolver<'a, DB> { | |||
154 | krate: Crate, | 154 | krate: Crate, |
155 | module_tree: Arc<ModuleTree>, | 155 | module_tree: Arc<ModuleTree>, |
156 | processed_imports: FxHashSet<(ModuleId, ImportId)>, | 156 | processed_imports: FxHashSet<(ModuleId, ImportId)>, |
157 | /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import) | ||
158 | glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>, | ||
157 | result: ItemMap, | 159 | result: ItemMap, |
158 | } | 160 | } |
159 | 161 | ||
@@ -173,6 +175,7 @@ where | |||
173 | krate, | 175 | krate, |
174 | module_tree, | 176 | module_tree, |
175 | processed_imports: FxHashSet::default(), | 177 | processed_imports: FxHashSet::default(), |
178 | glob_imports: FxHashMap::default(), | ||
176 | result: ItemMap::default(), | 179 | result: ItemMap::default(), |
177 | } | 180 | } |
178 | } | 181 | } |
@@ -264,14 +267,72 @@ where | |||
264 | import: &ImportData, | 267 | import: &ImportData, |
265 | ) -> ReachedFixedPoint { | 268 | ) -> ReachedFixedPoint { |
266 | log::debug!("resolving import: {:?}", import); | 269 | log::debug!("resolving import: {:?}", import); |
267 | if import.is_glob { | ||
268 | return ReachedFixedPoint::Yes; | ||
269 | }; | ||
270 | let original_module = Module { krate: self.krate, module_id }; | 270 | let original_module = Module { krate: self.krate, module_id }; |
271 | let (def, reached_fixedpoint) = | 271 | let (def, reached_fixedpoint) = |
272 | self.result.resolve_path_fp(self.db, original_module, &import.path); | 272 | self.result.resolve_path_fp(self.db, original_module, &import.path); |
273 | 273 | ||
274 | if reached_fixedpoint == ReachedFixedPoint::Yes { | 274 | if reached_fixedpoint != ReachedFixedPoint::Yes { |
275 | return reached_fixedpoint; | ||
276 | } | ||
277 | |||
278 | if import.is_glob { | ||
279 | log::debug!("glob import: {:?}", import); | ||
280 | match def.take_types() { | ||
281 | Some(ModuleDef::Module(m)) => { | ||
282 | if m.krate != self.krate { | ||
283 | tested_by!(glob_across_crates); | ||
284 | // glob import from other crate => we can just import everything once | ||
285 | let item_map = self.db.item_map(m.krate); | ||
286 | let scope = &item_map[m.module_id]; | ||
287 | let items = scope | ||
288 | .items | ||
289 | .iter() | ||
290 | .map(|(name, res)| (name.clone(), res.clone())) | ||
291 | .collect::<Vec<_>>(); | ||
292 | self.update(module_id, Some(import_id), &items); | ||
293 | } else { | ||
294 | // glob import from same crate => we do an initial | ||
295 | // import, and then need to propagate any further | ||
296 | // additions | ||
297 | let scope = &self.result[m.module_id]; | ||
298 | let items = scope | ||
299 | .items | ||
300 | .iter() | ||
301 | .map(|(name, res)| (name.clone(), res.clone())) | ||
302 | .collect::<Vec<_>>(); | ||
303 | self.update(module_id, Some(import_id), &items); | ||
304 | // record the glob import in case we add further items | ||
305 | self.glob_imports | ||
306 | .entry(m.module_id) | ||
307 | .or_default() | ||
308 | .push((module_id, import_id)); | ||
309 | } | ||
310 | } | ||
311 | Some(ModuleDef::Enum(e)) => { | ||
312 | tested_by!(glob_enum); | ||
313 | // glob import from enum => just import all the variants | ||
314 | let variants = e.variants(self.db); | ||
315 | let resolutions = variants | ||
316 | .into_iter() | ||
317 | .filter_map(|variant| { | ||
318 | let res = Resolution { | ||
319 | def: PerNs::both(variant.into(), e.into()), | ||
320 | import: Some(import_id), | ||
321 | }; | ||
322 | let name = variant.name(self.db)?; | ||
323 | Some((name, res)) | ||
324 | }) | ||
325 | .collect::<Vec<_>>(); | ||
326 | self.update(module_id, Some(import_id), &resolutions); | ||
327 | } | ||
328 | Some(d) => { | ||
329 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
330 | } | ||
331 | None => { | ||
332 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
333 | } | ||
334 | } | ||
335 | } else { | ||
275 | let last_segment = import.path.segments.last().unwrap(); | 336 | let last_segment = import.path.segments.last().unwrap(); |
276 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | 337 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); |
277 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | 338 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); |
@@ -284,17 +345,61 @@ where | |||
284 | } | 345 | } |
285 | } | 346 | } |
286 | } | 347 | } |
287 | self.update(module_id, |items| { | 348 | let resolution = Resolution { def, import: Some(import_id) }; |
288 | let res = Resolution { def, import: Some(import_id) }; | 349 | self.update(module_id, None, &[(name, resolution)]); |
289 | items.items.insert(name, res); | ||
290 | }); | ||
291 | } | 350 | } |
292 | reached_fixedpoint | 351 | reached_fixedpoint |
293 | } | 352 | } |
294 | 353 | ||
295 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | 354 | fn update( |
355 | &mut self, | ||
356 | module_id: ModuleId, | ||
357 | import: Option<ImportId>, | ||
358 | resolutions: &[(Name, Resolution)], | ||
359 | ) { | ||
360 | self.update_recursive(module_id, import, resolutions, 0) | ||
361 | } | ||
362 | |||
363 | fn update_recursive( | ||
364 | &mut self, | ||
365 | module_id: ModuleId, | ||
366 | import: Option<ImportId>, | ||
367 | resolutions: &[(Name, Resolution)], | ||
368 | depth: usize, | ||
369 | ) { | ||
370 | if depth > 100 { | ||
371 | // prevent stack overflows (but this shouldn't be possible) | ||
372 | panic!("infinite recursion in glob imports!"); | ||
373 | } | ||
296 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); | 374 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); |
297 | f(module_items) | 375 | let mut changed = false; |
376 | for (name, res) in resolutions { | ||
377 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
378 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
379 | existing.def.types = res.def.types; | ||
380 | existing.import = import.or(res.import); | ||
381 | changed = true; | ||
382 | } | ||
383 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
384 | existing.def.values = res.def.values; | ||
385 | existing.import = import.or(res.import); | ||
386 | changed = true; | ||
387 | } | ||
388 | } | ||
389 | if !changed { | ||
390 | return; | ||
391 | } | ||
392 | let glob_imports = self | ||
393 | .glob_imports | ||
394 | .get(&module_id) | ||
395 | .into_iter() | ||
396 | .flat_map(|v| v.iter()) | ||
397 | .cloned() | ||
398 | .collect::<Vec<_>>(); | ||
399 | for (glob_importing_module, glob_import) in glob_imports { | ||
400 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
401 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
402 | } | ||
298 | } | 403 | } |
299 | } | 404 | } |
300 | 405 | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 3dfad6bf2..9c0e4ef29 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -165,6 +165,126 @@ fn re_exports() { | |||
165 | } | 165 | } |
166 | 166 | ||
167 | #[test] | 167 | #[test] |
168 | fn glob_1() { | ||
169 | let (item_map, module_id) = item_map( | ||
170 | " | ||
171 | //- /lib.rs | ||
172 | mod foo; | ||
173 | use foo::*; | ||
174 | <|> | ||
175 | |||
176 | //- /foo/mod.rs | ||
177 | pub mod bar; | ||
178 | pub use self::bar::Baz; | ||
179 | pub struct Foo; | ||
180 | |||
181 | //- /foo/bar.rs | ||
182 | pub struct Baz; | ||
183 | ", | ||
184 | ); | ||
185 | check_module_item_map( | ||
186 | &item_map, | ||
187 | module_id, | ||
188 | " | ||
189 | Baz: t v | ||
190 | Foo: t v | ||
191 | bar: t | ||
192 | foo: t | ||
193 | ", | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn glob_2() { | ||
199 | let (item_map, module_id) = item_map( | ||
200 | " | ||
201 | //- /lib.rs | ||
202 | mod foo; | ||
203 | use foo::*; | ||
204 | <|> | ||
205 | |||
206 | //- /foo/mod.rs | ||
207 | pub mod bar; | ||
208 | pub use self::bar::*; | ||
209 | pub struct Foo; | ||
210 | |||
211 | //- /foo/bar.rs | ||
212 | pub struct Baz; | ||
213 | pub use super::*; | ||
214 | ", | ||
215 | ); | ||
216 | check_module_item_map( | ||
217 | &item_map, | ||
218 | module_id, | ||
219 | " | ||
220 | Baz: t v | ||
221 | Foo: t v | ||
222 | bar: t | ||
223 | foo: t | ||
224 | ", | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn glob_enum() { | ||
230 | covers!(glob_enum); | ||
231 | let (item_map, module_id) = item_map( | ||
232 | " | ||
233 | //- /lib.rs | ||
234 | enum Foo { | ||
235 | Bar, Baz | ||
236 | } | ||
237 | use self::Foo::*; | ||
238 | <|> | ||
239 | ", | ||
240 | ); | ||
241 | check_module_item_map( | ||
242 | &item_map, | ||
243 | module_id, | ||
244 | " | ||
245 | Bar: t v | ||
246 | Baz: t v | ||
247 | Foo: t | ||
248 | ", | ||
249 | ); | ||
250 | } | ||
251 | |||
252 | #[test] | ||
253 | fn glob_across_crates() { | ||
254 | covers!(glob_across_crates); | ||
255 | let (mut db, sr) = MockDatabase::with_files( | ||
256 | " | ||
257 | //- /main.rs | ||
258 | use test_crate::*; | ||
259 | |||
260 | //- /lib.rs | ||
261 | pub struct Baz; | ||
262 | ", | ||
263 | ); | ||
264 | let main_id = sr.files[RelativePath::new("/main.rs")]; | ||
265 | let lib_id = sr.files[RelativePath::new("/lib.rs")]; | ||
266 | |||
267 | let mut crate_graph = CrateGraph::default(); | ||
268 | let main_crate = crate_graph.add_crate_root(main_id); | ||
269 | let lib_crate = crate_graph.add_crate_root(lib_id); | ||
270 | crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate).unwrap(); | ||
271 | |||
272 | db.set_crate_graph(Arc::new(crate_graph)); | ||
273 | |||
274 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
275 | let krate = module.krate(&db).unwrap(); | ||
276 | let item_map = db.item_map(krate); | ||
277 | |||
278 | check_module_item_map( | ||
279 | &item_map, | ||
280 | module.module_id, | ||
281 | " | ||
282 | Baz: t v | ||
283 | ", | ||
284 | ); | ||
285 | } | ||
286 | |||
287 | #[test] | ||
168 | fn module_resolution_works_for_non_standard_filenames() { | 288 | fn module_resolution_works_for_non_standard_filenames() { |
169 | let (item_map, module_id) = item_map_custom_crate_root( | 289 | let (item_map, module_id) = item_map_custom_crate_root( |
170 | " | 290 | " |