193: Inline modules r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2018-11-05 11:12:04 +00:00
commit de9bb9cfef
12 changed files with 200 additions and 89 deletions

View file

@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
db::{self, SyntaxDatabase},
descriptors::function::FnScopes,
descriptors::module::{ModuleId, ModuleScope, ModuleTree},
descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
descriptors::DescriptorDatabase,
input::FilesDatabase,
Cancelable, FileId,
@ -35,7 +35,7 @@ pub(crate) fn resolve_based_completion(
let source_root_id = db.file_source_root(file_id);
let file = db.file_syntax(file_id);
let module_tree = db.module_tree(source_root_id)?;
let module_id = match module_tree.any_module_for_file(file_id) {
let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
None => return Ok(None),
Some(it) => it,
};

View file

@ -5,16 +5,16 @@ use std::sync::Arc;
use ra_syntax::{
ast::{self, AstNode, FnDefNode},
SmolStr, TextRange,
TextRange,
};
use crate::{
db::SyntaxDatabase,
descriptors::function::{resolve_local_name, FnId, FnScopes},
descriptors::module::{ModuleId, ModuleScope, ModuleTree},
descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
input::SourceRootId,
syntax_ptr::LocalSyntaxPtr,
Cancelable, FileId,
Cancelable,
};
salsa::query_group! {
@ -23,7 +23,7 @@ salsa::query_group! {
type ModuleTreeQuery;
use fn module::imp::module_tree;
}
fn submodules(file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> {
fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
type SubmodulesQuery;
use fn module::imp::submodules;
}

View file

@ -19,25 +19,66 @@ use super::{
ModuleTree, Problem,
};
pub(crate) fn submodules(
db: &impl DescriptorDatabase,
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))
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub(crate) enum Submodule {
Declaration(SmolStr),
Definition(SmolStr, ModuleSource),
}
pub(crate) 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;
impl Submodule {
fn name(&self) -> &SmolStr {
match self {
Submodule::Declaration(name) => name,
Submodule::Definition(name, _) => name,
}
Some((name, module))
})
}
}
pub(crate) fn submodules(
db: &impl DescriptorDatabase,
source: ModuleSource,
) -> Cancelable<Arc<Vec<Submodule>>> {
db::check_canceled(db)?;
let file_id = source.file_id();
let submodules = match source.resolve(db) {
ModuleSourceNode::Root(it) => collect_submodules(file_id, it.ast()),
ModuleSourceNode::Inline(it) => it
.ast()
.item_list()
.map(|it| collect_submodules(file_id, it))
.unwrap_or_else(Vec::new),
};
return Ok(Arc::new(submodules));
fn collect_submodules<'a>(
file_id: FileId,
root: impl ast::ModuleItemOwner<'a>,
) -> Vec<Submodule> {
modules(root)
.map(|(name, m)| {
if m.has_semi() {
Submodule::Declaration(name)
} else {
let src = ModuleSource::new_inline(file_id, m);
Submodule::Definition(name, src)
}
})
.collect()
}
}
pub(crate) fn modules<'a>(
root: impl ast::ModuleItemOwner<'a>,
) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
root.items()
.filter_map(|item| match item {
ast::ModuleItem::Module(m) => Some(m),
_ => None,
})
.filter_map(|module| {
let name = module.name()?.text();
Some((name, module))
})
}
pub(crate) fn module_scope(
@ -66,11 +107,6 @@ pub(crate) fn module_tree(
Ok(Arc::new(res))
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct Submodule {
pub name: SmolStr,
}
fn create_module_tree<'a>(
db: &impl DescriptorDatabase,
source_root: SourceRootId,
@ -85,7 +121,8 @@ fn create_module_tree<'a>(
let source_root = db.source_root(source_root);
for &file_id in source_root.files.iter() {
if visited.contains(&file_id) {
let source = ModuleSource::File(file_id);
if visited.contains(&source) {
continue; // TODO: use explicit crate_roots here
}
assert!(!roots.contains_key(&file_id));
@ -96,7 +133,7 @@ fn create_module_tree<'a>(
&mut visited,
&mut roots,
None,
file_id,
source,
)?;
roots.insert(file_id, module_id);
}
@ -107,36 +144,63 @@ fn build_subtree(
db: &impl DescriptorDatabase,
source_root: &SourceRoot,
tree: &mut ModuleTree,
visited: &mut FxHashSet<FileId>,
visited: &mut FxHashSet<ModuleSource>,
roots: &mut FxHashMap<FileId, ModuleId>,
parent: Option<LinkId>,
file_id: FileId,
source: ModuleSource,
) -> Cancelable<ModuleId> {
visited.insert(file_id);
visited.insert(source);
let id = tree.push_mod(ModuleData {
source: ModuleSource::File(file_id),
source,
parent,
children: Vec::new(),
});
for name in db.submodules(file_id)?.iter() {
let (points_to, problem) = resolve_submodule(file_id, name, &source_root.file_resolver);
for sub in db.submodules(source)?.iter() {
let link = tree.push_link(LinkData {
name: name.clone(),
name: sub.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, source_root, tree, visited, roots, Some(link), file_id),
})
.collect::<Cancelable<Vec<_>>>()?;
let (points_to, problem) = match sub {
Submodule::Declaration(name) => {
let (points_to, problem) =
resolve_submodule(source, &name, &source_root.file_resolver);
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,
source_root,
tree,
visited,
roots,
Some(link),
ModuleSource::File(file_id),
),
})
.collect::<Cancelable<Vec<_>>>()?;
(points_to, problem)
}
Submodule::Definition(_name, submodule_source) => {
let points_to = build_subtree(
db,
source_root,
tree,
visited,
roots,
Some(link),
*submodule_source,
)?;
(vec![points_to], None)
}
};
tree.link_mut(link).points_to = points_to;
tree.link_mut(link).problem = problem;
}
@ -144,10 +208,17 @@ fn build_subtree(
}
fn resolve_submodule(
file_id: FileId,
source: ModuleSource,
name: &SmolStr,
file_resolver: &FileResolverImp,
) -> (Vec<FileId>, Option<Problem>) {
let file_id = match source {
ModuleSource::File(it) => it,
ModuleSource::Inline(..) => {
// TODO
return (Vec::new(), None);
}
};
let mod_name = file_resolver.file_stem(file_id);
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";

View file

@ -25,17 +25,17 @@ pub(crate) struct ModuleTree {
}
impl ModuleTree {
pub(crate) fn modules_for_file(&self, file_id: FileId) -> Vec<ModuleId> {
pub(crate) fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
self.mods
.iter()
.enumerate()
.filter(|(_idx, it)| it.source.is_file(file_id))
.filter(|(_idx, it)| it.source == source)
.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()
pub(crate) fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
self.modules_for_source(source).pop()
}
}
@ -142,9 +142,7 @@ impl LinkId {
.1;
ast.into()
}
ModuleSourceNode::Inline(..) => {
unimplemented!("https://github.com/rust-analyzer/rust-analyzer/issues/181")
}
ModuleSourceNode::Inline(it) => it,
}
}
}
@ -157,6 +155,12 @@ struct ModuleData {
}
impl ModuleSource {
pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
assert!(!module.has_semi());
let ptr = SyntaxPtr::new(file_id, module.syntax());
ModuleSource::Inline(ptr)
}
pub(crate) fn as_file(self) -> Option<FileId> {
match self {
ModuleSource::File(f) => Some(f),
@ -164,6 +168,13 @@ impl ModuleSource {
}
}
pub(crate) fn file_id(self) -> FileId {
match self {
ModuleSource::File(f) => f,
ModuleSource::Inline(ptr) => ptr.file_id(),
}
}
fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
match self {
ModuleSource::File(file_id) => {
@ -178,10 +189,6 @@ impl ModuleSource {
}
}
}
fn is_file(self, file_id: FileId) -> bool {
self.as_file() == Some(file_id)
}
}
#[derive(Hash, Debug, PartialEq, Eq)]

