Skip to content

Commit 6abba17

Browse files
jmintbVeykril
authored andcommitted
Implement function type matching
1 parent 14a7a61 commit 6abba17

File tree

3 files changed

+95
-101
lines changed

3 files changed

+95
-101
lines changed

crates/hir/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4567,8 +4567,8 @@ impl Type {
45674567
// FIXME: Document this
45684568
#[derive(Debug)]
45694569
pub struct Callable {
4570-
pub ty: Type,
4571-
pub sig: CallableSig,
4570+
ty: Type,
4571+
sig: CallableSig,
45724572
callee: Callee,
45734573
/// Whether this is a method that was called with method call syntax.
45744574
pub(crate) is_bound_method: bool,

crates/ide-completion/src/render.rs

Lines changed: 89 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ pub(crate) mod variant;
1010
pub(crate) mod union_literal;
1111
pub(crate) mod literal;
1212

13-
use core::panic;
14-
15-
use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
13+
use hir::{AsAssocItem, Function, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
1614
use ide_db::{
1715
documentation::{Documentation, HasDocs},
1816
helpers::item_name,
@@ -395,17 +393,14 @@ fn render_resolution_path(
395393
ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => {
396394
set_item_relevance(adt.ty(db))
397395
}
398-
ScopeDef::ModuleDef(ModuleDef::Function(func)) => {
399-
set_item_relevance(func.ty(db).as_callable(db).unwrap().ty)
400-
}
401-
ScopeDef::ModuleDef(ModuleDef::Variant(variant)) => {
402-
set_item_relevance(variant.parent_enum(db).ty(db))
403-
}
396+
// Functions are handled at the start of the function.
397+
ScopeDef::ModuleDef(ModuleDef::Function(_)) => (), // TODO: Should merge with the match case earlier in the function?
398+
// Enum variants are handled at the start of the function.
399+
ScopeDef::ModuleDef(ModuleDef::Variant(_)) => (),
404400
ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)),
405401
ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)),
406402
ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)),
407403
ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)),
408-
409404
ScopeDef::GenericParam(_)
410405
| ScopeDef::Label(_)
411406
| ScopeDef::Unknown
@@ -502,6 +497,20 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo
502497
}
503498
}
504499

500+
fn match_types(
501+
ctx: &CompletionContext<'_>,
502+
ty1: &hir::Type,
503+
ty2: &hir::Type,
504+
) -> Option<CompletionRelevanceTypeMatch> {
505+
if ty1 == ty2 {
506+
Some(CompletionRelevanceTypeMatch::Exact)
507+
} else if ty1.could_unify_with(ctx.db, ty2) {
508+
Some(CompletionRelevanceTypeMatch::CouldUnify)
509+
} else {
510+
None
511+
}
512+
}
513+
505514
fn compute_type_match(
506515
ctx: &CompletionContext<'_>,
507516
completion_ty: &hir::Type,
@@ -514,35 +523,42 @@ fn compute_type_match(
514523
return None;
515524
}
516525

517-
if completion_ty == expected_type {
518-
Some(CompletionRelevanceTypeMatch::Exact)
519-
} else if expected_type.could_unify_with(ctx.db, completion_ty) {
520-
Some(CompletionRelevanceTypeMatch::CouldUnify)
521-
} else {
522-
None
523-
}
526+
match_types(ctx, expected_type, completion_ty)
524527
}
525528

