mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 04:23:25 +00:00
resolve extern crates propertly
This commit is contained in:
parent
e89da32bb7
commit
7784c7a701
4 changed files with 98 additions and 51 deletions
|
@ -5,7 +5,7 @@ use relative_path::{RelativePath, RelativePathBuf};
|
||||||
|
|
||||||
use crate::{FileId, FileResolver, SourceRoot, FileResolverImp};
|
use crate::{FileId, FileResolver, SourceRoot, FileResolverImp};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct FileMap(Vec<(FileId, RelativePathBuf)>);
|
pub struct FileMap(Vec<(FileId, RelativePathBuf)>);
|
||||||
|
|
||||||
impl FileMap {
|
impl FileMap {
|
||||||
|
@ -28,6 +28,11 @@ impl FileMap {
|
||||||
self.iter().map(|(id, _)| id).collect()
|
self.iter().map(|(id, _)| id).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_id(&self, path: &str) -> FileId {
|
||||||
|
assert!(path.starts_with('/'));
|
||||||
|
self.iter().find(|(_, p)| p == &path[1..]).unwrap().0
|
||||||
|
}
|
||||||
|
|
||||||
fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
|
fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use salsa::{self, Database};
|
use salsa::{self, Database};
|
||||||
use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE};
|
use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE, CrateGraph};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
|
use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
|
||||||
|
|
||||||
|
@ -16,7 +16,24 @@ pub(crate) struct MockDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockDatabase {
|
impl MockDatabase {
|
||||||
|
pub(crate) fn with_files(fixture: &str) -> (MockDatabase, FileMap) {
|
||||||
|
let (db, file_map, position) = MockDatabase::from_fixture(fixture);
|
||||||
|
assert!(position.is_none());
|
||||||
|
(db, file_map)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) {
|
pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) {
|
||||||
|
let (db, _, position) = MockDatabase::from_fixture(fixture);
|
||||||
|
let position = position.expect("expected a marker ( <|> )");
|
||||||
|
(db, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_crate_graph(&mut self, crate_graph: CrateGraph) {
|
||||||
|
self.query_mut(ra_db::CrateGraphQuery)
|
||||||
|
.set((), Arc::new(crate_graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_fixture(fixture: &str) -> (MockDatabase, FileMap, Option<FilePosition>) {
|
||||||
let mut db = MockDatabase::default();
|
let mut db = MockDatabase::default();
|
||||||
|
|
||||||
let mut position = None;
|
let mut position = None;
|
||||||
|
@ -32,11 +49,10 @@ impl MockDatabase {
|
||||||
db.add_file(&mut file_map, &entry.meta, &entry.text);
|
db.add_file(&mut file_map, &entry.meta, &entry.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let position = position.expect("expected a marker (<|>)");
|
let source_root = file_map.clone().into_source_root();
|
||||||
let source_root = file_map.into_source_root();
|
|
||||||
db.query_mut(ra_db::SourceRootQuery)
|
db.query_mut(ra_db::SourceRootQuery)
|
||||||
.set(WORKSPACE, Arc::new(source_root));
|
.set(WORKSPACE, Arc::new(source_root));
|
||||||
(db, position)
|
(db, file_map, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId {
|
fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId {
|
||||||
|
|
|
@ -228,7 +228,7 @@ where
|
||||||
|
|
||||||
pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> {
|
pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> {
|
||||||
for (&module_id, items) in self.input.iter() {
|
for (&module_id, items) in self.input.iter() {
|
||||||
self.populate_module(module_id, items)
|
self.populate_module(module_id, items)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for &module_id in self.input.keys() {
|
for &module_id in self.input.keys() {
|
||||||
|
@ -238,11 +238,25 @@ where
|
||||||
Ok(self.result)
|
Ok(self.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
|
fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> {
|
||||||
let file_id = module_id.source(&self.module_tree).file_id();
|
let file_id = module_id.source(&self.module_tree).file_id();
|
||||||
|
|
||||||
let mut module_items = ModuleScope::default();
|
let mut module_items = ModuleScope::default();
|
||||||
|
|
||||||
|
// Populate extern crates prelude
|
||||||
|
{
|
||||||
|
let root_id = module_id.crate_root(&self.module_tree);
|
||||||
|
let file_id = root_id.source(&self.module_tree).file_id();
|
||||||
|
let crate_graph = self.db.crate_graph();
|
||||||
|
if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) {
|
||||||
|
let krate = Crate::new(crate_id);
|
||||||
|
for dep in krate.dependencies(self.db) {
|
||||||
|
if let Some(module) = dep.krate.root_module(self.db)? {
|
||||||
|
self.add_module_item(&mut module_items, dep.name, module.module_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
for import in input.imports.iter() {
|
for import in input.imports.iter() {
|
||||||
if let Some(name) = import.path.segments.iter().last() {
|
if let Some(name) = import.path.segments.iter().last() {
|
||||||
if let ImportKind::Named(import) = import.kind {
|
if let ImportKind::Named(import) = import.kind {
|
||||||
|
@ -256,10 +270,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Populate explicitelly declared items, except modules
|
||||||
for item in input.items.iter() {
|
for item in input.items.iter() {
|
||||||
if item.kind == MODULE {
|
if item.kind == MODULE {
|
||||||
// handle submodules separatelly
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let def_loc = DefLoc {
|
let def_loc = DefLoc {
|
||||||
|
@ -279,22 +292,28 @@ where
|
||||||
module_items.items.insert(item.name.clone(), resolution);
|
module_items.items.insert(item.name.clone(), resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populate modules
|
||||||
for (name, module_id) in module_id.children(&self.module_tree) {
|
for (name, module_id) in module_id.children(&self.module_tree) {
|
||||||
let def_loc = DefLoc {
|
self.add_module_item(&mut module_items, name, module_id);
|
||||||
kind: DefKind::Module,
|
|
||||||
source_root_id: self.source_root,
|
|
||||||
module_id,
|
|
||||||
source_item_id: module_id.source(&self.module_tree).0,
|
|
||||||
};
|
|
||||||
let def_id = def_loc.id(self.db);
|
|
||||||
let resolution = Resolution {
|
|
||||||
def_id: Some(def_id),
|
|
||||||
import: None,
|
|
||||||
};
|
|
||||||
module_items.items.insert(name, resolution);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.result.per_module.insert(module_id, module_items);
|
self.result.per_module.insert(module_id, module_items);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, module_id: ModuleId) {
|
||||||
|
let def_loc = DefLoc {
|
||||||
|
kind: DefKind::Module,
|
||||||
|
source_root_id: self.source_root,
|
||||||
|
module_id,
|
||||||
|
source_item_id: module_id.source(&self.module_tree).0,
|
||||||
|
};
|
||||||
|
let def_id = def_loc.id(self.db);
|
||||||
|
let resolution = Resolution {
|
||||||
|
def_id: Some(def_id),
|
||||||
|
import: None,
|
||||||
|
};
|
||||||
|
module_items.items.insert(name, resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_imports(&mut self, module_id: ModuleId) -> Cancelable<()> {
|
fn resolve_imports(&mut self, module_id: ModuleId) -> Cancelable<()> {
|
||||||
|
@ -309,35 +328,9 @@ where
|
||||||
ImportKind::Glob => return Ok(()),
|
ImportKind::Glob => return Ok(()),
|
||||||
ImportKind::Named(ptr) => ptr,
|
ImportKind::Named(ptr) => ptr,
|
||||||
};
|
};
|
||||||
let mut segments = import.path.segments.iter().enumerate();
|
|
||||||
|
|
||||||
let mut curr = match import.path.kind {
|
let mut curr = match import.path.kind {
|
||||||
PathKind::Plain => {
|
PathKind::Plain | PathKind::Self_ => module_id,
|
||||||
let root_id = module_id.crate_root(&self.module_tree);
|
|
||||||
let file_id = root_id.source(&self.module_tree).file_id();
|
|
||||||
let crate_graph = self.db.crate_graph();
|
|
||||||
let crate_id = match crate_graph.crate_id_for_crate_root(file_id) {
|
|
||||||
None => return Ok(()),
|
|
||||||
Some(it) => it,
|
|
||||||
};
|
|
||||||
let krate = Crate::new(crate_id);
|
|
||||||
let crate_name = match segments.next() {
|
|
||||||
None => return Ok(()),
|
|
||||||
Some((_, it)) => it,
|
|
||||||
};
|
|
||||||
match krate
|
|
||||||
.dependencies(self.db)
|
|
||||||
.into_iter()
|
|
||||||
.find(|it| &it.name == crate_name)
|
|
||||||
{
|
|
||||||
None => return Ok(()),
|
|
||||||
Some(dep) => match dep.krate.root_module(self.db)? {
|
|
||||||
None => return Ok(()),
|
|
||||||
Some(it) => it.module_id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PathKind::Self_ => module_id,
|
|
||||||
PathKind::Super => {
|
PathKind::Super => {
|
||||||
match module_id.parent(&self.module_tree) {
|
match module_id.parent(&self.module_tree) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
|
@ -348,7 +341,7 @@ where
|
||||||
PathKind::Crate => module_id.crate_root(&self.module_tree),
|
PathKind::Crate => module_id.crate_root(&self.module_tree),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, name) in segments {
|
for (i, name) in import.path.segments.iter().enumerate() {
|
||||||
let is_last = i == import.path.segments.len() - 1;
|
let is_last = i == import.path.segments.len() - 1;
|
||||||
|
|
||||||
let def_id = match self.result.per_module[&curr].items.get(name) {
|
let def_id = match self.result.per_module[&curr].items.get(name) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use salsa::Database;
|
use salsa::Database;
|
||||||
use ra_db::FilesDatabase;
|
use ra_db::{FilesDatabase, CrateGraph};
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -21,7 +21,7 @@ fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_map() {
|
fn item_map_smoke_test() {
|
||||||
let (item_map, module_id) = item_map(
|
let (item_map, module_id) = item_map(
|
||||||
"
|
"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
|
@ -42,6 +42,39 @@ fn test_item_map() {
|
||||||
assert!(resolution.def_id.is_some());
|
assert!(resolution.def_id.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn item_map_across_crates() {
|
||||||
|
let (mut db, files) = MockDatabase::with_files(
|
||||||
|
"
|
||||||
|
//- /main.rs
|
||||||
|
use test_crate::Baz;
|
||||||
|
|
||||||
|
//- /lib.rs
|
||||||
|
pub struct Baz;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let main_id = files.file_id("/main.rs");
|
||||||
|
let lib_id = files.file_id("/lib.rs");
|
||||||
|
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
let main_crate = crate_graph.add_crate_root(main_id);
|
||||||
|
let lib_crate = crate_graph.add_crate_root(lib_id);
|
||||||
|
crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate);
|
||||||
|
|
||||||
|
db.set_crate_graph(crate_graph);
|
||||||
|
|
||||||
|
let source_root = db.file_source_root(main_id);
|
||||||
|
let module = hir::source_binder::module_from_file_id(&db, main_id)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let module_id = module.module_id;
|
||||||
|
let item_map = db.item_map(source_root).unwrap();
|
||||||
|
|
||||||
|
let name = SmolStr::from("Baz");
|
||||||
|
let resolution = &item_map.per_module[&module_id].items[&name];
|
||||||
|
assert!(resolution.def_id.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn typing_inside_a_function_should_not_invalidate_item_map() {
|
fn typing_inside_a_function_should_not_invalidate_item_map() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (mut db, pos) = MockDatabase::with_position(
|
||||||
|
|
Loading…
Reference in a new issue