diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-13 20:14:39 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-13 20:14:39 +0000 |
commit | cb4327b3a9f0858235dc20b7c5c7e25c6c330aab (patch) | |
tree | 489b3497b2762dcabf60d2674031585431e16959 /crates/ra_hir/src/nameres | |
parent | 65266c644a31e6b321e5afb3c5a2ee75be76cb0c (diff) | |
parent | 911e32bca9b73e66eceb6bbee3768c82e94597d5 (diff) |
Merge #816
816: Prelude & Edition 2015 import resolution r=matklad a=flodiebold
I implemented the prelude import, but it turned out to be useless without being able to resolve any of the imports in the prelude :sweat_smile: So I had to add some edition handling and handle 2015-style imports (at least the simplified scheme proposed in rust-lang/rust#57745). So now finally `Option` resolves :smile:
One remaining problem is that we don't actually know the edition for sysroot crates. They're currently hardcoded to 2015, but there's already a bunch of PRs upgrading the editions of various rustc crates, so we'll have to detect the edition somehow, or just change the hardcoding to 2018 later, I guess...
~Also currently missing is completion for prelude names, though that shouldn't be hard to add. And `Vec` still doesn't resolve, so I need to look into that.~
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r-- | crates/ra_hir/src/nameres/lower.rs | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 112 |
2 files changed, 120 insertions, 8 deletions
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs index 3cd496d7f..81d80654c 100644 --- a/crates/ra_hir/src/nameres/lower.rs +++ b/crates/ra_hir/src/nameres/lower.rs | |||
@@ -2,13 +2,13 @@ use std::sync::Arc; | |||
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | AstNode, SourceFile, TreeArc, AstPtr, | 4 | AstNode, SourceFile, TreeArc, AstPtr, |
5 | ast::{self, ModuleItemOwner, NameOwner}, | 5 | ast::{self, ModuleItemOwner, NameOwner, AttrsOwner}, |
6 | }; | 6 | }; |
7 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | 7 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; |
8 | use rustc_hash::FxHashMap; | 8 | use rustc_hash::FxHashMap; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | SourceItemId, Path, PathKind, ModuleSource, Name, | 11 | SourceItemId, Path, ModuleSource, Name, |
12 | HirFileId, MacroCallLoc, AsName, PerNs, Function, | 12 | HirFileId, MacroCallLoc, AsName, PerNs, Function, |
13 | ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type, | 13 | ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type, |
14 | ids::LocationCtx, PersistentHirDatabase, | 14 | ids::LocationCtx, PersistentHirDatabase, |
@@ -23,6 +23,7 @@ pub(super) struct ImportData { | |||
23 | pub(super) path: Path, | 23 | pub(super) path: Path, |
24 | pub(super) alias: Option<Name>, | 24 | pub(super) alias: Option<Name>, |
25 | pub(super) is_glob: bool, | 25 | pub(super) is_glob: bool, |
26 | pub(super) is_prelude: bool, | ||
26 | pub(super) is_extern_crate: bool, | 27 | pub(super) is_extern_crate: bool, |
27 | } | 28 | } |
28 | 29 | ||
@@ -179,18 +180,14 @@ impl LoweredModule { | |||
179 | self.add_use_item(source_map, it); | 180 | self.add_use_item(source_map, it); |
180 | } | 181 | } |
181 | ast::ModuleItemKind::ExternCrateItem(it) => { | 182 | ast::ModuleItemKind::ExternCrateItem(it) => { |
182 | // Lower `extern crate x` to `use ::x`. This is kind of cheating | ||
183 | // and only works if we always interpret absolute paths in the | ||
184 | // 2018 style; otherwise `::x` could also refer to a module in | ||
185 | // the crate root. | ||
186 | if let Some(name_ref) = it.name_ref() { | 183 | if let Some(name_ref) = it.name_ref() { |
187 | let mut path = Path::from_name_ref(name_ref); | 184 | let path = Path::from_name_ref(name_ref); |
188 | path.kind = PathKind::Abs; | ||
189 | let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name); | 185 | let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name); |
190 | self.imports.alloc(ImportData { | 186 | self.imports.alloc(ImportData { |
191 | path, | 187 | path, |
192 | alias, | 188 | alias, |
193 | is_glob: false, | 189 | is_glob: false, |
190 | is_prelude: false, | ||
194 | is_extern_crate: true, | 191 | is_extern_crate: true, |
195 | }); | 192 | }); |
196 | } | 193 | } |
@@ -214,11 +211,14 @@ impl LoweredModule { | |||
214 | } | 211 | } |
215 | 212 | ||
216 | fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) { | 213 | fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) { |
214 | let is_prelude = | ||
215 | item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false)); | ||
217 | Path::expand_use_item(item, |path, segment, alias| { | 216 | Path::expand_use_item(item, |path, segment, alias| { |
218 | let import = self.imports.alloc(ImportData { | 217 | let import = self.imports.alloc(ImportData { |
219 | path, | 218 | path, |
220 | alias, | 219 | alias, |
221 | is_glob: segment.is_none(), | 220 | is_glob: segment.is_none(), |
221 | is_prelude, | ||
222 | is_extern_crate: false, | 222 | is_extern_crate: false, |
223 | }); | 223 | }); |
224 | if let Some(segment) = segment { | 224 | if let Some(segment) = segment { |
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 6dbe759d1..6402c89c0 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -266,6 +266,45 @@ fn glob_across_crates() { | |||
266 | } | 266 | } |
267 | 267 | ||
268 | #[test] | 268 | #[test] |
269 | fn edition_2015_imports() { | ||
270 | let mut db = MockDatabase::with_files( | ||
271 | " | ||
272 | //- /main.rs | ||
273 | mod foo; | ||
274 | mod bar; | ||
275 | |||
276 | //- /bar.rs | ||
277 | struct Bar; | ||
278 | |||
279 | //- /foo.rs | ||
280 | use bar::Bar; | ||
281 | use other_crate::FromLib; | ||
282 | |||
283 | //- /lib.rs | ||
284 | 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 | ", | ||
304 | ); | ||
305 | } | ||
306 | |||
307 | #[test] | ||
269 | fn module_resolution_works_for_non_standard_filenames() { | 308 | fn module_resolution_works_for_non_standard_filenames() { |
270 | let mut db = MockDatabase::with_files( | 309 | let mut db = MockDatabase::with_files( |
271 | " | 310 | " |
@@ -297,6 +336,43 @@ fn module_resolution_works_for_non_standard_filenames() { | |||
297 | } | 336 | } |
298 | 337 | ||
299 | #[test] | 338 | #[test] |
339 | fn std_prelude() { | ||
340 | covers!(std_prelude); | ||
341 | let mut db = MockDatabase::with_files( | ||
342 | " | ||
343 | //- /main.rs | ||
344 | use Foo::*; | ||
345 | |||
346 | //- /lib.rs | ||
347 | mod prelude; | ||
348 | #[prelude_import] | ||
349 | use prelude::*; | ||
350 | |||
351 | //- /prelude.rs | ||
352 | pub enum Foo { Bar, Baz }; | ||
353 | ", | ||
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 | ); | ||
373 | } | ||
374 | |||
375 | #[test] | ||
300 | fn name_res_works_for_broken_modules() { | 376 | fn name_res_works_for_broken_modules() { |
301 | covers!(name_res_works_for_broken_modules); | 377 | covers!(name_res_works_for_broken_modules); |
302 | let (item_map, module_id) = item_map( | 378 | let (item_map, module_id) = item_map( |
@@ -467,6 +543,42 @@ fn extern_crate_rename() { | |||
467 | } | 543 | } |
468 | 544 | ||
469 | #[test] | 545 | #[test] |
546 | fn extern_crate_rename_2015_edition() { | ||
547 | let mut db = MockDatabase::with_files( | ||
548 | " | ||
549 | //- /main.rs | ||
550 | extern crate alloc as alloc_crate; | ||
551 | |||
552 | mod alloc; | ||
553 | mod sync; | ||
554 | |||
555 | //- /sync.rs | ||
556 | use alloc_crate::Arc; | ||
557 | |||
558 | //- /lib.rs | ||
559 | struct Arc; | ||
560 | ", | ||
561 | ); | ||
562 | db.set_crate_graph_from_fixture(crate_graph! { | ||
563 | "main": ("/main.rs", "2015", ["alloc"]), | ||
564 | "alloc": ("/lib.rs", []), | ||
565 | }); | ||
566 | let sync_id = db.file_id_of("/sync.rs"); | ||
567 | |||
568 | let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap(); | ||
569 | let krate = module.krate(&db).unwrap(); | ||
570 | let item_map = db.item_map(krate); | ||
571 | |||
572 | check_module_item_map( | ||
573 | &item_map, | ||
574 | module.module_id, | ||
575 | " | ||
576 | Arc: t v | ||
577 | ", | ||
578 | ); | ||
579 | } | ||
580 | |||
581 | #[test] | ||
470 | fn import_across_source_roots() { | 582 | fn import_across_source_roots() { |
471 | let mut db = MockDatabase::with_files( | 583 | let mut db = MockDatabase::with_files( |
472 | " | 584 | " |