Auto merge of #12316 - jonas-schievink:closure-param-hints, r=jonas-schievink

feat: Show parameter inlay hints for closure invocations

Fixes https://github.com/rust-lang/rust-analyzer/issues/12268
This commit is contained in:
bors 2022-05-19 16:54:09 +00:00
commit bf37fe23f3
3 changed files with 77 additions and 22 deletions

View file

@ -62,9 +62,9 @@ use hir_ty::{
subst_prefix, subst_prefix,
traits::FnTrait, traits::FnTrait,
AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
DebruijnIndex, GenericArgData, InEnvironment, Interner, ParamKind, QuantifiedWhereClause, ClosureId, DebruijnIndex, GenericArgData, InEnvironment, Interner, ParamKind,
Scalar, Solution, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, QuantifiedWhereClause, Scalar, Solution, Substitution, TraitEnvironment, TraitRefExt, Ty,
TyKind, TyVariableKind, WhereClause, TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind, WhereClause,
}; };
use itertools::Itertools; use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind; use nameres::diagnostics::DefDiagnosticKind;
@ -2819,10 +2819,14 @@ impl Type {
} }
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
let def = self.ty.callable_def(db); let callee = match self.ty.kind(Interner) {
TyKind::Closure(id, _) => Callee::Closure(*id),
TyKind::Function(_) => Callee::FnPtr,
_ => Callee::Def(self.ty.callable_def(db)?),
};
let sig = self.ty.callable_sig(db)?; let sig = self.ty.callable_sig(db)?;
Some(Callable { ty: self.clone(), sig, def, is_bound_method: false }) Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
} }
pub fn is_closure(&self) -> bool { pub fn is_closure(&self) -> bool {
@ -3265,34 +3269,43 @@ impl Type {
} }
} }
// FIXME: closures
#[derive(Debug)] #[derive(Debug)]
pub struct Callable { pub struct Callable {
ty: Type, ty: Type,
sig: CallableSig, sig: CallableSig,
def: Option<CallableDefId>, callee: Callee,
pub(crate) is_bound_method: bool, pub(crate) is_bound_method: bool,
} }
#[derive(Debug)]
enum Callee {
Def(CallableDefId),
Closure(ClosureId),
FnPtr,
}
pub enum CallableKind { pub enum CallableKind {
Function(Function), Function(Function),
TupleStruct(Struct), TupleStruct(Struct),
TupleEnumVariant(Variant), TupleEnumVariant(Variant),
Closure, Closure,
FnPtr,
} }
impl Callable { impl Callable {
pub fn kind(&self) -> CallableKind { pub fn kind(&self) -> CallableKind {
match self.def { use Callee::*;
Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), match self.callee {
Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
None => CallableKind::Closure, Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
Closure(_) => CallableKind::Closure,
FnPtr => CallableKind::FnPtr,
} }
} }
pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> { pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
let func = match self.def { let func = match self.callee {
Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it, Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
_ => return None, _ => return None,
}; };
let src = func.lookup(db.upcast()).source(db.upcast()); let src = func.lookup(db.upcast()).source(db.upcast());
@ -3312,8 +3325,9 @@ impl Callable {
.iter() .iter()
.skip(if self.is_bound_method { 1 } else { 0 }) .skip(if self.is_bound_method { 1 } else { 0 })
.map(|ty| self.ty.derived(ty.clone())); .map(|ty| self.ty.derived(ty.clone()));
let patterns = match self.def { let map_param = |it: ast::Param| it.pat().map(Either::Right);
Some(CallableDefId::FunctionId(func)) => { let patterns = match self.callee {
Callee::Def(CallableDefId::FunctionId(func)) => {
let src = func.lookup(db.upcast()).source(db.upcast()); let src = func.lookup(db.upcast()).source(db.upcast());
src.value.param_list().map(|param_list| { src.value.param_list().map(|param_list| {
param_list param_list
@ -3321,9 +3335,20 @@ impl Callable {
.map(|it| Some(Either::Left(it))) .map(|it| Some(Either::Left(it)))
.filter(|_| !self.is_bound_method) .filter(|_| !self.is_bound_method)
.into_iter() .into_iter()
.chain(param_list.params().map(|it| it.pat().map(Either::Right))) .chain(param_list.params().map(map_param))
}) })
} }
Callee::Closure(closure_id) => match closure_source(db, closure_id) {
Some(src) => src.param_list().map(|param_list| {
param_list
.self_param()
.map(|it| Some(Either::Left(it)))
.filter(|_| !self.is_bound_method)
.into_iter()
.chain(param_list.params().map(map_param))
}),
None => None,
},
_ => None, _ => None,
}; };
patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
@ -3333,6 +3358,18 @@ impl Callable {
} }
} }
fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
let (owner, expr_id) = db.lookup_intern_closure(closure.into());
let (_, source_map) = db.body_with_source_map(owner);
let ast = source_map.expr_syntax(expr_id).ok()?;
let root = ast.file_syntax(db.upcast());
let expr = ast.value.to_node(&root);
match expr {
ast::Expr::ClosureExpr(it) => Some(it),
_ => None,
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BindingMode { pub enum BindingMode {
Move, Move,

View file

@ -1169,6 +1169,23 @@ fn main() {
); );
} }
#[test]
fn param_hints_on_closure() {
check_params(
r#"
fn main() {
let clo = |a: u8, b: u8| a + b;
clo(
1,
//^ a
2,
//^ b
);
}
"#,
);
}
#[test] #[test]
fn param_name_similar_to_fn_name_still_hints() { fn param_name_similar_to_fn_name_still_hints() {
check_params( check_params(
@ -2000,7 +2017,8 @@ fn main() {
; ;
let _: i32 = multiply(1, 2); let _: i32 = multiply(1, 2);
//^ a ^ b
let multiply_ref = &multiply; let multiply_ref = &multiply;
//^^^^^^^^^^^^ &|i32, i32| -> i32 //^^^^^^^^^^^^ &|i32, i32| -> i32

View file

@ -149,7 +149,7 @@ fn signature_help_for_call(
variant.name(db) variant.name(db)
); );
} }
hir::CallableKind::Closure => (), hir::CallableKind::Closure | hir::CallableKind::FnPtr => (),
} }
res.signature.push('('); res.signature.push('(');
@ -189,7 +189,7 @@ fn signature_help_for_call(
hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => { hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
render(func.ret_type(db)) render(func.ret_type(db))
} }
hir::CallableKind::Function(_) | hir::CallableKind::Closure => { hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => {
render(callable.return_type()) render(callable.return_type())
} }
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
@ -914,8 +914,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
(S) -> i32 (s: S) -> i32
^ ^^^^
"#]], "#]],
) )
} }