Repository: rust-analyzer/rust-analyzer
Pull request #9097 (2021-06-01 11:46:45)

feat: Implement per-edition preludes
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](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 https://github.com/rust-analyzer/rust-analyzer/issues/8815.

bors r+


Patch:
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index ee52794aa8..219ed4c071 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -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",
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 7f9fdb3793..5984243055 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_expand/src/name.rs b/crates/hir_expand/src/name.rs
index b07fbf8b3f..abc6753bf1 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -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,
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 20188a7ddc..bd955aa85c 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -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()
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index 29ae12dcf4..3128519661 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -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;
\ No newline at end of file
+pub use core::ops;


Test patch:
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 9f652731d9..58c01354a4 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 3065efd65e..3716184386 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;
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 7647bb08b7..d14103aab1 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -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 {}
 }
 "#,
     );
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index a4c132bc5f..058eb91299 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -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;
+        }
     }
 }
 "#,
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index ad9edf11cd..1019e783b5 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -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 {
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index ac312981df..3418ed21e7 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -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;
+    }
+}
+    "#,
+    );
+}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 49add4ab99..588f0d1d41 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -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> {