9097: feat: Implement per-edition preludes r=jonas-schievink a=jonas-schievink

Part of https://github.com/rust-analyzer/rust-analyzer/issues/9056

Our previous implementation was incorrect (presumably because of the misleading comment in libstd [here](a7890c7952/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 https://github.com/rust-analyzer/rust-analyzer/issues/8815.

bors r+

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-06-01 11:46:59 +00:00 committed by GitHub
commit 4f63e79eb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 318 additions and 141 deletions

View file

@ -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",
@ -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");
@ -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",

View file

@ -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 {

View file

@ -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#"

View file

@ -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;

View file

@ -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,

View file

@ -982,14 +982,18 @@ fn test() {
} //^ S
//- /lib.rs crate:core
#[prelude_import]
use clone::*;
mod clone {
trait Clone {
pub mod prelude {
pub mod rust_2018 {
#[rustc_builtin_macro]
pub macro Clone {}
pub use crate::clone::Clone;
}
}
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
#[rustc_builtin_macro]
macro Clone {}
}
"#,
);
@ -1001,14 +1005,22 @@ fn infer_derive_clone_in_core() {
r#"
//- /lib.rs crate:core
#[prelude_import]
use clone::*;
mod clone {
trait Clone {
use prelude::rust_2018::*;
pub mod prelude {
pub mod rust_2018 {
#[rustc_builtin_macro]
pub macro Clone {}
pub use crate::clone::Clone;
}
}
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
#[rustc_builtin_macro]
macro Clone {}
}
#[derive(Clone)]
pub struct S;
@ -1037,14 +1049,18 @@ fn test() {
}
//- /lib.rs crate:core
#[prelude_import]
use clone::*;
mod clone {
trait Clone {
pub mod prelude {
pub mod rust_2018 {
#[rustc_builtin_macro]
pub macro Clone {}
pub use crate::clone::Clone;
}
}
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
#[rustc_builtin_macro]
macro Clone {}
}
"#,
);

View file

@ -796,7 +796,7 @@ fn test() {
fn method_resolution_trait_from_prelude() {
check_types(
r#"
//- /main.rs crate:main deps:other_crate
//- /main.rs crate:main deps:core
struct S;
impl Clone for S {}
@ -805,12 +805,12 @@ fn test() {
//^ S
}
//- /lib.rs crate:other_crate
#[prelude_import] use foo::*;
mod foo {
trait Clone {
fn clone(&self) -> Self;
//- /lib.rs crate:core
pub mod prelude {
pub mod rust_2018 {
pub trait Clone {
fn clone(&self) -> Self;
}
}
}
"#,

View file

@ -426,11 +426,12 @@ fn test() {
//- /std.rs crate:std
#[prelude_import]
use prelude::*;
use self::prelude::rust_2018::*;
pub mod prelude {
pub use crate::iter::Iterator;
pub use crate::option::Option;
pub mod rust_2018 {
pub use crate::iter::Iterator;
pub use crate::option::Option;
}
}
pub mod iter {

View file

@ -2712,3 +2712,23 @@ fn main() {
"#]],
);
}
#[test]
fn prelude_2015() {
check_types(
r#"
//- /main.rs edition:2015 crate:main deps:core
fn f() {
Rust;
//^ Rust
}
//- /core.rs crate:core
pub mod prelude {
pub mod rust_2015 {
pub struct Rust;
}
}
"#,
);
}

View file

@ -20,11 +20,12 @@ fn test() {
} //^ u64
//- /core.rs crate:core
#[prelude_import] use future::*;
mod future {
#[lang = "future_trait"]
trait Future {
type Output;
pub mod prelude {
pub mod rust_2018 {
#[lang = "future_trait"]
pub trait Future {
type Output;
}
}
}
"#,
@ -136,17 +137,15 @@ fn test() {
} //^ i32
//- /core.rs crate:core
#[prelude_import] use ops::*;
mod ops {
trait Try {
pub mod ops {
pub trait Try {
type Ok;
type Error;
}
}
#[prelude_import] use result::*;
mod result {
enum Result<O, E> {
pub mod result {
pub enum Result<O, E> {
Ok(O),
Err(E)
}
@ -156,6 +155,12 @@ mod result {
type Error = E;
}
}
pub mod prelude {
pub mod rust_2018 {
pub use crate::{result::*, ops::*};
}
}
"#,
);
}
@ -190,8 +195,7 @@ mov convert {
impl<T> From<T> for T {}
}
#[prelude_import] use result::*;
mod result {
pub mod result {
use crate::convert::From;
use crate::ops::{Try, FromResidual};
@ -208,6 +212,12 @@ mod result {
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
}
pub mod prelude {
pub mod rust_2018 {
pub use crate::result::*;
}
}
"#,
);
}
@ -217,6 +227,7 @@ fn infer_for_loop() {
check_types(
r#"
//- /main.rs crate:main deps:core,alloc
#![no_std]
use alloc::collections::Vec;
fn test() {
@ -228,14 +239,19 @@ fn test() {
}
//- /core.rs crate:core
#[prelude_import] use iter::*;
mod iter {
trait IntoIterator {
pub mod iter {
pub trait IntoIterator {
type Item;
}
}
pub mod prelude {
pub mod rust_2018 {
pub use crate::iter::*;
}
}
//- /alloc.rs crate:alloc deps:core
#![no_std]
mod collections {
struct Vec<T> {}
impl<T> Vec<T> {

View file

@ -385,10 +385,11 @@ fn foo() {
fn foo() { let x: $0 }
//- /std/lib.rs crate:std
#[prelude_import]
use prelude::*;
mod prelude { struct Option; }
pub mod prelude {
pub mod rust_2018 {
pub struct Option;
}
}
"#,
expect![[r#"
fn foo() fn()
@ -406,12 +407,10 @@ mod prelude { struct Option; }
fn f() {$0}
//- /std/lib.rs crate:std
#[prelude_import]
pub use prelude::*;
#[macro_use]
mod prelude {
pub use crate::concat;
pub mod prelude {
pub mod rust_2018 {
pub use crate::concat;
}
}
mod macros {
@ -436,16 +435,18 @@ mod macros {
fn foo() { let x: $0 }
//- /core/lib.rs crate:core
#[prelude_import]
use prelude::*;
mod prelude { struct Option; }
pub mod prelude {
pub mod rust_2018 {
pub struct Option;
}
}
//- /std/lib.rs crate:std deps:core
#[prelude_import]
use prelude::*;
mod prelude { struct String; }
pub mod prelude {
pub mod rust_2018 {
pub struct String;
}
}
"#,
expect![[r#"
fn foo() fn()

View file

@ -128,17 +128,19 @@ pub mod option {
}
pub mod prelude {
pub use crate::{
cmp::Ord,
convert::{From, Into},
default::Default,
iter::{IntoIterator, Iterator},
ops::{Fn, FnMut, FnOnce},
option::Option::{self, *},
};
pub mod rust_2018 {
pub use crate::{
cmp::Ord,
convert::{From, Into},
default::Default,
iter::{IntoIterator, Iterator},
ops::{Fn, FnMut, FnOnce},
option::Option::{self, *},
};
}
}
#[prelude_import]
pub use prelude::*;
pub use prelude::rust_2018::*;
//- /libstd.rs crate:std deps:core
//! Signatures of traits, types and functions from the std lib for use in tests.
@ -148,4 +150,4 @@ mod return_keyword {}
/// Docs for prim_str
mod prim_str {}
pub use core::ops;
pub use core::ops;