From 1526eb25c98fd16a9c0d114d0ed44e8fec1cc19c Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Sun, 10 Feb 2019 20:44:34 +0100
Subject: [PATCH 1/6] Import the prelude

---
 crates/ra_hir/src/marks.rs            |  1 +
 crates/ra_hir/src/nameres.rs          | 50 ++++++++++++++++++++++++---
 crates/ra_hir/src/nameres/lower.rs    |  7 +++-
 crates/ra_hir/src/nameres/tests.rs    | 37 ++++++++++++++++++++
 crates/ra_hir/src/resolve.rs          | 12 +++----
 crates/ra_syntax/src/ast/generated.rs |  1 +
 crates/ra_syntax/src/grammar.ron      |  3 +-
 7 files changed, 99 insertions(+), 12 deletions(-)

diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index 50d4e824cd7..16852a6a140 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -6,4 +6,5 @@ test_utils::marks!(
     type_var_resolves_to_int_var
     glob_enum
     glob_across_crates
+    std_prelude
 );
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index ffb20d56432..2ba6038c67d 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -34,6 +34,10 @@ use crate::{
 /// module, the set of visible items.
 #[derive(Default, Debug, PartialEq, Eq)]
 pub struct ItemMap {
+    /// The prelude module for this crate. This either comes from an import
+    /// marked with the `prelude_import` attribute, or (in the normal case) from
+    /// a dependency (`std` or `core`).
+    prelude: Option<Module>,
     pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
     per_module: ArenaMap<ModuleId, ModuleScope>,
 }
@@ -211,6 +215,13 @@ where
             if let Some(module) = dep.krate.root_module(self.db) {
                 self.result.extern_prelude.insert(dep.name.clone(), module.into());
             }
+            // look for the prelude
+            if self.result.prelude.is_none() {
+                let item_map = self.db.item_map(dep.krate);
+                if item_map.prelude.is_some() {
+                    self.result.prelude = item_map.prelude;
+                }
+            }
         }
     }
 
