From 1526eb25c98fd16a9c0d114d0ed44e8fec1cc19c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 20:44:34 +0100 Subject: 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 50d4e824c..16852a6a1 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 ffb20d564..2ba6038c6 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, pub(crate) extern_prelude: FxHashMap, per_module: ArenaMap, } @@ -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 { + fn resolve_in_prelude( + &self, + db: &impl PersistentHirDatabase, + original_module: Module, + name: &Name, + ) -> PerNs { + 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 { + // 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 3cd496d7f..922dbe9c1 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, 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 6dbe759d1..68ebe963a 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 40c860cf4..fde4d6580 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 { + pub fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs { 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 { 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 { + fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs { 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 5df3f2ccf..7c5e8ce5e 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 2e4b2d776..304bc5909 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" ] -- cgit v1.2.3 From 3a9934e2c3280864877a90c5ced777bad898d73a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 22:34:29 +0100 Subject: 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 ++-- crates/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 8decc65c5..76998ea30 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, } 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 31442713d..e006c6d27 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 5ca870867..145ed39a1 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 de3ec4e0a..d77a56ce8 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 017ac5de3..550d69641 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 4cf842452..0526f7584 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 5866be519..e28aca259 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, is_member: bool, dependencies: Vec, + 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 + '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 3b1e07149..e5c93fd85 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); -- cgit v1.2.3 From d5ad38cbb87103d8713855e0ec705fd957249afd Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 11 Feb 2019 23:11:12 +0100 Subject: 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 | 68 +++++++++++++++++++++++++++++++++---- crates/ra_hir/src/nameres/tests.rs | 45 ++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 76998ea30..aa535ac4d 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 { 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 2ac05c836..fb7ad0867 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 { self.dependencies_impl(db) } + pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option { 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 2ba6038c6..028e2bee3 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -18,9 +18,11 @@ pub(crate) mod lower; use std::{time, sync::Arc}; +use rustc_hash::{FxHashMap, FxHashSet}; + use ra_arena::map::ArenaMap; +use ra_db::Edition; use test_utils::tested_by; -use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ Module, ModuleDef, @@ -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 { - 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 { + 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, ReachedFixedPoint) { @@ -496,11 +530,31 @@ impl ItemMap { let mut curr_per_ns: PerNs = 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 68ebe963a..bee475c3a 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( -- cgit v1.2.3 From 70839b7ef8217fd019ce5bd3a643a8a16f5fa829 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 13 Feb 2019 20:31:27 +0100 Subject: 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 aa535ac4d..e45a510b3 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 145ed39a1..f1cad77c5 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)>); +pub struct CrateGraphFixture(pub FxHashMap)>); #[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 bee475c3a..e764e0855 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 e28aca259..81cb506b7 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, is_member: bool, dependencies: Vec, - 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 + '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 e5c93fd85..1b18ac836 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); -- cgit v1.2.3 From 92c595a6a6f7624092432d28ffd7e0ffd189cbda Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 13 Feb 2019 20:42:43 +0100 Subject: 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 028e2bee3..cd7b41cff 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 { + 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 922dbe9c1..81d80654c 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 e764e0855..6402c89c0 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( -- cgit v1.2.3 From 911e32bca9b73e66eceb6bbee3768c82e94597d5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 13 Feb 2019 20:53:42 +0100 Subject: Complete names from prelude --- crates/ra_hir/src/nameres.rs | 2 +- crates/ra_hir/src/resolve.rs | 12 +++-- crates/ra_ide_api/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 cd7b41cff..e35b4b129 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, + pub(crate) prelude: Option, pub(crate) extern_prelude: FxHashMap, per_module: ArenaMap, } diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index fde4d6580..91a531801 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> { + pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { 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 = 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)) { + fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs)) { 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 445788407..eeaf26d93 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 000000000..2b5a1a8ea --- /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 + } +] -- cgit v1.2.3