mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
Resolve 2015 style imports
This commit is contained in:
parent
3a9934e2c3
commit
d5ad38cbb8
4 changed files with 119 additions and 9 deletions
|
@ -119,6 +119,10 @@ impl CrateGraph {
|
||||||
self.arena[&crate_id].file_id
|
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
|
// 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> {
|
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)?;
|
let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use relative_path::RelativePathBuf;
|
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 ra_syntax::{ast::self, TreeArc, SyntaxNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -38,13 +38,20 @@ impl Crate {
|
||||||
pub fn crate_id(&self) -> CrateId {
|
pub fn crate_id(&self) -> CrateId {
|
||||||
self.crate_id
|
self.crate_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dependencies(&self, db: &impl PersistentHirDatabase) -> Vec<CrateDependency> {
|
pub fn dependencies(&self, db: &impl PersistentHirDatabase) -> Vec<CrateDependency> {
|
||||||
self.dependencies_impl(db)
|
self.dependencies_impl(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
|
pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
|
||||||
self.root_module_impl(db)
|
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?
|
// TODO: should this be in source_binder?
|
||||||
pub fn source_root_crates(
|
pub fn source_root_crates(
|
||||||
db: &impl PersistentHirDatabase,
|
db: &impl PersistentHirDatabase,
|
||||||
|
|
|
@ -18,10 +18,12 @@ pub(crate) mod lower;
|
||||||
|
|
||||||
use std::{time, sync::Arc};
|
use std::{time, sync::Arc};
|
||||||
|
|
||||||
use ra_arena::map::ArenaMap;
|
|
||||||
use test_utils::tested_by;
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
|
use ra_arena::map::ArenaMap;
|
||||||
|
use ra_db::Edition;
|
||||||
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Module, ModuleDef,
|
Module, ModuleDef,
|
||||||
Path, PathKind, PersistentHirDatabase,
|
Path, PathKind, PersistentHirDatabase,
|
||||||
|
@ -32,8 +34,9 @@ use crate::{
|
||||||
|
|
||||||
/// `ItemMap` is the result of module name resolution. It contains, for each
|
/// `ItemMap` is the result of module name resolution. It contains, for each
|
||||||
/// module, the set of visible items.
|
/// module, the set of visible items.
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct ItemMap {
|
pub struct ItemMap {
|
||||||
|
edition: Edition,
|
||||||
/// The prelude module for this crate. This either comes from an import
|
/// The prelude module for this crate. This either comes from an import
|
||||||
/// marked with the `prelude_import` attribute, or (in the normal case) from
|
/// marked with the `prelude_import` attribute, or (in the normal case) from
|
||||||
/// a dependency (`std` or `core`).
|
/// a dependency (`std` or `core`).
|
||||||
|
@ -180,7 +183,12 @@ where
|
||||||
module_tree,
|
module_tree,
|
||||||
processed_imports: FxHashSet::default(),
|
processed_imports: FxHashSet::default(),
|
||||||
glob_imports: FxHashMap::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_id: ImportId,
|
||||||
import: &ImportData,
|
import: &ImportData,
|
||||||
) -> ReachedFixedPoint {
|
) -> ReachedFixedPoint {
|
||||||
log::debug!("resolving import: {:?}", import);
|
log::debug!("resolving import: {:?} ({:?})", import, self.result.edition);
|
||||||
let original_module = Module { krate: self.krate, module_id };
|
let original_module = Module { krate: self.krate, module_id };
|
||||||
let (def, reached_fixedpoint) =
|
let (def, reached_fixedpoint) = self.result.resolve_path_fp(
|
||||||
self.result.resolve_path_fp(self.db, original_module, &import.path);
|
self.db,
|
||||||
|
ResolveMode::Import,
|
||||||
|
original_module,
|
||||||
|
&import.path,
|
||||||
|
);
|
||||||
|
|
||||||
if reached_fixedpoint != ReachedFixedPoint::Yes {
|
if reached_fixedpoint != ReachedFixedPoint::Yes {
|
||||||
return reached_fixedpoint;
|
return reached_fixedpoint;
|
||||||
|
@ -417,6 +429,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum ResolveMode {
|
||||||
|
Import,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum ReachedFixedPoint {
|
enum ReachedFixedPoint {
|
||||||
Yes,
|
Yes,
|
||||||
|
@ -445,7 +463,7 @@ impl ItemMap {
|
||||||
original_module: Module,
|
original_module: Module,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> PerNs<ModuleDef> {
|
) -> 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(
|
fn resolve_in_prelude(
|
||||||
|
@ -484,11 +502,27 @@ impl ItemMap {
|
||||||
from_scope.or(from_extern_prelude).or(from_prelude)
|
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
|
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
||||||
// the result.
|
// the result.
|
||||||
fn resolve_path_fp(
|
fn resolve_path_fp(
|
||||||
&self,
|
&self,
|
||||||
db: &impl PersistentHirDatabase,
|
db: &impl PersistentHirDatabase,
|
||||||
|
mode: ResolveMode,
|
||||||
original_module: Module,
|
original_module: Module,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
|
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
|
||||||
|
@ -496,11 +530,31 @@ impl ItemMap {
|
||||||
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
|
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
|
||||||
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
|
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
|
||||||
PathKind::Self_ => PerNs::types(original_module.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 => {
|
PathKind::Plain => {
|
||||||
let segment = match segments.next() {
|
let segment = match segments.next() {
|
||||||
Some((_, segment)) => segment,
|
Some((_, segment)) => segment,
|
||||||
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
||||||
};
|
};
|
||||||
|
log::debug!("resolving {:?} in module", segment);
|
||||||
self.resolve_name_in_module(db, original_module, &segment.name)
|
self.resolve_name_in_module(db, original_module, &segment.name)
|
||||||
}
|
}
|
||||||
PathKind::Super => {
|
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]
|
#[test]
|
||||||
fn module_resolution_works_for_non_standard_filenames() {
|
fn module_resolution_works_for_non_standard_filenames() {
|
||||||
let mut db = MockDatabase::with_files(
|
let mut db = MockDatabase::with_files(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue