Return multiple modules in parent_module

This commit is contained in:
Lukas Wirth 2021-03-15 14:51:20 +01:00
parent f2c39d0cdf
commit 2e3c156b0e
5 changed files with 76 additions and 35 deletions

1
Cargo.lock generated
View file

@ -486,6 +486,7 @@ dependencies = [
"log", "log",
"profile", "profile",
"rustc-hash", "rustc-hash",
"smallvec",
"stdx", "stdx",
"syntax", "syntax",
"tt", "tt",

View file

@ -15,6 +15,7 @@ rustc-hash = "1.1.0"
either = "1.5.3" either = "1.5.3"
arrayvec = "0.5.1" arrayvec = "0.5.1"
itertools = "0.10.0" itertools = "0.10.0"
smallvec = "1.4.0"
stdx = { path = "../stdx", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" }

View file

@ -259,6 +259,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
} }
pub fn to_module_def(&self, file: FileId) -> Option<Module> { pub fn to_module_def(&self, file: FileId) -> Option<Module> {
self.imp.to_module_def(file).next()
}
pub fn to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
self.imp.to_module_def(file) self.imp.to_module_def(file)
} }
@ -537,8 +541,8 @@ impl<'db> SemanticsImpl<'db> {
f(&mut ctx) f(&mut ctx)
} }
fn to_module_def(&self, file: FileId) -> Option<Module> { fn to_module_def(&self, file: FileId) -> impl Iterator<Item = Module> {
self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
} }
fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {

View file

@ -12,6 +12,7 @@ use hir_def::{
}; };
use hir_expand::{name::AsName, AstId, MacroDefKind}; use hir_expand::{name::AsName, AstId, MacroDefKind};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use stdx::impl_from; use stdx::impl_from;
use syntax::{ use syntax::{
ast::{self, NameOwner}, ast::{self, NameOwner},
@ -28,14 +29,19 @@ pub(super) struct SourceToDefCtx<'a, 'b> {
} }
impl SourceToDefCtx<'_, '_> { impl SourceToDefCtx<'_, '_> {
pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> { pub(super) fn file_to_def(&mut self, file: FileId) -> SmallVec<[ModuleId; 1]> {
let _p = profile::span("SourceBinder::to_module_def"); let _p = profile::span("SourceBinder::to_module_def");
self.db.relevant_crates(file).iter().find_map(|&crate_id| { let mut mods = SmallVec::new();
for &crate_id in self.db.relevant_crates(file).iter() {
// FIXME: inner items // FIXME: inner items
let crate_def_map = self.db.crate_def_map(crate_id); let crate_def_map = self.db.crate_def_map(crate_id);
let local_id = crate_def_map.modules_for_file(file).next()?; mods.extend(
Some(crate_def_map.module_id(local_id)) crate_def_map
}) .modules_for_file(file)
.map(|local_id| crate_def_map.module_id(local_id)),
)
}
mods
} }
pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
@ -55,7 +61,7 @@ impl SourceToDefCtx<'_, '_> {
Some(parent_declaration) => self.module_to_def(parent_declaration), Some(parent_declaration) => self.module_to_def(parent_declaration),
None => { None => {
let file_id = src.file_id.original_file(self.db.upcast()); let file_id = src.file_id.original_file(self.db.upcast());
self.file_to_def(file_id) self.file_to_def(file_id).get(0).copied()
} }
}?; }?;
@ -185,7 +191,7 @@ impl SourceToDefCtx<'_, '_> {
) -> Option<MacroDefId> { ) -> Option<MacroDefId> {
let kind = MacroDefKind::Declarative; let kind = MacroDefKind::Declarative;
let file_id = src.file_id.original_file(self.db.upcast()); let file_id = src.file_id.original_file(self.db.upcast());
let krate = self.file_to_def(file_id)?.krate(); let krate = self.file_to_def(file_id).get(0).copied()?.krate();
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast())); let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast()));
Some(MacroDefId { krate, ast_id, kind, local_inner: false }) Some(MacroDefId { krate, ast_id, kind, local_inner: false })
@ -245,7 +251,7 @@ impl SourceToDefCtx<'_, '_> {
return Some(res); return Some(res);
} }
let def = self.file_to_def(src.file_id.original_file(self.db.upcast()))?; let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?;
Some(def.into()) Some(def.into())
} }

