aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-10 20:15:41 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-10 20:15:41 +0000
commitffd407affff967884fd51a977335469e819c7194 (patch)
treede0f95ec9e49ee97148047622e333353f24c1433 /crates/ra_hir
parentb952c270ee87cc36500b3bb03bab9268f2184d1a (diff)
parent2f24e740db3365afac56aad3e8a533340369ef7d (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.rs4
-rw-r--r--crates/ra_hir/src/marks.rs2
-rw-r--r--crates/ra_hir/src/nameres.rs127
-rw-r--r--crates/ra_hir/src/nameres/tests.rs120
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)]
65pub struct Resolution { 65pub 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]
168fn 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]
198fn 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]
229fn 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]
253fn 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]
168fn module_resolution_works_for_non_standard_filenames() { 288fn 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 "