Skip to content

Commit 8482286

Browse files
authored
feat: completion of self and Self (#579)
1 parent 058072c commit 8482286

12 files changed

+195
-42
lines changed

crates/mun_hir/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ pub use crate::{
2828
ids::{AssocItemId, ItemLoc},
2929
in_file::InFile,
3030
name::Name,
31-
name_resolution::PerNs,
31+
name_resolution::{Namespace, PerNs},
3232
path::{Path, PathKind},
3333
primitive_type::{FloatBitness, IntBitness, Signedness},
3434
resolve::{resolver_for_expr, resolver_for_scope, Resolver, TypeNs, ValueNs},
3535
ty::{
3636
lower::CallableDef, FloatTy, InferenceResult, IntTy, ResolveBitness, Substitution, Ty,
37-
TyKind,
37+
TyKind, TypableDef,
3838
},
3939
visibility::{HasVisibility, Visibility},
4040
};

crates/mun_hir/src/semantics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ impl<'db> Semantics<'db> {
109109
return SourceAnalyzer::new_for_body(self.db, def, node, offset)
110110
}
111111
SourceToDefContainer::ModuleId(id) => id.resolver(self.db.upcast()),
112+
SourceToDefContainer::Impl(id) => id.resolver(self.db.upcast()),
112113
};
113114

114115
SourceAnalyzer::new_for_resolver(resolver, node)

crates/mun_hir/src/semantics/source_to_def.rs

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use rustc_hash::FxHashMap;
44

55
use crate::{
66
code_model::src::HasSource,
7-
ids::{DefWithBodyId, FunctionId, ItemDefinitionId, Lookup, StructId, TypeAliasId},
7+
ids::{DefWithBodyId, FunctionId, ImplId, ItemDefinitionId, Lookup, StructId, TypeAliasId},
88
item_scope::ItemScope,
9-
DefDatabase, HirDatabase, InFile,
9+
AssocItemId, DefDatabase, HirDatabase, InFile,
1010
};
1111

