Skip to content

Commit

Permalink
feat: completion of self and Self (#579)
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra authored Jan 13, 2025
1 parent 058072c commit 8482286
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 42 deletions.
4 changes: 2 additions & 2 deletions crates/mun_hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ pub use crate::{
ids::{AssocItemId, ItemLoc},
in_file::InFile,
name::Name,
name_resolution::PerNs,
name_resolution::{Namespace, PerNs},
path::{Path, PathKind},
primitive_type::{FloatBitness, IntBitness, Signedness},
resolve::{resolver_for_expr, resolver_for_scope, Resolver, TypeNs, ValueNs},
ty::{
lower::CallableDef, FloatTy, InferenceResult, IntTy, ResolveBitness, Substitution, Ty,
TyKind,
TyKind, TypableDef,
},
visibility::{HasVisibility, Visibility},
};
Expand Down
1 change: 1 addition & 0 deletions crates/mun_hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ impl<'db> Semantics<'db> {
return SourceAnalyzer::new_for_body(self.db, def, node, offset)
}
SourceToDefContainer::ModuleId(id) => id.resolver(self.db.upcast()),
SourceToDefContainer::Impl(id) => id.resolver(self.db.upcast()),
};

SourceAnalyzer::new_for_resolver(resolver, node)
Expand Down
98 changes: 69 additions & 29 deletions crates/mun_hir/src/semantics/source_to_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use rustc_hash::FxHashMap;

use crate::{
code_model::src::HasSource,
ids::{DefWithBodyId, FunctionId, ItemDefinitionId, Lookup, StructId, TypeAliasId},
ids::{DefWithBodyId, FunctionId, ImplId, ItemDefinitionId, Lookup, StructId, TypeAliasId},
item_scope::ItemScope,
DefDatabase, HirDatabase, InFile,
AssocItemId, DefDatabase, HirDatabase, InFile,
};