View file

@ -1,6 +1,7 @@
use hir::Semantics; use hir::Semantics;
use ide_db::base_db::{CrateId, FileId, FilePosition}; use ide_db::base_db::{CrateId, FileId, FilePosition};
use ide_db::RootDatabase; use ide_db::RootDatabase;
use itertools::Itertools;
use syntax::{ use syntax::{
algo::find_node_at_offset, algo::find_node_at_offset,
ast::{self, AstNode}, ast::{self, AstNode},
@ -18,8 +19,7 @@ use crate::NavigationTarget;
// | VS Code | **Rust Analyzer: Locate parent module** // | VS Code | **Rust Analyzer: Locate parent module**
// |=== // |===
/// This returns `Vec` because a module may be included from several places. We /// This returns `Vec` because a module may be included from several places.
/// don't handle this case yet though, so the Vec has length at most one.
pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
let sema = Semantics::new(db); let sema = Semantics::new(db);
let source_file = sema.parse(position.file_id); let source_file = sema.parse(position.file_id);
@ -37,27 +37,23 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
} }
} }
let module = match module { match module {
Some(module) => sema.to_def(&module), Some(module) => sema
None => sema.to_module_def(position.file_id), .to_def(&module)
}; .into_iter()
let module = match module { .map(|module| NavigationTarget::from_module_to_decl(db, module))
None => return Vec::new(), .collect(),
Some(it) => it, None => sema
}; .to_module_defs(position.file_id)
let nav = NavigationTarget::from_module_to_decl(db, module); .map(|module| NavigationTarget::from_module_to_decl(db, module))
vec![nav] .collect(),
}
} }
/// Returns `Vec` for the same reason as `parent_module` /// Returns `Vec` for the same reason as `parent_module`
pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
let sema = Semantics::new(db); let sema = Semantics::new(db);
let module = match sema.to_module_def(file_id) { sema.to_module_defs(file_id).map(|module| module.krate().into()).unique().collect()
Some(it) => it,
None => return Vec::new(),
};
let krate = module.krate();
vec![krate.into()]
} }
#[cfg(test)] #[cfg(test)]
@ -67,11 +63,13 @@ mod tests {
use crate::fixture; use crate::fixture;
fn check(ra_fixture: &str) { fn check(ra_fixture: &str) {
let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture); let (analysis, position, expected) = fixture::annotations(ra_fixture);
let mut navs = analysis.parent_module(position).unwrap(); let navs = analysis.parent_module(position).unwrap();
assert_eq!(navs.len(), 1); let navs = navs
let nav = navs.pop().unwrap(); .iter()
assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
.collect::<Vec<_>>();
assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs);
} }
#[test] #[test]
@ -120,15 +118,46 @@ mod foo {
} }
#[test] #[test]
fn test_resolve_crate_root() { fn test_resolve_multi_parent_module() {
let (analysis, file_id) = fixture::file( check(
r#" r#"
//- /main.rs //- /main.rs
mod foo; mod foo;
//^^^
#[path = "foo.rs"]
mod bar;
//^^^
//- /foo.rs //- /foo.rs
$0 $0
"#,
);
}
#[test]
fn test_resolve_crate_root() {
let (analysis, file_id) = fixture::file(
r#"
//- /foo.rs
$0
//- /main.rs
mod foo;
"#, "#,
); );
assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1);
} }
#[test]
fn test_resolve_multi_parent_crate() {
let (analysis, file_id) = fixture::file(
r#"
//- /baz.rs
$0
//- /foo.rs crate:foo
mod baz;
//- /bar.rs crate:bar
mod baz;
"#,
);
assert_eq!(analysis.crate_for(file_id).unwrap().len(), 2);
}
} }