Skip to content

Commit

Permalink
fix(LSP): use generic self type to narrow down methods to complete (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Nov 26, 2024
1 parent 7b56904 commit 454b77b
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 44 deletions.
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2351,7 +2351,7 @@ impl Methods {
}

/// Select the 1 matching method with an object type matching `typ`
fn find_matching_method(
pub fn find_matching_method(
&self,
typ: &Type,
has_self_param: bool,
Expand Down
84 changes: 41 additions & 43 deletions tooling/lsp/src/requests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,55 +645,53 @@ impl<'a> NodeFinder<'a> {

let struct_id = get_type_struct_id(typ);
let is_primitive = typ.is_primitive();
let has_self_param = matches!(function_kind, FunctionKind::SelfType(..));

for (name, methods) in methods_by_name {
for (func_id, method_type) in methods.iter() {
if function_kind == FunctionKind::Any {
if let Some(method_type) = method_type {
if method_type.unify(typ).is_err() {
continue;
}
}
}

if let Some(struct_id) = struct_id {
let modifiers = self.interner.function_modifiers(&func_id);
let visibility = modifiers.visibility;
if !struct_member_is_visible(
struct_id,
visibility,
self.module_id,
self.def_maps,
) {
continue;
}
}
let Some(func_id) =
methods.find_matching_method(typ, has_self_param, self.interner).or_else(|| {
// Also try to find a method assuming typ is `&mut typ`:
// we want to suggest methods that take `&mut self` even though a variable might not
// be mutable, so a user can know they need to mark it as mutable.
let typ = Type::MutableReference(Box::new(typ.clone()));
methods.find_matching_method(&typ, has_self_param, self.interner)
})
else {
continue;
};

if is_primitive
&& !method_call_is_visible(
typ,
func_id,
self.module_id,
self.interner,
self.def_maps,
)
{
if let Some(struct_id) = struct_id {
let modifiers = self.interner.function_modifiers(&func_id);
let visibility = modifiers.visibility;
if !struct_member_is_visible(struct_id, visibility, self.module_id, self.def_maps) {
continue;
}
}

if name_matches(name, prefix) {
let completion_items = self.function_completion_items(
name,
func_id,
function_completion_kind,
function_kind,
None, // attribute first type
self_prefix,
);
if !completion_items.is_empty() {
self.completion_items.extend(completion_items);
self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id));
}
if is_primitive
&& !method_call_is_visible(
typ,
func_id,
self.module_id,
self.interner,
self.def_maps,
)
{
continue;
}

if name_matches(name, prefix) {
let completion_items = self.function_completion_items(
name,
func_id,
function_completion_kind,
function_kind,
None, // attribute first type
self_prefix,
);
if !completion_items.is_empty() {
self.completion_items.extend(completion_items);
self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id));
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions tooling/lsp/src/requests/completion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2780,4 +2780,37 @@ fn main() {
)
.await;
}

#[test]
async fn test_suggests_methods_based_on_type_generics() {
let src = r#"
struct Foo<T> {
t: T,
}
impl Foo<Field> {
fn bar_baz(_self: Self) -> Field {
5
}
}
impl Foo<u32> {
fn bar(_self: Self) -> Field {
5
}
fn baz(_self: Self) -> Field {
6
}
}
fn main() -> pub Field {
let foo: Foo<Field> = Foo { t: 5 };
foo.b>|<
}
"#;
let items = get_completions(src).await;
assert_eq!(items.len(), 1);
assert!(items[0].label == "bar_baz()");
}
}

0 comments on commit 454b77b

Please sign in to comment.