526-
fn compute_type_match2(
529+
fn compute_function_type_match(
527530
ctx: &CompletionContext<'_>,
528-
completion_ty1: &hir::Type,
529-
completion_ty2: &hir::Type,
531+
func: &Function,
530532
) -> Option<CompletionRelevanceTypeMatch> {
531-
let expected_type = completion_ty1;
533+
// We compute a vec of function parameters + the return type for the expected
534+
// type as well as the function we are matching with. Doing this allows for
535+
// matching all of the types in one iterator.
532536

533-
// We don't ever consider unit type to be an exact type match, since
534-
// nearly always this is not meaningful to the user.
535-
if expected_type.is_unit() {
537+
let expected_callable = ctx.expected_type.as_ref()?.as_callable(ctx.db)?;
538+
let expected_types = expected_callable.params(ctx.db).into_iter().map(|param| param.1);
539+
let actual_types =
540+
func.ty(ctx.db).as_callable(ctx.db)?.params(ctx.db).into_iter().map(|param| param.1);
541+
542+
if expected_types.len() != actual_types.len() {
536543
return None;
537544
}
538545

539-
if completion_ty2 == expected_type {
540-
Some(CompletionRelevanceTypeMatch::Exact)
541-
} else if expected_type.could_unify_with(ctx.db, completion_ty2) {
542-
Some(CompletionRelevanceTypeMatch::CouldUnify)
543-
} else {
544-
None
546+
let mut matches = expected_types
547+
.zip(actual_types)
548+
.chain([(expected_callable.return_type(), func.ret_type(ctx.db))])
549+
.map(|(expected_type, actual_type)| match_types(ctx, &expected_type, &actual_type));
550+
551+
// Any missing type match indicates that these types can not be unified.
552+
if matches.any(|type_match| type_match.is_none()) {
553+
return None;
545554
}
555+
556+
// If any of the types are unifiable but not exact we consider the function types as a whole
557+
// to be unifiable. Otherwise if every pair of types is an exact match the functions are an
558+
// exact type match.
559+
matches
560+
.find(|type_match| matches!(type_match, Some(CompletionRelevanceTypeMatch::CouldUnify)))
561+
.unwrap_or(Some(CompletionRelevanceTypeMatch::Exact))
546562
}
547563

548564
fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {
@@ -796,7 +812,7 @@ fn main() {
796812
);
797813
}
798814

799-
// TODO: does this test even make sense?
815+
// TODO: How dowe test ModuleDef::Variant(Variant?)
800816
#[test]
801817
fn set_enum_variant_type_completion_info() {
802818
check_relevance(
@@ -820,7 +836,7 @@ pub mod test_mod_a {
820836
fn test(input: dep::test_mod_b::Enum) { }
821837
822838
fn main() {
823-
test(Enum$0);
839+
test(Enum::Variant$0);
824840
}
825841
"#,
826842
expect![[r#"
@@ -859,7 +875,7 @@ fn main() {
859875
}
860876
"#,
861877
expect![[r#"
862-
fn Function (use dep::test_mod_a::Function) [type_could_unify+requires_import]
878+
fn Function (use dep::test_mod_a::Function) [type+requires_import]
863879
fn main []
864880
fn test []
865881
md dep []
@@ -868,7 +884,6 @@ fn main() {
868884
);
869885
}
870886

871-
// TODO This test does not trigger the const case
872887
#[test]
873888
fn set_const_type_completion_info() {
874889
check_relevance(
@@ -933,8 +948,38 @@ fn main() {
933948
);
934949
}
935950

936-
// TODO: seems like something is going wrong here. Exapt type match has no effect
937-
// EDIT: maybe it is actually working
951+
#[test]
952+
fn set_self_type_completion_info_with_params() {
953+
check_relevance(
954+
r#"
955+
//- /lib.rs crate:dep
956+
pub struct Struct;
957+
958+
impl Struct {
959+
pub fn Function(&self, input: i32) -> bool {
960+
false
961+
}
962+
}
963+
964+
965+
//- /main.rs crate:main deps:dep
966+
967+
use dep::Struct;
968+
969+
970+
fn test(input: fn(&dep::Struct, i32) -> bool) { }
971+
972+
fn main() {
973+
test(Struct::Function$0);
974+
}
975+
976+
"#,
977+
expect![[r#"
978+
me Function [type]
979+
"#]],
980+
);
981+
}
982+
938983
#[test]
939984
fn set_self_type_completion_info() {
940985
check_relevance(
@@ -964,34 +1009,26 @@ fn func(input: Struct) { }
9641009
);
9651010
}
9661011

967-
// TODO: how do we actually test builtins?
968-
9691012
#[test]
9701013
fn set_builtin_type_completion_info() {
9711014
check_relevance(
9721015
r#"
973-
//- /lib.rs crate:dep
974-
975-
pub mod test_mod_b {
976-
static STATIC: i32 = 5;
977-
}
1016+
//- /main.rs crate:main
9781017
979-
pub mod test_mod_a {
980-
static STATIC: &str = "test";
981-
}
982-
983-
//- /main.rs crate:main deps:dep
984-
985-
fn test(input: i32) { }
1018+
fn test(input: bool) { }
1019+
pub Input: bool = false;
9861020
9871021
fn main() {
988-
test(STATIC$0);
1022+
let input = false;
1023+
let inputbad = 3;
1024+
test(inp$0);
9891025
}
9901026
"#,
9911027
expect![[r#"
1028+
lc input [type+name+local]
1029+
lc inputbad [local]
9921030
fn main() []
9931031
fn test(…) []
994-
md dep []
9951032
"#]],
9961033
);
9971034
}

crates/ide-completion/src/render/function.rs

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
//! Renderer for function calls.
22
3-
use hir::{db::HirDatabase, AsAssocItem, Callable, HirDisplay, Type};
3+
use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
44
use ide_db::{SnippetCap, SymbolKind};
55
use itertools::Itertools;
66
use stdx::{format_to, to_lower_snake_case};
77
use syntax::{AstNode, SmolStr};
88

99
use crate::{
1010
context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
11-
item::{
12-
Builder, CompletionItem, CompletionItemKind, CompletionRelevance,
13-
CompletionRelevanceTypeMatch,
14-
},
11+
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
1512
render::{
16-
compute_exact_name_match, compute_ref_match, compute_type_match, compute_type_match2,
17-
RenderContext,
13+
compute_exact_name_match, compute_function_type_match, compute_ref_match, RenderContext,
1814
},
1915
CallableSnippets,
2016
};
@@ -85,47 +81,8 @@ fn render(
8581
.and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
8682
.map_or(false, |trait_| completion.is_ops_trait(trait_));
8783

88-
// TODO next step figure out how to unify function typesk, we need to convert fndef to actual callable type
89-
90-
let type_match = if let Some(ref t) = completion.expected_type {
91-
if let Some(t) = t.as_callable(db) {
92-
let (mut param_types_exp, ret_type_exp) = (
93-
t.params(db).into_iter().map(|(_, ty)| ty).collect::<Vec<Type>>(),
94-
t.return_type(),
95-
);
96-
97-
param_types_exp.push(ret_type_exp);
98-
99-
let mut param_types = func
100-
.ty(db)
101-
.as_callable(db)
102-
.unwrap()
103-
.params(db)
104-
.into_iter()
105-
.map(|(_, ty)| ty)
106-
.collect::<Vec<Type>>();
107-
param_types.push(ret_type.clone());
108-
109-
if param_types.len() != param_types_exp.len() {
110-
None
111-
} else {
112-
if param_types_exp.iter().zip(param_types).all(|(expected_type, item_type)| {
113-
compute_type_match2(completion, &expected_type, &item_type).is_some()
114-
}) {
115-
Some(CompletionRelevanceTypeMatch::CouldUnify)
116-
} else {
117-
None
118-
}
119-
}
120-
} else {
121-
None
122-
}
123-
} else {
124-
None
125-
};
126-
12784
item.set_relevance(CompletionRelevance {
128-
type_match,
85+
type_match: compute_function_type_match(completion, &func),
12986
exact_name_match: compute_exact_name_match(completion, &call),
13087
is_op_method,
13188
..ctx.completion_relevance()

0 commit comments

Comments
 (0)