Skip to content

Commit

Permalink
Merge #9097
Browse files Browse the repository at this point in the history
9097: feat: Implement per-edition preludes r=jonas-schievink a=jonas-schievink

Part of #9056

Our previous implementation was incorrect (presumably because of the misleading comment in libstd [here](https://github.com/rust-lang/rust/blob/a7890c7952bdc9445eb6c63dc671fa7a1ab0260d/library/std/src/lib.rs#L339-L343)). `#[prelude_import]` does not define the prelude, it can only override the implicit prelude for the current crate.

This PR fixes that, which also makes the prelude imports in `rustc_span` work. Closes #8815.

bors r+

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
  • Loading branch information
bors[bot] and jonas-schievink authored Jun 1, 2021
2 parents 71117e6 + f96c1a0 commit 4f63e79
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 141 deletions.
24 changes: 13 additions & 11 deletions crates/hir_def/src/find_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,9 +682,11 @@ pub struct S;
//- /main.rs crate:main deps:std
$0
//- /std.rs crate:std
pub mod prelude { pub struct S; }
#[prelude_import]
pub use prelude::*;
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"S",
"S",
Expand All @@ -700,11 +702,11 @@ pub use prelude::*;
$0
//- /std.rs crate:std
pub mod prelude {
pub enum Option<T> { Some(T), None }
pub use Option::*;
pub mod rust_2018 {
pub enum Option<T> { Some(T), None }
pub use Option::*;
}
}
#[prelude_import]
pub use prelude::*;
"#;
check_found_path(code, "None", "None", "None", "None");
check_found_path(code, "Some", "Some", "Some", "Some");
Expand Down Expand Up @@ -1080,11 +1082,11 @@ fn f() {
}
//- /std.rs crate:std
pub mod prelude {
pub enum Option { None }
pub use Option::*;
pub mod rust_2018 {
pub enum Option { None }
pub use Option::*;
}
}
#[prelude_import]
pub use prelude::*;
"#,
"None",
"None",
Expand Down
68 changes: 58 additions & 10 deletions crates/hir_def/src/nameres/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
131 changes: 100 additions & 31 deletions crates/hir_def/src/nameres/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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#"
Expand All @@ -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#"
Expand All @@ -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#"
Expand Down
32 changes: 15 additions & 17 deletions crates/hir_def/src/nameres/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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; }
}
}
}
}
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions crates/hir_expand/src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ pub mod known {
result,
boxed,
option,
prelude,
rust_2015,
rust_2018,
rust_2021,
// Components of known path (type name)
Iterator,
IntoIterator,
Expand Down
Loading

0 comments on commit 4f63e79

Please sign in to comment.