Skip to content

Commit 21eef0d

Browse files
authored
feat(LSP): suggest trait methods from where clauses (#6915)
1 parent bb71bcb commit 21eef0d

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ impl<'a> ModCollector<'a> {
500500
.def_interner
501501
.push_function_definition(func_id, modifiers, trait_id.0, location);
502502

503+
let referenced = ReferenceId::Function(func_id);
504+
context.def_interner.add_definition_location(referenced, Some(trait_id.0));
505+
503506
if !trait_item.doc_comments.is_empty() {
504507
context.def_interner.set_doc_comments(
505508
ReferenceId::Function(func_id),

tooling/lsp/src/requests/completion.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use noirc_frontend::{
3131
},
3232
},
3333
hir_def::traits::Trait,
34-
node_interner::{NodeInterner, ReferenceId, StructId},
34+
node_interner::{FuncId, NodeInterner, ReferenceId, StructId},
3535
parser::{Item, ItemKind, ParsedSubModule},
3636
token::{MetaAttribute, Token, Tokens},
3737
Kind, ParsedModule, StructType, Type, TypeBinding,
@@ -120,6 +120,8 @@ struct NodeFinder<'a> {
120120
use_segment_positions: UseSegmentPositions,
121121
self_type: Option<Type>,
122122
in_comptime: bool,
123+
/// The function we are in, if any
124+
func_id: Option<FuncId>,
123125
}
124126

125127
impl<'a> NodeFinder<'a> {
@@ -165,6 +167,7 @@ impl<'a> NodeFinder<'a> {
165167
use_segment_positions: UseSegmentPositions::default(),
166168
self_type: None,
167169
in_comptime: false,
170+
func_id: None,
168171
}
169172
}
170173

@@ -639,6 +642,13 @@ impl<'a> NodeFinder<'a> {
639642
function_completion_kind: FunctionCompletionKind,
640643
self_prefix: bool,
641644
) {
645+
self.complete_trait_constraints_methods(
646+
typ,
647+
prefix,
648+
function_kind,
649+
function_completion_kind,
650+
);
651+
642652
let Some(methods_by_name) = self.interner.get_type_methods(typ) else {
643653
return;
644654
};
@@ -697,6 +707,31 @@ impl<'a> NodeFinder<'a> {
697707
}
698708
}
699709

710+
fn complete_trait_constraints_methods(
711+
&mut self,
712+
typ: &Type,
713+
prefix: &str,
714+
function_kind: FunctionKind,
715+
function_completion_kind: FunctionCompletionKind,
716+
) {
717+
let Some(func_id) = self.func_id else {
718+
return;
719+
};
720+
721+
let func_meta = self.interner.function_meta(&func_id);
722+
for constraint in &func_meta.trait_constraints {
723+
if *typ == constraint.typ {
724+
let trait_ = self.interner.get_trait(constraint.trait_bound.trait_id);
725+
self.complete_trait_methods(
726+
trait_,
727+
prefix,
728+
function_kind,
729+
function_completion_kind,
730+
);
731+
}
732+
}
733+
}
734+
700735
fn complete_trait_methods(
701736
&mut self,
702737
trait_: &Trait,
@@ -1125,8 +1160,17 @@ impl<'a> Visitor for NodeFinder<'a> {
11251160
let old_in_comptime = self.in_comptime;
11261161
self.in_comptime = noir_function.def.is_comptime;
11271162

1163+
if let Some(ReferenceId::Function(func_id)) = self
1164+
.interner
1165+
.reference_at_location(Location::new(noir_function.name_ident().span(), self.file))
1166+
{
1167+
self.func_id = Some(func_id);
1168+
}
1169+
11281170
noir_function.def.body.accept(Some(span), self);
11291171

1172+
self.func_id = None;
1173+
11301174
self.in_comptime = old_in_comptime;
11311175
self.type_parameters = old_type_parameters;
11321176
self.self_type = None;
@@ -1207,7 +1251,7 @@ impl<'a> Visitor for NodeFinder<'a> {
12071251

12081252
fn visit_trait_item_function(
12091253
&mut self,
1210-
_name: &Ident,
1254+
name: &Ident,
12111255
generics: &UnresolvedGenerics,
12121256
parameters: &[(Ident, UnresolvedType)],
12131257
return_type: &noirc_frontend::ast::FunctionReturnType,
@@ -1232,7 +1276,16 @@ impl<'a> Visitor for NodeFinder<'a> {
12321276
for (name, _) in parameters {
12331277
self.local_variables.insert(name.to_string(), name.span());
12341278
}
1279+
1280+
if let Some(ReferenceId::Function(func_id)) =
1281+
self.interner.reference_at_location(Location::new(name.span(), self.file))
1282+
{
1283+
self.func_id = Some(func_id);
1284+
}
1285+
12351286
body.accept(None, self);
1287+
1288+
self.func_id = None;
12361289
};
12371290

12381291
self.type_parameters = old_type_parameters;

tooling/lsp/src/requests/completion/tests.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2861,4 +2861,22 @@ fn main() {
28612861
assert_eq!(items.len(), 1);
28622862
assert!(items[0].label == "bar_baz()");
28632863
}
2864+
2865+
#[test]
2866+
async fn test_suggests_trait_method_from_where_clause_in_function() {
2867+
let src = r#"
2868+
trait Foo {
2869+
fn foo(self) -> i32;
2870+
}
2871+
2872+
fn something<T>(x: T) -> i32
2873+
where
2874+
T: Foo,
2875+
{
2876+
x.fo>|<
2877+
}
2878+
"#;
2879+
let items = get_completions(src).await;
2880+
assert_eq!(items.len(), 1);
2881+
}
28642882
}

0 commit comments

Comments
 (0)