Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/signature_help.rs')
| -rw-r--r-- | crates/ide/src/signature_help.rs | 156 |
1 files changed, 135 insertions, 21 deletions
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index b2eb5a5fff..378a38892c 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -201,7 +201,21 @@ fn signature_help_for_call( variant.name(db).display(db) ); } - hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (), + hir::CallableKind::Closure(closure) => { + let fn_trait = closure.fn_trait(db); + format_to!(res.signature, "impl {fn_trait}") + } + hir::CallableKind::FnPtr => format_to!(res.signature, "fn"), + hir::CallableKind::FnImpl(fn_trait) => match callable.ty().as_adt() { + // FIXME: Render docs of the concrete trait impl function + Some(adt) => format_to!( + res.signature, + "<{} as {fn_trait}>::{}", + adt.name(db).display(db), + fn_trait.function_name() + ), + None => format_to!(res.signature, "impl {fn_trait}"), + }, } res.signature.push('('); @@ -210,12 +224,15 @@ fn signature_help_for_call( format_to!(res.signature, "{}", self_param.display(db)) } let mut buf = String::new(); - for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() { + for (idx, p) in callable.params().into_iter().enumerate() { buf.clear(); - if let Some(pat) = pat { - match pat { - Either::Left(_self) => format_to!(buf, "self: "), - Either::Right(pat) => format_to!(buf, "{}: ", pat), + if let Some(param) = p.source(sema.db) { + match param.value { + Either::Right(param) => match param.pat() { + Some(pat) => format_to!(buf, "{}: ", pat), + None => format_to!(buf, "?: "), + }, + Either::Left(_) => format_to!(buf, "self: "), } } // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is @@ -223,9 +240,9 @@ fn signature_help_for_call( // In that case, fall back to render definitions of the respective parameters. // This is overly conservative: we do not substitute known type vars // (see FIXME in tests::impl_trait) and falling back on any unknowns. - match (ty.contains_unknown(), fn_params.as_deref()) { + match (p.ty().contains_unknown(), fn_params.as_deref()) { (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)), - _ => format_to!(buf, "{}", ty.display(db)), + _ => format_to!(buf, "{}", p.ty().display(db)), } res.push_call_param(&buf); } @@ -242,9 +259,9 @@ fn signature_help_for_call( render(func.ret_type(db)) } hir::CallableKind::Function(_) - | hir::CallableKind::Closure + | hir::CallableKind::Closure(_) | hir::CallableKind::FnPtr - | hir::CallableKind::Other => render(callable.return_type()), + | hir::CallableKind::FnImpl(_) => render(callable.return_type()), hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} } Some(res) @@ -1346,14 +1363,42 @@ fn test() { S.foo($0); } struct S; fn foo(s: S) -> i32 { 92 } fn main() { + let _move = S; + (|s| {{_move}; foo(s)})($0) +} + "#, + expect![[r#" + impl FnOnce(s: S) -> i32 + ^^^^ + "#]], + ); + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } +fn main() { (|s| foo(s))($0) } "#, expect![[r#" - (s: S) -> i32 - ^^^^ + impl Fn(s: S) -> i32 + ^^^^ "#]], - ) + ); + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } +fn main() { + let mut mutate = 0; + (|s| { mutate = 1; foo(s) })($0) +} + "#, + expect![[r#" + impl FnMut(s: S) -> i32 + ^^^^ + "#]], + ); } #[test] @@ -1383,13 +1428,82 @@ fn main(f: fn(i32, f64) -> char) { } "#, expect![[r#" - (i32, f64) -> char - --- ^^^ + fn(i32, f64) -> char + --- ^^^ "#]], ) } #[test] + fn call_info_for_fn_impl() { + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnMut<(i32, f64)> for S {} +impl core::ops::Fn<(i32, f64)> for S {} +fn main() { + S($0); +} + "#, + expect![[r#" + <S as Fn>::call(i32, f64) -> char + ^^^ --- + "#]], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnMut<(i32, f64)> for S {} +impl core::ops::Fn<(i32, f64)> for S {} +fn main() { + S(1, $0); +} + "#, + expect![[r#" + <S as Fn>::call(i32, f64) -> char + --- ^^^ + "#]], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnOnce<(char, char)> for S { + type Output = f64; +} +fn main() { + S($0); +} + "#, + expect![""], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnOnce<(char, char)> for S { + type Output = f64; +} +fn main() { + // FIXME: The ide layer loses the calling info here so we get an ambiguous trait solve result + S(0i32, $0); +} + "#, + expect![""], + ); + } + + #[test] fn call_info_for_unclosed_call() { check( r#" @@ -1794,19 +1908,19 @@ fn f<F: FnOnce(u8, u16) -> i32>(f: F) { } "#, expect![[r#" - (u8, u16) -> i32 - ^^ --- + impl FnOnce(u8, u16) -> i32 + ^^ --- "#]], ); check( r#" -fn f<T, F: FnOnce(&T, u16) -> &T>(f: F) { +fn f<T, F: FnMut(&T, u16) -> &T>(f: F) { f($0) } "#, expect![[r#" - (&T, u16) -> &T - ^^ --- + impl FnMut(&T, u16) -> &T + ^^ --- "#]], ); } @@ -1826,7 +1940,7 @@ fn take<C, Error>( } "#, expect![[r#" - () -> i32 + impl Fn() -> i32 "#]], ); } |