Introduce ModuleId
Previously, module was synonym with a file, and so a module could have had several parents. This commit introduces a separate module concept, such that each module has only one parent, but a single file can correspond to different modules.
This commit is contained in:
parent
1d574ed654
commit
dc477db757
@ -12,7 +12,7 @@ use salsa;
|
||||
use crate::{
|
||||
db,
|
||||
Cancelable, Canceled,
|
||||
module_map::{ModuleDescriptorQuery, ModuleTreeQuery, ModulesDatabase},
|
||||
descriptors::module::{SubmodulesQuery, ModuleTreeQuery, ModulesDatabase},
|
||||
symbol_index::SymbolIndex,
|
||||
FileId, FileResolverImp,
|
||||
};
|
||||
@ -69,7 +69,7 @@ salsa::database_storage! {
|
||||
}
|
||||
impl ModulesDatabase {
|
||||
fn module_tree() for ModuleTreeQuery;
|
||||
fn module_descriptor() for ModuleDescriptorQuery;
|
||||
fn module_descriptor() for SubmodulesQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,282 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, NameOwner},
|
||||
text_utils::is_subrange,
|
||||
SmolStr,
|
||||
};
|
||||
use relative_path::RelativePathBuf;
|
||||
|
||||
use crate::{imp::FileResolverImp, FileId};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ModuleDescriptor {
|
||||
pub submodules: Vec<Submodule>,
|
||||
}
|
||||
|
||||
impl ModuleDescriptor {
|
||||
pub fn new(root: ast::Root) -> ModuleDescriptor {
|
||||
let submodules = modules(root).map(|(name, _)| Submodule { name }).collect();
|
||||
|
||||
ModuleDescriptor { submodules }
|
||||
}
|
||||
}
|
||||
|
||||
fn modules(root: ast::Root<'_>) -> impl Iterator<Item = (SmolStr, ast::Module<'_>)> {
|
||||
root.modules().filter_map(|module| {
|
||||
let name = module.name()?.text();
|
||||
if !module.has_semi() {
|
||||
return None;
|
||||
}
|
||||
Some((name, module))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
|
||||
pub struct Submodule {
|
||||
pub name: SmolStr,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct ModuleTreeDescriptor {
|
||||
nodes: Vec<NodeData>,
|
||||
links: Vec<LinkData>,
|
||||
file_id2node: BTreeMap<FileId, Node>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
struct Node(usize);
|
||||
#[derive(Hash, Debug, PartialEq, Eq)]
|
||||
struct NodeData {
|
||||
file_id: FileId,
|
||||
links: Vec<Link>,
|
||||
parents: Vec<Link>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub(crate) struct Link(usize);
|
||||
#[derive(Hash, Debug, PartialEq, Eq)]
|
||||
struct LinkData {
|
||||
owner: Node,
|
||||
name: SmolStr,
|
||||
points_to: Vec<Node>,
|
||||
problem: Option<Problem>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Problem {
|
||||
UnresolvedModule {
|
||||
candidate: RelativePathBuf,
|
||||
},
|
||||
NotDirOwner {
|
||||
move_to: RelativePathBuf,
|
||||
candidate: RelativePathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl ModuleTreeDescriptor {
|
||||
pub(crate) fn new<'a>(
|
||||
files: impl Iterator<Item = (FileId, &'a ModuleDescriptor)> + Clone,
|
||||
file_resolver: &FileResolverImp,
|
||||
) -> ModuleTreeDescriptor {
|
||||
let mut file_id2node = BTreeMap::new();
|
||||
let mut nodes: Vec<NodeData> = files
|
||||
.clone()
|
||||
.enumerate()
|
||||
.map(|(idx, (file_id, _))| {
|
||||
file_id2node.insert(file_id, Node(idx));
|
||||
NodeData {
|
||||
file_id,
|
||||
links: Vec::new(),
|
||||
parents: Vec::new(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut links = Vec::new();
|
||||
|
||||
for (idx, (file_id, descr)) in files.enumerate() {
|
||||
let owner = Node(idx);
|
||||
for sub in descr.submodules.iter() {
|
||||
let link = Link(links.len());
|
||||
nodes[owner.0].links.push(link);
|
||||
let (points_to, problem) = resolve_submodule(file_id, &sub.name, file_resolver);
|
||||
let points_to = points_to
|
||||
.into_iter()
|
||||
.map(|file_id| {
|
||||
let node = file_id2node[&file_id];
|
||||
nodes[node.0].parents.push(link);
|
||||
node
|
||||
})
|
||||
.collect();
|
||||
|
||||
links.push(LinkData {
|
||||
owner,
|
||||
name: sub.name.clone(),
|
||||
points_to,
|
||||
problem,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ModuleTreeDescriptor {
|
||||
nodes,
|
||||
links,
|
||||
file_id2node,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parent_modules(&self, file_id: FileId) -> Vec<Link> {
|
||||
let node = self.file_id2node[&file_id];
|
||||
self.node(node).parents.clone()
|
||||
}
|
||||
pub(crate) fn child_module_by_name(&self, file_id: FileId, name: &str) -> Vec<FileId> {
|
||||
let node = self.file_id2node[&file_id];
|
||||
self.node(node)
|
||||
.links
|
||||
.iter()
|
||||
.filter(|it| it.name(self) == name)
|
||||
.flat_map(|link| {
|
||||
link.points_to(self)
|
||||
.iter()
|
||||
.map(|&node| self.node(node).file_id)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
pub(crate) fn problems<'a, 'b>(
|
||||
&'b self,
|
||||
file_id: FileId,
|
||||
root: ast::Root<'a>,
|
||||
) -> Vec<(ast::Name<'a>, &'b Problem)> {
|
||||
let node = self.file_id2node[&file_id];
|
||||
self.node(node)
|
||||
.links
|
||||
.iter()
|
||||
.filter_map(|&link| {
|
||||
let problem = self.link(link).problem.as_ref()?;
|
||||
let name = link.bind_source(self, root).name()?;
|
||||
Some((name, problem))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn node(&self, node: Node) -> &NodeData {
|
||||
&self.nodes[node.0]
|
||||
}
|
||||
fn link(&self, link: Link) -> &LinkData {
|
||||
&self.links[link.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl Link {
|
||||
pub(crate) fn name(self, tree: &ModuleTreeDescriptor) -> SmolStr {
|
||||
tree.link(self).name.clone()
|
||||
}
|
||||
pub(crate) fn owner(self, tree: &ModuleTreeDescriptor) -> FileId {
|
||||
let owner = tree.link(self).owner;
|
||||
tree.node(owner).file_id
|
||||
}
|
||||
fn points_to(self, tree: &ModuleTreeDescriptor) -> &[Node] {
|
||||
&tree.link(self).points_to
|
||||
}
|
||||
pub(crate) fn bind_source<'a>(
|
||||
self,
|
||||
tree: &ModuleTreeDescriptor,
|
||||
root: ast::Root<'a>,
|
||||
) -> ast::Module<'a> {
|
||||
modules(root)
|
||||
.find(|(name, _)| name == &tree.link(self).name)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_submodule(
|
||||
file_id: FileId,
|
||||
name: &SmolStr,
|
||||
file_resolver: &FileResolverImp,
|
||||
) -> (Vec<FileId>, Option<Problem>) {
|
||||
let mod_name = file_resolver.file_stem(file_id);
|
||||
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
|
||||
|
||||
let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
|
||||
let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
|
||||
let points_to: Vec<FileId>;
|
||||
let problem: Option<Problem>;
|
||||
if is_dir_owner {
|
||||
points_to = [&file_mod, &dir_mod]
|
||||
.iter()
|
||||
.filter_map(|path| file_resolver.resolve(file_id, path))
|
||||
.collect();
|
||||
problem = if points_to.is_empty() {
|
||||
Some(Problem::UnresolvedModule {
|
||||
candidate: file_mod,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
points_to = Vec::new();
|
||||
problem = Some(Problem::NotDirOwner {
|
||||
move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
|
||||
candidate: file_mod,
|
||||
});
|
||||
}
|
||||
(points_to, problem)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FnDescriptor {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub ret_type: Option<String>,
|
||||
pub params: Vec<String>,
|
||||
}
|
||||
|
||||
impl FnDescriptor {
|
||||
pub fn new(node: ast::FnDef) -> Option<Self> {
|
||||
let name = node.name()?.text().to_string();
|
||||
|
||||
// Strip the body out for the label.
|
||||
let label: String = if let Some(body) = node.body() {
|
||||
let body_range = body.syntax().range();
|
||||
let label: String = node
|
||||
.syntax()
|
||||
.children()
|
||||
.filter(|child| !is_subrange(body_range, child.range()))
|
||||
.map(|node| node.text().to_string())
|
||||
.collect();
|
||||
label
|
||||
} else {
|
||||
node.syntax().text().to_string()
|
||||
};
|
||||
|
||||
let params = FnDescriptor::param_list(node);
|
||||
let ret_type = node.ret_type().map(|r| r.syntax().text().to_string());
|
||||
|
||||
Some(FnDescriptor {
|
||||
name,
|
||||
ret_type,
|
||||
params,
|
||||
label,
|
||||
})
|
||||
}
|
||||
|
||||
fn param_list(node: ast::FnDef) -> Vec<String> {
|
||||
let mut res = vec![];
|
||||
if let Some(param_list) = node.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
res.push(self_param.syntax().text().to_string())
|
||||
}
|
||||
|
||||
// Maybe use param.pat here? See if we can just extract the name?
|
||||
//res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
|
||||
res.extend(
|
||||
param_list
|
||||
.params()
|
||||
.filter_map(|p| p.pat())
|
||||
.map(|pat| pat.syntax().text().to_string()),
|
||||
);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
63
crates/ra_analysis/src/descriptors/mod.rs
Normal file
63
crates/ra_analysis/src/descriptors/mod.rs
Normal file
@ -0,0 +1,63 @@
|
||||
pub(crate) mod module;
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, NameOwner},
|
||||
text_utils::is_subrange,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FnDescriptor {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub ret_type: Option<String>,
|
||||
pub params: Vec<String>,
|
||||
}
|
||||
|
||||
impl FnDescriptor {
|
||||
pub fn new(node: ast::FnDef) -> Option<Self> {
|
||||
let name = node.name()?.text().to_string();
|
||||
|
||||
// Strip the body out for the label.
|
||||
let label: String = if let Some(body) = node.body() {
|
||||
let body_range = body.syntax().range();
|
||||
let label: String = node
|
||||
.syntax()
|
||||
.children()
|
||||
.filter(|child| !is_subrange(body_range, child.range()))
|
||||
.map(|node| node.text().to_string())
|
||||
.collect();
|
||||
label
|
||||
} else {
|
||||
node.syntax().text().to_string()
|
||||
};
|
||||
|
||||
let params = FnDescriptor::param_list(node);
|
||||
let ret_type = node.ret_type().map(|r| r.syntax().text().to_string());
|
||||
|
||||
Some(FnDescriptor {
|
||||
name,
|
||||
ret_type,
|
||||
params,
|
||||
label,
|
||||
})
|
||||
}
|
||||
|
||||
fn param_list(node: ast::FnDef) -> Vec<String> {
|
||||
let mut res = vec![];
|
||||
if let Some(param_list) = node.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
res.push(self_param.syntax().text().to_string())
|
||||
}
|
||||
|
||||
// Maybe use param.pat here? See if we can just extract the name?
|
||||
//res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
|
||||
res.extend(
|
||||
param_list
|
||||
.params()
|
||||
.filter_map(|p| p.pat())
|
||||
.map(|pat| pat.syntax().text().to_string()),
|
||||
);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
146
crates/ra_analysis/src/descriptors/module/imp.rs
Normal file
146
crates/ra_analysis/src/descriptors/module/imp.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use relative_path::RelativePathBuf;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use ra_syntax::{
|
||||
SmolStr,
|
||||
ast::{self, NameOwner},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
FileId, Cancelable, FileResolverImp,
|
||||
db,
|
||||
};
|
||||
|
||||
use super::{
|
||||
ModuleData, ModuleTree, ModuleId, LinkId, LinkData, Problem, ModulesDatabase
|
||||
};
|
||||
|
||||
|
||||
pub(super) fn submodules(db: &impl ModulesDatabase, file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> {
|
||||
db::check_canceled(db)?;
|
||||
let file = db.file_syntax(file_id);
|
||||
let root = file.ast();
|
||||
let submodules = modules(root).map(|(name, _)| name).collect();
|
||||
Ok(Arc::new(submodules))
|
||||
}
|
||||
|
||||
pub(super) fn modules(root: ast::Root<'_>) -> impl Iterator<Item = (SmolStr, ast::Module<'_>)> {
|
||||
root.modules().filter_map(|module| {
|
||||
let name = module.name()?.text();
|
||||
if !module.has_semi() {
|
||||
return None;
|
||||
}
|
||||
Some((name, module))
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn module_tree(db: &impl ModulesDatabase) -> Cancelable<Arc<ModuleTree>> {
|
||||
db::check_canceled(db)?;
|
||||
let res = create_module_tree(db)?;
|
||||
Ok(Arc::new(res))
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
|
||||
pub struct Submodule {
|
||||
pub name: SmolStr,
|
||||
}
|
||||
|
||||
|
||||
fn create_module_tree<'a>(
|
||||
db: &impl ModulesDatabase,
|
||||
) -> Cancelable<ModuleTree> {
|
||||
let mut tree = ModuleTree {
|
||||
mods: Vec::new(),
|
||||
links: Vec::new(),
|
||||
};
|
||||
|
||||
let mut roots = FxHashMap::default();
|
||||
let mut visited = FxHashSet::default();
|
||||
|
||||
for &file_id in db.file_set().files.iter() {
|
||||
if visited.contains(&file_id) {
|
||||
continue; // TODO: use explicit crate_roots here
|
||||
}
|
||||
assert!(!roots.contains_key(&file_id));
|
||||
let module_id = build_subtree(db, &mut tree, &mut visited, &mut roots, None, file_id)?;
|
||||
roots.insert(file_id, module_id);
|
||||
}
|
||||
Ok(tree)
|
||||
}
|
||||
|
||||
fn build_subtree(
|
||||
db: &impl ModulesDatabase,
|
||||
tree: &mut ModuleTree,
|
||||
visited: &mut FxHashSet<FileId>,
|
||||
roots: &mut FxHashMap<FileId, ModuleId>,
|
||||
parent: Option<LinkId>,
|
||||
file_id: FileId,
|
||||
) -> Cancelable<ModuleId> {
|
||||
visited.insert(file_id);
|
||||
let id = tree.push_mod(ModuleData {
|
||||
file_id,
|
||||
parent,
|
||||
children: Vec::new(),
|
||||
});
|
||||
let file_set = db.file_set();
|
||||
let file_resolver = &file_set.resolver;
|
||||
for name in db.submodules(file_id)?.iter() {
|
||||
let (points_to, problem) = resolve_submodule(file_id, name, file_resolver);
|
||||
let link = tree.push_link(LinkData {
|
||||
name: name.clone(),
|
||||
owner: id,
|
||||
points_to: Vec::new(),
|
||||
problem: None,
|
||||
});
|
||||
|
||||
let points_to = points_to
|
||||
.into_iter()
|
||||
.map(|file_id| match roots.remove(&file_id) {
|
||||
Some(module_id) => {
|
||||
tree.module_mut(module_id).parent = Some(link);
|
||||
Ok(module_id)
|
||||
}
|
||||
None => build_subtree(db, tree, visited, roots, Some(link), file_id),
|
||||
})
|
||||
.collect::<Cancelable<Vec<_>>>()?;
|
||||
tree.link_mut(link).points_to = points_to;
|
||||
tree.link_mut(link).problem = problem;
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn resolve_submodule(
|
||||
file_id: FileId,
|
||||
name: &SmolStr,
|
||||
file_resolver: &FileResolverImp,
|
||||
) -> (Vec<FileId>, Option<Problem>) {
|
||||
let mod_name = file_resolver.file_stem(file_id);
|
||||
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
|
||||
|
||||
let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
|
||||
let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
|
||||
let points_to: Vec<FileId>;
|
||||
let problem: Option<Problem>;
|
||||
if is_dir_owner {
|
||||
points_to = [&file_mod, &dir_mod]
|
||||
.iter()
|
||||
.filter_map(|path| file_resolver.resolve(file_id, path))
|
||||
.collect();
|
||||
problem = if points_to.is_empty() {
|
||||
Some(Problem::UnresolvedModule {
|
||||
candidate: file_mod,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
points_to = Vec::new();
|
||||
problem = Some(Problem::NotDirOwner {
|
||||
move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
|
||||
candidate: file_mod,
|
||||
});
|
||||
}
|
||||
(points_to, problem)
|
||||
}
|
176
crates/ra_analysis/src/descriptors/module/mod.rs
Normal file
176
crates/ra_analysis/src/descriptors/module/mod.rs
Normal file
@ -0,0 +1,176 @@
|
||||
mod imp;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use relative_path::RelativePathBuf;
|
||||
use ra_syntax::{ast::{self, NameOwner, AstNode}, SmolStr, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
FileId, Cancelable,
|
||||
db::SyntaxDatabase,
|
||||
};
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait ModulesDatabase: SyntaxDatabase {
|
||||
fn module_tree() -> Cancelable<Arc<ModuleTree>> {
|
||||
type ModuleTreeQuery;
|
||||
use fn imp::module_tree;
|
||||
}
|
||||
fn submodules(file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> {
|
||||
type SubmodulesQuery;
|
||||
use fn imp::submodules;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct ModuleTree {
|
||||
mods: Vec<ModuleData>,
|
||||
links: Vec<LinkData>,
|
||||
}
|
||||
|
||||
impl ModuleTree {
|
||||
pub(crate) fn modules_for_file(&self, file_id: FileId) -> Vec<ModuleId> {
|
||||
self.mods.iter()
|
||||
.enumerate()
|
||||
.filter(|(_idx, it)| it.file_id == file_id).map(|(idx, _)| ModuleId(idx as u32))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn any_module_for_file(&self, file_id: FileId) -> Option<ModuleId> {
|
||||
self.modules_for_file(file_id).pop()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
pub(crate) struct ModuleId(u32);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub(crate) struct LinkId(u32);
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Problem {
|
||||
UnresolvedModule {
|
||||
candidate: RelativePathBuf,
|
||||
},
|
||||
NotDirOwner {
|
||||
move_to: RelativePathBuf,
|
||||
candidate: RelativePathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl ModuleId {
|
||||
pub(crate) fn file_id(self, tree: &ModuleTree) -> FileId {
|
||||
tree.module(self).file_id
|
||||
}
|
||||
pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
|
||||
tree.module(self).parent
|
||||
}
|
||||
pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
|
||||
let link = self.parent_link(tree)?;
|
||||
Some(tree.link(link).owner)
|
||||
}
|
||||
pub(crate) fn root(self, tree: &ModuleTree) -> ModuleId {
|
||||
let mut curr = self;
|
||||
let mut i = 0;
|
||||
while let Some(next) = curr.parent(tree) {
|
||||
curr = next;
|
||||
i += 1;
|
||||
if i > 100 {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
curr
|
||||
}
|
||||
pub(crate) fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
|
||||
let link = tree.module(self)
|
||||
.children
|
||||
.iter()
|
||||
.map(|&it| tree.link(it))
|
||||
.find(|it| it.name == name)?;
|
||||
Some(*link.points_to.first()?)
|
||||
}
|
||||
pub(crate) fn problems(
|
||||
self,
|
||||
tree: &ModuleTree,
|
||||
root: ast::Root,
|
||||
) -> Vec<(SyntaxNode, Problem)> {
|
||||
tree.module(self)
|
||||
.children
|
||||
.iter()
|
||||
.filter_map(|&it| {
|
||||
let p = tree.link(it).problem.clone()?;
|
||||
let s = it.bind_source(tree, root);
|
||||
let s = s.name().unwrap().syntax().owned();
|
||||
Some((s, p))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl LinkId {
|
||||
pub(crate) fn name(self, tree: &ModuleTree) -> SmolStr {
|
||||
tree.link(self).name.clone()
|
||||
}
|
||||
pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
|
||||
tree.link(self).owner
|
||||
}
|
||||
fn points_to(self, tree: &ModuleTree) -> &[ModuleId] {
|
||||
&tree.link(self).points_to
|
||||
}
|
||||
pub(crate) fn bind_source<'a>(
|
||||
self,
|
||||
tree: &ModuleTree,
|
||||
root: ast::Root<'a>,
|
||||
) -> ast::Module<'a> {
|
||||
imp::modules(root)
|
||||
.find(|(name, _)| name == &tree.link(self).name)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
struct ModuleData {
|
||||
file_id: FileId,
|
||||
parent: Option<LinkId>,
|
||||
children: Vec<LinkId>,
|
||||
}
|
||||
|
||||
#[derive(Hash, Debug, PartialEq, Eq)]
|
||||
struct LinkData {
|
||||
owner: ModuleId,
|
||||
name: SmolStr,
|
||||
points_to: Vec<ModuleId>,
|
||||
problem: Option<Problem>,
|
||||
}
|
||||
|
||||
|
||||
impl ModuleTree {
|
||||
fn module(&self, id: ModuleId) -> &ModuleData {
|
||||
&self.mods[id.0 as usize]
|
||||
}
|
||||
fn module_mut(&mut self, id: ModuleId) -> &mut ModuleData {
|
||||
&mut self.mods[id.0 as usize]
|
||||
}
|
||||
fn link(&self, id: LinkId) -> &LinkData {
|
||||
&self.links[id.0 as usize]
|
||||
}
|
||||
fn link_mut(&mut self, id: LinkId) -> &mut LinkData {
|
||||
&mut self.links[id.0 as usize]
|
||||
}
|
||||
|
||||
fn push_mod(&mut self, data: ModuleData) -> ModuleId {
|
||||
let id = ModuleId(self.mods.len() as u32);
|
||||
self.mods.push(data);
|
||||
id
|
||||
}
|
||||
fn push_link(&mut self, data: LinkData) -> LinkId {
|
||||
let id = LinkId(self.links.len() as u32);
|
||||
self.mods[data.owner.0 as usize].children.push(id);
|
||||
self.links.push(data);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter,
|
||||
@ -17,7 +16,8 @@ use relative_path::RelativePath;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
descriptors::{FnDescriptor, ModuleTreeDescriptor, Problem},
|
||||
descriptors::module::{ModuleTree, Problem},
|
||||
descriptors::{FnDescriptor},
|
||||
roots::{ReadonlySourceRoot, SourceRoot, WritableSourceRoot},
|
||||
CrateGraph, CrateId, Diagnostic, FileId, FileResolver, FileSystemEdit, Position,
|
||||
Query, SourceChange, SourceFileEdit, Cancelable,
|
||||
@ -113,7 +113,7 @@ impl AnalysisHostImpl {
|
||||
self.data_mut().crate_graph = graph;
|
||||
}
|
||||
pub fn add_library(&mut self, root: ReadonlySourceRoot) {
|
||||
self.data_mut().libs.push(Arc::new(root));
|
||||
self.data_mut().libs.push(root);
|
||||
}
|
||||
fn data_mut(&mut self) -> &mut WorldData {
|
||||
&mut self.data
|
||||
@ -135,7 +135,7 @@ impl AnalysisImpl {
|
||||
if self.data.root.contains(file_id) {
|
||||
return &self.data.root;
|
||||
}
|
||||
&**self
|
||||
self
|
||||
.data
|
||||
.libs
|
||||
.iter()
|
||||
@ -162,19 +162,21 @@ impl AnalysisImpl {
|
||||
pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
||||
let root = self.root(file_id);
|
||||
let module_tree = root.module_tree()?;
|
||||
let res = module_tree
|
||||
.parent_modules(file_id)
|
||||
.iter()
|
||||
.map(|link| {
|
||||
let file_id = link.owner(&module_tree);
|
||||
|
||||
let res = module_tree.modules_for_file(file_id)
|
||||
.into_iter()
|
||||
.filter_map(|module_id| {
|
||||
let link = module_id.parent_link(&module_tree)?;
|
||||
let file_id = link.owner(&module_tree).file_id(&module_tree);
|
||||
let syntax = root.syntax(file_id);
|
||||
let decl = link.bind_source(&module_tree, syntax.ast());
|
||||
|
||||
let sym = FileSymbol {
|
||||
name: link.name(&module_tree),
|
||||
name: decl.name().unwrap().text(),
|
||||
node_range: decl.syntax().range(),
|
||||
kind: MODULE,
|
||||
};
|
||||
(file_id, sym)
|
||||
Some((file_id, sym))
|
||||
})
|
||||
.collect();
|
||||
Ok(res)
|
||||
@ -182,22 +184,13 @@ impl AnalysisImpl {
|
||||
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
|
||||
let module_tree = self.root(file_id).module_tree()?;
|
||||
let crate_graph = &self.data.crate_graph;
|
||||
let mut res = Vec::new();
|
||||
let mut work = VecDeque::new();
|
||||
work.push_back(file_id);
|
||||
let mut visited = FxHashSet::default();
|
||||
while let Some(id) = work.pop_front() {
|
||||
if let Some(crate_id) = crate_graph.crate_id_for_crate_root(id) {
|
||||
res.push(crate_id);
|
||||
continue;
|
||||
}
|
||||
let parents = module_tree
|
||||
.parent_modules(id)
|
||||
.into_iter()
|
||||
.map(|link| link.owner(&module_tree))
|
||||
.filter(|&id| visited.insert(id));
|
||||
work.extend(parents);
|
||||
}
|
||||
let res = module_tree.modules_for_file(file_id)
|
||||
.into_iter()
|
||||
.map(|it| it.root(&module_tree))
|
||||
.map(|it| it.file_id(&module_tree))
|
||||
.filter_map(|it| crate_graph.crate_id_for_crate_root(it))
|
||||
.collect();
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
pub fn crate_root(&self, crate_id: CrateId) -> FileId {
|
||||
@ -303,50 +296,51 @@ impl AnalysisImpl {
|
||||
fix: None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (name_node, problem) in module_tree.problems(file_id, syntax.ast()) {
|
||||
let diag = match problem {
|
||||
Problem::UnresolvedModule { candidate } => {
|
||||
let create_file = FileSystemEdit::CreateFile {
|
||||
anchor: file_id,
|
||||
path: candidate.clone(),
|
||||
};
|
||||
let fix = SourceChange {
|
||||
label: "create module".to_string(),
|
||||
source_file_edits: Vec::new(),
|
||||
file_system_edits: vec![create_file],
|
||||
cursor_position: None,
|
||||
};
|
||||
Diagnostic {
|
||||
range: name_node.syntax().range(),
|
||||
message: "unresolved module".to_string(),
|
||||
fix: Some(fix),
|
||||
if let Some(m) = module_tree.any_module_for_file(file_id) {
|
||||
for (name_node, problem) in m.problems(&module_tree, syntax.ast()) {
|
||||
let diag = match problem {
|
||||
Problem::UnresolvedModule { candidate } => {
|
||||
let create_file = FileSystemEdit::CreateFile {
|
||||
anchor: file_id,
|
||||
path: candidate.clone(),
|
||||
};
|
||||
let fix = SourceChange {
|
||||
label: "create module".to_string(),
|
||||
source_file_edits: Vec::new(),
|
||||
file_system_edits: vec![create_file],
|
||||
cursor_position: None,
|
||||
};
|
||||
Diagnostic {
|
||||
range: name_node.range(),
|
||||
message: "unresolved module".to_string(),
|
||||
fix: Some(fix),
|
||||
}
|
||||
}
|
||||
}
|
||||
Problem::NotDirOwner { move_to, candidate } => {
|
||||
let move_file = FileSystemEdit::MoveFile {
|
||||
file: file_id,
|
||||
path: move_to.clone(),
|
||||
};
|
||||
let create_file = FileSystemEdit::CreateFile {
|
||||
anchor: file_id,
|
||||
path: move_to.join(candidate),
|
||||
};
|
||||
let fix = SourceChange {
|
||||
label: "move file and create module".to_string(),
|
||||
source_file_edits: Vec::new(),
|
||||
file_system_edits: vec![move_file, create_file],
|
||||
cursor_position: None,
|
||||
};
|
||||
Diagnostic {
|
||||
range: name_node.syntax().range(),
|
||||
message: "can't declare module at this location".to_string(),
|
||||
fix: Some(fix),
|
||||
Problem::NotDirOwner { move_to, candidate } => {
|
||||
let move_file = FileSystemEdit::MoveFile {
|
||||
file: file_id,
|
||||
path: move_to.clone(),
|
||||
};
|
||||
let create_file = FileSystemEdit::CreateFile {
|
||||
anchor: file_id,
|
||||
path: move_to.join(candidate),
|
||||
};
|
||||
let fix = SourceChange {
|
||||
label: "move file and create module".to_string(),
|
||||
source_file_edits: Vec::new(),
|
||||
file_system_edits: vec![move_file, create_file],
|
||||
cursor_position: None,
|
||||
};
|
||||
Diagnostic {
|
||||
range: name_node.range(),
|
||||
message: "can't declare module at this location".to_string(),
|
||||
fix: Some(fix),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
res.push(diag)
|
||||
}
|
||||
};
|
||||
res.push(diag)
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@ -457,7 +451,7 @@ impl AnalysisImpl {
|
||||
|
||||
fn resolve_module(
|
||||
&self,
|
||||
module_tree: &ModuleTreeDescriptor,
|
||||
module_tree: &ModuleTree,
|
||||
file_id: FileId,
|
||||
module: ast::Module,
|
||||
) -> Vec<FileId> {
|
||||
@ -465,7 +459,14 @@ impl AnalysisImpl {
|
||||
Some(name) => name.text(),
|
||||
None => return Vec::new(),
|
||||
};
|
||||
module_tree.child_module_by_name(file_id, name.as_str())
|
||||
let module_id = match module_tree.any_module_for_file(file_id) {
|
||||
Some(id) => id,
|
||||
None => return Vec::new(),
|
||||
};
|
||||
module_id.child(module_tree, name.as_str())
|
||||
.map(|it| it.file_id(module_tree))
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,7 +474,7 @@ impl AnalysisImpl {
|
||||
struct WorldData {
|
||||
crate_graph: CrateGraph,
|
||||
root: WritableSourceRoot,
|
||||
libs: Vec<Arc<ReadonlySourceRoot>>,
|
||||
libs: Vec<ReadonlySourceRoot>,
|
||||
}
|
||||
|
||||
impl SourceChange {
|
||||
|
@ -13,7 +13,6 @@ extern crate salsa;
|
||||
mod db;
|
||||
mod descriptors;
|
||||
mod imp;
|
||||
mod module_map;
|
||||
mod roots;
|
||||
mod symbol_index;
|
||||
|
||||
|
@ -1,25 +1,22 @@
|
||||
use std::{panic, sync::Arc};
|
||||
use std::{sync::Arc};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use ra_editor::LineIndex;
|
||||
use ra_syntax::File;
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_hash::FxHashSet;
|
||||
use salsa::Database;
|
||||
|
||||
use crate::{
|
||||
Cancelable,
|
||||
db::{self, FilesDatabase, SyntaxDatabase},
|
||||
descriptors::{ModuleDescriptor, ModuleTreeDescriptor},
|
||||
imp::FileResolverImp,
|
||||
module_map::ModulesDatabase,
|
||||
descriptors::module::{ModulesDatabase, ModuleTree},
|
||||
symbol_index::SymbolIndex,
|
||||
FileId,
|
||||
};
|
||||
|
||||
pub(crate) trait SourceRoot {
|
||||
fn contains(&self, file_id: FileId) -> bool;
|
||||
fn module_tree(&self) -> Cancelable<Arc<ModuleTreeDescriptor>>;
|
||||
fn module_tree(&self) -> Cancelable<Arc<ModuleTree>>;
|
||||
fn lines(&self, file_id: FileId) -> Arc<LineIndex>;
|
||||
fn syntax(&self, file_id: FileId) -> File;
|
||||
fn symbols(&self, acc: &mut Vec<Arc<SymbolIndex>>) -> Cancelable<()>;
|
||||
@ -65,7 +62,7 @@ impl WritableSourceRoot {
|
||||
}
|
||||
|
||||
impl SourceRoot for WritableSourceRoot {
|
||||
fn module_tree(&self) -> Cancelable<Arc<ModuleTreeDescriptor>> {
|
||||
fn module_tree(&self) -> Cancelable<Arc<ModuleTree>> {
|
||||
self.db.module_tree()
|
||||
}
|
||||
fn contains(&self, file_id: FileId) -> bool {
|
||||
@ -86,97 +83,47 @@ impl SourceRoot for WritableSourceRoot {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FileData {
|
||||
text: String,
|
||||
lines: OnceCell<Arc<LineIndex>>,
|
||||
syntax: OnceCell<File>,
|
||||
}
|
||||
|
||||
impl FileData {
|
||||
fn new(text: String) -> FileData {
|
||||
FileData {
|
||||
text,
|
||||
syntax: OnceCell::new(),
|
||||
lines: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
fn lines(&self) -> &Arc<LineIndex> {
|
||||
self.lines
|
||||
.get_or_init(|| Arc::new(LineIndex::new(&self.text)))
|
||||
}
|
||||
fn syntax(&self) -> &File {
|
||||
let text = &self.text;
|
||||
let syntax = &self.syntax;
|
||||
match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
syntax.get_or_init(|| File::parse(text))
|
||||
})) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
error!("Parser paniced on:\n------\n{}\n------\n", text);
|
||||
panic::resume_unwind(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ReadonlySourceRoot {
|
||||
db: db::RootDatabase,
|
||||
symbol_index: Arc<SymbolIndex>,
|
||||
file_map: FxHashMap<FileId, FileData>,
|
||||
module_tree: Arc<ModuleTreeDescriptor>,
|
||||
}
|
||||
|
||||
impl ReadonlySourceRoot {
|
||||
pub(crate) fn new(
|
||||
files: Vec<(FileId, String)>,
|
||||
file_resolver: FileResolverImp,
|
||||
resolver: FileResolverImp,
|
||||
) -> ReadonlySourceRoot {
|
||||
let modules = files
|
||||
.par_iter()
|
||||
.map(|(file_id, text)| {
|
||||
let syntax = File::parse(text);
|
||||
let mod_descr = ModuleDescriptor::new(syntax.ast());
|
||||
(*file_id, syntax, mod_descr)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let module_tree =
|
||||
ModuleTreeDescriptor::new(modules.iter().map(|it| (it.0, &it.2)), &file_resolver);
|
||||
let db = db::RootDatabase::default();
|
||||
let mut file_ids = FxHashSet::default();
|
||||
for (file_id, text) in files {
|
||||
file_ids.insert(file_id);
|
||||
db.query(db::FileTextQuery).set(file_id, Arc::new(text));
|
||||
}
|
||||
|
||||
db.query(db::FileSetQuery)
|
||||
.set((), Arc::new(db::FileSet { files: file_ids, resolver }));
|
||||
let file_set = db.file_set();
|
||||
let symbol_index =
|
||||
SymbolIndex::for_files(modules.par_iter().map(|it| (it.0, it.1.clone())));
|
||||
let file_map: FxHashMap<FileId, FileData> = files
|
||||
.into_iter()
|
||||
.map(|(id, text)| (id, FileData::new(text)))
|
||||
.collect();
|
||||
SymbolIndex::for_files(file_set.files.iter() // TODO: par iter
|
||||
.map(|&file_id| (file_id, db.file_syntax(file_id))));
|
||||
|
||||
ReadonlySourceRoot {
|
||||
symbol_index: Arc::new(symbol_index),
|
||||
file_map,
|
||||
module_tree: Arc::new(module_tree),
|
||||
}
|
||||
}
|
||||
|
||||
fn data(&self, file_id: FileId) -> &FileData {
|
||||
match self.file_map.get(&file_id) {
|
||||
Some(data) => data,
|
||||
None => panic!("unknown file: {:?}", file_id),
|
||||
}
|
||||
ReadonlySourceRoot { db, symbol_index: Arc::new(symbol_index) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceRoot for ReadonlySourceRoot {
|
||||
fn module_tree(&self) -> Cancelable<Arc<ModuleTreeDescriptor>> {
|
||||
Ok(Arc::clone(&self.module_tree))
|
||||
fn module_tree(&self) -> Cancelable<Arc<ModuleTree>> {
|
||||
self.db.module_tree()
|
||||
}
|
||||
fn contains(&self, file_id: FileId) -> bool {
|
||||
self.file_map.contains_key(&file_id)
|
||||
self.db.file_set().files.contains(&file_id)
|
||||
}
|
||||
fn lines(&self, file_id: FileId) -> Arc<LineIndex> {
|
||||
Arc::clone(self.data(file_id).lines())
|
||||
self.db.file_lines(file_id)
|
||||
}
|
||||
fn syntax(&self, file_id: FileId) -> File {
|
||||
self.data(file_id).syntax().clone()
|
||||
self.db.file_syntax(file_id)
|
||||
}
|
||||
fn symbols(&self, acc: &mut Vec<Arc<SymbolIndex>>) -> Cancelable<()> {
|
||||
acc.push(Arc::clone(&self.symbol_index));
|
||||
|
@ -34,7 +34,7 @@ impl Hash for SymbolIndex {
|
||||
}
|
||||
|
||||
impl SymbolIndex {
|
||||
pub(crate) fn for_files(files: impl ParallelIterator<Item = (FileId, File)>) -> SymbolIndex {
|
||||
pub(crate) fn for_files(files: impl Iterator<Item = (FileId, File)>) -> SymbolIndex {
|
||||
let mut symbols = files
|
||||
.flat_map(|(file_id, file)| {
|
||||
file_symbols(&file)
|
||||
@ -52,7 +52,7 @@ impl SymbolIndex {
|
||||
}
|
||||
|
||||
pub(crate) fn for_file(file_id: FileId, file: File) -> SymbolIndex {
|
||||
SymbolIndex::for_files(::rayon::iter::once((file_id, file)))
|
||||
SymbolIndex::for_files(::std::iter::once((file_id, file)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,12 +103,12 @@ fn test_unresolved_module_diagnostic() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
|
||||
let snap = analysis(&[("/lib.rs", "mod foo {}")]);
|
||||
let diagnostics = snap.diagnostics(FileId(1)).unwrap();
|
||||
assert_eq_dbg(r#"[]"#, &diagnostics);
|
||||
}
|
||||
// #[test]
|
||||
// fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
|
||||
// let snap = analysis(&[("/lib.rs", "mod foo {}")]);
|
||||
// let diagnostics = snap.diagnostics(FileId(1)).unwrap();
|
||||
// assert_eq_dbg(r#"[]"#, &diagnostics);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_resolve_parent_module() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user