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:
Aleksey Kladov 2018-10-23 19:15:31 +03:00
parent 1d574ed654
commit dc477db757
10 changed files with 493 additions and 443 deletions

View File

@ -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;
}
}
}

View File

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

View 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
}
}

View 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)
}

View 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
}
}

View File

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

View File

@ -13,7 +13,6 @@ extern crate salsa;
mod db;
mod descriptors;
mod imp;
mod module_map;
mod roots;
mod symbol_index;

View File

@ -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));

View File

@ -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)))
}
}

View 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() {