Auto merge of #11991 - iDawer:completion_detail.impl_trait, r=iDawer

fix: completion detail shows `{unknown}` for async functions and for RPITs

Fix: completion detail shows `{unknown}` for `impl Trait` in return position.
Fix #11438 : completion detail shows `{unknown}` for return types in async functions.

#### API changes
Add `hir::Function::async_ret_type` method
This commit is contained in:
bors 2022-04-16 09:26:09 +00:00
commit 40396b463e
4 changed files with 77 additions and 6 deletions

View File

@ -1358,12 +1358,29 @@ pub fn name(self, db: &dyn HirDatabase) -> Name {
/// Get this function's return type
pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
let resolver = self.id.resolver(db.upcast());
let ret_type = &db.function_data(self.id).ret_type;
let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
let ty = ctx.lower_ty(ret_type);
let substs = TyBuilder::placeholder_subst(db, self.id);
let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
let ty = callable_sig.ret().clone();
Type::new_with_resolver_inner(db, &resolver, ty)
}
pub fn async_ret_type(self, db: &dyn HirDatabase) -> Option<Type> {
if !self.is_async(db) {
return None;
}
let resolver = self.id.resolver(db.upcast());
let substs = TyBuilder::placeholder_subst(db, self.id);
let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
let ret_ty = callable_sig.ret().clone();
for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() {
if let WhereClause::AliasEq(output_eq) = pred.into_value_and_skipped_binders().0 {
return Type::new_with_resolver_inner(db, &resolver, output_eq.ty).into();
}
}
never!("Async fn ret_type should be impl Future");
None
}
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
if !db.function_data(self.id).has_self_param() {
return None;

View File

@ -692,7 +692,14 @@ fn analyze_container(&self, sema: &Semantics<RootDatabase>) -> Option<ContainerI
(constness, expr.clone(), infer_expr_opt(expr))
},
ast::Fn(fn_) => {
(fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(sema.to_def(&fn_)?.ret_type(sema.db)))
let func = sema.to_def(&fn_)?;
let mut ret_ty = func.ret_type(sema.db);
if func.is_async(sema.db) {
if let Some(async_ret) = func.async_ret_type(sema.db) {
ret_ty = async_ret;
}
}
(fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(ret_ty))
},
ast::Static(statik) => {
(true, statik.body(), Some(sema.to_def(&statik)?.ty(sema.db)))
@ -4026,6 +4033,7 @@ fn extract_with_await() {
check_assist(
extract_function,
r#"
//- minicore: future
fn main() {
$0some_function().await;$0
}
@ -4055,6 +4063,7 @@ fn extract_with_await_and_result_not_producing_match_expr() {
check_assist(
extract_function,
r#"
//- minicore: future, result
async fn foo() -> Result<(), ()> {
$0async {}.await;
Err(())?$0
@ -4065,7 +4074,7 @@ async fn foo() -> Result<(), ()> {
fun_name().await?
}
async fn $0fun_name() -> _ {
async fn $0fun_name() -> Result<(), ()> {
async {}.await;
Err(())?
}
@ -4078,6 +4087,7 @@ fn extract_with_await_and_result_producing_match_expr() {
check_assist(
extract_function,
r#"
//- minicore: future
async fn foo() -> i32 {
loop {
let n = 1;$0
@ -4119,6 +4129,7 @@ fn extract_with_await_in_args() {
check_assist(
extract_function,
r#"
//- minicore: future
fn main() {
$0function_call("a", some_function().await);$0
}

View File

@ -228,7 +228,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
}
fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
let ret_ty = func.ret_type(db);
let mut ret_ty = func.ret_type(db);
let mut detail = String::new();
if func.is_const(db) {
@ -236,6 +236,9 @@ fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
}
if func.is_async(db) {
format_to!(detail, "async ");
if let Some(async_ret) = func.async_ret_type(db) {
ret_ty = async_ret;
}
}
if func.is_unsafe_to_call(db) {
format_to!(detail, "unsafe ");

View File

@ -602,3 +602,43 @@ fn variant fn() -> Enum
"#]],
);
}
#[test]
fn detail_impl_trait_in_return_position() {
check_empty(
r"
//- minicore: sized
trait Trait<T> {}
fn foo<U>() -> impl Trait<U> {}
fn main() {
self::$0
}
",
expect![[r"
tt Trait
fn main() fn()
fn foo() fn() -> impl Trait<U>
"]],
);
}
#[test]
fn detail_async_fn() {
check_empty(
r#"
//- minicore: future, sized
trait Trait<T> {}
async fn foo() -> u8 {}
async fn bar<U>() -> impl Trait<U> {}
fn main() {
self::$0
}
"#,
expect![[r"
tt Trait
fn main() fn()
fn bar() async fn() -> impl Trait<U>
fn foo() async fn() -> u8
"]],
);
}