Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Panic in match checking
<!--
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
Forum for questions: https://users.rust-lang.org/c/ide/14

Before submitting, please make sure that you're not running into one of these known issues:

 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file)
 2. some platform-specific imports are not resolved: #6038
 3. attribute proc macros are not supported: #6029
 4. the version string is misleading (includes the previous week): #8571

Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
-->
Version 71117e681 2021-06-01 nightly

Found this in my minimal reproducible examples repo:
```rust
enum Foo<T> { A(T) }
match &Foo::A(true) {
    _ => {}
    Foo::A(_) => {}
}
```

<details>
<summary>log</summary>

```
Panic context:
> 
version: 71117e681 2021-06-01 nightly
request: textDocument/codeAction CodeActionParams {
    text_document: TextDocumentIdentifier {
        uri: Url {
            scheme: "file",
            cannot_be_a_base: false,
            username: "",
            password: None,
            host: None,
            port: None,
            path: "/tmp/match-panic/src/extracted.rs",
            query: None,
            fragment: None,
        },
    },
    range: Range {
        start: Position {
            line: 132,
            character: 9,
        },
        end: Position {
            line: 132,
            character: 9,
        },
    },
    context: CodeActionContext {
        diagnostics: [],
        only: None,
    },
    work_done_progress_params: WorkDoneProgressParams {
        work_done_token: None,
    },
    partial_result_params: PartialResultParams {
        partial_result_token: None,
    },
}

thread '<unnamed>' panicked at 'Forgot to remove debug-print?', crates/hir_ty/src/diagnostics/expr.rs:365:25
stack backtrace:
   0: std::panicking::begin_panic
   1: hir_ty::diagnostics::expr::ExprValidator::validate_match::{{closure}}
   2: hir_ty::diagnostics::match_check::usefulness::MatchCheckCtx::bug
   3: hir_ty::diagnostics::match_check::usefulness::is_useful
   4: hir_ty::diagnostics::match_check::usefulness::compute_match_usefulness
   5: hir_ty::diagnostics::expr::ExprValidator::validate_body
   6: hir_ty::diagnostics::validate_body
   7: hir::Function::diagnostics
   8: hir::Module::diagnostics
   9: hir::Module::diagnostics
  10: ide::diagnostics::diagnostics
  11: ide::Analysis::assists_with_fixes::{{closure}}
  12: std::panicking::try
  13: rust_analyzer::handlers::handle_code_action
  14: <F as threadpool::FnBox>::call_box
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```
</details>

The example may appear while expanding nested or-patterns.

(Also seems like I shouldn't use debug-print when panicking)

Pull request:
internal: calculate pattern adjustments
This extends `InferenceResult` with `pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>`.

Fixes #9095 


Patch:
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 219ed4c071..ee52794aa8 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -682,11 +682,9 @@ pub struct S;
 //- /main.rs crate:main deps:std
 $0
 //- /std.rs crate:std
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct S;
-    }
-}
+pub mod prelude { pub struct S; }
+#[prelude_import]
+pub use prelude::*;
         "#,
             "S",
             "S",
@@ -702,11 +700,11 @@ pub mod prelude {
 $0
 //- /std.rs crate:std
 pub mod prelude {
-    pub mod rust_2018 {
-        pub enum Option<T> { Some(T), None }
-        pub use Option::*;
-    }
+    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");
@@ -1082,11 +1080,11 @@ fn f() {
 }
 //- /std.rs crate:std
 pub mod prelude {
-    pub mod rust_2018 {
-        pub enum Option { None }
-        pub use Option::*;
-    }
+    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 6b41921ae4..7f9fdb3793 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, Edition, FileId, ProcMacroId};
+use base_db::{CrateId, FileId, ProcMacroId};
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{
     ast_id_map::FileAstId,
     builtin_derive::find_builtin_derive,
     builtin_macro::find_builtin_macro,
-    name::{name, AsName, Name},
+    name::{AsName, Name},
     proc_macro::ProcMacroExpander,
     FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
 };
@@ -67,6 +67,14 @@ 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;
+            }
         }
     }
 
