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:
bors[bot] 2020-07-17 11:01:52 +00:00 committed by GitHub
commit 9b5ac1d82f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 35 deletions

View file

@ -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()
} }

View file

@ -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
}
"#,
)
}
} }

View file

@ -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, .. } => {

View file

@ -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>)
"#]],
)
}
} }

View file

@ -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('_'))