From 7f2bac82c653eb6f237696909bfbb53fc471d74a Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 27 Oct 2025 18:27:20 +0200 Subject: [PATCH] Show proper async function signatures in the signature help Co-authored-by: Lukas Wirth --- crates/ide/src/signature_help.rs | 157 ++++++++++++++++++++++++++++--- 1 file changed, 144 insertions(+), 13 deletions(-) diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index e927fd57ae..f9ec44813a 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -175,6 +175,9 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db); + if func.is_async(db) { + format_to!(res.signature, "async "); + } format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); let generic_params = GenericDef::Function(func) @@ -283,13 +286,16 @@ fn signature_help_for_call( } }; match callable.kind() { - hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => { - render(func.ret_type(db)) + hir::CallableKind::Function(func) => render(func.async_ret_type(db).unwrap_or_else(|| { + if callable.return_type().contains_unknown() { + func.ret_type(db) + } else { + callable.return_type() + } + })), + hir::CallableKind::Closure(_) | hir::CallableKind::FnPtr | hir::CallableKind::FnImpl(_) => { + render(callable.return_type()) } - hir::CallableKind::Function(_) - | hir::CallableKind::Closure(_) - | hir::CallableKind::FnPtr - | hir::CallableKind::FnImpl(_) => render(callable.return_type()), hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} } Some(res) @@ -751,13 +757,7 @@ mod tests { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - let fixture = format!( - r#" -//- minicore: sized, fn -{ra_fixture} - "# - ); - let (db, position) = position(&fixture); + let (db, position) = position(ra_fixture); let sig_help = hir::attach_db(&db, || crate::signature_help::signature_help(&db, position)); let actual = match sig_help { Some(sig_help) => { @@ -795,6 +795,7 @@ mod tests { fn test_fn_signature_two_args() { check( r#" +//- minicore: sized, fn fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo($03, ); } "#, @@ -805,6 +806,7 @@ fn bar() { foo($03, ); } ); check( r#" +//- minicore: sized, fn fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo(3$0, ); } "#, @@ -815,6 +817,7 @@ fn bar() { foo(3$0, ); } ); check( r#" +//- minicore: sized, fn fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo(3,$0 ); } "#, @@ -825,6 +828,7 @@ fn bar() { foo(3,$0 ); } ); check( r#" +//- minicore: sized, fn fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo(3, $0); } "#, @@ -839,6 +843,7 @@ fn bar() { foo(3, $0); } fn test_fn_signature_two_args_empty() { check( r#" +//- minicore: sized, fn fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo($0); } "#, @@ -853,6 +858,7 @@ fn bar() { foo($0); } fn test_fn_signature_two_args_first_generics() { check( r#" +//- minicore: sized, fn fn foo(x: T, y: U) -> u32 where T: Copy + Display, U: Debug { x + y } @@ -870,6 +876,7 @@ fn bar() { foo($03, ); } fn test_fn_signature_no_params() { check( r#" +//- minicore: sized, fn fn foo() -> T where T: Copy + Display {} fn bar() { foo($0); } "#, @@ -883,6 +890,7 @@ fn bar() { foo($0); } fn test_fn_signature_for_impl() { check( r#" +//- minicore: sized, fn struct F; impl F { pub fn new() { } } fn bar() { @@ -899,6 +907,7 @@ fn bar() { fn test_fn_signature_for_method_self() { check( r#" +//- minicore: sized, fn struct S; impl S { pub fn do_it(&self) {} } @@ -917,6 +926,7 @@ fn bar() { fn test_fn_signature_for_method_with_arg() { check( r#" +//- minicore: sized, fn struct S; impl S { fn foo(&self, x: i32) {} @@ -935,6 +945,7 @@ fn main() { S.foo($0); } fn test_fn_signature_for_generic_method() { check( r#" +//- minicore: sized, fn struct S(T); impl S { fn foo(&self, x: T) {} @@ -953,6 +964,7 @@ fn main() { S(1u32).foo($0); } fn test_fn_signature_for_method_with_arg_as_assoc_fn() { check( r#" +//- minicore: sized, fn struct S; impl S { fn foo(&self, x: i32) {} @@ -971,6 +983,7 @@ fn main() { S::foo($0); } fn test_fn_signature_with_docs_simple() { check( r#" +//- minicore: sized, fn /// test // non-doc-comment fn foo(j: u32) -> u32 { @@ -994,6 +1007,7 @@ fn bar() { fn test_fn_signature_with_docs() { check( r#" +//- minicore: sized, fn /// Adds one to the number given. /// /// # Examples @@ -1031,6 +1045,7 @@ pub fn r#do() { fn test_fn_signature_with_docs_impl() { check( r#" +//- minicore: sized, fn struct addr; impl addr { /// Adds one to the number given. @@ -1073,6 +1088,7 @@ pub fn do_it() { fn test_fn_signature_with_docs_from_actix() { check( r#" +//- minicore: sized, fn trait Actor { /// Actor execution context type type Context; @@ -1106,6 +1122,7 @@ fn foo(mut r: impl WriteHandler<()>) { fn call_info_bad_offset() { check( r#" +//- minicore: sized, fn fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo $0 (3, ); } "#, @@ -1117,6 +1134,7 @@ fn bar() { foo $0 (3, ); } fn outside_of_arg_list() { check( r#" +//- minicore: sized, fn fn foo(a: u8) {} fn f() { foo(123)$0 @@ -1126,6 +1144,7 @@ fn f() { ); check( r#" +//- minicore: sized, fn fn foo(a: u8) {} fn f() { foo::$0() @@ -1135,6 +1154,7 @@ fn f() { ); check( r#" +//- minicore: sized, fn fn foo(a: u8) -> u8 {a} fn bar(a: u8) -> u8 {a} fn f() { @@ -1148,6 +1168,7 @@ fn f() { ); check( r#" +//- minicore: sized, fn struct Vec(T); struct Vec2(T); fn f() { @@ -1165,6 +1186,7 @@ fn f() { fn test_nested_method_in_lambda() { check( r#" +//- minicore: sized, fn struct Foo; impl Foo { fn bar(&self, _: u32) { } } @@ -1186,6 +1208,7 @@ fn main() { fn works_for_tuple_structs() { check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32); fn main() { @@ -1205,6 +1228,7 @@ fn main() { fn tuple_struct_pat() { check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32); fn main() { @@ -1224,6 +1248,7 @@ fn main() { fn tuple_struct_pat_rest() { check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32, f32, u16); fn main() { @@ -1239,6 +1264,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32, f32, u16, u8); fn main() { @@ -1254,6 +1280,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32, f32, u16); fn main() { @@ -1269,6 +1296,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32, f32, u16, u8); fn main() { @@ -1284,6 +1312,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32, f32, u16); fn main() { @@ -1299,6 +1328,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn /// A cool tuple struct struct S(u32, i32, f32, u16); fn main() { @@ -1318,6 +1348,7 @@ fn main() { fn generic_struct() { check( r#" +//- minicore: sized, fn struct S(T); fn main() { let s = S($0); @@ -1334,6 +1365,7 @@ fn main() { fn works_for_enum_variants() { check( r#" +//- minicore: sized, fn enum E { /// A Variant A(i32), @@ -1360,6 +1392,7 @@ fn main() { fn cant_call_struct_record() { check( r#" +//- minicore: sized, fn struct S { x: u32, y: i32 } fn main() { let s = S($0); @@ -1373,6 +1406,7 @@ fn main() { fn cant_call_enum_record() { check( r#" +//- minicore: sized, fn enum E { /// A Variant A(i32), @@ -1394,6 +1428,7 @@ fn main() { fn fn_signature_for_call_in_macro() { check( r#" +//- minicore: sized, fn macro_rules! id { ($($tt:tt)*) => { $($tt)* } } fn foo() { } id! { @@ -1410,6 +1445,7 @@ id! { fn fn_signature_for_method_call_defined_in_macro() { check( r#" +//- minicore: sized, fn macro_rules! id { ($($tt:tt)*) => { $($tt)* } } struct S; id! { @@ -1429,6 +1465,7 @@ fn test() { S.foo($0); } fn call_info_for_lambdas() { check( r#" +//- minicore: sized, fn struct S; fn foo(s: S) -> i32 { 92 } fn main() { @@ -1443,6 +1480,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn struct S; fn foo(s: S) -> i32 { 92 } fn main() { @@ -1456,6 +1494,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn struct S; fn foo(s: S) -> i32 { 92 } fn main() { @@ -1474,6 +1513,7 @@ fn main() { fn call_info_for_fn_def_over_reference() { check( r#" +//- minicore: sized, fn struct S; fn foo(s: S) -> i32 { 92 } fn main() { @@ -1492,6 +1532,7 @@ fn main() { fn call_info_for_fn_ptr() { check( r#" +//- minicore: sized, fn fn main(f: fn(i32, f64) -> char) { f(0, $0) } @@ -1507,6 +1548,7 @@ fn main(f: fn(i32, f64) -> char) { fn call_info_for_fn_impl() { check( r#" +//- minicore: sized, fn struct S; impl core::ops::FnOnce<(i32, f64)> for S { type Output = char; @@ -1524,6 +1566,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn struct S; impl core::ops::FnOnce<(i32, f64)> for S { type Output = char; @@ -1541,6 +1584,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn struct S; impl core::ops::FnOnce<(i32, f64)> for S { type Output = char; @@ -1556,6 +1600,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn struct S; impl core::ops::FnOnce<(i32, f64)> for S { type Output = char; @@ -1576,6 +1621,7 @@ fn main() { fn call_info_for_unclosed_call() { check( r#" +//- minicore: sized, fn fn foo(foo: u32, bar: u32) {} fn main() { foo($0 @@ -1588,6 +1634,7 @@ fn main() { // check with surrounding space check( r#" +//- minicore: sized, fn fn foo(foo: u32, bar: u32) {} fn main() { foo( $0 @@ -1603,6 +1650,7 @@ fn main() { fn test_multiline_argument() { check( r#" +//- minicore: sized, fn fn callee(a: u8, b: u8) {} fn main() { callee(match 0 { @@ -1613,6 +1661,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn callee(a: u8, b: u8) {} fn main() { callee(match 0 { @@ -1626,6 +1675,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn callee(a: u8, b: u8) {} fn main() { callee($0match 0 { @@ -1643,6 +1693,7 @@ fn main() { fn test_generics_simple() { check( r#" +//- minicore: sized, fn /// Option docs. enum Option { Some(T), @@ -1666,6 +1717,7 @@ fn f() { fn test_generics_on_variant() { check( r#" +//- minicore: sized, fn /// Option docs. enum Option { /// Some docs. @@ -1693,6 +1745,7 @@ fn f() { fn test_lots_of_generics() { check( r#" +//- minicore: sized, fn trait Tr {} struct S(T); @@ -1716,6 +1769,7 @@ fn f() { fn test_generics_in_trait_ufcs() { check( r#" +//- minicore: sized, fn trait Tr { fn f() {} } @@ -1739,6 +1793,7 @@ fn f() { fn test_generics_in_method_call() { check( r#" +//- minicore: sized, fn struct S; impl S { @@ -1760,6 +1815,7 @@ fn f() { fn test_generic_param_in_method_call() { check( r#" +//- minicore: sized, fn struct Foo; impl Foo { fn test(&mut self, val: V) {} @@ -1779,6 +1835,7 @@ fn sup() { fn test_generic_kinds() { check( r#" +//- minicore: sized, fn fn callee<'a, const A: u8, T, const C: u8>() {} fn f() { @@ -1792,6 +1849,7 @@ fn f() { ); check( r#" +//- minicore: sized, fn fn callee<'a, const A: u8, T, const C: u8>() {} fn f() { @@ -1809,6 +1867,7 @@ fn f() { fn test_trait_assoc_types() { check( r#" +//- minicore: sized, fn trait Trait<'a, T> { type Assoc; } @@ -1821,6 +1880,7 @@ fn f() -> impl Trait<(), $0 ); check( r#" +//- minicore: sized, fn trait Iterator { type Item; } @@ -1833,6 +1893,7 @@ fn f() -> impl Iterator<$0 ); check( r#" +//- minicore: sized, fn trait Iterator { type Item; } @@ -1845,6 +1906,7 @@ fn f() -> impl Iterator impl Tr<$0 ); check( r#" +//- minicore: sized, fn trait Tr { type A; type B; @@ -1871,6 +1934,7 @@ fn f() -> impl Tr impl Tr impl Tr impl Sub<$0 fn no_assoc_types_outside_type_bounds() { check( r#" +//- minicore: sized, fn trait Tr { type Assoc; } @@ -1938,6 +2005,7 @@ impl Tr<$0 // FIXME: Substitute type vars in impl trait (`U` -> `i8`) check( r#" +//- minicore: sized, fn trait Trait {} struct Wrap(T); fn foo(x: Wrap>) {} @@ -1956,6 +2024,7 @@ fn f() { fn fully_qualified_syntax() { check( r#" +//- minicore: sized, fn fn f() { trait A { fn foo(&self, other: Self); } A::foo(&self$0, other); @@ -1972,6 +2041,7 @@ fn f() { fn help_for_generic_call() { check( r#" +//- minicore: sized, fn fn f i32>(f: F) { f($0) } @@ -1983,6 +2053,7 @@ fn f i32>(f: F) { ); check( r#" +//- minicore: sized, fn fn f &T>(f: F) { f($0) } @@ -2004,6 +2075,7 @@ fn f &T>(f: F) { // https://github.com/rust-lang/rust/pull/146602 should fix the solver bug. check( r#" +//- minicore: sized, fn fn f() { take(2)($0); } @@ -2022,6 +2094,7 @@ fn take( fn record_literal() { check( r#" +//- minicore: sized, fn struct Strukt { t: T, u: U, @@ -2045,6 +2118,7 @@ fn f() { fn record_literal_nonexistent_field() { check( r#" +//- minicore: sized, fn struct Strukt { a: u8, } @@ -2066,6 +2140,7 @@ fn f() { fn tuple_variant_record_literal() { check( r#" +//- minicore: sized, fn enum Opt { Some(u8), } @@ -2080,6 +2155,7 @@ fn f() { ); check( r#" +//- minicore: sized, fn enum Opt { Some(u8), } @@ -2098,6 +2174,7 @@ fn f() { fn record_literal_self() { check( r#" +//- minicore: sized, fn struct S { t: u8 } impl S { fn new() -> Self { @@ -2116,6 +2193,7 @@ impl S { fn record_pat() { check( r#" +//- minicore: sized, fn struct Strukt { t: T, u: U, @@ -2139,6 +2217,7 @@ fn f() { fn test_enum_in_nested_method_in_lambda() { check( r#" +//- minicore: sized, fn enum A { A, B @@ -2162,6 +2241,7 @@ fn main() { fn test_tuple_expr_free() { check( r#" +//- minicore: sized, fn fn main() { (0$0, 1, 3); } @@ -2173,6 +2253,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { ($0 1, 3); } @@ -2184,6 +2265,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { (1, 3 $0); } @@ -2195,6 +2277,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { (1, 3 $0,); } @@ -2210,6 +2293,7 @@ fn main() { fn test_tuple_expr_expected() { check( r#" +//- minicore: sized, fn fn main() { let _: (&str, u32, u32)= ($0, 1, 3); } @@ -2222,6 +2306,7 @@ fn main() { // FIXME: Should typeck report a 4-ary tuple for the expression here? check( r#" +//- minicore: sized, fn fn main() { let _: (&str, u32, u32, u32) = ($0, 1, 3); } @@ -2233,6 +2318,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let _: (&str, u32, u32)= ($0, 1, 3, 5); } @@ -2248,6 +2334,7 @@ fn main() { fn test_tuple_pat_free() { check( r#" +//- minicore: sized, fn fn main() { let ($0, 1, 3); } @@ -2259,6 +2346,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (0$0, 1, 3); } @@ -2270,6 +2358,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let ($0 1, 3); } @@ -2281,6 +2370,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0); } @@ -2292,6 +2382,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0,); } @@ -2303,6 +2394,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0, ..); } @@ -2314,6 +2406,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3, .., $0); } @@ -2330,6 +2423,7 @@ fn main() { fn test_tuple_pat_expected() { check( r#" +//- minicore: sized, fn fn main() { let (0$0, 1, 3): (i32, i32, i32); } @@ -2341,6 +2435,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let ($0, 1, 3): (i32, i32, i32); } @@ -2352,6 +2447,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0): (i32,); } @@ -2363,6 +2459,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0, ..): (i32, i32, i32, i32); } @@ -2374,6 +2471,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3, .., $0): (i32, i32, i32); } @@ -2388,6 +2486,7 @@ fn main() { fn test_tuple_pat_expected_inferred() { check( r#" +//- minicore: sized, fn fn main() { let (0$0, 1, 3) = (1, 2 ,3); } @@ -2399,6 +2498,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let ($0 1, 3) = (1, 2, 3); } @@ -2411,6 +2511,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0) = (1,); } @@ -2422,6 +2523,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3 $0, ..) = (1, 2, 3, 4); } @@ -2433,6 +2535,7 @@ fn main() { ); check( r#" +//- minicore: sized, fn fn main() { let (1, 3, .., $0) = (1, 2, 3); } @@ -2448,6 +2551,7 @@ fn main() { fn test_tuple_generic_param() { check( r#" +//- minicore: sized, fn struct S(T); fn main() { @@ -2465,6 +2569,7 @@ fn main() { fn test_enum_generic_param() { check( r#" +//- minicore: sized, fn enum Option { Some(T), None, @@ -2485,6 +2590,7 @@ fn main() { fn test_enum_variant_generic_param() { check( r#" +//- minicore: sized, fn enum Option { Some(T), None, @@ -2505,6 +2611,7 @@ fn main() { fn test_generic_arg_with_default() { check( r#" +//- minicore: sized, fn struct S { field: T, } @@ -2521,6 +2628,7 @@ fn main() { check( r#" +//- minicore: sized, fn struct S { field: C, } @@ -2535,4 +2643,27 @@ fn main() { "#]], ); } + + #[test] + fn test_async_function() { + check( + r#" +//- minicore: sized, fn, future, result +pub async fn conn_mut(f: F) -> Result +where + F: FnOnce() -> T, +{ + Ok(f()) +} + +fn main() { + conn_mut($0) +} + "#, + expect![[r#" + async fn conn_mut T, T>(f: F) -> Result + ^^^^ + "#]], + ); + } }