Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/semantics/source_to_def.rs')
-rw-r--r--crates/hir/src/semantics/source_to_def.rs219
1 files changed, 148 insertions, 71 deletions
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 18cbaa15ae..466bf7f6c8 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -87,37 +87,38 @@
use either::Either;
use hir_def::{
- dyn_map::{
- keys::{self, Key},
- DynMap,
- },
- hir::{BindingId, Expr, LabelId},
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId,
Lookup, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId,
UnionId, UseId, VariantId,
+ dyn_map::{
+ DynMap,
+ keys::{self, Key},
+ },
+ hir::{BindingId, Expr, LabelId},
};
use hir_expand::{
- attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, InMacroFile, MacroCallId,
- MacroFileIdExt,
+ EditionedFileId, ExpansionInfo, HirFileId, InMacroFile, MacroCallId, attrs::AttrId,
+ name::AsName,
};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
-use span::{EditionedFileId, FileId, MacroFileId};
+use span::FileId;
use stdx::impl_from;
use syntax::{
- ast::{self, HasName},
AstNode, AstPtr, SyntaxNode,
+ ast::{self, HasName},
};
+use tt::TextRange;
-use crate::{db::HirDatabase, semantics::child_by_source::ChildBySource, InFile, InlineAsmOperand};
+use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource};
#[derive(Default)]
pub(super) struct SourceToDefCache {
pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>,
- expansion_info_cache: FxHashMap<MacroFileId, ExpansionInfo>,
+ expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo>,
pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
- pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroFileId>>,
+ pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroCallId>>,
/// Rootnode to HirFileId cache
pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>,
}
@@ -137,14 +138,14 @@ impl SourceToDefCache {
&mut self,
db: &dyn HirDatabase,
file: EditionedFileId,
- ) -> Option<MacroFileId> {
+ ) -> Option<MacroCallId> {
if let Some(&m) = self.included_file_cache.get(&file) {
return m;
}
self.included_file_cache.insert(file, None);
- for &crate_id in db.relevant_crates(file.into()).iter() {
+ for &crate_id in db.relevant_crates(file.file_id(db)).iter() {
db.include_macro_invoc(crate_id).iter().for_each(|&(macro_call_id, file_id)| {
- self.included_file_cache.insert(file_id, Some(MacroFileId { macro_call_id }));
+ self.included_file_cache.insert(file_id, Some(macro_call_id));
});
}
self.included_file_cache.get(&file).copied().flatten()
@@ -153,10 +154,10 @@ impl SourceToDefCache {
pub(super) fn get_or_insert_expansion(
&mut self,
db: &dyn HirDatabase,
- macro_file: MacroFileId,
+ macro_file: MacroCallId,
) -> &ExpansionInfo {
self.expansion_info_cache.entry(macro_file).or_insert_with(|| {
- let exp_info = macro_file.expansion_info(db.upcast());
+ let exp_info = macro_file.expansion_info(db);
let InMacroFile { file_id, value } = exp_info.expanded();
Self::cache(&mut self.root_to_file_cache, value, file_id.into());
@@ -176,13 +177,14 @@ impl SourceToDefCtx<'_, '_> {
let _p = tracing::info_span!("SourceToDefCtx::file_to_def").entered();
self.cache.file_to_def_cache.entry(file).or_insert_with(|| {
let mut mods = SmallVec::new();
+
for &crate_id in self.db.relevant_crates(file).iter() {
// Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = self.db.crate_def_map(crate_id);
let n_mods = mods.len();
let modules = |file| {
crate_def_map
- .modules_for_file(file)
+ .modules_for_file(self.db, file)
.map(|local_id| crate_def_map.module_id(local_id))
};
mods.extend(modules(file));
@@ -191,18 +193,16 @@ impl SourceToDefCtx<'_, '_> {
self.db
.include_macro_invoc(crate_id)
.iter()
- .filter(|&&(_, file_id)| file_id == file)
+ .filter(|&&(_, file_id)| file_id.file_id(self.db) == file)
.flat_map(|&(macro_call_id, file_id)| {
- self.cache
- .included_file_cache
- .insert(file_id, Some(MacroFileId { macro_call_id }));
+ self.cache.included_file_cache.insert(file_id, Some(macro_call_id));
modules(
macro_call_id
- .lookup(self.db.upcast())
+ .lookup(self.db)
.kind
.file_id()
- .original_file(self.db.upcast())
- .file_id(),
+ .original_file(self.db)
+ .file_id(self.db),
)
}),
);
@@ -218,7 +218,7 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option<ModuleId> {
let _p = tracing::info_span!("module_to_def").entered();
let parent_declaration = self
- .ancestors_with_macros(src.syntax_ref(), |_, ancestor| {
+ .parent_ancestors_with_macros(src.syntax_ref(), |_, ancestor, _| {
ancestor.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose()
})
.map(|it| it.transpose());
@@ -231,21 +231,21 @@ impl SourceToDefCtx<'_, '_> {
self.module_to_def(parent_declaration.as_ref())
}
None => {
- let file_id = src.file_id.original_file(self.db.upcast());
- self.file_to_def(file_id.file_id()).first().copied()
+ let file_id = src.file_id.original_file(self.db);
+ self.file_to_def(file_id.file_id(self.db)).first().copied()
}
}?;
let child_name = src.value.name()?.as_name();
- let def_map = parent_module.def_map(self.db.upcast());
+ let def_map = parent_module.def_map(self.db);
let &child_id = def_map[parent_module.local_id].children.get(&child_name)?;
Some(def_map.module_id(child_id))
}
pub(super) fn source_file_to_def(&mut self, src: InFile<&ast::SourceFile>) -> Option<ModuleId> {
let _p = tracing::info_span!("source_file_to_def").entered();
- let file_id = src.file_id.original_file(self.db.upcast());
- self.file_to_def(file_id.file_id()).first().copied()
+ let file_id = src.file_id.original_file(self.db);
+ self.file_to_def(file_id.file_id(self.db)).first().copied()
}
pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option<TraitId> {
@@ -344,7 +344,7 @@ impl SourceToDefCtx<'_, '_> {
})
.position(|it| it == *src.value)?;
let container = self.find_pat_or_label_container(src.syntax_ref())?;
- let (_, source_map) = self.db.body_with_source_map(container);
+ let source_map = self.db.body_with_source_map(container).1;
let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?.as_expr()?;
Some(InlineAsmOperand { owner: container, expr, index })
}
@@ -377,7 +377,8 @@ impl SourceToDefCtx<'_, '_> {
src: InFile<&ast::Label>,
) -> Option<(DefWithBodyId, LabelId)> {
let container = self.find_pat_or_label_container(src.syntax_ref())?;
- let (_body, source_map) = self.db.body_with_source_map(container);
+ let source_map = self.db.body_with_source_map(container).1;
+
let label_id = source_map.node_label(src)?;
Some((container, label_id))
}
@@ -516,45 +517,22 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
let _p = tracing::info_span!("find_container").entered();
- let def =
- self.ancestors_with_macros(src, |this, container| this.container_to_def(container));
+ let def = self.parent_ancestors_with_macros(src, |this, container, child| {
+ this.container_to_def(container, child)
+ });
if let Some(def) = def {
return Some(def);
}
let def = self
- .file_to_def(src.file_id.original_file(self.db.upcast()).file_id())
+ .file_to_def(src.file_id.original_file(self.db).file_id(self.db))
.first()
.copied()?;
Some(def.into())
}
- /// Skips the attributed item that caused the macro invocation we are climbing up
- fn ancestors_with_macros<T>(
- &mut self,
- node: InFile<&SyntaxNode>,
- mut cb: impl FnMut(&mut Self, InFile<SyntaxNode>) -> Option<T>,
- ) -> Option<T> {
- let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
- Some(parent) => Some(node.with_value(parent)),
- None => {
- let macro_file = node.file_id.macro_file()?;
- let expansion_info = this.cache.get_or_insert_expansion(this.db, macro_file);
- expansion_info.arg().map(|node| node?.parent()).transpose()
- }
- };
- let mut node = node.cloned();
- while let Some(parent) = parent(self, node.as_ref()) {
- if let Some(res) = cb(self, parent.clone()) {
- return Some(res);
- }
- node = parent;
- }
- None
- }
-
fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
- self.ancestors_with_macros(src, |this, InFile { file_id, value }| {
+ self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
let item = ast::Item::cast(value)?;
match &item {
ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
@@ -575,8 +553,9 @@ impl SourceToDefCtx<'_, '_> {
})
}
+ // FIXME: Remove this when we do inference in signatures
fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
- self.ancestors_with_macros(src, |this, InFile { file_id, value }| {
+ self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
let item = match ast::Item::cast(value.clone()) {
Some(it) => it,
None => {
@@ -597,7 +576,43 @@ impl SourceToDefCtx<'_, '_> {
})
}
- fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildContainer> {
+ /// Skips the attributed item that caused the macro invocation we are climbing up
+ fn parent_ancestors_with_macros<T>(
+ &mut self,
+ node: InFile<&SyntaxNode>,
+ mut cb: impl FnMut(
+ &mut Self,
+ /*parent: */ InFile<SyntaxNode>,
+ /*child: */ &SyntaxNode,
+ ) -> Option<T>,
+ ) -> Option<T> {
+ let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
+ Some(parent) => Some(node.with_value(parent)),
+ None => {
+ let macro_file = node.file_id.macro_file()?;
+ let expansion_info = this.cache.get_or_insert_expansion(this.db, macro_file);
+ expansion_info.arg().map(|node| node?.parent()).transpose()
+ }
+ };
+ let mut deepest_child_in_same_file = node.cloned();
+ let mut node = node.cloned();
+ while let Some(parent) = parent(self, node.as_ref()) {
+ if parent.file_id != node.file_id {
+ deepest_child_in_same_file = parent.clone();
+ }
+ if let Some(res) = cb(self, parent.clone(), &deepest_child_in_same_file.value) {
+ return Some(res);
+ }
+ node = parent;
+ }
+ None
+ }
+
+ fn container_to_def(
+ &mut self,
+ container: InFile<SyntaxNode>,
+ child: &SyntaxNode,
+ ) -> Option<ChildContainer> {
let cont = if let Some(item) = ast::Item::cast(container.value.clone()) {
match &item {
ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
@@ -612,29 +627,92 @@ impl SourceToDefCtx<'_, '_> {
}
ast::Item::Struct(it) => {
let def = self.struct_to_def(container.with_value(it))?;
- VariantId::from(def).into()
+ let is_in_body = it.field_list().is_some_and(|it| {
+ it.syntax().text_range().contains(child.text_range().start())
+ });
+ if is_in_body {
+ VariantId::from(def).into()
+ } else {
+ ChildContainer::GenericDefId(def.into())
+ }
}
ast::Item::Union(it) => {
let def = self.union_to_def(container.with_value(it))?;
- VariantId::from(def).into()
+ let is_in_body = it.record_field_list().is_some_and(|it| {
+ it.syntax().text_range().contains(child.text_range().start())
+ });
+ if is_in_body {
+ VariantId::from(def).into()
+ } else {
+ ChildContainer::GenericDefId(def.into())
+ }
}
ast::Item::Fn(it) => {
let def = self.fn_to_def(container.with_value(it))?;
- DefWithBodyId::from(def).into()
+ let child_offset = child.text_range().start();
+ let is_in_body =
+ it.body().is_some_and(|it| it.syntax().text_range().contains(child_offset));
+ let in_param_pat = || {
+ it.param_list().is_some_and(|it| {
+ it.self_param()
+ .and_then(|it| {
+ Some(TextRange::new(
+ it.syntax().text_range().start(),
+ it.name()?.syntax().text_range().end(),
+ ))
+ })
+ .is_some_and(|r| r.contains_inclusive(child_offset))
+ || it
+ .params()
+ .filter_map(|it| it.pat())
+ .any(|it| it.syntax().text_range().contains(child_offset))
+ })
+ };
+ if is_in_body || in_param_pat() {
+ DefWithBodyId::from(def).into()
+ } else {
+ ChildContainer::GenericDefId(def.into())
+ }
}
ast::Item::Static(it) => {
let def = self.static_to_def(container.with_value(it))?;
- DefWithBodyId::from(def).into()
+ let is_in_body = it.body().is_some_and(|it| {
+ it.syntax().text_range().contains(child.text_range().start())
+ });
+ if is_in_body {
+ DefWithBodyId::from(def).into()
+ } else {
+ ChildContainer::GenericDefId(def.into())
+ }
}
ast::Item::Const(it) => {
let def = self.const_to_def(container.with_value(it))?;
- DefWithBodyId::from(def).into()
+ let is_in_body = it.body().is_some_and(|it| {
+ it.syntax().text_range().contains(child.text_range().start())
+ });
+ if is_in_body {
+ DefWithBodyId::from(def).into()
+ } else {
+ ChildContainer::GenericDefId(def.into())
+ }
}
_ => return None,
}
- } else {
- let it = ast::Variant::cast(container.value)?;
+ } else if let Some(it) = ast::Variant::cast(container.value.clone()) {
let def = self.enum_variant_to_def(InFile::new(container.file_id, &it))?;
+ let is_in_body =
+ it.eq_token().is_some_and(|it| it.text_range().end() < child.text_range().start());
+ if is_in_body { DefWithBodyId::from(def).into() } else { VariantId::from(def).into() }
+ } else {
+ let it = match Either::<ast::Pat, ast::Name>::cast(container.value)? {
+ Either::Left(it) => ast::Param::cast(it.syntax().parent()?)?.syntax().parent(),
+ Either::Right(it) => ast::SelfParam::cast(it.syntax().parent()?)?.syntax().parent(),
+ }
+ .and_then(ast::ParamList::cast)?
+ .syntax()
+ .parent()
+ .and_then(ast::Fn::cast)?;
+ let def = self.fn_to_def(InFile::new(container.file_id, &it))?;
DefWithBodyId::from(def).into()
};
Some(cont)
@@ -671,7 +749,6 @@ impl_from! {
impl ChildContainer {
fn child_by_source(self, db: &dyn HirDatabase, file_id: HirFileId) -> DynMap {
let _p = tracing::info_span!("ChildContainer::child_by_source").entered();
- let db = db.upcast();
match self {
ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
ChildContainer::ModuleId(it) => it.child_by_source(db, file_id),