@@ -275,8 +283,6 @@ 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() {
@@ -454,71 +460,6 @@ 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.clone(),
-            [krate.clone(), name![prelude], edition].iter().cloned(),
-        );
-        // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
-        // FIXME remove this fallback
-        let fallback_path =
-            ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].iter().cloned());
-
-        for path in &[path, fallback_path] {
-            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);
-                    return;
-                }
-                _ => {
-                    log::debug!(
-                        "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
@@ -777,8 +718,6 @@ 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 00b8adc1eb..b07fbf8b3f 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -176,11 +176,6 @@ pub mod known {
         result,
         boxed,
         option,
-        prelude,
-        rust_2015,
-        rust_2018,
-        rust_2021,
-        v1,
         // Components of known path (type name)
         Iterator,
         IntoIterator,
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 3efbce7736..a2a4d61db5 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -357,17 +357,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             infer: &infer,
             db,
             pattern_arena: &pattern_arena,
-            eprint_panic_context: &|| {
+            panic_context: &|| {
                 use syntax::AstNode;
-                if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) {
-                    let root = scrutinee_sptr.file_syntax(db.upcast());
-                    if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() {
-                        eprintln!(
-                            "Match checking is about to panic on this expression:\n{}",
-                            match_ast.to_string(),
-                        );
-                    }
-                }
+                let match_expr_text = source_map
+                    .expr_syntax(match_expr)
+                    .ok()
+                    .and_then(|scrutinee_sptr| {
+                        let root = scrutinee_sptr.file_syntax(db.upcast());
+                        scrutinee_sptr.value.to_node(&root).syntax().parent()
+                    })
+                    .map(|node| node.to_string());
+                format!(
+                    "expression:\n{}",
+                    match_expr_text.as_deref().unwrap_or("<synthesized expr>")
+                )
             },
         };
         let report = compute_match_usefulness(&cx, &m_arms);
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index a9a99f57a2..c8e1b23dee 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> {
     }
 
     pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
-        // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics")
+        // XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
+        // When lowering of & and box patterns are implemented this should be tested
+        // in a manner of `match_ergonomics_issue_9095` test.
+        // Pattern adjustment is part of RFC 2005-match-ergonomics.
         // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
         let unadjusted_pat = self.lower_pattern_unadjusted(pat);
-        unadjusted_pat
+        self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
+            unadjusted_pat,
+            |subpattern, ref_ty| Pat {
+                ty: ref_ty.clone(),
+                kind: Box::new(PatKind::Deref { subpattern }),
+            },
+        )
     }
 
     fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
@@ -1236,6 +1245,21 @@ fn main(f: Foo) {
         );
     }
 
