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 @@ pub fn crate_root(&self, crate_id: CrateId) -> FileId { 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 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 { 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 2ba6038c67d..028e2bee337 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -18,10 +18,12 @@ 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 @@ /// `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 @@ fn new( 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 @@ fn resolve_import( 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 @@ fn update_recursive( } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ResolveMode { + Import, + Other, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ReachedFixedPoint { Yes, @@ -445,7 +463,7 @@ pub(crate) fn resolve_path( 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 @@ pub(crate) fn resolve_name_in_module( 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 @@ fn resolve_path_fp( 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 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(