1212
pub(super) type SourceToDefCache = FxHashMap<SourceToDefContainer, SourceToDefMap>;
@@ -24,25 +24,33 @@ impl SourceToDefContext<'_, '_> {
2424
&mut self,
2525
src: InFile<&SyntaxNode>,
2626
) -> Option<SourceToDefContainer> {
27-
for container in std::iter::successors(Some(src.cloned()), move |node| {
27+
let mut ancestors = std::iter::successors(Some(src.cloned()), move |node| {
2828
node.value.parent().map(|parent| node.with_value(parent))
2929
})
30-
.skip(1)
31-
{
32-
let res: SourceToDefContainer = match_ast! {
33-
match (container.value) {
34-
ast::FunctionDef(it) => {
35-
let def = self.fn_to_def(container.with_value(it))?;
36-
DefWithBodyId::from(def).into()
37-
},
38-
_ => continue,
39-
}
40-
};
41-
return Some(res);
42-
}
30+
.skip(1);
4331

44-
let def = self.file_to_def(src.file_id)?;
45-
Some(def.into())
32+
ancestors.find_map(|node| self.container_to_def(node))
33+
}
34+
35+
/// Find the container associated with the given ast node.
36+
fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<SourceToDefContainer> {
37+
Some(match_ast! {
38+
match (container.value) {
39+
ast::FunctionDef(it) => {
40+
let def = self.fn_to_def(container.with_value(it))?;
41+
SourceToDefContainer::DefWithBodyId(def.into())
42+
},
43+
ast::Impl(it) => {
44+
let def = self.impl_to_def(container.with_value(it))?;
45+
SourceToDefContainer::Impl(def)
46+
},
47+
ast::SourceFile(_) => {
48+
let def = self.file_to_def(container.file_id)?;
49+
SourceToDefContainer::ModuleId(def)
50+
},
51+
_ => return None,
52+
}
53+
})
4654
}
4755

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

67+
/// Find the `ImplId` associated with the specified syntax tree node.
68+
fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> {
69+
let container = self.find_container(src.as_ref().map(AstNode::syntax))?;
70+
let db = self.db;
71+
let def_map = &*self
72+
.cache
73+
.entry(container)
74+
.or_insert_with(|| container.source_to_def_map(db));
75+
def_map.impls.get(&src).copied()
76+
}
77+
5978
/// Finds the `ModuleId` associated with the specified `file`
6079
fn file_to_def(&self, file_id: FileId) -> Option<ModuleId> {
6180
let source_root_id = self.db.file_source_root(file_id);
6281
let packages = self.db.packages();
63-
let result = packages
82+
let package_id = packages
6483
.iter()
65-
.filter(|package_id| packages[*package_id].source_root == source_root_id)
66-
.find_map(|package_id| {
67-
let module_tree = self.db.module_tree(package_id);
68-
let module_id = module_tree.module_for_file(file_id)?;
69-
Some(ModuleId {
70-
package: package_id,
71-
local_id: module_id,
72-
})
73-
});
74-
result
84+
.find(|package_id| packages[*package_id].source_root == source_root_id)?;
85+
let module_tree = self.db.module_tree(package_id);
86+
let module_id = module_tree.module_for_file(file_id)?;
87+
Some(ModuleId {
88+
package: package_id,
89+
local_id: module_id,
90+
})
7591
}
7692
}
7793

@@ -80,6 +96,7 @@ impl SourceToDefContext<'_, '_> {
8096
pub(crate) enum SourceToDefContainer {
8197
DefWithBodyId(DefWithBodyId),
8298
ModuleId(ModuleId),
99+
Impl(ImplId),
83100
}
84101

85102
impl From<DefWithBodyId> for SourceToDefContainer {
@@ -99,6 +116,7 @@ impl SourceToDefContainer {
99116
match self {
100117
SourceToDefContainer::DefWithBodyId(id) => id.source_to_def_map(db),
101118
SourceToDefContainer::ModuleId(id) => id.source_to_def_map(db),
119+
SourceToDefContainer::Impl(id) => id.source_to_def_map(db),
102120
}
103121
}
104122
}
@@ -148,6 +166,27 @@ impl SourceToDef for ItemScope {
148166
self.declarations()
149167
.for_each(|item| add_module_def(db.upcast(), &mut result, item));
150168

169+
self.impls().for_each(|id| {
170+
let src = id.lookup(db.upcast()).source(db.upcast());
171+
result.impls.insert(src, id);
172+
});
173+
174+
result
175+
}
176+
}
177+
178+
impl SourceToDef for ImplId {
179+
fn source_to_def_map(&self, db: &dyn HirDatabase) -> SourceToDefMap {
180+
let mut result = SourceToDefMap::default();
181+
let impl_items = db.impl_data(*self);
182+
for &assoc_item in &impl_items.items {
183+
match assoc_item {
184+
AssocItemId::FunctionId(id) => {
185+
let src = id.lookup(db.upcast()).source(db.upcast());
186+
result.functions.insert(src, id);
187+
}
188+
}
189+
}
151190
result
152191
}
153192
}
@@ -156,6 +195,7 @@ impl SourceToDef for ItemScope {
156195
#[derive(Default)]
157196
pub(crate) struct SourceToDefMap {
158197
functions: FxHashMap<InFile<ast::FunctionDef>, FunctionId>,
198+
impls: FxHashMap<InFile<ast::Impl>, ImplId>,
159199
structs: FxHashMap<InFile<ast::StructDef>, StructId>,
160200
type_aliases: FxHashMap<InFile<ast::TypeAliasDef>, TypeAliasId>,
161201
}

crates/mun_hir/src/source_analyzer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::{
1414

1515
/// A `SourceAnalyzer` is a wrapper which exposes the HIR API in terms of the
1616
/// original source file. It's useful to query things from the syntax.
17+
#[derive(Debug)]
1718
pub(crate) struct SourceAnalyzer {
1819
/// The file for which this analyzer was constructed
1920
pub(crate) file_id: FileId,

crates/mun_hir/src/ty.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use std::{fmt, iter::FromIterator, mem, ops::Deref, sync::Arc};
88

99
pub(crate) use infer::infer_query;
1010
pub use infer::InferenceResult;
11+
pub use lower::TypableDef;
1112
pub(crate) use lower::{
12-
callable_item_sig, fn_sig_for_fn, type_for_def, type_for_impl_self, CallableDef, TypableDef,
13+
callable_item_sig, fn_sig_for_fn, type_for_def, type_for_impl_self, CallableDef,
1314
};
1415
pub use primitives::{FloatTy, IntTy};
1516
pub use resolve::ResolveBitness;

crates/mun_language_server/src/completion/dot.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,34 @@ mod tests {
9191
Some(CompletionKind::Reference)
9292
));
9393
}
94+
95+
#[test]
96+
fn test_param() {
97+
insta::assert_snapshot!(completion_string(
98+
r#"
99+
struct Foo { bar: i32 }
100+
101+
fn foo(bar: Foo) {
102+
bar.$0
103+
}
104+
"#,
105+
Some(CompletionKind::Reference)
106+
));
107+
}
108+
109+
#[test]
110+
fn test_self() {
111+
insta::assert_snapshot!(completion_string(
112+
r#"
113+
struct Foo { bar: i32 }
114+
115+
impl Foo {
116+
fn foo(self) {
117+
self.$0
118+
}
119+
}
120+
"#,
121+
Some(CompletionKind::Reference)
122+
));
123+
}
94124
}

crates/mun_language_server/src/completion/expr.rs

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,25 @@ pub(super) fn complete_expr_path(
1414
) {
1515
match qualified {
1616
Qualified::With {
17-
resolution: Some(PathResolution::Def(ModuleDef::Struct(s))),
17+
resolution: Some(resolution),
1818
..
1919
} => {
20-
let ty = s.ty(ctx.db);
21-
MethodResolutionCtx::new(ctx.db, ty.clone())
22-
.with_association(AssociationMode::WithoutSelf)
23-
.collect(|item, _visible| {
24-
match item {
25-
AssocItemId::FunctionId(f) => result.add_function(ctx, f.into(), None),
26-
};
27-
None::<()>
28-
});
20+
let ty = match resolution {
21+
PathResolution::Def(ModuleDef::Struct(st)) => Some(st.ty(ctx.db)),
22+
PathResolution::SelfType(imp) => Some(imp.self_ty(ctx.db)),
23+
_ => None,
24+
};
25+
26+
if let Some(ty) = ty {
27+
MethodResolutionCtx::new(ctx.db, ty)
28+
.with_association(AssociationMode::WithoutSelf)
29+
.collect(|item, _visible| {
30+
match item {
31+
AssocItemId::FunctionId(f) => result.add_function(ctx, f.into(), None),
32+
};
33+
None::<()>
34+
});
35+
}
2936
}
3037
Qualified::No => {
3138
// Iterate over all items in the current scope and add completions for them
@@ -74,4 +81,49 @@ mod tests {
7481
Some(CompletionKind::Reference)
7582
));
7683
}
84+
85+
#[test]
86+
fn test_parameter() {
87+
insta::assert_snapshot!(completion_string(
88+
r#"
89+
fn bar() {
90+
let a = 0;
91+
foo(f$0)
92+
}
93+
"#,
94+
Some(CompletionKind::Reference)
95+
));
96+
}
97+
98+
#[test]
99+
fn test_associated_self() {
100+
insta::assert_snapshot!(completion_string(
101+
r#"
102+
struct Foo;
103+
104+
impl Foo {
105+
fn foo() {
106+
Self::$0
107+
}
108+
}
109+
"#,
110+
Some(CompletionKind::Reference)
111+
));
112+
}
113+
114+
#[test]
115+
fn test_complete_self() {
116+
insta::assert_snapshot!(completion_string(
117+
r#"
118+
struct Foo;
119+
120+
impl Foo {
121+
fn foo(self) {
122+
$0
123+
}
124+
}
125+
"#,
126+
Some(CompletionKind::Reference)
127+
));
128+
}
77129
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: crates/mun_language_server/src/completion/dot.rs
3+
expression: "completion_string(r#\"\n struct Foo { bar: i32 }\n\n fn foo(bar: Foo) {\n bar.$0\n }\n \"#,\nSome(CompletionKind::Reference))"
4+
---
5+
fd bar i32
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: crates/mun_language_server/src/completion/dot.rs
3+
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))"
4+
---
5+
fd bar i32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: crates/mun_language_server/src/completion/expr.rs
3+
expression: "completion_string(r#\"\n struct Foo;\n\n impl Foo {\n fn foo() {\n Self::$0\n }\n }\n \"#,\nSome(CompletionKind::Reference))"
4+
---
5+
fn foo -> ()

0 commit comments

Comments
 (0)