/// can be called as a method.
pub has_self_param: bool,
pub is_unsafe: bool,
+ pub is_varargs: bool,
pub visibility: RawVisibility,
}
attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
has_self_param: func.has_self_param,
is_unsafe: func.is_unsafe,
+ is_varargs: func.is_varargs,
visibility: item_tree[func.visibility].clone(),
})
}
pub has_self_param: bool,
pub is_unsafe: bool,
pub params: Box<[TypeRef]>,
+ pub is_varargs: bool,
pub ret_type: TypeRef,
pub ast_id: FileAstId<ast::FnDef>,
}
params.push(type_ref);
}
}
+
+ let mut is_varargs = false;
+ if let Some(params) = func.param_list() {
+ if let Some(last) = params.params().last() {
+ is_varargs = last.dotdotdot_token().is_some();
+ }
+ }
+
let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) {
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
_ => TypeRef::unit(),
has_self_param,
is_unsafe: func.unsafe_token().is_some(),
params: params.into_boxed_slice(),
+ is_varargs,
ret_type,
ast_id,
};
Array(Box<TypeRef> /*, Expr*/),
Slice(Box<TypeRef>),
/// A fn pointer. Last element of the vector is the return type.
- Fn(Vec<TypeRef>),
+ Fn(Vec<TypeRef>, bool /*varargs*/),
// For
ImplTrait(Vec<TypeBound>),
DynTrait(Vec<TypeBound>),
.and_then(|rt| rt.type_ref())
.map(|it| TypeRef::from_ast(ctx, it))
.unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
+ let mut is_varargs = false;
let mut params = if let Some(pl) = inner.param_list() {
+ if let Some(param) = pl.params().last() {
+ is_varargs = param.dotdotdot_token().is_some();
+ }
+
pl.params()
.map(|p| p.ascribed_type())
.map(|it| TypeRef::from_ast_opt(&ctx, it))
Vec::new()
};
params.push(ret_ty);
- TypeRef::Fn(params)
+ TypeRef::Fn(params, is_varargs)
}
// for types are close enough for our purposes to the inner type for now...
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()),
fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
f(type_ref);
match type_ref {
- TypeRef::Fn(types) | TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
+ TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
+ types.iter().for_each(|t| go(t, f))
+ }
TypeRef::RawPtr(type_ref, _)
| TypeRef::Reference(type_ref, _)
| TypeRef::Array(type_ref)
};
let sig = db.callable_item_signature(callee);
+ if sig.value.is_varargs {
+ return None;
+ }
+
let params = sig.value.params();
let mut param_count = params.len();
"#,
);
}
+
+ #[test]
+ fn varargs() {
+ check_diagnostics(
+ r#"
+extern "C" {
+ fn fixed(fixed: u8);
+ fn varargs(fixed: u8, ...);
+ fn varargs2(...);
+}
+
+fn f() {
+ unsafe {
+ fixed(0);
+ fixed(0, 1);
+ //^^^^^^^^^^^ Expected 1 argument, found 2
+ varargs(0);
+ varargs(0, 1);
+ varargs2();
+ varargs2(0);
+ varargs2(0, 1);
+ }
+}
+ "#,
+ )
+ }
}
write!(f, ")")?;
}
}
- TypeCtor::FnPtr { .. } => {
- let sig = FnSig::from_fn_ptr_substs(&self.parameters);
+ TypeCtor::FnPtr { is_varargs, .. } => {
+ let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs);
write!(f, "fn(")?;
f.write_joined(sig.params(), ", ")?;
+ if is_varargs {
+ if sig.params().is_empty() {
+ write!(f, "...")?;
+ } else {
+ write!(f, ", ...")?;
+ }
+ }
write!(f, ")")?;
let ret = sig.ret();
if *ret != Ty::unit() {
};
sig_tys.push(ret_ty.clone());
let sig_ty = Ty::apply(
- TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 },
+ TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1, is_varargs: false },
Substs(sig_tys.clone().into()),
);
let closure_ty =
/// fn foo() -> i32 { 1 }
/// let bar: fn() -> i32 = foo;
/// ```
- FnPtr { num_args: u16 },
+ FnPtr { num_args: u16, is_varargs: bool },
/// The never type `!`.
Never,
}
}
}
- TypeCtor::FnPtr { num_args } => num_args as usize + 1,
+ TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1,
TypeCtor::Tuple { cardinality } => cardinality as usize,
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FnSig {
params_and_return: Arc<[Ty]>,
+ is_varargs: bool,
}
/// A polymorphic function signature.
pub type PolyFnSig = Binders<FnSig>;
impl FnSig {
- pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig {
+ pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> FnSig {
params.push(ret);
- FnSig { params_and_return: params.into() }
+ FnSig { params_and_return: params.into(), is_varargs }
}
- pub fn from_fn_ptr_substs(substs: &Substs) -> FnSig {
- FnSig { params_and_return: Arc::clone(&substs.0) }
+ pub fn from_fn_ptr_substs(substs: &Substs, is_varargs: bool) -> FnSig {
+ FnSig { params_and_return: Arc::clone(&substs.0), is_varargs }
}
pub fn params(&self) -> &[Ty] {
}
pub fn fn_ptr(sig: FnSig) -> Self {
Ty::apply(
- TypeCtor::FnPtr { num_args: sig.params().len() as u16 },
+ TypeCtor::FnPtr { num_args: sig.params().len() as u16, is_varargs: sig.is_varargs },
Substs(sig.params_and_return),
)
}
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> {
match self {
Ty::Apply(a_ty) => match a_ty.ctor {
- TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)),
+ TypeCtor::FnPtr { is_varargs, .. } => {
+ Some(FnSig::from_fn_ptr_substs(&a_ty.parameters, is_varargs))
+ }
TypeCtor::FnDef(def) => {
let sig = db.callable_item_signature(def);
Some(sig.subst(&a_ty.parameters))
Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
}
TypeRef::Placeholder => Ty::Unknown,
- TypeRef::Fn(params) => {
+ TypeRef::Fn(params, is_varargs) => {
let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect());
- Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig)
+ Ty::apply(
+ TypeCtor::FnPtr { num_args: sig.len() as u16 - 1, is_varargs: *is_varargs },
+ sig,
+ )
}
TypeRef::DynTrait(bounds) => {
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
let generics = generics(db.upcast(), def.into());
let num_binders = generics.len();
- Binders::new(num_binders, FnSig::from_params_and_return(params, ret))
+ Binders::new(num_binders, FnSig::from_params_and_return(params, ret, data.is_varargs))
}
/// Build the declared type of a function. This should not need to look at the
let params =
fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
let ret = type_for_adt(db, def.into());
- Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value))
+ Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
}
/// Build the type of a tuple struct constructor.
let params =
fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
let ret = type_for_adt(db, def.parent.into());
- Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value))
+ Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
}
/// Build the type of a tuple enum variant constructor.
.build(),
);
let sig_ty = Ty::apply(
- TypeCtor::FnPtr { num_args },
+ TypeCtor::FnPtr { num_args, is_varargs: false },
Substs::builder(num_args as usize + 1)
.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
.build(),
Ty::Apply(apply_ty) => match apply_ty.ctor {
TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
TypeCtor::Array => array_to_chalk(db, apply_ty.parameters),
- TypeCtor::FnPtr { num_args: _ } => {
+ TypeCtor::FnPtr { num_args: _, is_varargs: _ } => {
let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution })
.intern(&Interner)
substitution.shifted_out(&Interner).expect("fn ptr should have no binders"),
);
Ty::Apply(ApplicationTy {
- ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 },
+ ctor: TypeCtor::FnPtr {
+ num_args: (parameters.len() - 1) as u16,
+ is_varargs: false,
+ },
parameters,
})
}