Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/render.rs')
| -rw-r--r-- | crates/ide-completion/src/render.rs | 141 |
1 files changed, 89 insertions, 52 deletions
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 6f5d211188..309c193749 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -10,9 +10,7 @@ pub(crate) mod variant; pub(crate) mod union_literal; pub(crate) mod literal; -use core::panic; - -use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; +use hir::{AsAssocItem, Function, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; use ide_db::{ documentation::{Documentation, HasDocs}, helpers::item_name, @@ -395,17 +393,14 @@ fn render_resolution_path( ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => { set_item_relevance(adt.ty(db)) } - ScopeDef::ModuleDef(ModuleDef::Function(func)) => { - set_item_relevance(func.ty(db).as_callable(db).unwrap().ty) - } - ScopeDef::ModuleDef(ModuleDef::Variant(variant)) => { - set_item_relevance(variant.parent_enum(db).ty(db)) - } + // Functions are handled at the start of the function. + ScopeDef::ModuleDef(ModuleDef::Function(_)) => (), // TODO: Should merge with the match case earlier in the function? + // Enum variants are handled at the start of the function. + ScopeDef::ModuleDef(ModuleDef::Variant(_)) => (), ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)), ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)), ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)), ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)), - ScopeDef::GenericParam(_) | ScopeDef::Label(_) | ScopeDef::Unknown @@ -502,6 +497,20 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo } } +fn match_types( + ctx: &CompletionContext<'_>, + ty1: &hir::Type, + ty2: &hir::Type, +) -> Option<CompletionRelevanceTypeMatch> { + if ty1 == ty2 { + Some(CompletionRelevanceTypeMatch::Exact) + } else if ty1.could_unify_with(ctx.db, ty2) { + Some(CompletionRelevanceTypeMatch::CouldUnify) + } else { + None + } +} + fn compute_type_match( ctx: &CompletionContext<'_>, completion_ty: &hir::Type, @@ -514,35 +523,42 @@ fn compute_type_match( return None; } - if completion_ty == expected_type { - Some(CompletionRelevanceTypeMatch::Exact) - } else if expected_type.could_unify_with(ctx.db, completion_ty) { - Some(CompletionRelevanceTypeMatch::CouldUnify) - } else { - None - } + match_types(ctx, expected_type, completion_ty) } -fn compute_type_match2( +fn compute_function_type_match( ctx: &CompletionContext<'_>, - completion_ty1: &hir::Type, - completion_ty2: &hir::Type, + func: &Function, ) -> Option<CompletionRelevanceTypeMatch> { - let expected_type = completion_ty1; + // We compute a vec of function parameters + the return type for the expected + // type as well as the function we are matching with. Doing this allows for + // matching all of the types in one iterator. - // We don't ever consider unit type to be an exact type match, since - // nearly always this is not meaningful to the user. - if expected_type.is_unit() { + let expected_callable = ctx.expected_type.as_ref()?.as_callable(ctx.db)?; + let expected_types = expected_callable.params(ctx.db).into_iter().map(|param| param.1); + let actual_types = + func.ty(ctx.db).as_callable(ctx.db)?.params(ctx.db).into_iter().map(|param| param.1); + + if expected_types.len() != actual_types.len() { return None; } - if completion_ty2 == expected_type { - Some(CompletionRelevanceTypeMatch::Exact) - } else if expected_type.could_unify_with(ctx.db, completion_ty2) { - Some(CompletionRelevanceTypeMatch::CouldUnify) - } else { - None + let mut matches = expected_types + .zip(actual_types) + .chain([(expected_callable.return_type(), func.ret_type(ctx.db))]) + .map(|(expected_type, actual_type)| match_types(ctx, &expected_type, &actual_type)); + + // Any missing type match indicates that these types can not be unified. + if matches.any(|type_match| type_match.is_none()) { + return None; } + + // If any of the types are unifiable but not exact we consider the function types as a whole + // to be unifiable. Otherwise if every pair of types is an exact match the functions are an + // exact type match. + matches + .find(|type_match| matches!(type_match, Some(CompletionRelevanceTypeMatch::CouldUnify))) + .unwrap_or(Some(CompletionRelevanceTypeMatch::Exact)) } fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { @@ -796,7 +812,7 @@ fn main() { ); } - // TODO: does this test even make sense? + // TODO: How dowe test ModuleDef::Variant(Variant?) #[test] fn set_enum_variant_type_completion_info() { check_relevance( @@ -820,7 +836,7 @@ pub mod test_mod_a { fn test(input: dep::test_mod_b::Enum) { } fn main() { - test(Enum$0); + test(Enum::Variant$0); } "#, expect![[r#" @@ -859,7 +875,7 @@ fn main() { } "#, expect![[r#" - fn Function (use dep::test_mod_a::Function) [type_could_unify+requires_import] + fn Function (use dep::test_mod_a::Function) [type+requires_import] fn main [] fn test [] md dep [] @@ -868,7 +884,6 @@ fn main() { ); } - // TODO This test does not trigger the const case #[test] fn set_const_type_completion_info() { check_relevance( @@ -933,8 +948,38 @@ fn main() { ); } - // TODO: seems like something is going wrong here. Exapt type match has no effect - // EDIT: maybe it is actually working + #[test] + fn set_self_type_completion_info_with_params() { + check_relevance( + r#" +//- /lib.rs crate:dep +pub struct Struct; + +impl Struct { + pub fn Function(&self, input: i32) -> bool { + false + } +} + + +//- /main.rs crate:main deps:dep + +use dep::Struct; + + +fn test(input: fn(&dep::Struct, i32) -> bool) { } + +fn main() { + test(Struct::Function$0); +} + +"#, + expect![[r#" + me Function [type] + "#]], + ); + } + #[test] fn set_self_type_completion_info() { check_relevance( @@ -964,34 +1009,26 @@ fn func(input: Struct) { } ); } - // TODO: how do we actually test builtins? - #[test] fn set_builtin_type_completion_info() { check_relevance( r#" -//- /lib.rs crate:dep - -pub mod test_mod_b { - static STATIC: i32 = 5; -} +//- /main.rs crate:main - pub mod test_mod_a { - static STATIC: &str = "test"; -} - -//- /main.rs crate:main deps:dep - -fn test(input: i32) { } +fn test(input: bool) { } + pub Input: bool = false; fn main() { - test(STATIC$0); + let input = false; + let inputbad = 3; + test(inp$0); } "#, expect![[r#" + lc input [type+name+local] + lc inputbad [local] fn main() [] fn test(…) [] - md dep [] "#]], ); } |