From f96c1a0414ee302fe96503d89f2998483345c8a9 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 1 Jun 2021 13:39:19 +0200 Subject: Implement per-edition preludes --- crates/hir_def/src/nameres/collector.rs | 68 ++++++++++++--- crates/hir_def/src/nameres/tests.rs | 131 ++++++++++++++++++++++------- crates/hir_def/src/nameres/tests/macros.rs | 32 ++++--- 3 files changed, 173 insertions(+), 58 deletions(-) (limited to 'crates/hir_def/src/nameres') diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 7f9fdb379..598424305 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -5,13 +5,13 @@ use std::iter; -use base_db::{CrateId, FileId, ProcMacroId}; +use base_db::{CrateId, Edition, FileId, ProcMacroId}; use cfg::{CfgExpr, CfgOptions}; use hir_expand::{ ast_id_map::FileAstId, builtin_derive::find_builtin_derive, builtin_macro::find_builtin_macro, - name::{AsName, Name}, + name::{name, AsName, Name}, proc_macro::ProcMacroExpander, FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, }; @@ -67,14 +67,6 @@ pub(super) fn collect_defs( def_map .extern_prelude .insert(dep.as_name(), dep_def_map.module_id(dep_def_map.root).into()); - - // look for the prelude - // If the dependency defines a prelude, we overwrite an already defined - // prelude. This is necessary to import the "std" prelude if a crate - // depends on both "core" and "std". - if dep_def_map.prelude.is_some() { - def_map.prelude = dep_def_map.prelude; - } } } @@ -283,6 +275,8 @@ impl DefCollector<'_> { let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) { + self.inject_prelude(&attrs); + // Process other crate-level attributes. for attr in &*attrs { let attr_name = match attr.path.as_ident() { @@ -460,6 +454,58 @@ impl DefCollector<'_> { } } + fn inject_prelude(&mut self, crate_attrs: &Attrs) { + // See compiler/rustc_builtin_macros/src/standard_library_imports.rs + + if crate_attrs.by_key("no_core").exists() { + // libcore does not get a prelude. + return; + } + + let krate = if crate_attrs.by_key("no_std").exists() { + name![core] + } else { + let std = name![std]; + if self.def_map.extern_prelude().any(|(name, _)| *name == std) { + std + } else { + // If `std` does not exist for some reason, fall back to core. This mostly helps + // keep r-a's own tests minimal. + name![core] + } + }; + + let edition = match self.def_map.edition { + Edition::Edition2015 => name![rust_2015], + Edition::Edition2018 => name![rust_2018], + Edition::Edition2021 => name![rust_2021], + }; + + let path_kind = if self.def_map.edition == Edition::Edition2015 { + PathKind::Plain + } else { + PathKind::Abs + }; + let path = + ModPath::from_segments(path_kind, [krate, name![prelude], edition].iter().cloned()); + + let (per_ns, _) = + self.def_map.resolve_path(self.db, self.def_map.root, &path, BuiltinShadowMode::Other); + + match &per_ns.types { + Some((ModuleDefId::ModuleId(m), _)) => { + self.def_map.prelude = Some(*m); + } + _ => { + log::error!( + "could not resolve prelude path `{}` to module (resolved to {:?})", + path, + per_ns.types + ); + } + } + } + /// Adds a definition of procedural macro `name` to the root module. /// /// # Notes on procedural macro resolution @@ -718,6 +764,8 @@ impl DefCollector<'_> { match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if import.is_prelude { + // Note: This dodgily overrides the injected prelude. The rustc + // implementation seems to work the same though. cov_mark::hit!(std_prelude); self.def_map.prelude = Some(m); } else if m.krate != self.def_map.krate { diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 9f652731d..58c01354a 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs @@ -246,15 +246,16 @@ fn std_prelude() { check( r#" //- /main.rs crate:main deps:test_crate +#[prelude_import] +use ::test_crate::prelude::*; + use Foo::*; //- /lib.rs crate:test_crate -mod prelude; -#[prelude_import] -use prelude::*; +pub mod prelude; //- /prelude.rs -pub enum Foo { Bar, Baz }; +pub enum Foo { Bar, Baz } "#, expect![[r#" crate @@ -466,6 +467,74 @@ pub struct Bar; ); } +#[test] +fn no_std_prelude() { + check( + r#" + //- /main.rs crate:main deps:core,std + #![cfg_attr(not(never), no_std)] + use Rust; + + //- /core.rs crate:core + pub mod prelude { + pud mod rust_2018 { + pub struct Rust; + } + } + //- /std.rs crate:std deps:core + pub mod prelude { + pud mod rust_2018 { + } + } + "#, + expect![[r#" + crate + Rust: t v + "#]], + ); +} + +#[test] +fn edition_specific_preludes() { + // We can't test the 2015 prelude here since you can't reexport its contents with 2015's + // absolute paths. + + check( + r#" + //- /main.rs edition:2018 crate:main deps:std + use Rust2018; + + //- /std.rs crate:std + pub mod prelude { + pud mod rust_2018 { + pub struct Rust2018; + } + } + "#, + expect![[r#" + crate + Rust2018: t v + "#]], + ); + check( + r#" + //- /main.rs edition:2021 crate:main deps:std + use Rust2021; + + //- /std.rs crate:std + pub mod prelude { + pud mod rust_2021 { + pub struct Rust2021; + } + } + "#, + expect![[r#" + crate + Rust2021: t v + "#]], + ); +} + #[test] fn std_prelude_takes_precedence_above_core_prelude() { check( @@ -474,18 +543,18 @@ fn std_prelude_takes_precedence_above_core_prelude() { use {Foo, Bar}; //- /std.rs crate:std deps:core -#[prelude_import] -pub use self::prelude::*; -mod prelude { - pub struct Foo; - pub use core::prelude::Bar; +pub mod prelude { + pub mod rust_2018 { + pub struct Foo; + pub use core::prelude::rust_2018::Bar; + } } //- /core.rs crate:core -#[prelude_import] -pub use self::prelude::*; -mod prelude { - pub struct Bar; +pub mod prelude { + pub mod rust_2018 { + pub struct Bar; + } } "#, expect![[r#" @@ -504,15 +573,15 @@ fn cfg_not_test() { use {Foo, Bar, Baz}; //- /lib.rs crate:std -#[prelude_import] -pub use self::prelude::*; -mod prelude { - #[cfg(test)] - pub struct Foo; - #[cfg(not(test))] - pub struct Bar; - #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] - pub struct Baz; +pub mod prelude { + pub mod rust_2018 { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } } "#, expect![[r#" @@ -532,15 +601,15 @@ fn cfg_test() { use {Foo, Bar, Baz}; //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42 -#[prelude_import] -pub use self::prelude::*; -mod prelude { - #[cfg(test)] - pub struct Foo; - #[cfg(not(test))] - pub struct Bar; - #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] - pub struct Baz; +pub mod prelude { + pub mod rust_2018 { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } } "#, expect![[r#" diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 3065efd65..371618438 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -264,7 +264,7 @@ fn prelude_is_macro_use() { cov_mark::check!(prelude_is_macro_use); check( r#" -//- /main.rs crate:main deps:foo +//- /main.rs crate:main deps:std structs!(Foo); structs_priv!(Bar); structs_outside!(Out); @@ -276,21 +276,20 @@ mod bar; structs!(Baz); crate::structs!(MacroNotResolved3); -//- /lib.rs crate:foo -#[prelude_import] -use self::prelude::*; - -mod prelude { - #[macro_export] - macro_rules! structs { - ($i:ident) => { struct $i; } - } - - mod priv_mod { +//- /lib.rs crate:std +pub mod prelude { + pub mod rust_2018 { #[macro_export] - macro_rules! structs_priv { + macro_rules! structs { ($i:ident) => { struct $i; } } + + mod priv_mod { + #[macro_export] + macro_rules! structs_priv { + ($i:ident) => { struct $i; } + } + } } } @@ -617,12 +616,11 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() { foo!(); //- /std.rs crate:std deps:core -#[prelude_import] -use self::prelude::*; - pub use core::foo; -mod prelude {} +pub mod prelude { + pub mod rust_2018 {} +} #[macro_use] mod std_macros; -- cgit v1.2.3