pub(super) type SourceToDefCache = FxHashMap<SourceToDefContainer, SourceToDefMap>;
Expand All @@ -24,25 +24,33 @@ impl SourceToDefContext<'_, '_> {
&mut self,
src: InFile<&SyntaxNode>,
) -> Option<SourceToDefContainer> {
for container in std::iter::successors(Some(src.cloned()), move |node| {
let mut ancestors = std::iter::successors(Some(src.cloned()), move |node| {
node.value.parent().map(|parent| node.with_value(parent))
})
.skip(1)
{
let res: SourceToDefContainer = match_ast! {
match (container.value) {
ast::FunctionDef(it) => {
let def = self.fn_to_def(container.with_value(it))?;
DefWithBodyId::from(def).into()
},
_ => continue,
}
};
return Some(res);
}
.skip(1);

let def = self.file_to_def(src.file_id)?;
Some(def.into())
ancestors.find_map(|node| self.container_to_def(node))
}

/// Find the container associated with the given ast node.
fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<SourceToDefContainer> {
Some(match_ast! {
match (container.value) {
ast::FunctionDef(it) => {
let def = self.fn_to_def(container.with_value(it))?;
SourceToDefContainer::DefWithBodyId(def.into())
},
ast::Impl(it) => {
let def = self.impl_to_def(container.with_value(it))?;
SourceToDefContainer::Impl(def)
},
ast::SourceFile(_) => {
let def = self.file_to_def(container.file_id)?;
SourceToDefContainer::ModuleId(def)
},
_ => return None,
}
})
}

/// Find the `FunctionId` associated with the specified syntax tree node.
Expand All @@ -56,22 +64,30 @@ impl SourceToDefContext<'_, '_> {
def_map.functions.get(&src).copied()
}

/// Find the `ImplId` associated with the specified syntax tree node.
fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> {
let container = self.find_container(src.as_ref().map(AstNode::syntax))?;
let db = self.db;
let def_map = &*self
.cache
.entry(container)
.or_insert_with(|| container.source_to_def_map(db));
def_map.impls.get(&src).copied()
}

/// Finds the `ModuleId` associated with the specified `file`
fn file_to_def(&self, file_id: FileId) -> Option<ModuleId> {
let source_root_id = self.db.file_source_root(file_id);
let packages = self.db.packages();
let result = packages
let package_id = packages
.iter()
.filter(|package_id| packages[*package_id].source_root == source_root_id)
.find_map(|package_id| {
let module_tree = self.db.module_tree(package_id);
let module_id = module_tree.module_for_file(file_id)?;
Some(ModuleId {
package: package_id,
local_id: module_id,
})
});
result
.find(|package_id| packages[*package_id].source_root == source_root_id)?;
let module_tree = self.db.module_tree(package_id);
let module_id = module_tree.module_for_file(file_id)?;
Some(ModuleId {
package: package_id,
local_id: module_id,
})
}
}

Expand All @@ -80,6 +96,7 @@ impl SourceToDefContext<'_, '_> {
pub(crate) enum SourceToDefContainer {
DefWithBodyId(DefWithBodyId),
ModuleId(ModuleId),
Impl(ImplId),
}

impl From<DefWithBodyId> for SourceToDefContainer {
Expand All @@ -99,6 +116,7 @@ impl SourceToDefContainer {
match self {
SourceToDefContainer::DefWithBodyId(id) => id.source_to_def_map(db),
SourceToDefContainer::ModuleId(id) => id.source_to_def_map(db),
SourceToDefContainer::Impl(id) => id.source_to_def_map(db),
}
}
}
Expand Down Expand Up @@ -148,6 +166,27 @@ impl SourceToDef for ItemScope {
self.declarations()
.for_each(|item| add_module_def(db.upcast(), &mut result, item));

self.impls().for_each(|id| {
let src = id.lookup(db.upcast()).source(db.upcast());
result.impls.insert(src, id);
});

result
}
}

impl SourceToDef for ImplId {
fn source_to_def_map(&self, db: &dyn HirDatabase) -> SourceToDefMap {
let mut result = SourceToDefMap::default();
let impl_items = db.impl_data(*self);
for &assoc_item in &impl_items.items {
match assoc_item {
AssocItemId::FunctionId(id) => {
let src = id.lookup(db.upcast()).source(db.upcast());
result.functions.insert(src, id);
}
}
}
result
}
}
Expand All @@ -156,6 +195,7 @@ impl SourceToDef for ItemScope {
#[derive(Default)]
pub(crate) struct SourceToDefMap {
functions: FxHashMap<InFile<ast::FunctionDef>, FunctionId>,
impls: FxHashMap<InFile<ast::Impl>, ImplId>,
structs: FxHashMap<InFile<ast::StructDef>, StructId>,
type_aliases: FxHashMap<InFile<ast::TypeAliasDef>, TypeAliasId>,
}
1 change: 1 addition & 0 deletions crates/mun_hir/src/source_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{

/// A `SourceAnalyzer` is a wrapper which exposes the HIR API in terms of the
/// original source file. It's useful to query things from the syntax.
#[derive(Debug)]
pub(crate) struct SourceAnalyzer {
/// The file for which this analyzer was constructed
pub(crate) file_id: FileId,
Expand Down
3 changes: 2 additions & 1 deletion crates/mun_hir/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use std::{fmt, iter::FromIterator, mem, ops::Deref, sync::Arc};

pub(crate) use infer::infer_query;
pub use infer::InferenceResult;
pub use lower::TypableDef;
pub(crate) use lower::{
callable_item_sig, fn_sig_for_fn, type_for_def, type_for_impl_self, CallableDef, TypableDef,
callable_item_sig, fn_sig_for_fn, type_for_def, type_for_impl_self, CallableDef,
};
pub use primitives::{FloatTy, IntTy};
pub use resolve::ResolveBitness;
Expand Down
30 changes: 30 additions & 0 deletions crates/mun_language_server/src/completion/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,34 @@ mod tests {
Some(CompletionKind::Reference)
));
}

#[test]
fn test_param() {
insta::assert_snapshot!(completion_string(
r#"
struct Foo { bar: i32 }
fn foo(bar: Foo) {
bar.$0
}
"#,
Some(CompletionKind::Reference)
));
}

#[test]
fn test_self() {
insta::assert_snapshot!(completion_string(
r#"
struct Foo { bar: i32 }
impl Foo {
fn foo(self) {
self.$0
}
}
"#,
Some(CompletionKind::Reference)
));
}
}
72 changes: 62 additions & 10 deletions crates/mun_language_server/src/completion/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,25 @@ pub(super) fn complete_expr_path(
) {
match qualified {
Qualified::With {
resolution: Some(PathResolution::Def(ModuleDef::Struct(s))),
resolution: Some(resolution),
..
} => {
let ty = s.ty(ctx.db);
MethodResolutionCtx::new(ctx.db, ty.clone())
.with_association(AssociationMode::WithoutSelf)
.collect(|item, _visible| {
match item {
AssocItemId::FunctionId(f) => result.add_function(ctx, f.into(), None),
};
None::<()>
});
let ty = match resolution {
PathResolution::Def(ModuleDef::Struct(st)) => Some(st.ty(ctx.db)),
PathResolution::SelfType(imp) => Some(imp.self_ty(ctx.db)),
_ => None,
};

if let Some(ty) = ty {
MethodResolutionCtx::new(ctx.db, ty)
.with_association(AssociationMode::WithoutSelf)
.collect(|item, _visible| {
match item {
AssocItemId::FunctionId(f) => result.add_function(ctx, f.into(), None),
};
None::<()>
});
}
}
Qualified::No => {
// Iterate over all items in the current scope and add completions for them
Expand Down Expand Up @@ -74,4 +81,49 @@ mod tests {
Some(CompletionKind::Reference)
));
}

#[test]
fn test_parameter() {
insta::assert_snapshot!(completion_string(
r#"
fn bar() {
let a = 0;
foo(f$0)
}
"#,
Some(CompletionKind::Reference)
));
}

#[test]
fn test_associated_self() {
insta::assert_snapshot!(completion_string(
r#"
struct Foo;
impl Foo {
fn foo() {
Self::$0
}
}
"#,
Some(CompletionKind::Reference)
));
}

#[test]
fn test_complete_self() {
insta::assert_snapshot!(completion_string(
r#"
struct Foo;
impl Foo {
fn foo(self) {
$0
}
}
"#,
Some(CompletionKind::Reference)
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/mun_language_server/src/completion/dot.rs
expression: "completion_string(r#\"\n struct Foo { bar: i32 }\n\n fn foo(bar: Foo) {\n bar.$0\n }\n \"#,\nSome(CompletionKind::Reference))"
---
fd bar i32
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/mun_language_server/src/completion/dot.rs
expression: "completion_string(r#\"\n struct Foo { bar: i32 }\n\n impl Foo {\n fn foo(self) {\n self.$0\n }\n }\n \"#,\nSome(CompletionKind::Reference))"
---
fd bar i32
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/mun_language_server/src/completion/expr.rs
expression: "completion_string(r#\"\n struct Foo;\n\n impl Foo {\n fn foo() {\n Self::$0\n }\n }\n \"#,\nSome(CompletionKind::Reference))"
---
fn foo -> ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
source: crates/mun_language_server/src/completion/expr.rs
expression: "completion_string(r#\"\n struct Foo;\n\n impl Foo {\n fn foo(self) {\n $0\n }\n }\n \"#,\nSome(CompletionKind::Reference))"
---
lc self Foo
sp Self
st Foo
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: crates/mun_language_server/src/completion/expr.rs
expression: "completion_string(r#\"\n fn bar() {\n let a = 0;\n foo(f$0)\n }\n \"#,\nSome(CompletionKind::Reference))"
---
lc a i32
fn bar -> ()

0 comments on commit 8482286

Please sign in to comment.