Merge #7614
7614: Unleash inner item resolution to users r=jonas-schievink a=jonas-schievink ![Peek 2021-02-09 17-30](https://user-images.githubusercontent.com/1786438/107394800-8627f300-6afc-11eb-8662-ed07226bc58f.gif) Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
96a9ab7250
@ -9,7 +9,7 @@ use crate::{
|
||||
body::Body,
|
||||
db::DefDatabase,
|
||||
expr::{Expr, ExprId, Pat, PatId, Statement},
|
||||
DefWithBodyId,
|
||||
BlockId, DefWithBodyId,
|
||||
};
|
||||
|
||||
pub type ScopeId = Idx<ScopeData>;
|
||||
@ -39,6 +39,7 @@ impl ScopeEntry {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ScopeData {
|
||||
parent: Option<ScopeId>,
|
||||
block: Option<BlockId>,
|
||||
entries: Vec<ScopeEntry>,
|
||||
}
|
||||
|
||||
@ -61,6 +62,11 @@ impl ExprScopes {
|
||||
&self.scopes[scope].entries
|
||||
}
|
||||
|
||||
/// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
|
||||
pub fn block(&self, scope: ScopeId) -> Option<BlockId> {
|
||||
self.scopes[scope].block
|
||||
}
|
||||
|
||||
pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
|
||||
std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
|
||||
}
|
||||
@ -79,11 +85,15 @@ impl ExprScopes {
|
||||
}
|
||||
|
||||
fn root_scope(&mut self) -> ScopeId {
|
||||
self.scopes.alloc(ScopeData { parent: None, entries: vec![] })
|
||||
self.scopes.alloc(ScopeData { parent: None, block: None, entries: vec![] })
|
||||
}
|
||||
|
||||
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
|
||||
self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] })
|
||||
self.scopes.alloc(ScopeData { parent: Some(parent), block: None, entries: vec![] })
|
||||
}
|
||||
|
||||
fn new_block_scope(&mut self, parent: ScopeId, block: BlockId) -> ScopeId {
|
||||
self.scopes.alloc(ScopeData { parent: Some(parent), block: Some(block), entries: vec![] })
|
||||
}
|
||||
|
||||
fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
|
||||
@ -136,7 +146,11 @@ fn compute_block_scopes(
|
||||
fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
|
||||
scopes.set_scope(expr, scope);
|
||||
match &body[expr] {
|
||||
Expr::Block { statements, tail, .. } => {
|
||||
Expr::Block { statements, tail, id, .. } => {
|
||||
let scope = scopes.new_block_scope(scope, *id);
|
||||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||
// via the block itself (important for blocks that only contain items, no expressions).
|
||||
scopes.set_scope(expr, scope);
|
||||
compute_block_scopes(&statements, *tail, body, scopes, scope);
|
||||
}
|
||||
Expr::For { iterable, pat, body: body_expr, .. } => {
|
||||
|
@ -1,10 +1,10 @@
|
||||
mod block;
|
||||
|
||||
use base_db::{fixture::WithFixture, FilePosition, SourceDatabase};
|
||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||
use expect_test::Expect;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{test_db::TestDB, BlockId, ModuleDefId};
|
||||
use crate::{test_db::TestDB, ModuleDefId};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -37,104 +37,8 @@ fn check_diagnostics(ra_fixture: &str) {
|
||||
fn block_def_map_at(ra_fixture: &str) -> String {
|
||||
let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
|
||||
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
|
||||
let mut block =
|
||||
block_at_pos(&db, &def_map, position).expect("couldn't find enclosing function or block");
|
||||
loop {
|
||||
let def_map = db.block_def_map(block).unwrap_or_else(|| def_map.clone());
|
||||
let new_block = block_at_pos(&db, &def_map, position);
|
||||
match new_block {
|
||||
Some(new_block) => {
|
||||
assert_ne!(block, new_block);
|
||||
block = new_block;
|
||||
}
|
||||
None => {
|
||||
return def_map.dump(&db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) -> Option<BlockId> {
|
||||
// Find the smallest (innermost) function containing the cursor.
|
||||
let mut size = None;
|
||||
let mut fn_def = None;
|
||||
for (_, module) in def_map.modules() {
|
||||
let file_id = module.definition_source(db).file_id;
|
||||
if file_id != position.file_id.into() {
|
||||
continue;
|
||||
}
|
||||
let root = db.parse_or_expand(file_id).unwrap();
|
||||
let ast_map = db.ast_id_map(file_id);
|
||||
let item_tree = db.item_tree(file_id);
|
||||
for decl in module.scope.declarations() {
|
||||
if let ModuleDefId::FunctionId(it) = decl {
|
||||
let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root);
|
||||
let range = ast.syntax().text_range();
|
||||
|
||||
if !range.contains(position.offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_size = match size {
|
||||
None => range.len(),
|
||||
Some(size) => {
|
||||
if range.len() < size {
|
||||
range.len()
|
||||
} else {
|
||||
size
|
||||
}
|
||||
}
|
||||
};
|
||||
if size != Some(new_size) {
|
||||
size = Some(new_size);
|
||||
fn_def = Some(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (body, source_map) = db.body_with_source_map(fn_def?.into());
|
||||
|
||||
// Now find the smallest encompassing block expression in the function body.
|
||||
let mut size = None;
|
||||
let mut block_id = None;
|
||||
for (expr_id, expr) in body.exprs.iter() {
|
||||
if let Expr::Block { id, .. } = expr {
|
||||
if let Ok(ast) = source_map.expr_syntax(expr_id) {
|
||||
if ast.file_id != position.file_id.into() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let root = db.parse_or_expand(ast.file_id).unwrap();
|
||||
let ast = ast.value.to_node(&root);
|
||||
let range = ast.syntax().text_range();
|
||||
|
||||
if !range.contains(position.offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_size = match size {
|
||||
None => range.len(),
|
||||
Some(size) => {
|
||||
if range.len() < size {
|
||||
range.len()
|
||||
} else {
|
||||
size
|
||||
}
|
||||
}
|
||||
};
|
||||
if size != Some(new_size) {
|
||||
size = Some(new_size);
|
||||
block_id = Some(*id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(block_id.expect("can't find block containing cursor"))
|
||||
let module = db.module_at_position(position);
|
||||
module.def_map(&db).dump(&db)
|
||||
}
|
||||
|
||||
fn check_at(ra_fixture: &str, expect: Expect) {
|
||||
|
@ -232,3 +232,30 @@ fn f() {
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn super_does_not_resolve_to_block_module() {
|
||||
check_at(
|
||||
r#"
|
||||
fn main() {
|
||||
struct Struct {}
|
||||
mod module {
|
||||
use super::Struct;
|
||||
|
||||
$0
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
block scope
|
||||
Struct: t
|
||||
module: t
|
||||
|
||||
block scope::module
|
||||
Struct: _
|
||||
|
||||
crate
|
||||
main: v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ use crate::{
|
||||
ModuleDefId, ModuleId,
|
||||
};
|
||||
|
||||
// FIXME: handle local items
|
||||
|
||||
/// Find a path that can be used to refer to a certain item. This can depend on
|
||||
/// *from where* you're referring to the item, hence the `from` parameter.
|
||||
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
|
||||
@ -107,9 +105,9 @@ fn find_path_inner(
|
||||
|
||||
// - if the item is already in scope, return the name under which it is
|
||||
let def_map = from.def_map(db);
|
||||
let from_scope: &crate::item_scope::ItemScope = &def_map[from.local_id].scope;
|
||||
let scope_name =
|
||||
if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
|
||||
let scope_name = def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
|
||||
def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
|
||||
});
|
||||
if prefixed.is_none() && scope_name.is_some() {
|
||||
return scope_name
|
||||
.map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
|
||||
@ -117,7 +115,7 @@ fn find_path_inner(
|
||||
|
||||
// - if the item is the crate root, return `crate`
|
||||
let root = def_map.module_id(def_map.root());
|
||||
if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) {
|
||||
if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
|
||||
return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
|
||||
}
|
||||
|
||||
@ -230,7 +228,12 @@ fn find_path_inner(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
|
||||
if let Some(mut prefix) = prefixed.map(PrefixKind::prefix) {
|
||||
if matches!(prefix, PathKind::Crate | PathKind::Super(0)) && def_map.block_id().is_some() {
|
||||
// Inner items cannot be referred to via `crate::` or `self::` paths.
|
||||
prefix = PathKind::Plain;
|
||||
}
|
||||
|
||||
best_path.or_else(|| {
|
||||
scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
|
||||
})
|
||||
@ -358,14 +361,14 @@ mod tests {
|
||||
/// module the cursor is in.
|
||||
fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
|
||||
let (db, pos) = TestDB::with_position(ra_fixture);
|
||||
let module = db.module_for_file(pos.file_id);
|
||||
let module = db.module_at_position(pos);
|
||||
let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
|
||||
let ast_path =
|
||||
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
|
||||
let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
|
||||
|
||||
let crate_def_map = module.def_map(&db);
|
||||
let resolved = crate_def_map
|
||||
let def_map = module.def_map(&db);
|
||||
let resolved = def_map
|
||||
.resolve_path(
|
||||
&db,
|
||||
module.local_id,
|
||||
@ -788,4 +791,82 @@ mod tests {
|
||||
check_found_path(code, "u8", "u8", "u8", "u8");
|
||||
check_found_path(code, "u16", "u16", "u16", "u16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_items() {
|
||||
check_found_path(
|
||||
r#"
|
||||
fn main() {
|
||||
struct Inner {}
|
||||
$0
|
||||
}
|
||||
"#,
|
||||
"Inner",
|
||||
"Inner",
|
||||
"Inner",
|
||||
"Inner",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_items_from_outer_scope() {
|
||||
check_found_path(
|
||||
r#"
|
||||
fn main() {
|
||||
struct Struct {}
|
||||
{
|
||||
$0
|
||||
}
|
||||
}
|
||||
"#,
|
||||
"Struct",
|
||||
"Struct",
|
||||
"Struct",
|
||||
"Struct",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_items_from_inner_module() {
|
||||
check_found_path(
|
||||
r#"
|
||||
fn main() {
|
||||
mod module {
|
||||
struct Struct {}
|
||||
}
|
||||
{
|
||||
$0
|
||||
}
|
||||
}
|
||||
"#,
|
||||
"module::Struct",
|
||||
"module::Struct",
|
||||
"module::Struct",
|
||||
"module::Struct",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn inner_items_from_parent_module() {
|
||||
// FIXME: ItemTree currently associates all inner items with `main`. Luckily, this sort of
|
||||
// code is very rare, so this isn't terrible.
|
||||
// To fix it, we should probably build dedicated `ItemTree`s for inner items, and not store
|
||||
// them in the file's main ItemTree. This would also allow us to stop parsing function
|
||||
// bodies when we only want to compute the crate's main DefMap.
|
||||
check_found_path(
|
||||
r#"
|
||||
fn main() {
|
||||
struct Struct {}
|
||||
mod module {
|
||||
$0
|
||||
}
|
||||
}
|
||||
"#,
|
||||
"super::Struct",
|
||||
"super::Struct",
|
||||
"super::Struct",
|
||||
"super::Struct",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -316,7 +316,7 @@ impl DefMap {
|
||||
///
|
||||
/// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns
|
||||
/// `None`, iteration continues.
|
||||
fn with_ancestor_maps<T>(
|
||||
pub fn with_ancestor_maps<T>(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
local_mod: LocalModuleId,
|
||||
|
@ -10,7 +10,6 @@ use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
body::scope::{ExprScopes, ScopeId},
|
||||
body::Body,
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
expr::{ExprId, PatId},
|
||||
@ -58,8 +57,6 @@ enum Scope {
|
||||
AdtScope(AdtId),
|
||||
/// Local bindings
|
||||
ExprScope(ExprScope),
|
||||
/// Temporary hack to support local items.
|
||||
LocalItemsScope(Arc<Body>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -169,13 +166,7 @@ impl Resolver {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
match scope {
|
||||
Scope::ExprScope(_) => continue,
|
||||
Scope::GenericParams { .. }
|
||||
| Scope::ImplDefScope(_)
|
||||
| Scope::LocalItemsScope(_)
|
||||
if skip_to_mod =>
|
||||
{
|
||||
continue
|
||||
}
|
||||
Scope::GenericParams { .. } | Scope::ImplDefScope(_) if skip_to_mod => continue,
|
||||
|
||||
Scope::GenericParams { params, def } => {
|
||||
if let Some(local_id) = params.find_type_by_name(first_name) {
|
||||
@ -199,41 +190,13 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
Scope::ModuleScope(m) => {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(
|
||||
db,
|
||||
m.module_id,
|
||||
&path,
|
||||
BuiltinShadowMode::Other,
|
||||
);
|
||||
let res = to_type_ns(module_def)?;
|
||||
return Some((res, idx));
|
||||
}
|
||||
Scope::LocalItemsScope(body) => {
|
||||
let def = body.item_scope.get(first_name);
|
||||
if let Some(res) = to_type_ns(def) {
|
||||
return Some((res, None));
|
||||
if let Some(res) = m.resolve_path_in_type_ns(db, path) {
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
|
||||
let res = match per_ns.take_types()? {
|
||||
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
||||
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
|
||||
|
||||
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
|
||||
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||
|
||||
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
||||
|
||||
ModuleDefId::FunctionId(_)
|
||||
| ModuleDefId::ConstId(_)
|
||||
| ModuleDefId::StaticId(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_type_ns_fully(
|
||||
@ -280,7 +243,6 @@ impl Resolver {
|
||||
| Scope::ExprScope(_)
|
||||
| Scope::GenericParams { .. }
|
||||
| Scope::ImplDefScope(_)
|
||||
| Scope::LocalItemsScope(_)
|
||||
if skip_to_mod =>
|
||||
{
|
||||
continue
|
||||
@ -335,63 +297,14 @@ impl Resolver {
|
||||
}
|
||||
|
||||
Scope::ModuleScope(m) => {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(
|
||||
db,
|
||||
m.module_id,
|
||||
&path,
|
||||
BuiltinShadowMode::Other,
|
||||
);
|
||||
return match idx {
|
||||
None => {
|
||||
let value = to_value_ns(module_def)?;
|
||||
Some(ResolveValueResult::ValueNs(value))
|
||||
}
|
||||
Some(idx) => {
|
||||
let ty = match module_def.take_types()? {
|
||||
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
||||
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
||||
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
|
||||
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||
|
||||
ModuleDefId::ModuleId(_)
|
||||
| ModuleDefId::FunctionId(_)
|
||||
| ModuleDefId::EnumVariantId(_)
|
||||
| ModuleDefId::ConstId(_)
|
||||
| ModuleDefId::StaticId(_) => return None,
|
||||
};
|
||||
Some(ResolveValueResult::Partial(ty, idx))
|
||||
}
|
||||
};
|
||||
}
|
||||
Scope::LocalItemsScope(body) => {
|
||||
// we don't bother looking in the builtin scope here because there are no builtin values
|
||||
let def = to_value_ns(body.item_scope.get(first_name));
|
||||
|
||||
if let Some(res) = def {
|
||||
return Some(ResolveValueResult::ValueNs(res));
|
||||
if let Some(def) = m.resolve_path_in_value_ns(db, path) {
|
||||
return Some(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
|
||||
fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
|
||||
let res = match per_ns.take_values()? {
|
||||
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
|
||||
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
|
||||
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
|
||||
ModuleDefId::ConstId(it) => ValueNs::ConstId(it),
|
||||
ModuleDefId::StaticId(it) => ValueNs::StaticId(it),
|
||||
|
||||
ModuleDefId::AdtId(AdtId::EnumId(_))
|
||||
| ModuleDefId::AdtId(AdtId::UnionId(_))
|
||||
| ModuleDefId::TraitId(_)
|
||||
| ModuleDefId::TypeAliasId(_)
|
||||
| ModuleDefId::BuiltinType(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_value_ns_fully(
|
||||
@ -410,11 +323,6 @@ impl Resolver {
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<MacroDefId> {
|
||||
// Search item scope legacy macro first
|
||||
if let Some(def) = self.resolve_local_macro_def(path) {
|
||||
return Some(def);
|
||||
}
|
||||
|
||||
let (item_map, module) = self.module_scope()?;
|
||||
item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
|
||||
}
|
||||
@ -447,16 +355,6 @@ impl Resolver {
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
|
||||
let name = path.as_ident()?;
|
||||
self.scopes.iter().rev().find_map(|scope| {
|
||||
if let Scope::LocalItemsScope(body) = scope {
|
||||
return body.item_scope.get_legacy_macro(name);
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn module(&self) -> Option<ModuleId> {
|
||||
let (def_map, local_id) = self.module_scope()?;
|
||||
Some(def_map.module_id(local_id))
|
||||
@ -538,9 +436,6 @@ impl Scope {
|
||||
});
|
||||
}
|
||||
}
|
||||
Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| {
|
||||
f(name.clone(), ScopeDef::PerNs(def));
|
||||
}),
|
||||
&Scope::GenericParams { ref params, def: parent } => {
|
||||
for (local_id, param) in params.types.iter() {
|
||||
if let Some(ref name) = param.name {
|
||||
@ -584,10 +479,19 @@ pub fn resolver_for_scope(
|
||||
scope_id: Option<ScopeId>,
|
||||
) -> Resolver {
|
||||
let mut r = owner.resolver(db);
|
||||
r = r.push_local_items_scope(db.body(owner));
|
||||
let scopes = db.expr_scopes(owner);
|
||||
let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
|
||||
for scope in scope_chain.into_iter().rev() {
|
||||
if let Some(block) = scopes.block(scope) {
|
||||
if let Some(def_map) = db.block_def_map(block) {
|
||||
let root = def_map.root();
|
||||
r = r.push_module_scope(def_map, root);
|
||||
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
||||
// already traverses all parents, so this is O(n²). I think we could only store the
|
||||
// innermost module scope instead?
|
||||
}
|
||||
}
|
||||
|
||||
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
|
||||
}
|
||||
r
|
||||
@ -612,10 +516,6 @@ impl Resolver {
|
||||
self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
|
||||
}
|
||||
|
||||
fn push_local_items_scope(self, body: Arc<Body>) -> Resolver {
|
||||
self.push_scope(Scope::LocalItemsScope(body))
|
||||
}
|
||||
|
||||
fn push_expr_scope(
|
||||
self,
|
||||
owner: DefWithBodyId,
|
||||
@ -626,6 +526,85 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleItemMap {
|
||||
fn resolve_path_in_value_ns(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<ResolveValueResult> {
|
||||
let (module_def, idx) =
|
||||
self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other);
|
||||
match idx {
|
||||
None => {
|
||||
let value = to_value_ns(module_def)?;
|
||||
Some(ResolveValueResult::ValueNs(value))
|
||||
}
|
||||
Some(idx) => {
|
||||
let ty = match module_def.take_types()? {
|
||||
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
||||
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
||||
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
|
||||
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||
|
||||
ModuleDefId::ModuleId(_)
|
||||
| ModuleDefId::FunctionId(_)
|
||||
| ModuleDefId::EnumVariantId(_)
|
||||
| ModuleDefId::ConstId(_)
|
||||
| ModuleDefId::StaticId(_) => return None,
|
||||
};
|
||||
Some(ResolveValueResult::Partial(ty, idx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_path_in_type_ns(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<(TypeNs, Option<usize>)> {
|
||||
let (module_def, idx) =
|
||||
self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other);
|
||||
let res = to_type_ns(module_def)?;
|
||||
Some((res, idx))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
|
||||
let res = match per_ns.take_values()? {
|
||||
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
|
||||
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
|
||||
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
|
||||
ModuleDefId::ConstId(it) => ValueNs::ConstId(it),
|
||||
ModuleDefId::StaticId(it) => ValueNs::StaticId(it),
|
||||
|
||||
ModuleDefId::AdtId(AdtId::EnumId(_))
|
||||
| ModuleDefId::AdtId(AdtId::UnionId(_))
|
||||
| ModuleDefId::TraitId(_)
|
||||
| ModuleDefId::TypeAliasId(_)
|
||||
| ModuleDefId::BuiltinType(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
|
||||
let res = match per_ns.take_types()? {
|
||||
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
||||
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
|
||||
|
||||
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
|
||||
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||
|
||||
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
||||
|
||||
ModuleDefId::FunctionId(_)
|
||||
| ModuleDefId::ConstId(_)
|
||||
| ModuleDefId::StaticId(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub trait HasResolver: Copy {
|
||||
/// Builds a resolver for type references inside this def.
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver;
|
||||
|
@ -5,17 +5,17 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
|
||||
use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, Upcast};
|
||||
use base_db::{AnchoredPath, SourceDatabase};
|
||||
use hir_expand::db::AstDatabase;
|
||||
use hir_expand::diagnostics::Diagnostic;
|
||||
use hir_expand::diagnostics::DiagnosticSinkBuilder;
|
||||
use hir_expand::{db::AstDatabase, InFile};
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{TextRange, TextSize};
|
||||
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
||||
use test_utils::extract_annotations;
|
||||
|
||||
use crate::{db::DefDatabase, ModuleDefId, ModuleId};
|
||||
use crate::{db::DefDatabase, nameres::DefMap, Lookup, ModuleDefId, ModuleId};
|
||||
|
||||
#[salsa::database(
|
||||
base_db::SourceDatabaseExtStorage,
|
||||
@ -84,6 +84,97 @@ impl TestDB {
|
||||
panic!("Can't find module for file")
|
||||
}
|
||||
|
||||
pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId {
|
||||
let file_module = self.module_for_file(position.file_id);
|
||||
let mut def_map = file_module.def_map(self);
|
||||
|
||||
def_map = match self.block_at_position(&def_map, position) {
|
||||
Some(it) => it,
|
||||
None => return file_module,
|
||||
};
|
||||
loop {
|
||||
let new_map = self.block_at_position(&def_map, position);
|
||||
match new_map {
|
||||
Some(new_block) if !Arc::ptr_eq(&new_block, &def_map) => {
|
||||
def_map = new_block;
|
||||
}
|
||||
_ => {
|
||||
// FIXME: handle `mod` inside block expression
|
||||
return def_map.module_id(def_map.root());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> {
|
||||
// Find the smallest (innermost) function in `def_map` containing the cursor.
|
||||
let mut size = None;
|
||||
let mut fn_def = None;
|
||||
for (_, module) in def_map.modules() {
|
||||
let file_id = module.definition_source(self).file_id;
|
||||
if file_id != position.file_id.into() {
|
||||
continue;
|
||||
}
|
||||
let root = self.parse_or_expand(file_id).unwrap();
|
||||
let ast_map = self.ast_id_map(file_id);
|
||||
let item_tree = self.item_tree(file_id);
|
||||
for decl in module.scope.declarations() {
|
||||
if let ModuleDefId::FunctionId(it) = decl {
|
||||
let ast =
|
||||
ast_map.get(item_tree[it.lookup(self).id.value].ast_id).to_node(&root);
|
||||
let range = ast.syntax().text_range();
|
||||
|
||||
if !range.contains(position.offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_size = match size {
|
||||
None => range.len(),
|
||||
Some(size) => {
|
||||
if range.len() < size {
|
||||
range.len()
|
||||
} else {
|
||||
size
|
||||
}
|
||||
}
|
||||
};
|
||||
if size != Some(new_size) {
|
||||
size = Some(new_size);
|
||||
fn_def = Some(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the innermost block expression that has a `DefMap`.
|
||||
let def_with_body = fn_def?.into();
|
||||
let (_, source_map) = self.body_with_source_map(def_with_body);
|
||||
let scopes = self.expr_scopes(def_with_body);
|
||||
let root = self.parse(position.file_id);
|
||||
|
||||
let scope_iter = algo::ancestors_at_offset(&root.syntax_node(), position.offset)
|
||||
.filter_map(|node| {
|
||||
let block = ast::BlockExpr::cast(node)?;
|
||||
let expr = ast::Expr::from(block);
|
||||
let expr_id = source_map.node_expr(InFile::new(position.file_id.into(), &expr))?;
|
||||
let scope = scopes.scope_for(expr_id).unwrap();
|
||||
Some(scope)
|
||||
});
|
||||
|
||||
for scope in scope_iter {
|
||||
let containing_blocks =
|
||||
scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
|
||||
|
||||
for block in containing_blocks {
|
||||
if let Some(def_map) = self.block_def_map(block) {
|
||||
return Some(def_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
|
||||
*self.events.lock().unwrap() = Some(Vec::new());
|
||||
f();
|
||||
|
Loading…
x
Reference in New Issue
Block a user