+    #[test]
+    fn match_ergonomics_issue_9095() {
+        check_diagnostics(
+            r#"
+enum Foo<T> { A(T) }
+fn main() {
+    match &Foo::A(true) {
+        _ => {}
+        Foo::A(_) => {}
+    }
+}
+"#,
+        );
+    }
+
     mod false_negatives {
         //! The implementation of match checking here is a work in progress. As we roll this out, we
         //! prefer false negatives to false positives (ideally there would be no false positives). This
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
index 83b094a893..bd76a606cb 100644
--- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> {
     pub(crate) db: &'a dyn HirDatabase,
     /// Lowered patterns from arms plus generated by the check.
     pub(crate) pattern_arena: &'a RefCell<PatternArena>,
-    pub(crate) eprint_panic_context: &'a dyn Fn(),
+    pub(crate) panic_context: &'a dyn Fn() -> String,
 }
 
 impl<'a> MatchCheckCtx<'a> {
@@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> {
 
     #[track_caller]
     pub(super) fn bug(&self, info: &str) -> ! {
-        (self.eprint_panic_context)();
-        panic!("bug: {}", info);
+        panic!("bug: {}\n{}", info, (self.panic_context)());
     }
 }
 
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 7a4268819b..0e9f777dab 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -150,6 +150,8 @@ pub struct InferenceResult {
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
     /// Interned Unknown to return references to.
     standard_types: InternedStandardTypes,
+    /// Stores the types which were implicitly dereferenced in pattern binding modes.
+    pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
 }
 
 impl InferenceResult {
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 83e0a7a9e9..a21f44d6ae 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> {
         let mut expected = self.resolve_ty_shallow(expected);
 
         if is_non_ref_pat(&body, pat) {
+            let mut pat_adjustments = Vec::new();
             while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
+                pat_adjustments.push(expected.clone());
                 expected = self.resolve_ty_shallow(inner);
                 default_bm = match default_bm {
                     BindingMode::Move => BindingMode::Ref(mutability),
@@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> {
                     BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
                 }
             }
+
+            if !pat_adjustments.is_empty() {
+                pat_adjustments.shrink_to_fit();
+                self.result.pat_adjustments.insert(pat, pat_adjustments);
+            }
         } else if let Pat::Ref { .. } = &body[pat] {
             cov_mark::hit!(match_ergonomics_ref);
             // When you encounter a `&pat` pattern, reset to Move.
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index bd955aa85c..20188a7ddc 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -385,11 +385,10 @@ fn foo() {
 fn foo() { let x: $0 }
 
 //- /std/lib.rs crate:std
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct Option;
-    }
-}
+#[prelude_import]
+use prelude::*;
+
+mod prelude { struct Option; }
 "#,
             expect![[r#"
                 fn foo()  fn()
@@ -407,10 +406,12 @@ pub mod prelude {
 fn f() {$0}
 
 //- /std/lib.rs crate:std
-pub mod prelude {
-    pub mod rust_2018 {
-        pub use crate::concat;
-    }
+#[prelude_import]
+pub use prelude::*;
+
+#[macro_use]
+mod prelude {
+    pub use crate::concat;
 }
 
 mod macros {
@@ -435,18 +436,16 @@ mod macros {
 fn foo() { let x: $0 }
 
 //- /core/lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct Option;
-    }
-}
+#[prelude_import]
+use prelude::*;
+
+mod prelude { struct Option; }
 
 //- /std/lib.rs crate:std deps:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct String;
-    }
-}
+#[prelude_import]
+use prelude::*;
+
+mod prelude { 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 3128519661..29ae12dcf4 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -128,19 +128,17 @@ pub mod option {
 }
 
 pub mod prelude {
-    pub mod rust_2018 {
-        pub use crate::{
-            cmp::Ord,
-            convert::{From, Into},
-            default::Default,
-            iter::{IntoIterator, Iterator},
-            ops::{Fn, FnMut, FnOnce},
-            option::Option::{self, *},
-        };
-    }
+    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::rust_2018::*;
+pub use prelude::*;
 //- /libstd.rs crate:std deps:core
 //! Signatures of traits, types and functions from the std lib for use in tests.
 
@@ -150,4 +148,4 @@ mod return_keyword {}
 /// Docs for prim_str
 mod prim_str {}
 
-pub use core::ops;
+pub use core::ops;
\ No newline at end of file


Test patch:
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 58c01354a4..9f652731d9 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -246,16 +246,15 @@ 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
-pub mod prelude;
+mod prelude;
+#[prelude_import]
+use prelude::*;
 
 //- /prelude.rs
-pub enum Foo { Bar, Baz }
+pub enum Foo { Bar, Baz };
 "#,
         expect![[r#"
             crate
@@ -467,74 +466,6 @@ 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(
@@ -543,18 +474,18 @@ fn std_prelude_takes_precedence_above_core_prelude() {
 use {Foo, Bar};
 
 //- /std.rs crate:std deps:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct Foo;
-        pub use core::prelude::rust_2018::Bar;
-    }
+#[prelude_import]
+pub use self::prelude::*;
+mod prelude {
+    pub struct Foo;
+    pub use core::prelude::Bar;
 }
 
 //- /core.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub struct Bar;
-    }
+#[prelude_import]
+pub use self::prelude::*;
+mod prelude {
+    pub struct Bar;
 }
 "#,
         expect![[r#"
@@ -573,15 +504,15 @@ fn cfg_not_test() {
 use {Foo, Bar, Baz};
 
 //- /lib.rs crate:std
-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;
-    }
+#[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;
 }
 "#,
         expect![[r#"
@@ -601,15 +532,15 @@ fn cfg_test() {
 use {Foo, Bar, Baz};
 
 //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
-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;
-    }
+#[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;
 }
 "#,
         expect![[r#"
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 3716184386..3065efd65e 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:std
+//- /main.rs crate:main deps:foo
 structs!(Foo);
 structs_priv!(Bar);
 structs_outside!(Out);
@@ -276,20 +276,21 @@ mod bar;
 structs!(Baz);
 crate::structs!(MacroNotResolved3);
 
-//- /lib.rs crate:std
-pub mod prelude {
-    pub mod rust_2018 {
+//- /lib.rs crate:foo
+#[prelude_import]
+use self::prelude::*;
+
+mod prelude {
+    #[macro_export]
+    macro_rules! structs {
+        ($i:ident) => { struct $i; }
+    }
+
+    mod priv_mod {
         #[macro_export]
-        macro_rules! structs {
+        macro_rules! structs_priv {
             ($i:ident) => { struct $i; }
         }
-
-        mod priv_mod {
-            #[macro_export]
-            macro_rules! structs_priv {
-                ($i:ident) => { struct $i; }
-            }
-        }
     }
 }
 
@@ -616,11 +617,12 @@ 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;
 
-pub mod prelude {
-    pub mod rust_2018 {}
-}
+mod prelude {}
 
 #[macro_use]
 mod std_macros;
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index d14103aab1..7647bb08b7 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -982,18 +982,14 @@ fn test() {
 }         //^ S
 
 //- /lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        pub use crate::clone::Clone;
-    }
-}
-
-pub mod clone {
-    pub trait Clone {
+#[prelude_import]
+use clone::*;
+mod clone {
+    trait Clone {
         fn clone(&self) -> Self;
     }
+    #[rustc_builtin_macro]
+    macro Clone {}
 }
 "#,
     );
@@ -1005,22 +1001,14 @@ fn infer_derive_clone_in_core() {
         r#"
 //- /lib.rs crate:core
 #[prelude_import]
-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 {
+use clone::*;
+mod clone {
+    trait Clone {
         fn clone(&self) -> Self;
     }
+    #[rustc_builtin_macro]
+    macro Clone {}
 }
-
 #[derive(Clone)]
 pub struct S;
 
@@ -1049,18 +1037,14 @@ fn test() {
 }
 
 //- /lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        pub use crate::clone::Clone;
-    }
-}
-
-pub mod clone {
-    pub trait Clone {
+#[prelude_import]
+use clone::*;
+mod clone {
+    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 058eb91299..a4c132bc5f 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:core
+//- /main.rs crate:main deps:other_crate
 struct S;
 impl Clone for S {}
 
@@ -805,12 +805,12 @@ fn test() {
           //^ S
 }
 
-//- /lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        pub trait Clone {
-            fn clone(&self) -> Self;
-        }
+//- /lib.rs crate:other_crate
+#[prelude_import] use foo::*;
+
+mod foo {
+    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 1019e783b5..ad9edf11cd 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -426,12 +426,11 @@ fn test() {
 
 //- /std.rs crate:std
 #[prelude_import]
-use self::prelude::rust_2018::*;
+use prelude::*;
+
 pub mod prelude {
-    pub mod rust_2018 {
-        pub use crate::iter::Iterator;
-        pub use crate::option::Option;
-    }
+    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 3418ed21e7..ac312981df 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2712,23 +2712,3 @@ 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 588f0d1d41..49add4ab99 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -20,12 +20,11 @@ fn test() {
 } //^ u64
 
 //- /core.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        #[lang = "future_trait"]
-        pub trait Future {
-            type Output;
-        }
+#[prelude_import] use future::*;
+mod future {
+    #[lang = "future_trait"]
+    trait Future {
+        type Output;
     }
 }
 "#,
@@ -137,15 +136,17 @@ fn test() {
 } //^ i32
 
 //- /core.rs crate:core
-pub mod ops {
-    pub trait Try {
+#[prelude_import] use ops::*;
+mod ops {
+    trait Try {
         type Ok;
         type Error;
     }
 }
 
-pub mod result {
-    pub enum Result<O, E> {
+#[prelude_import] use result::*;
+mod result {
+    enum Result<O, E> {
         Ok(O),
         Err(E)
     }
@@ -155,12 +156,6 @@ pub mod result {
         type Error = E;
     }
 }
-
-pub mod prelude {
-    pub mod rust_2018 {
-        pub use crate::{result::*, ops::*};
-    }
-}
 "#,
     );
 }
@@ -195,7 +190,8 @@ mov convert {
     impl<T> From<T> for T {}
 }
 
-pub mod result {
+#[prelude_import] use result::*;
+mod result {
     use crate::convert::From;
     use crate::ops::{Try, FromResidual};
 
@@ -212,12 +208,6 @@ pub 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::*;
-    }
-}
 "#,
     );
 }
@@ -227,7 +217,6 @@ fn infer_for_loop() {
     check_types(
         r#"
 //- /main.rs crate:main deps:core,alloc
-#![no_std]
 use alloc::collections::Vec;
 
 fn test() {
@@ -239,19 +228,14 @@ fn test() {
 }
 
 //- /core.rs crate:core
-pub mod iter {
-    pub trait IntoIterator {
+#[prelude_import] use iter::*;
+mod iter {
+    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> {