mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge #5417
5417: Mismatched arg count works for lambdas r=jonas-schievink a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
9b5ac1d82f
5 changed files with 86 additions and 35 deletions
|
@ -1233,9 +1233,13 @@ impl Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
|
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
|
||||||
let (id, substs) = self.ty.value.as_callable()?;
|
let def = match self.ty.value {
|
||||||
let sig = db.callable_item_signature(id).subst(substs);
|
Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(def), parameters: _ }) => Some(def),
|
||||||
Some(Callable { ty: self.clone(), sig, id, is_bound_method: false })
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let sig = self.ty.value.callable_sig(db)?;
|
||||||
|
Some(Callable { ty: self.clone(), sig, def, is_bound_method: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_closure(&self) -> bool {
|
pub fn is_closure(&self) -> bool {
|
||||||
|
@ -1525,7 +1529,7 @@ impl HirDisplay for Type {
|
||||||
pub struct Callable {
|
pub struct Callable {
|
||||||
ty: Type,
|
ty: Type,
|
||||||
sig: FnSig,
|
sig: FnSig,
|
||||||
id: CallableDefId,
|
def: Option<CallableDefId>,
|
||||||
pub(crate) is_bound_method: bool,
|
pub(crate) is_bound_method: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,19 +1537,21 @@ pub enum CallableKind {
|
||||||
Function(Function),
|
Function(Function),
|
||||||
TupleStruct(Struct),
|
TupleStruct(Struct),
|
||||||
TupleEnumVariant(EnumVariant),
|
TupleEnumVariant(EnumVariant),
|
||||||
|
Closure,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Callable {
|
impl Callable {
|
||||||
pub fn kind(&self) -> CallableKind {
|
pub fn kind(&self) -> CallableKind {
|
||||||
match self.id {
|
match self.def {
|
||||||
CallableDefId::FunctionId(it) => CallableKind::Function(it.into()),
|
Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
|
||||||
CallableDefId::StructId(it) => CallableKind::TupleStruct(it.into()),
|
Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
|
||||||
CallableDefId::EnumVariantId(it) => CallableKind::TupleEnumVariant(it.into()),
|
Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
|
||||||
|
None => CallableKind::Closure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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.id {
|
let func = match self.def {
|
||||||
CallableDefId::FunctionId(it) if self.is_bound_method => it,
|
Some(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());
|
||||||
|
@ -1565,8 +1571,8 @@ 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.id {
|
let patterns = match self.def {
|
||||||
CallableDefId::FunctionId(func) => {
|
Some(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
|
||||||
|
@ -1577,8 +1583,7 @@ impl Callable {
|
||||||
.chain(param_list.params().map(|it| it.pat().map(Either::Right)))
|
.chain(param_list.params().map(|it| it.pat().map(Either::Right)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
CallableDefId::StructId(_) => None,
|
_ => None,
|
||||||
CallableDefId::EnumVariantId(_) => None,
|
|
||||||
};
|
};
|
||||||
patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
|
patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,28 +158,32 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_method_call = matches!(expr, Expr::MethodCall { .. });
|
let is_method_call = matches!(expr, Expr::MethodCall { .. });
|
||||||
let (callee, args) = match expr {
|
let (sig, args) = match expr {
|
||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args } => {
|
||||||
let callee = &self.infer.type_of_expr[*callee];
|
let callee = &self.infer.type_of_expr[*callee];
|
||||||
let (callable, _) = callee.as_callable()?;
|
let sig = callee.callable_sig(db)?;
|
||||||
|
(sig, args.clone())
|
||||||
(callable, args.clone())
|
|
||||||
}
|
}
|
||||||
Expr::MethodCall { receiver, args, .. } => {
|
Expr::MethodCall { receiver, args, .. } => {
|
||||||
let callee = self.infer.method_resolution(call_id)?;
|
|
||||||
let mut args = args.clone();
|
let mut args = args.clone();
|
||||||
args.insert(0, *receiver);
|
args.insert(0, *receiver);
|
||||||
(callee.into(), args)
|
|
||||||
|
// FIXME: note that we erase information about substs here. This
|
||||||
|
// is not right, but, luckily, doesn't matter as we care only
|
||||||
|
// about the number of params
|
||||||
|
let callee = self.infer.method_resolution(call_id)?;
|
||||||
|
let sig = db.callable_item_signature(callee.into()).value;
|
||||||
|
|
||||||
|
(sig, args)
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sig = db.callable_item_signature(callee);
|
if sig.is_varargs {
|
||||||
if sig.value.is_varargs {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let params = sig.value.params();
|
let params = sig.params();
|
||||||
|
|
||||||
let mut param_count = params.len();
|
let mut param_count = params.len();
|
||||||
let mut arg_count = args.len();
|
let mut arg_count = args.len();
|
||||||
|
@ -542,4 +546,20 @@ fn f() {
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arg_count_lambda() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let f = |()| ();
|
||||||
|
f();
|
||||||
|
//^^^ Expected 1 argument, found 0
|
||||||
|
f(());
|
||||||
|
f((), ());
|
||||||
|
//^^^^^^^^^ Expected 1 argument, found 2
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -767,15 +767,6 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_callable(&self) -> Option<(CallableDefId, &Substs)> {
|
|
||||||
match self {
|
|
||||||
Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(callable_def), parameters }) => {
|
|
||||||
Some((*callable_def, parameters))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_never(&self) -> bool {
|
pub fn is_never(&self) -> bool {
|
||||||
matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
|
matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
|
||||||
}
|
}
|
||||||
|
@ -807,7 +798,7 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> {
|
pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> {
|
||||||
match self {
|
match self {
|
||||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||||
TypeCtor::FnPtr { is_varargs, .. } => {
|
TypeCtor::FnPtr { is_varargs, .. } => {
|
||||||
|
|
|
@ -70,6 +70,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||||
variant.name(db)
|
variant.name(db)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
hir::CallableKind::Closure => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
res.signature.push('(');
|
res.signature.push('(');
|
||||||
|
@ -93,7 +94,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||||
res.signature.push(')');
|
res.signature.push(')');
|
||||||
|
|
||||||
match callable.kind() {
|
match callable.kind() {
|
||||||
hir::CallableKind::Function(_) => {
|
hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
|
||||||
let ret_type = callable.return_type();
|
let ret_type = callable.return_type();
|
||||||
if !ret_type.is_unit() {
|
if !ret_type.is_unit() {
|
||||||
format_to!(res.signature, " -> {}", ret_type.display(db));
|
format_to!(res.signature, " -> {}", ret_type.display(db));
|
||||||
|
@ -702,4 +703,36 @@ id! {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_info_for_lambdas() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S;
|
||||||
|
fn foo(s: S) -> i32 { 92 }
|
||||||
|
fn main() {
|
||||||
|
(|s| foo(s))(<|>)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(S) -> i32
|
||||||
|
(<S>)
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_info_for_fn_ptr() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main(f: fn(i32, f64) -> char) {
|
||||||
|
f(0, <|>)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, f64) -> char
|
||||||
|
(i32, <f64>)
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,7 +262,9 @@ fn should_show_param_name_hint(
|
||||||
let param_name = param_name.trim_start_matches('_');
|
let param_name = param_name.trim_start_matches('_');
|
||||||
let fn_name = match callable.kind() {
|
let fn_name = match callable.kind() {
|
||||||
hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
|
hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
|
||||||
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => None,
|
hir::CallableKind::TupleStruct(_)
|
||||||
|
| hir::CallableKind::TupleEnumVariant(_)
|
||||||
|
| hir::CallableKind::Closure => None,
|
||||||
};
|
};
|
||||||
if param_name.is_empty()
|
if param_name.is_empty()
|
||||||
|| Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
|
|| Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
|
||||||
|
|
Loading…
Reference in a new issue