View file

@ -220,27 +220,32 @@ impl AnalysisImpl {
let source_root = self.db.file_source_root(file_id);
self.db.module_tree(source_root)
}
pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> {
pub fn parent_module(
&self,
file_id: FileId,
offset: TextUnit,
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
let module_tree = self.module_tree(file_id)?;
let file = self.db.file_syntax(file_id);
let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), offset) {
Some(m) if !m.has_semi() => ModuleSource::new_inline(file_id, m),
_ => ModuleSource::File(file_id),
};
let res = module_tree
.modules_for_file(file_id)
.modules_for_source(module_source)
.into_iter()
.filter_map(|module_id| {
let link = module_id.parent_link(&module_tree)?;
let file_id = match link.owner(&module_tree).source(&module_tree) {
ModuleSource::File(file_id) => file_id,
ModuleSource::Inline(..) => {
//TODO: https://github.com/rust-analyzer/rust-analyzer/issues/181
return None;
}
};
let file_id = link.owner(&module_tree).source(&module_tree).file_id();
let decl = link.bind_source(&module_tree, &*self.db);
let decl = decl.ast();
let decl_name = decl.name().unwrap();
let sym = FileSymbol {
name: decl.name().unwrap().text(),
node_range: decl.syntax().range(),
name: decl_name.text(),
node_range: decl_name.syntax().range(),
kind: MODULE,
};
Some((file_id, sym))
@ -252,7 +257,7 @@ impl AnalysisImpl {
let module_tree = self.module_tree(file_id)?;
let crate_graph = self.db.crate_graph();
let res = module_tree
.modules_for_file(file_id)
.modules_for_source(ModuleSource::File(file_id))
.into_iter()
.map(|it| it.root(&module_tree))
.filter_map(|it| it.source(&module_tree).as_file())
@ -376,7 +381,7 @@ impl AnalysisImpl {
fix: None,
})
.collect::<Vec<_>>();
if let Some(m) = module_tree.any_module_for_file(file_id) {
if let Some(m) = module_tree.any_module_for_source(ModuleSource::File(file_id)) {
for (name_node, problem) in m.problems(&module_tree, &*self.db) {
let diag = match problem {
Problem::UnresolvedModule { candidate } => {
@ -539,7 +544,7 @@ impl AnalysisImpl {
Some(name) => name.text(),
None => return Vec::new(),
};
let module_id = match module_tree.any_module_for_file(file_id) {
let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
Some(id) => id,
None => return Vec::new(),
};

View file

@ -263,8 +263,12 @@ impl Analysis {
) -> Cancelable<Vec<(FileId, TextRange)>> {
Ok(self.imp.find_all_refs(file_id, offset))
}
pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> {
self.imp.parent_module(file_id)
pub fn parent_module(
&self,
file_id: FileId,
offset: TextUnit,
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
self.imp.parent_module(file_id, offset)
}
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
self.imp.crate_for(file_id)

View file

@ -22,6 +22,10 @@ impl SyntaxPtr {
let local = LocalSyntaxPtr::new(node);
SyntaxPtr { file_id, local }
}
pub(crate) fn file_id(self) -> FileId {
self.file_id
}
}
/// A pionter to a syntax node inside a file.

View file

@ -92,9 +92,28 @@ fn test_resolve_parent_module() {
<|>// empty
",
);
let symbols = analysis.parent_module(pos.file_id).unwrap();
let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
assert_eq_dbg(
r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#,
&symbols,
);
}
#[test]
fn test_resolve_parent_module_for_inline() {
let (analysis, pos) = analysis_and_position(
"
//- /lib.rs
mod foo {
mod bar {
mod baz { <|> }
}
}
",
);
let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
assert_eq_dbg(
r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#,
&symbols,
);
}

View file

@ -218,11 +218,13 @@ pub fn handle_goto_definition(
pub fn handle_parent_module(
world: ServerWorld,
params: TextDocumentIdentifier,
params: req::TextDocumentPositionParams,
) -> Result<Vec<Location>> {
let file_id = params.try_conv_with(&world)?;
let file_id = params.text_document.try_conv_with(&world)?;
let line_index = world.analysis().file_line_index(file_id);
let offset = params.position.conv_with(&line_index);
let mut res = Vec::new();
for (file_id, symbol) in world.analysis().parent_module(file_id)? {
for (file_id, symbol) in world.analysis().parent_module(file_id, offset)? {
let line_index = world.analysis().file_line_index(file_id);
let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
res.push(location);

View file

@ -93,7 +93,7 @@ pub struct Decoration {
pub enum ParentModule {}
impl Request for ParentModule {
type Params = TextDocumentIdentifier;
type Params = TextDocumentPositionParams;
type Result = Vec<Location>;
const METHOD: &'static str = "m/parentModule";
}

View file

@ -6,10 +6,6 @@ import {
SourceChange
} from './apply_source_change';
interface OnEnterParams {
textDocument: lc.TextDocumentIdentifier;
position: lc.Position;
}
export async function handle(event: { text: string }): Promise<boolean> {
const editor = vscode.window.activeTextEditor;
@ -20,7 +16,7 @@ export async function handle(event: { text: string }): Promise<boolean> {
) {
return false;
}
const request: OnEnterParams = {
const request: lc.TextDocumentPositionParams = {
textDocument: { uri: editor.document.uri.toString() },
position: Server.client.code2ProtocolConverter.asPosition(
editor.selection.active

View file

@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { Location, TextDocumentIdentifier } from 'vscode-languageclient';
import * as lc from 'vscode-languageclient';
import { Server } from '../server';
export async function handle() {
@ -8,10 +8,13 @@ export async function handle() {
if (editor == null || editor.document.languageId !== 'rust') {
return;
}
const request: TextDocumentIdentifier = {
uri: editor.document.uri.toString()
const request: lc.TextDocumentPositionParams = {
textDocument: { uri: editor.document.uri.toString() },
position: Server.client.code2ProtocolConverter.asPosition(
editor.selection.active
)
};
const response = await Server.client.sendRequest<Location[]>(
const response = await Server.client.sendRequest<lc.Location[]>(
'm/parentModule',
request
);