@@ -279,7 +290,10 @@ where
             log::debug!("glob import: {:?}", import);
             match def.take_types() {
                 Some(ModuleDef::Module(m)) => {
-                    if m.krate != self.krate {
+                    if import.is_prelude {
+                        tested_by!(std_prelude);
+                        self.result.prelude = Some(m);
+                    } else if m.krate != self.krate {
                         tested_by!(glob_across_crates);
                         // glob import from other crate => we can just import everything once
                         let item_map = self.db.item_map(m.krate);
@@ -434,12 +448,40 @@ impl ItemMap {
         self.resolve_path_fp(db, original_module, path).0
     }
 
-    pub(crate) fn resolve_name_in_module(&self, module: Module, name: &Name) -> PerNs<ModuleDef> {
+    fn resolve_in_prelude(
+        &self,
+        db: &impl PersistentHirDatabase,
+        original_module: Module,
+        name: &Name,
+    ) -> PerNs<ModuleDef> {
+        if let Some(prelude) = self.prelude {
+            let resolution = if prelude.krate == original_module.krate {
+                self[prelude.module_id].items.get(name).cloned()
+            } else {
+                db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
+            };
+            resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
+        } else {
+            PerNs::none()
+        }
+    }
+
+    pub(crate) fn resolve_name_in_module(
+        &self,
+        db: &impl PersistentHirDatabase,
+        module: Module,
+        name: &Name,
+    ) -> PerNs<ModuleDef> {
+        // Resolve in:
+        //  - current module / scope
+        //  - extern prelude
+        //  - std prelude
         let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
         let from_extern_prelude =
             self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
+        let from_prelude = self.resolve_in_prelude(db, module, name);
 
-        from_scope.or(from_extern_prelude)
+        from_scope.or(from_extern_prelude).or(from_prelude)
     }
 
     // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@@ -459,7 +501,7 @@ impl ItemMap {
                     Some((_, segment)) => segment,
                     None => return (PerNs::none(), ReachedFixedPoint::Yes),
                 };
-                self.resolve_name_in_module(original_module, &segment.name)
+                self.resolve_name_in_module(db, original_module, &segment.name)
             }
             PathKind::Super => {
                 if let Some(p) = original_module.parent(db) {
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs
index 3cd496d7f53..922dbe9c18e 100644
--- a/crates/ra_hir/src/nameres/lower.rs
+++ b/crates/ra_hir/src/nameres/lower.rs
@@ -2,7 +2,7 @@ use std::sync::Arc;
 
 use ra_syntax::{
     AstNode, SourceFile, TreeArc, AstPtr,
-    ast::{self, ModuleItemOwner, NameOwner},
+    ast::{self, ModuleItemOwner, NameOwner, AttrsOwner},
 };
 use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
 use rustc_hash::FxHashMap;
@@ -23,6 +23,7 @@ pub(super) struct ImportData {
     pub(super) path: Path,
     pub(super) alias: Option<Name>,
     pub(super) is_glob: bool,
+    pub(super) is_prelude: bool,
     pub(super) is_extern_crate: bool,
 }
 
@@ -191,6 +192,7 @@ impl LoweredModule {
                         path,
                         alias,
                         is_glob: false,
+                        is_prelude: false,
                         is_extern_crate: true,
                     });
                 }
@@ -214,11 +216,14 @@ impl LoweredModule {
     }
 
     fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) {
+        let is_prelude =
+            item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false));
         Path::expand_use_item(item, |path, segment, alias| {
             let import = self.imports.alloc(ImportData {
                 path,
                 alias,
                 is_glob: segment.is_none(),
+                is_prelude,
                 is_extern_crate: false,
             });
             if let Some(segment) = segment {
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index 6dbe759d111..68ebe963a7b 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -296,6 +296,43 @@ fn module_resolution_works_for_non_standard_filenames() {
     );
 }
 
+#[test]
+fn std_prelude() {
+    covers!(std_prelude);
+    let mut db = MockDatabase::with_files(
+        "
+        //- /main.rs
+        use Foo::*;
+
+        //- /lib.rs
+        mod prelude;
+        #[prelude_import]
+        use prelude::*;
+
+        //- /prelude.rs
+        pub enum Foo { Bar, Baz };
+    ",
+    );
+    db.set_crate_graph_from_fixture(crate_graph! {
+        "main": ("/main.rs", ["test_crate"]),
+        "test_crate": ("/lib.rs", []),
+    });
+    let main_id = db.file_id_of("/main.rs");
+
+    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
+    let krate = module.krate(&db).unwrap();
+    let item_map = db.item_map(krate);
+
+    check_module_item_map(
+        &item_map,
+        module.module_id,
+        "
+            Bar: t v
+            Baz: t v
+        ",
+    );
+}
+
 #[test]
 fn name_res_works_for_broken_modules() {
     covers!(name_res_works_for_broken_modules);
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs
index 40c860cf495..fde4d6580d4 100644
--- a/crates/ra_hir/src/resolve.rs
+++ b/crates/ra_hir/src/resolve.rs
@@ -56,10 +56,10 @@ pub enum Resolution {
 }
 
 impl Resolver {
-    pub fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
+    pub fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
         let mut resolution = PerNs::none();
         for scope in self.scopes.iter().rev() {
-            resolution = resolution.or(scope.resolve_name(name));
+            resolution = resolution.or(scope.resolve_name(db, name));
             if resolution.is_both() {
                 return resolution;
             }
@@ -69,9 +69,9 @@ impl Resolver {
 
     pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
         if let Some(name) = path.as_ident() {
-            self.resolve_name(name)
+            self.resolve_name(db, name)
         } else if path.is_self() {
-            self.resolve_name(&Name::self_param())
+            self.resolve_name(db, &Name::self_param())
         } else {
             let (item_map, module) = match self.module() {
                 Some(m) => m,
@@ -143,13 +143,13 @@ impl Resolver {
 }
 
 impl Scope {
-    fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
+    fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
         match self {
             Scope::ModuleScope(m) => {
                 if let Some(KnownName::SelfParam) = name.as_known_name() {
                     PerNs::types(Resolution::Def(m.module.into()))
                 } else {
-                    m.item_map.resolve_name_in_module(m.module, name).map(Resolution::Def)
+                    m.item_map.resolve_name_in_module(db, m.module, name).map(Resolution::Def)
                 }
             }
             Scope::GenericParams(gp) => match gp.find_by_name(name) {
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 5df3f2ccfb8..7c5e8ce5ed0 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -4210,6 +4210,7 @@ impl ToOwned for UseItem {
 }
 
 
+impl ast::AttrsOwner for UseItem {}
 impl UseItem {
     pub fn use_tree(&self) -> Option<&UseTree> {
         super::child_opt(self)
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 2e4b2d776c2..304bc59096e 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -596,7 +596,8 @@ Grammar(
             options: [ "Pat", "TypeRef" ],
         ),
         "UseItem": (
-            options: [ "UseTree" ]
+            traits: ["AttrsOwner"],
+            options: [ "UseTree" ],
         ),
         "UseTree": (
             options: [ "Path", "UseTreeList", "Alias" ]

From 3a9934e2c3280864877a90c5ced777bad898d73a Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Sun, 10 Feb 2019 22:34:29 +0100
Subject: [PATCH 2/6] Keep track of crate edition

---
 crates/ra_db/src/input.rs                     | 31 ++++++++++++-------
 crates/ra_db/src/lib.rs                       |  2 +-
 crates/ra_hir/src/mock.rs                     |  5 +--
 crates/ra_ide_api/src/lib.rs                  |  3 +-
 crates/ra_ide_api/src/mock_analysis.rs        |  6 ++--
 crates/ra_ide_api/tests/test/main.rs          |  4 +--
 .../ra_project_model/src/cargo_workspace.rs   |  5 +++
 crates/ra_project_model/src/lib.rs            | 12 +++++--
 8 files changed, 44 insertions(+), 24 deletions(-)

diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 8decc65c5e3..76998ea303b 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -56,15 +56,22 @@ pub struct CyclicDependencies;
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct CrateId(pub u32);
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Edition {
+    Edition2018,
+    Edition2015,
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 struct CrateData {
     file_id: FileId,
+    edition: Edition,
     dependencies: Vec<Dependency>,
 }
 
 impl CrateData {
-    fn new(file_id: FileId) -> CrateData {
-        CrateData { file_id, dependencies: Vec::new() }
+    fn new(file_id: FileId, edition: Edition) -> CrateData {
+        CrateData { file_id, edition, dependencies: Vec::new() }
     }
 
     fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
@@ -85,9 +92,9 @@ impl Dependency {
 }
 
 impl CrateGraph {
-    pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
+    pub fn add_crate_root(&mut self, file_id: FileId, edition: Edition) -> CrateId {
         let crate_id = CrateId(self.arena.len() as u32);
-        let prev = self.arena.insert(crate_id, CrateData::new(file_id));
+        let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition));
         assert!(prev.is_none());
         crate_id
     }
@@ -159,14 +166,14 @@ impl CrateGraph {
 
 #[cfg(test)]
 mod tests {
-    use super::{CrateGraph, FileId, SmolStr};
+    use super::{CrateGraph, FileId, SmolStr, Edition::Edition2018};
 
     #[test]
-    fn it_should_painc_because_of_cycle_dependencies() {
+    fn it_should_panic_because_of_cycle_dependencies() {
         let mut graph = CrateGraph::default();
-        let crate1 = graph.add_crate_root(FileId(1u32));
-        let crate2 = graph.add_crate_root(FileId(2u32));
-        let crate3 = graph.add_crate_root(FileId(3u32));
+        let crate1 = graph.add_crate_root(FileId(1u32), Edition2018);
+        let crate2 = graph.add_crate_root(FileId(2u32), Edition2018);
+        let crate3 = graph.add_crate_root(FileId(3u32), Edition2018);
         assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok());
         assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok());
         assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err());
@@ -175,9 +182,9 @@ mod tests {
     #[test]
     fn it_works() {
         let mut graph = CrateGraph::default();
-        let crate1 = graph.add_crate_root(FileId(1u32));
-        let crate2 = graph.add_crate_root(FileId(2u32));
-        let crate3 = graph.add_crate_root(FileId(3u32));
+        let crate1 = graph.add_crate_root(FileId(1u32), Edition2018);
+        let crate2 = graph.add_crate_root(FileId(2u32), Edition2018);
+        let crate3 = graph.add_crate_root(FileId(3u32), Edition2018);
         assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok());
         assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok());
     }
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 31442713df2..e006c6d275e 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -14,7 +14,7 @@ pub use ::salsa as salsa;
 pub use crate::{
     cancellation::Canceled,
     input::{
-        FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, Dependency,
+        FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, Dependency, Edition,
     },
     loc2id::LocationIntener,
 };
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index 5ca87086753..145ed39a1f6 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -3,6 +3,7 @@ use std::{sync::Arc, panic};
 use parking_lot::Mutex;
 use ra_db::{
     FilePosition, FileId, CrateGraph, SourceRoot, SourceRootId, SourceDatabase, salsa,
+    Edition,
 };
 use relative_path::RelativePathBuf;
 use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
@@ -60,7 +61,7 @@ impl MockDatabase {
         let mut crate_graph = CrateGraph::default();
         for (crate_name, (crate_root, _)) in graph.0.iter() {
             let crate_root = self.file_id_of(&crate_root);
-            let crate_id = crate_graph.add_crate_root(crate_root);
+            let crate_id = crate_graph.add_crate_root(crate_root, Edition::Edition2018);
             ids.insert(crate_name, crate_id);
         }
         for (crate_name, (_, deps)) in graph.0.iter() {
@@ -144,7 +145,7 @@ impl MockDatabase {
 
         if is_crate_root {
             let mut crate_graph = CrateGraph::default();
-            crate_graph.add_crate_root(file_id);
+            crate_graph.add_crate_root(file_id, Edition::Edition2018);
             self.set_crate_graph(Arc::new(crate_graph));
         }
         file_id
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index de3ec4e0acc..d77a56ce805 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -62,7 +62,8 @@ pub use ra_ide_api_light::{
     LineIndex, LineCol, translate_offset_with_edit,
 };
 pub use ra_db::{
-    Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId
+    Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
+    Edition
 };
 pub use hir::Documentation;
 
diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs
index 017ac5de30d..550d6964175 100644
--- a/crates/ra_ide_api/src/mock_analysis.rs
+++ b/crates/ra_ide_api/src/mock_analysis.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
 use relative_path::RelativePathBuf;
 use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
 
-use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId};
+use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId, Edition::Edition2018};
 
 /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
 /// from a set of in-memory files.
@@ -89,9 +89,9 @@ impl MockAnalysis {
             let path = RelativePathBuf::from_path(&path[1..]).unwrap();
             let file_id = FileId(i as u32 + 1);
             if path == "/lib.rs" || path == "/main.rs" {
-                root_crate = Some(crate_graph.add_crate_root(file_id));
+                root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018));
             } else if path.ends_with("/lib.rs") {
-                let other_crate = crate_graph.add_crate_root(file_id);
+                let other_crate = crate_graph.add_crate_root(file_id, Edition2018);
                 let crate_name = path.parent().unwrap().file_name().unwrap();
                 if let Some(root_crate) = root_crate {
                     crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap();
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs
index 4cf84245201..0526f758453 100644
--- a/crates/ra_ide_api/tests/test/main.rs
+++ b/crates/ra_ide_api/tests/test/main.rs
@@ -1,7 +1,7 @@
 use insta::assert_debug_snapshot_matches;
 use ra_ide_api::{
     mock_analysis::{single_file, single_file_with_position, MockAnalysis},
-    AnalysisChange, CrateGraph, FileId, Query, NavigationTarget,
+    AnalysisChange, CrateGraph, Edition::Edition2018, FileId, Query, NavigationTarget
 };
 use ra_syntax::{TextRange, SmolStr};
 
@@ -36,7 +36,7 @@ fn test_resolve_crate_root() {
     assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
 
     let mut crate_graph = CrateGraph::default();
-    let crate_id = crate_graph.add_crate_root(root_file);
+    let crate_id = crate_graph.add_crate_root(root_file, Edition2018);
     let mut change = AnalysisChange::new();
     change.set_crate_graph(crate_graph);
     host.apply_change(change);
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 5866be51960..e28aca259a9 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -35,6 +35,7 @@ struct PackageData {
     targets: Vec<Target>,
     is_member: bool,
     dependencies: Vec<PackageDependency>,
+    edition: String,
 }
 
 #[derive(Debug, Clone)]
@@ -84,6 +85,9 @@ impl Package {
     pub fn root(self, ws: &CargoWorkspace) -> &Path {
         ws.packages[self].manifest.parent().unwrap()
     }
+    pub fn edition(self, ws: &CargoWorkspace) -> &str {
+        &ws.packages[self].edition
+    }
     pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
         ws.packages[self].targets.iter().cloned()
     }
@@ -135,6 +139,7 @@ impl CargoWorkspace {
                 manifest: meta_pkg.manifest_path.clone(),
                 targets: Vec::new(),
                 is_member,
+                edition: meta_pkg.edition,
                 dependencies: Vec::new(),
             });
             let pkg_data = &mut packages[pkg];
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 3b1e0714998..e5c93fd856f 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
 use failure::bail;
 use rustc_hash::FxHashMap;
 
-use ra_db::{CrateGraph, FileId};
+use ra_db::{CrateGraph, FileId, Edition};
 
 pub use crate::{
     cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
@@ -36,7 +36,8 @@ impl ProjectWorkspace {
         let mut sysroot_crates = FxHashMap::default();
         for krate in self.sysroot.crates() {
             if let Some(file_id) = load(krate.root(&self.sysroot)) {
-                sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
+                sysroot_crates
+                    .insert(krate, crate_graph.add_crate_root(file_id, Edition::Edition2015));
             }
         }
         for from in self.sysroot.crates() {
@@ -62,7 +63,12 @@ impl ProjectWorkspace {
             for tgt in pkg.targets(&self.cargo) {
                 let root = tgt.root(&self.cargo);
                 if let Some(file_id) = load(root) {
-                    let crate_id = crate_graph.add_crate_root(file_id);
+                    let edition = if pkg.edition(&self.cargo) == "2015" {
+                        Edition::Edition2015
+                    } else {
+                        Edition::Edition2018
+                    };
+                    let crate_id = crate_graph.add_crate_root(file_id, edition);
                     if tgt.kind(&self.cargo) == TargetKind::Lib {
                         lib_tgt = Some(crate_id);
                         pkg_to_lib_crate.insert(pkg, crate_id);

From d5ad38cbb87103d8713855e0ec705fd957249afd Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Mon, 11 Feb 2019 23:11:12 +0100
Subject: [PATCH 3/6] Resolve 2015 style imports

---
 crates/ra_db/src/input.rs           |  4 ++
 crates/ra_hir/src/code_model_api.rs |  9 +++-
 crates/ra_hir/src/nameres.rs        | 70 +++++++++++++++++++++++++----
 crates/ra_hir/src/nameres/tests.rs  | 45 +++++++++++++++++++
 4 files changed, 119 insertions(+), 9 deletions(-)

diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 76998ea303b..aa535ac4d38 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -119,6 +119,10 @@ impl CrateGraph {
         self.arena[&crate_id].file_id
     }
 
+    pub fn edition(&self, crate_id: CrateId) -> Edition {
+        self.arena[&crate_id].edition
+    }
+
     // TODO: this only finds one crate with the given root; we could have multiple
     pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
         let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?;
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index 2ac05c8366f..fb7ad086754 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use relative_path::RelativePathBuf;
-use ra_db::{CrateId, FileId, SourceRootId};
+use ra_db::{CrateId, FileId, SourceRootId, Edition};
 use ra_syntax::{ast::self, TreeArc, SyntaxNode};
 
 use crate::{
@@ -38,13 +38,20 @@ impl Crate {
     pub fn crate_id(&self) -> CrateId {
         self.crate_id
     }
+
     pub fn dependencies(&self, db: &impl PersistentHirDatabase) -> Vec<CrateDependency> {
         self.dependencies_impl(db)
     }
+
     pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
         self.root_module_impl(db)
     }
 
+    pub fn edition(&self, db: &impl PersistentHirDatabase) -> Edition {
+        let crate_graph = db.crate_graph();
+        crate_graph.edition(self.crate_id)
+    }
+
     // TODO: should this be in source_binder?
     pub fn source_root_crates(
         db: &impl PersistentHirDatabase,
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 2ba6038c67d..028e2bee337 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -18,10 +18,12 @@ pub(crate) mod lower;
 
 use std::{time, sync::Arc};
 
-use ra_arena::map::ArenaMap;
-use test_utils::tested_by;
 use rustc_hash::{FxHashMap, FxHashSet};
 
+use ra_arena::map::ArenaMap;
+use ra_db::Edition;
+use test_utils::tested_by;
+
 use crate::{
     Module, ModuleDef,
     Path, PathKind, PersistentHirDatabase,
@@ -32,8 +34,9 @@ use crate::{
 
 /// `ItemMap` is the result of module name resolution. It contains, for each
 /// module, the set of visible items.
-#[derive(Default, Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct ItemMap {
+    edition: Edition,
     /// The prelude module for this crate. This either comes from an import
     /// marked with the `prelude_import` attribute, or (in the normal case) from
     /// a dependency (`std` or `core`).
@@ -180,7 +183,12 @@ where
             module_tree,
             processed_imports: FxHashSet::default(),
             glob_imports: FxHashMap::default(),
-            result: ItemMap::default(),
+            result: ItemMap {
+                edition: krate.edition(db),
+                prelude: None,
+                extern_prelude: FxHashMap::default(),
+                per_module: ArenaMap::default(),
+            },
         }
     }
 
@@ -277,10 +285,14 @@ where
         import_id: ImportId,
         import: &ImportData,
     ) -> ReachedFixedPoint {
-        log::debug!("resolving import: {:?}", import);
+        log::debug!("resolving import: {:?} ({:?})", import, self.result.edition);
         let original_module = Module { krate: self.krate, module_id };
-        let (def, reached_fixedpoint) =
-            self.result.resolve_path_fp(self.db, original_module, &import.path);
+        let (def, reached_fixedpoint) = self.result.resolve_path_fp(
+            self.db,
+            ResolveMode::Import,
+            original_module,
+            &import.path,
+        );
 
         if reached_fixedpoint != ReachedFixedPoint::Yes {
             return reached_fixedpoint;
@@ -417,6 +429,12 @@ where
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ResolveMode {
+    Import,
+    Other,
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 enum ReachedFixedPoint {
     Yes,
@@ -445,7 +463,7 @@ impl ItemMap {
         original_module: Module,
         path: &Path,
     ) -> PerNs<ModuleDef> {
-        self.resolve_path_fp(db, original_module, path).0
+        self.resolve_path_fp(db, ResolveMode::Other, original_module, path).0
     }
 
     fn resolve_in_prelude(
@@ -484,11 +502,27 @@ impl ItemMap {
         from_scope.or(from_extern_prelude).or(from_prelude)
     }
 
+    fn resolve_name_in_crate_root_or_extern_prelude(
+        &self,
+        db: &impl PersistentHirDatabase,
+        module: Module,
+        name: &Name,
+    ) -> PerNs<ModuleDef> {
+        let crate_root = module.crate_root(db);
+        let from_crate_root =
+            self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
+        let from_extern_prelude =
+            self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
+
+        from_crate_root.or(from_extern_prelude)
+    }
+
     // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
     // the result.
     fn resolve_path_fp(
         &self,
         db: &impl PersistentHirDatabase,
+        mode: ResolveMode,
         original_module: Module,
         path: &Path,
     ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
@@ -496,11 +530,31 @@ impl ItemMap {
         let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
             PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
             PathKind::Self_ => PerNs::types(original_module.into()),
+            // plain import or absolute path in 2015: crate-relative with
+            // fallback to extern prelude (with the simplification in
+            // rust-lang/rust#57745)
+            // TODO there must be a nicer way to write this condition
+            PathKind::Plain | PathKind::Abs
+                if self.edition == Edition::Edition2015
+                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
+            {
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return (PerNs::none(), ReachedFixedPoint::Yes),
+                };
+                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
+                self.resolve_name_in_crate_root_or_extern_prelude(
+                    db,
+                    original_module,
+                    &segment.name,
+                )
+            }
             PathKind::Plain => {
                 let segment = match segments.next() {
                     Some((_, segment)) => segment,
                     None => return (PerNs::none(), ReachedFixedPoint::Yes),
                 };
+                log::debug!("resolving {:?} in module", segment);
                 self.resolve_name_in_module(db, original_module, &segment.name)
             }
             PathKind::Super => {
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index 68ebe963a7b..bee475c3acd 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -265,6 +265,51 @@ fn glob_across_crates() {
     );
 }
 
+#[test]
+fn edition_2015_imports() {
+    use ra_db::{CrateGraph, Edition};
+    let mut db = MockDatabase::with_files(
+        "
+        //- /main.rs
+        mod foo;
+        mod bar;
+
+        //- /bar.rs
+        struct Bar;
+
+        //- /foo.rs
+        use bar::Bar;
+        use other_crate::FromLib;
+
+        //- /lib.rs
+        struct FromLib;
+    ",
+    );
+    let main_id = db.file_id_of("/main.rs");
+    let lib_id = db.file_id_of("/lib.rs");
+    let foo_id = db.file_id_of("/foo.rs");
+
+    let mut crate_graph = CrateGraph::default();
+    let main_crate = crate_graph.add_crate_root(main_id, Edition::Edition2015);
+    let lib_crate = crate_graph.add_crate_root(lib_id, Edition::Edition2018);
+    crate_graph.add_dep(main_crate, "other_crate".into(), lib_crate).unwrap();
+
+    db.set_crate_graph(Arc::new(crate_graph));
+
+    let module = crate::source_binder::module_from_file_id(&db, foo_id).unwrap();
+    let krate = module.krate(&db).unwrap();
+    let item_map = db.item_map(krate);
+
+    check_module_item_map(
+        &item_map,
+        module.module_id,
+        "
+            Bar: t v
+            FromLib: t v
+        ",
+    );
+}
+
 #[test]
 fn module_resolution_works_for_non_standard_filenames() {
     let mut db = MockDatabase::with_files(

From 70839b7ef8217fd019ce5bd3a643a8a16f5fa829 Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Wed, 13 Feb 2019 20:31:27 +0100
Subject: [PATCH 4/6] Make edition handling a bit nicer and allow specifying
 edition in crate_graph macro

---
 crates/ra_db/src/input.rs                      |  9 +++++++++
 crates/ra_hir/src/mock.rs                      | 15 +++++++++------
 crates/ra_hir/src/nameres/tests.rs             | 14 ++++----------
 crates/ra_project_model/src/cargo_workspace.rs |  9 +++++----
 crates/ra_project_model/src/lib.rs             |  6 +-----
 5 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index aa535ac4d38..e45a510b34e 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -62,6 +62,15 @@ pub enum Edition {
     Edition2015,
 }
 
+impl Edition {
+    pub fn from_string(s: &str) -> Edition {
+        match s {
+            "2015" => Edition::Edition2015,
+            "2018" | _ => Edition::Edition2018,
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 struct CrateData {
     file_id: FileId,
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index 145ed39a1f6..f1cad77c5a5 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -59,12 +59,12 @@ impl MockDatabase {
     pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) {
         let mut ids = FxHashMap::default();
         let mut crate_graph = CrateGraph::default();
-        for (crate_name, (crate_root, _)) in graph.0.iter() {
+        for (crate_name, (crate_root, edition, _)) in graph.0.iter() {
             let crate_root = self.file_id_of(&crate_root);
-            let crate_id = crate_graph.add_crate_root(crate_root, Edition::Edition2018);
+            let crate_id = crate_graph.add_crate_root(crate_root, *edition);
             ids.insert(crate_name, crate_id);
         }
-        for (crate_name, (_, deps)) in graph.0.iter() {
+        for (crate_name, (_, _, deps)) in graph.0.iter() {
             let from = ids[crate_name];
             for dep in deps {
                 let to = ids[dep];
@@ -233,16 +233,19 @@ impl MockDatabase {
 }
 
 #[derive(Default)]
-pub struct CrateGraphFixture(pub FxHashMap<String, (String, Vec<String>)>);
+pub struct CrateGraphFixture(pub FxHashMap<String, (String, Edition, Vec<String>)>);
 
 #[macro_export]
 macro_rules! crate_graph {
-    ($($crate_name:literal: ($crate_path:literal, [$($dep:literal),*]),)*) => {{
+    ($($crate_name:literal: ($crate_path:literal, $($edition:literal,)? [$($dep:literal),*]),)*) => {{
         let mut res = $crate::mock::CrateGraphFixture::default();
         $(
+            #[allow(unused_mut, unused_assignments)]
+            let mut edition = ra_db::Edition::Edition2018;
+            $(edition = ra_db::Edition::from_string($edition);)?
             res.0.insert(
                 $crate_name.to_string(),
-                ($crate_path.to_string(), vec![$($dep.to_string()),*])
+                ($crate_path.to_string(), edition, vec![$($dep.to_string()),*])
             );
         )*
         res
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index bee475c3acd..e764e0855de 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -267,7 +267,6 @@ fn glob_across_crates() {
 
 #[test]
 fn edition_2015_imports() {
-    use ra_db::{CrateGraph, Edition};
     let mut db = MockDatabase::with_files(
         "
         //- /main.rs
@@ -285,17 +284,12 @@ fn edition_2015_imports() {
         struct FromLib;
     ",
     );
-    let main_id = db.file_id_of("/main.rs");
-    let lib_id = db.file_id_of("/lib.rs");
+    db.set_crate_graph_from_fixture(crate_graph! {
+        "main": ("/main.rs", "2015", ["other_crate"]),
+        "other_crate": ("/lib.rs", "2018", []),
+    });
     let foo_id = db.file_id_of("/foo.rs");
 
-    let mut crate_graph = CrateGraph::default();
-    let main_crate = crate_graph.add_crate_root(main_id, Edition::Edition2015);
-    let lib_crate = crate_graph.add_crate_root(lib_id, Edition::Edition2018);
-    crate_graph.add_dep(main_crate, "other_crate".into(), lib_crate).unwrap();
-
-    db.set_crate_graph(Arc::new(crate_graph));
-
     let module = crate::source_binder::module_from_file_id(&db, foo_id).unwrap();
     let krate = module.krate(&db).unwrap();
     let item_map = db.item_map(krate);
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index e28aca259a9..81cb506b73a 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -4,6 +4,7 @@ use cargo_metadata::{MetadataCommand, CargoOpt};
 use ra_arena::{Arena, RawId, impl_arena_id};
 use rustc_hash::FxHashMap;
 use failure::format_err;
+use ra_db::Edition;
 
 use crate::Result;
 
@@ -35,7 +36,7 @@ struct PackageData {
     targets: Vec<Target>,
     is_member: bool,
     dependencies: Vec<PackageDependency>,
-    edition: String,
+    edition: Edition,
 }
 
 #[derive(Debug, Clone)]
@@ -85,8 +86,8 @@ impl Package {
     pub fn root(self, ws: &CargoWorkspace) -> &Path {
         ws.packages[self].manifest.parent().unwrap()
     }
-    pub fn edition(self, ws: &CargoWorkspace) -> &str {
-        &ws.packages[self].edition
+    pub fn edition(self, ws: &CargoWorkspace) -> Edition {
+        ws.packages[self].edition
     }
     pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
         ws.packages[self].targets.iter().cloned()
@@ -139,7 +140,7 @@ impl CargoWorkspace {
                 manifest: meta_pkg.manifest_path.clone(),
                 targets: Vec::new(),
                 is_member,
-                edition: meta_pkg.edition,
+                edition: Edition::from_string(&meta_pkg.edition),
                 dependencies: Vec::new(),
             });
             let pkg_data = &mut packages[pkg];
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index e5c93fd856f..1b18ac8361e 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -63,11 +63,7 @@ impl ProjectWorkspace {
             for tgt in pkg.targets(&self.cargo) {
                 let root = tgt.root(&self.cargo);
                 if let Some(file_id) = load(root) {
-                    let edition = if pkg.edition(&self.cargo) == "2015" {
-                        Edition::Edition2015
-                    } else {
-                        Edition::Edition2018
-                    };
+                    let edition = pkg.edition(&self.cargo);
                     let crate_id = crate_graph.add_crate_root(file_id, edition);
                     if tgt.kind(&self.cargo) == TargetKind::Lib {
                         lib_tgt = Some(crate_id);

From 92c595a6a6f7624092432d28ffd7e0ffd189cbda Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Wed, 13 Feb 2019 20:42:43 +0100
Subject: [PATCH 5/6] Handle extern crates better, so they work correctly in
 2015 edition

(see the removed comment.)
---
 crates/ra_hir/src/nameres.rs       | 25 ++++++++++++++-------
 crates/ra_hir/src/nameres/lower.rs |  9 ++------
 crates/ra_hir/src/nameres/tests.rs | 36 ++++++++++++++++++++++++++++++
 3 files changed, 55 insertions(+), 15 deletions(-)

diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 028e2bee337..cd7b41cff15 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -287,12 +287,18 @@ where
     ) -> ReachedFixedPoint {
         log::debug!("resolving import: {:?} ({:?})", import, self.result.edition);
         let original_module = Module { krate: self.krate, module_id };
-        let (def, reached_fixedpoint) = self.result.resolve_path_fp(
-            self.db,
-            ResolveMode::Import,
-            original_module,
-            &import.path,
-        );
+
+        let (def, reached_fixedpoint) = if import.is_extern_crate {
+            let res = self.result.resolve_name_in_extern_prelude(
+                &import
+                    .path
+                    .as_ident()
+                    .expect("extern crate should have been desugared to one-element path"),
+            );
+            (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes })
+        } else {
+            self.result.resolve_path_fp(self.db, ResolveMode::Import, original_module, &import.path)
+        };
 
         if reached_fixedpoint != ReachedFixedPoint::Yes {
             return reached_fixedpoint;
@@ -502,6 +508,10 @@ impl ItemMap {
         from_scope.or(from_extern_prelude).or(from_prelude)
     }
 
+    fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
+        self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
+    }
+
     fn resolve_name_in_crate_root_or_extern_prelude(
         &self,
         db: &impl PersistentHirDatabase,
@@ -511,8 +521,7 @@ impl ItemMap {
         let crate_root = module.crate_root(db);
         let from_crate_root =
             self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
-        let from_extern_prelude =
-            self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
+        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
 
         from_crate_root.or(from_extern_prelude)
     }
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs
index 922dbe9c18e..81d80654c96 100644
--- a/crates/ra_hir/src/nameres/lower.rs
+++ b/crates/ra_hir/src/nameres/lower.rs
@@ -8,7 +8,7 @@ use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
 use rustc_hash::FxHashMap;
 
 use crate::{
-    SourceItemId, Path, PathKind, ModuleSource, Name,
+    SourceItemId, Path, ModuleSource, Name,
     HirFileId, MacroCallLoc, AsName, PerNs, Function,
     ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type,
     ids::LocationCtx, PersistentHirDatabase,
@@ -180,13 +180,8 @@ impl LoweredModule {
                 self.add_use_item(source_map, it);
             }
             ast::ModuleItemKind::ExternCrateItem(it) => {
-                // Lower `extern crate x` to `use ::x`. This is kind of cheating
-                // and only works if we always interpret absolute paths in the
-                // 2018 style; otherwise `::x` could also refer to a module in
-                // the crate root.
                 if let Some(name_ref) = it.name_ref() {
-                    let mut path = Path::from_name_ref(name_ref);
-                    path.kind = PathKind::Abs;
+                    let path = Path::from_name_ref(name_ref);
                     let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name);
                     self.imports.alloc(ImportData {
                         path,
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index e764e0855de..6402c89c08b 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -542,6 +542,42 @@ fn extern_crate_rename() {
     );
 }
 
+#[test]
+fn extern_crate_rename_2015_edition() {
+    let mut db = MockDatabase::with_files(
+        "
+        //- /main.rs
+        extern crate alloc as alloc_crate;
+
+        mod alloc;
+        mod sync;
+
+        //- /sync.rs
+        use alloc_crate::Arc;
+
+        //- /lib.rs
+        struct Arc;
+        ",
+    );
+    db.set_crate_graph_from_fixture(crate_graph! {
+        "main": ("/main.rs", "2015", ["alloc"]),
+        "alloc": ("/lib.rs", []),
+    });
+    let sync_id = db.file_id_of("/sync.rs");
+
+    let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap();
+    let krate = module.krate(&db).unwrap();
+    let item_map = db.item_map(krate);
+
+    check_module_item_map(
+        &item_map,
+        module.module_id,
+        "
+        Arc: t v
+        ",
+    );
+}
+
 #[test]
 fn import_across_source_roots() {
     let mut db = MockDatabase::with_files(

From 911e32bca9b73e66eceb6bbee3768c82e94597d5 Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Wed, 13 Feb 2019 20:53:42 +0100
Subject: [PATCH 6/6] Complete names from prelude

---
 crates/ra_hir/src/nameres.rs                  |  2 +-
 crates/ra_hir/src/resolve.rs                  | 12 +++--
 .../src/completion/complete_scope.rs          | 21 +++++++-
 .../completion_item__completes_prelude.snap   | 54 +++++++++++++++++++
 4 files changed, 84 insertions(+), 5 deletions(-)
 create mode 100644 crates/ra_ide_api/src/completion/snapshots/completion_item__completes_prelude.snap

diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index cd7b41cff15..e35b4b129ca 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -40,7 +40,7 @@ pub struct ItemMap {
     /// The prelude module for this crate. This either comes from an import
     /// marked with the `prelude_import` attribute, or (in the normal case) from
     /// a dependency (`std` or `core`).
-    prelude: Option<Module>,
+    pub(crate) prelude: Option<Module>,
     pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
     per_module: ArenaMap<ModuleId, ModuleScope>,
 }
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs
index fde4d6580d4..91a531801b7 100644
--- a/crates/ra_hir/src/resolve.rs
+++ b/crates/ra_hir/src/resolve.rs
@@ -82,10 +82,10 @@ impl Resolver {
         }
     }
 
-    pub fn all_names(&self) -> FxHashMap<Name, PerNs<Resolution>> {
+    pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> {
         let mut names = FxHashMap::default();
         for scope in self.scopes.iter().rev() {
-            scope.collect_names(&mut |name, res| {
+            scope.collect_names(db, &mut |name, res| {
                 let current: &mut PerNs<Resolution> = names.entry(name).or_default();
                 if current.types.is_none() {
                     current.types = res.types;
@@ -174,7 +174,7 @@ impl Scope {
         }
     }
 
-    fn collect_names(&self, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
+    fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
         match self {
             Scope::ModuleScope(m) => {
                 // TODO: should we provide `self` here?
@@ -190,6 +190,12 @@ impl Scope {
                 m.item_map.extern_prelude.iter().for_each(|(name, def)| {
                     f(name.clone(), PerNs::types(Resolution::Def(*def)));
                 });
+                if let Some(prelude) = m.item_map.prelude {
+                    let prelude_item_map = db.item_map(prelude.krate);
+                    prelude_item_map[prelude.module_id].entries().for_each(|(name, res)| {
+                        f(name.clone(), res.def.map(Resolution::Def));
+                    });
+                }
             }
             Scope::GenericParams(gp) => {
                 for param in &gp.params {
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index 4457884072d..eeaf26d939b 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -4,7 +4,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.is_trivial_path {
         return;
     }
-    let names = ctx.resolver.all_names();
+    let names = ctx.resolver.all_names(ctx.db);
 
     names.into_iter().for_each(|(name, res)| {
         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
@@ -165,4 +165,23 @@ mod tests {
     fn completes_self_in_methods() {
         check_reference_completion("self_in_methods", r"impl S { fn foo(&self) { <|> } }")
     }
+
+    #[test]
+    fn completes_prelude() {
+        check_reference_completion(
+            "completes_prelude",
+            "
+            //- /main.rs
+            fn foo() { let x: <|> }
+
+            //- /std/lib.rs
+            #[prelude_import]
+            use prelude::*;
+
+            mod prelude {
+                struct Option;
+            }
+            ",
+        );
+    }
 }
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__completes_prelude.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__completes_prelude.snap
new file mode 100644
index 00000000000..2b5a1a8ea07
--- /dev/null
+++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__completes_prelude.snap
@@ -0,0 +1,54 @@
+---
+created: "2019-02-13T19:52:43.734834624Z"
+creator: insta@0.6.2
+source: crates/ra_ide_api/src/completion/completion_item.rs
+expression: kind_completions
+---
+[
+    CompletionItem {
+        completion_kind: Reference,
+        label: "Option",
+        kind: Some(
+            Struct
+        ),
+        detail: None,
+        documentation: None,
+        lookup: None,
+        insert_text: None,
+        insert_text_format: PlainText,
+        source_range: [18; 18),
+        text_edit: None
+    },
+    CompletionItem {
+        completion_kind: Reference,
+        label: "foo",
+        kind: Some(
+            Function
+        ),
+        detail: Some(
+            "fn foo()"
+        ),
+        documentation: None,
+        lookup: None,
+        insert_text: Some(
+            "foo()$0"
+        ),
+        insert_text_format: Snippet,
+        source_range: [18; 18),
+        text_edit: None
+    },
+    CompletionItem {
+        completion_kind: Reference,
+        label: "std",
+        kind: Some(
+            Module
+        ),
+        detail: None,
+        documentation: None,
+        lookup: None,
+        insert_text: None,
+        insert_text_format: PlainText,
+        source_range: [18; 18),
+        text_edit: None
+    }
+]