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.rs153
1 files changed, 116 insertions, 37 deletions
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index d5695f1c21..4a11ed1901 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -110,6 +110,7 @@ use syntax::{
AstNode, AstPtr, SyntaxNode,
ast::{self, HasName},
};
+use tt::TextRange;
use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource};
@@ -221,7 +222,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| {
+ .ancestors_with_macros(src.syntax_ref(), |_, ancestor, _| {
ancestor.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose()
})
.map(|it| it.transpose());
@@ -520,8 +521,9 @@ 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.ancestors_with_macros(src, |this, container, child| {
+ this.container_to_def(container, child)
+ });
if let Some(def) = def {
return Some(def);
}
@@ -533,32 +535,8 @@ impl SourceToDefCtx<'_, '_> {
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.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),
@@ -579,8 +557,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.ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
let item = match ast::Item::cast(value.clone()) {
Some(it) => it,
None => {
@@ -601,7 +580,44 @@ 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 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(),
@@ -616,29 +632,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)