Resolve 2015 style imports
This commit is contained in:
parent
3a9934e2c3
commit
d5ad38cbb8
@ -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)?;
|
||||
|
@ -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,
|
||||
|
@ -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 => {
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user