234: Global module r=matklad a=matklad

This series of commits re-introdces `ModuleDescriptor` as one stop shop for all information about a module.

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2018-11-20 14:33:54 +00:00
commit 713c3ea30b
9 changed files with 233 additions and 203 deletions

View file

@ -2,7 +2,6 @@ mod reference_completion;
use ra_editor::find_node_at_offset;
use ra_syntax::{
algo::find_leaf_at_offset,
algo::visit::{visitor_ctx, VisitorCtx},
ast,
AstNode, AtomEdit,
@ -12,8 +11,9 @@ use rustc_hash::{FxHashMap};
use crate::{
db::{self, SyntaxDatabase},
descriptors::{DescriptorDatabase, module::ModuleSource},
input::{FilesDatabase},
descriptors::{
module::{ModuleDescriptor}
},
Cancelable, FilePosition
};
@ -38,14 +38,7 @@ pub(crate) fn completions(
original_file.reparse(&edit)
};
let leaf = match find_leaf_at_offset(original_file.syntax(), position.offset).left_biased() {
None => return Ok(None),
Some(it) => it,
};
let source_root_id = db.file_source_root(position.file_id);
let module_tree = db.module_tree(source_root_id)?;
let module_source = ModuleSource::for_node(position.file_id, leaf);
let module_id = match module_tree.any_module_for_source(module_source) {
let module = match ModuleDescriptor::guess_from_position(db, position)? {
None => return Ok(None),
Some(it) => it,
};
@ -55,15 +48,7 @@ pub(crate) fn completions(
// First, let's try to complete a reference to some declaration.
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
has_completions = true;
reference_completion::completions(
&mut res,
db,
source_root_id,
&module_tree,
module_id,
&file,
name_ref,
)?;
reference_completion::completions(&mut res, db, &module, &file, name_ref)?;
// special case, `trait T { fn foo(i_am_a_name_ref) {} }`
if is_node::<ast::Param>(name_ref.syntax()) {
param_completions(name_ref.syntax(), &mut res);

View file

@ -9,20 +9,16 @@ use ra_syntax::{
use crate::{
db::RootDatabase,
input::{SourceRootId},
completion::CompletionItem,
descriptors::module::{ModuleId, ModuleTree},
descriptors::module::{ModuleDescriptor},
descriptors::function::FnScopes,
descriptors::DescriptorDatabase,
Cancelable
};
pub(super) fn completions(
acc: &mut Vec<CompletionItem>,
db: &RootDatabase,
source_root_id: SourceRootId,
module_tree: &ModuleTree,
module_id: ModuleId,
module: &ModuleDescriptor,
file: &SourceFileNode,
name_ref: ast::NameRef,
) -> Cancelable<()> {
@ -40,7 +36,7 @@ pub(super) fn completions(
complete_expr_snippets(acc);
}
let module_scope = db.module_scope(source_root_id, module_id)?;
let module_scope = module.scope(db)?;
acc.extend(
module_scope
.entries()
@ -56,9 +52,7 @@ pub(super) fn completions(
}),
);
}
NameRefKind::CratePath(path) => {
complete_path(acc, db, source_root_id, module_tree, module_id, path)?
}
NameRefKind::CratePath(path) => complete_path(acc, db, module, path)?,
NameRefKind::BareIdentInMod => {
let name_range = name_ref.syntax().range();
let top_node = name_ref
@ -171,16 +165,14 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi
fn complete_path(
acc: &mut Vec<CompletionItem>,
db: &RootDatabase,
source_root_id: SourceRootId,
module_tree: &ModuleTree,
module_id: ModuleId,
module: &ModuleDescriptor,
crate_path: Vec<ast::NameRef>,
) -> Cancelable<()> {
let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
let target_module = match find_target_module(module, crate_path) {
None => return Ok(()),
Some(it) => it,
};
let module_scope = db.module_scope(source_root_id, target_module_id)?;
let module_scope = target_module.scope(db)?;
let completions = module_scope.entries().iter().map(|entry| CompletionItem {
label: entry.name().to_string(),
lookup: None,
@ -191,14 +183,13 @@ fn complete_path(
}
fn find_target_module(
module_tree: &ModuleTree,
module_id: ModuleId,
module: &ModuleDescriptor,
mut crate_path: Vec<ast::NameRef>,
) -> Option<ModuleId> {
) -> Option<ModuleDescriptor> {
crate_path.pop();
let mut target_module = module_id.root(&module_tree);
let mut target_module = module.crate_root();
for name in crate_path {
target_module = target_module.child(module_tree, name.text().as_str())?;
target_module = target_module.child(name.text().as_str())?;
}
Some(target_module)
}

View file

@ -85,10 +85,10 @@ salsa::database_storage! {
}
impl DescriptorDatabase {
fn module_tree() for ModuleTreeQuery;
fn module_descriptor() for SubmodulesQuery;
fn module_scope() for ModuleScopeQuery;
fn fn_syntax() for FnSyntaxQuery;
fn fn_scopes() for FnScopesQuery;
fn _fn_syntax() for FnSyntaxQuery;
fn _submodules() for SubmodulesQuery;
}
}
}

View file

@ -15,7 +15,7 @@ pub(crate) fn fn_syntax(db: &impl DescriptorDatabase, fn_id: FnId) -> FnDefNode
}
pub(crate) fn fn_scopes(db: &impl DescriptorDatabase, fn_id: FnId) -> Arc<FnScopes> {
let syntax = db.fn_syntax(fn_id);
let syntax = db._fn_syntax(fn_id);
let res = FnScopes::new(syntax.borrowed());
Arc::new(res)
}

View file

@ -20,27 +20,28 @@ use crate::{
salsa::query_group! {
pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
type FnScopesQuery;
use fn function::imp::fn_scopes;
}
fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
type ModuleTreeQuery;
use fn module::imp::module_tree;
}
fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
type SubmodulesQuery;
use fn module::imp::submodules;
}
fn module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> {
fn _module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> {
type ModuleScopeQuery;
use fn module::imp::module_scope;
}
fn fn_syntax(fn_id: FnId) -> FnDefNode {
fn _fn_syntax(fn_id: FnId) -> FnDefNode {
type FnSyntaxQuery;
// Don't retain syntax trees in memory
storage volatile;
use fn function::imp::fn_syntax;
}
fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
type FnScopesQuery;
use fn function::imp::fn_scopes;
fn _submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
type SubmodulesQuery;
use fn module::imp::submodules;
}
}
}

View file

@ -86,7 +86,7 @@ pub(crate) fn module_scope(
source_root_id: SourceRootId,
module_id: ModuleId,
) -> Cancelable<Arc<ModuleScope>> {
let tree = db.module_tree(source_root_id)?;
let tree = db._module_tree(source_root_id)?;
let source = module_id.source(&tree).resolve(db);
let res = match source {
ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
@ -155,7 +155,7 @@ fn build_subtree(
parent,
children: Vec::new(),
});
for sub in db.submodules(source)?.iter() {
for sub in db._submodules(source)?.iter() {
let link = tree.push_link(LinkData {
name: sub.name().clone(),
owner: id,

View file

@ -1,16 +1,137 @@
pub(super) mod imp;
pub(crate) mod scope;
use std::sync::Arc;
use ra_editor::find_node_at_offset;
use ra_syntax::{
algo::generate,
ast::{self, AstNode, NameOwner},
SmolStr, SyntaxNode, SyntaxNodeRef,
SmolStr, SyntaxNode,
};
use relative_path::RelativePathBuf;
use crate::{db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId};
use crate::{
db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable,
descriptors::DescriptorDatabase,
input::SourceRootId
};
pub(crate) use self::scope::ModuleScope;
/// `ModuleDescriptor` is API entry point to get all the information
/// about a particular module.
#[derive(Debug, Clone)]
pub(crate) struct ModuleDescriptor {
tree: Arc<ModuleTree>,
source_root_id: SourceRootId,
module_id: ModuleId,
}
impl ModuleDescriptor {
/// Lookup `ModuleDescriptor` by `FileId`. Note that this is inherently
/// lossy transformation: in general, a single source might correspond to
/// several modules.
pub fn guess_from_file_id(
db: &impl DescriptorDatabase,
file_id: FileId,
) -> Cancelable<Option<ModuleDescriptor>> {
ModuleDescriptor::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id))
}
/// Lookup `ModuleDescriptor` by position in the source code. Note that this
/// is inherently lossy transformation: in general, a single source might
/// correspond to several modules.
pub fn guess_from_position(
db: &impl DescriptorDatabase,
position: FilePosition,
) -> Cancelable<Option<ModuleDescriptor>> {
let file = db.file_syntax(position.file_id);
let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
{
Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
_ => ModuleSource::SourceFile(position.file_id),
};
ModuleDescriptor::guess_from_source(db, position.file_id, module_source)
}
fn guess_from_source(
db: &impl DescriptorDatabase,
file_id: FileId,
module_source: ModuleSource,
) -> Cancelable<Option<ModuleDescriptor>> {
let source_root_id = db.file_source_root(file_id);
let module_tree = db._module_tree(source_root_id)?;
let res = match module_tree.any_module_for_source(module_source) {
None => None,
Some(module_id) => Some(ModuleDescriptor {
tree: module_tree,
source_root_id,
module_id,
}),
};
Ok(res)
}
/// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
/// Returns `None` for the root module
pub fn parent_link_source(
&self,
db: &impl DescriptorDatabase,
) -> Option<(FileId, ast::ModuleNode)> {
let link = self.module_id.parent_link(&self.tree)?;
let file_id = link.owner(&self.tree).source(&self.tree).file_id();
let src = link.bind_source(&self.tree, db);
Some((file_id, src))
}
pub fn source(&self) -> ModuleSource {
self.module_id.source(&self.tree)
}
/// Parent module. Returns `None` if this is a root module.
pub fn parent(&self) -> Option<ModuleDescriptor> {
let parent_id = self.module_id.parent(&self.tree)?;
Some(ModuleDescriptor {
module_id: parent_id,
..self.clone()
})
}
/// The root of the tree this module is part of
pub fn crate_root(&self) -> ModuleDescriptor {
generate(Some(self.clone()), |it| it.parent())
.last()
.unwrap()
}
/// `name` is `None` for the crate's root module
pub fn name(&self) -> Option<SmolStr> {
let link = self.module_id.parent_link(&self.tree)?;
Some(link.name(&self.tree))
}
/// Finds a child module with the specified name.
pub fn child(&self, name: &str) -> Option<ModuleDescriptor> {
let child_id = self.module_id.child(&self.tree, name)?;
Some(ModuleDescriptor {
module_id: child_id,
..self.clone()
})
}
/// Returns a `ModuleScope`: a set of items, visible in this module.
pub fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<Arc<ModuleScope>> {
db._module_scope(self.source_root_id, self.module_id)
}
pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {
self.module_id.problems(&self.tree, db)
}
}
/// Phisically, rust source is organized as a set of files, but logically it is
/// organized as a tree of modules. Usually, a single file corresponds to a
/// single module, but it is not nessary the case.
@ -25,7 +146,7 @@ pub(crate) struct ModuleTree {
}
impl ModuleTree {
pub(crate) fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
self.mods
.iter()
.enumerate()
@ -34,7 +155,7 @@ impl ModuleTree {
.collect()
}
pub(crate) fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
self.modules_for_source(source).pop()
}
}
@ -58,17 +179,8 @@ enum ModuleSourceNode {
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub(crate) struct ModuleId(u32);
impl crate::loc2id::NumericId for ModuleId {
fn from_u32(id: u32) -> Self {
ModuleId(id)
}
fn to_u32(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) struct LinkId(u32);
struct LinkId(u32);
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Problem {
@ -82,30 +194,17 @@ pub enum Problem {
}
impl ModuleId {
pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
fn source(self, tree: &ModuleTree) -> ModuleSource {
tree.module(self).source
}
pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
tree.module(self).parent
}
pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
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;
// simplistic cycle detection
if i > 100 {
return self;
}
}
curr
}
pub(crate) fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
let link = tree
.module(self)
.children
@ -114,11 +213,7 @@ impl ModuleId {
.find(|it| it.name == name)?;
Some(*link.points_to.first()?)
}
pub(crate) fn problems(
self,
tree: &ModuleTree,
db: &impl SyntaxDatabase,
) -> Vec<(SyntaxNode, Problem)> {
fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> {
tree.module(self)
.children
.iter()
@ -133,14 +228,13 @@ impl ModuleId {
}
impl LinkId {
pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
fn owner(self, tree: &ModuleTree) -> ModuleId {
tree.link(self).owner
}
pub(crate) fn bind_source<'a>(
self,
tree: &ModuleTree,
db: &impl SyntaxDatabase,
) -> ast::ModuleNode {
fn name(self, tree: &ModuleTree) -> SmolStr {
tree.link(self).name.clone()
}
fn bind_source<'a>(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> ast::ModuleNode {
let owner = self.owner(tree);
match owner.source(tree).resolve(db) {
ModuleSourceNode::SourceFile(root) => {
@ -163,17 +257,7 @@ struct ModuleData {
}
impl ModuleSource {
pub(crate) fn for_node(file_id: FileId, node: SyntaxNodeRef) -> ModuleSource {
for node in node.ancestors() {
if let Some(m) = ast::Module::cast(node) {
if !m.has_semi() {
return ModuleSource::new_inline(file_id, m);
}
}
}
ModuleSource::SourceFile(file_id)
}
pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
assert!(!module.has_semi());
let ptr = SyntaxPtr::new(file_id, module.syntax());
ModuleSource::Module(ptr)

View file

@ -21,7 +21,7 @@ use crate::{
db::{self, FileSyntaxQuery, SyntaxDatabase},
descriptors::{
function::{FnDescriptor, FnId},
module::{ModuleSource, ModuleTree, Problem},
module::{ModuleDescriptor, Problem},
DeclarationDescriptor, DescriptorDatabase,
},
input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
@ -216,52 +216,41 @@ impl AnalysisImpl {
.sweep(salsa::SweepStrategy::default().discard_values());
Ok(query.search(&buf))
}
fn module_tree(&self, file_id: FileId) -> Cancelable<Arc<ModuleTree>> {
let source_root = self.db.file_source_root(file_id);
self.db.module_tree(source_root)
}
/// This return `Vec`: a module may be included from several places. We
/// don't handle this case yet though, so the Vec has length at most one.
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
let module_tree = self.module_tree(position.file_id)?;
let file = self.db.file_syntax(position.file_id);
let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
{
Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
_ => ModuleSource::SourceFile(position.file_id),
let descr = match ModuleDescriptor::guess_from_position(&*self.db, position)? {
None => return Ok(Vec::new()),
Some(it) => it,
};
let res = module_tree
.modules_for_source(module_source)
.into_iter()
.filter_map(|module_id| {
let link = module_id.parent_link(&module_tree)?;
let file_id = link.owner(&module_tree).source(&module_tree).file_id();
let decl = link.bind_source(&module_tree, &*self.db);
let decl = decl.borrowed();
let decl_name = decl.name().unwrap();
let sym = FileSymbol {
name: decl_name.text(),
node_range: decl_name.syntax().range(),
kind: MODULE,
};
Some((file_id, sym))
})
.collect();
Ok(res)
let (file_id, decl) = match descr.parent_link_source(&*self.db) {
None => return Ok(Vec::new()),
Some(it) => it,
};
let decl = decl.borrowed();
let decl_name = decl.name().unwrap();
let sym = FileSymbol {
name: decl_name.text(),
node_range: decl_name.syntax().range(),
kind: MODULE,
};
Ok(vec![(file_id, sym)])
}
/// Returns `Vec` for the same reason as `parent_module`
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
let module_tree = self.module_tree(file_id)?;
let crate_graph = self.db.crate_graph();
let res = module_tree
.modules_for_source(ModuleSource::SourceFile(file_id))
.into_iter()
.map(|it| it.root(&module_tree))
.filter_map(|it| it.source(&module_tree).as_file())
.filter_map(|it| crate_graph.crate_id_for_crate_root(it))
.collect();
let descr = match ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? {
None => return Ok(Vec::new()),
Some(it) => it,
};
let root = descr.crate_root();
let file_id = root
.source()
.as_file()
.expect("root module always has a file as a source");
Ok(res)
let crate_graph = self.db.crate_graph();
let crate_id = crate_graph.crate_id_for_crate_root(file_id);
Ok(crate_id.into_iter().collect())
}
pub fn crate_root(&self, crate_id: CrateId) -> FileId {
self.db.crate_graph().crate_roots[&crate_id]
@ -273,7 +262,6 @@ impl AnalysisImpl {
&self,
position: FilePosition,
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
let module_tree = self.module_tree(position.file_id)?;
let file = self.db.file_syntax(position.file_id);
let syntax = file.syntax();
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
@ -299,25 +287,23 @@ impl AnalysisImpl {
if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
if module.has_semi() {
let file_ids = self.resolve_module(&*module_tree, position.file_id, module);
let res = file_ids
.into_iter()
.map(|id| {
let name = module
.name()
.map(|n| n.text())
.unwrap_or_else(|| SmolStr::new(""));
let symbol = FileSymbol {
name,
node_range: TextRange::offset_len(0.into(), 0.into()),
kind: MODULE,
};
(id, symbol)
})
.collect();
return Ok(res);
let parent_module =
ModuleDescriptor::guess_from_file_id(&*self.db, position.file_id)?;
let child_name = module.name();
match (parent_module, child_name) {
(Some(parent_module), Some(child_name)) => {
if let Some(child) = parent_module.child(&child_name.text()) {
let file_id = child.source().file_id();
let symbol = FileSymbol {
name: child_name.text(),
node_range: TextRange::offset_len(0.into(), 0.into()),
kind: MODULE,
};
return Ok(vec![(file_id, symbol)]);
}
}
_ => (),
}
}
}
}
@ -364,7 +350,6 @@ impl AnalysisImpl {
}
pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
let module_tree = self.module_tree(file_id)?;
let syntax = self.db.file_syntax(file_id);
let mut res = ra_editor::diagnostics(&syntax)
@ -375,8 +360,8 @@ impl AnalysisImpl {
fix: None,
})
.collect::<Vec<_>>();
if let Some(m) = module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
for (name_node, problem) in m.problems(&module_tree, &*self.db) {
if let Some(m) = ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? {
for (name_node, problem) in m.problems(&*self.db) {
let diag = match problem {
Problem::UnresolvedModule { candidate } => {
let create_file = FileSystemEdit::CreateFile {
@ -526,27 +511,6 @@ impl AnalysisImpl {
query.limit(4);
self.world_symbols(query)
}
fn resolve_module(
&self,
module_tree: &ModuleTree,
file_id: FileId,
module: ast::Module,
) -> Vec<FileId> {
let name = match module.name() {
Some(name) => name.text(),
None => return Vec::new(),
};
let module_id = match module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
Some(id) => id,
None => return Vec::new(),
};
module_id
.child(module_tree, name.as_str())
.and_then(|it| it.source(&module_tree).as_file())
.into_iter()
.collect()
}
}
impl SourceChange {

View file

@ -72,17 +72,22 @@ pub(crate) trait NumericId: Clone + Eq + Hash {
fn to_u32(self) -> u32;
}
macro_rules! impl_numeric_id {
($id:ident) => {
impl NumericId for $id {
fn from_u32(id: u32) -> Self {
$id(id)
}
fn to_u32(self) -> u32 {
self.0
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct FnId(u32);
impl NumericId for FnId {
fn from_u32(id: u32) -> FnId {
FnId(id)
}
fn to_u32(self) -> u32 {
self.0
}
}
impl_numeric_id!(FnId);
pub(crate) trait IdDatabase: salsa::Database {
fn id_maps(&self) -> &IdMaps;