Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()");
}
}