Resolve 2015 style imports

This commit is contained in:
Florian Diebold 2019-02-11 23:11:12 +01:00
parent 3a9934e2c3
commit d5ad38cbb8
4 changed files with 119 additions and 9 deletions

View File

@ -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<CrateId> {
let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?;

View File

@ -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,

View File

@ -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<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 @@ 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<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 @@ fn resolve_path_fp(
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 => {

View File

@ -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(