mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
fix(completion): make the expected type a tad smarter with Fn
s
This commit changes how the expected type is calculated when working with Fn pointers, making the parenthesis stop vanishing when completing the function name. I've been bugged by the behaviour on parenthesis completion for a long while now. R-a assumes that the `LetStmt` type is the same as the function type I've just written. Worse is that all parenthesis vanish, even from functions that have completely different signatures. It will now verify if the signature is the same. While working on this, I noticed that record fields behave the same, so I also made it prioritize the field type instead of the current expression when possible, but I'm unsure if this is OK, so input is appreciated. ImplTraits as return types will still behave weirdly because lowering is disallowed at the time it resolves the function types.
This commit is contained in:
parent
34df29620a
commit
ed216e285d
3 changed files with 24 additions and 17 deletions
|
@ -4657,6 +4657,9 @@ impl Callable {
|
||||||
pub fn return_type(&self) -> Type {
|
pub fn return_type(&self) -> Type {
|
||||||
self.ty.derived(self.sig.ret().clone())
|
self.ty.derived(self.sig.ret().clone())
|
||||||
}
|
}
|
||||||
|
pub fn sig(&self) -> &CallableSig {
|
||||||
|
&self.sig
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
|
fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
|
||||||
|
|
|
@ -361,7 +361,12 @@ fn expected_type_and_name(
|
||||||
let ty = it.pat()
|
let ty = it.pat()
|
||||||
.and_then(|pat| sema.type_of_pat(&pat))
|
.and_then(|pat| sema.type_of_pat(&pat))
|
||||||
.or_else(|| it.initializer().and_then(|it| sema.type_of_expr(&it)))
|
.or_else(|| it.initializer().and_then(|it| sema.type_of_expr(&it)))
|
||||||
.map(TypeInfo::original);
|
.map(TypeInfo::original)
|
||||||
|
.filter(|ty| {
|
||||||
|
// don't infer the let type if the expr is a function,
|
||||||
|
// preventing parenthesis from vanishing
|
||||||
|
it.ty().is_some() || !ty.is_fn()
|
||||||
|
});
|
||||||
let name = match it.pat() {
|
let name = match it.pat() {
|
||||||
Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
|
Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
|
||||||
Some(_) | None => None,
|
Some(_) | None => None,
|
||||||
|
@ -415,20 +420,16 @@ fn expected_type_and_name(
|
||||||
})().unwrap_or((None, None))
|
})().unwrap_or((None, None))
|
||||||
},
|
},
|
||||||
ast::RecordExprField(it) => {
|
ast::RecordExprField(it) => {
|
||||||
|
let field_ty = sema.resolve_record_field(&it).map(|(_, _, ty)| ty);
|
||||||
|
let field_name = it.field_name().map(NameOrNameRef::NameRef);
|
||||||
if let Some(expr) = it.expr() {
|
if let Some(expr) = it.expr() {
|
||||||
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
||||||
(
|
let ty = field_ty
|
||||||
sema.type_of_expr(&expr).map(TypeInfo::original),
|
.or_else(|| sema.type_of_expr(&expr).map(TypeInfo::original));
|
||||||
it.field_name().map(NameOrNameRef::NameRef),
|
(ty, field_name)
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
cov_mark::hit!(expected_type_struct_field_followed_by_comma);
|
cov_mark::hit!(expected_type_struct_field_followed_by_comma);
|
||||||
let ty = sema.resolve_record_field(&it)
|
(field_ty, field_name)
|
||||||
.map(|(_, _, ty)| ty);
|
|
||||||
(
|
|
||||||
ty,
|
|
||||||
it.field_name().map(NameOrNameRef::NameRef),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// match foo { $0 }
|
// match foo { $0 }
|
||||||
|
|
|
@ -305,12 +305,15 @@ fn params(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't add parentheses if the expected type is some function reference.
|
// Don't add parentheses if the expected type is a function reference with the same signature.
|
||||||
if let Some(ty) = &ctx.expected_type {
|
if let Some(expected) = ctx.expected_type.as_ref().filter(|e| e.is_fn()) {
|
||||||
// FIXME: check signature matches?
|
if let Some(expected) = expected.as_callable(ctx.db) {
|
||||||
if ty.is_fn() {
|
if let Some(completed) = func.ty(ctx.db).as_callable(ctx.db) {
|
||||||
cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
|
if expected.sig() == completed.sig() {
|
||||||
return None;
|
cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue