use crate::{
error::ExtError as _,
ir,
ir::attrs::Attrs as _,
};
use proc_macro2::{
Ident,
Span,
TokenStream,
};
mod callable;
mod constructor;
mod impl_item;
mod iter;
mod message;
#[cfg(test)]
mod tests;
use self::callable::ensure_callable_invariants;
pub use self::{
callable::{
Callable,
CallableKind,
CallableWithSelector,
InputsIter,
Visibility,
},
constructor::Constructor,
impl_item::ImplItem,
iter::{
IterConstructors,
IterMessages,
},
message::{
Message,
Receiver,
},
};
use quote::TokenStreamExt as _;
use syn::spanned::Spanned;
use super::utils::extract_cfg_attributes;
#[derive(Debug, PartialEq, Eq)]
pub struct ItemImpl {
attrs: Vec<syn::Attribute>,
defaultness: Option<syn::token::Default>,
unsafety: Option<syn::token::Unsafe>,
impl_token: syn::token::Impl,
generics: syn::Generics,
trait_: Option<(Option<syn::Token![!]>, syn::Path, syn::token::For)>,
self_ty: Box<syn::Type>,
brace_token: syn::token::Brace,
items: Vec<ImplItem>,
namespace: Option<ir::Namespace>,
}
impl quote::ToTokens for ItemImpl {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.append_all(
self.attrs
.iter()
.filter(|attr| matches!(attr.style, syn::AttrStyle::Outer)),
);
self.defaultness.to_tokens(tokens);
self.unsafety.to_tokens(tokens);
self.impl_token.to_tokens(tokens);
self.generics.to_tokens(tokens);
if let Some((polarity, path, for_token)) = &self.trait_ {
polarity.to_tokens(tokens);
path.to_tokens(tokens);
for_token.to_tokens(tokens);
}
self.self_ty.to_tokens(tokens);
self.generics.where_clause.to_tokens(tokens);
self.brace_token.surround(tokens, |tokens| {
tokens.append_all(
self.attrs
.iter()
.filter(|attr| matches!(attr.style, syn::AttrStyle::Inner(_))),
);
tokens.append_all(&self.items);
});
}
}
impl ItemImpl {
pub(super) fn is_ink_impl_block(
item_impl: &syn::ItemImpl,
) -> Result<bool, syn::Error> {
if !ir::contains_ink_attributes(&item_impl.attrs)
&& item_impl
.items
.iter()
.all(|item| !ir::contains_ink_attributes(item.attrs()))
{
return Ok(false)
}
let (ink_attrs, _) = ir::partition_attributes(item_impl.attrs.clone())?;
let impl_block_span = item_impl.span();
if !ink_attrs.is_empty() {
let normalized =
ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| {
err.into_combine(format_err!(impl_block_span, "at this invocation",))
})?;
if normalized
.ensure_first(&ir::AttributeArgKind::Implementation)
.is_ok()
{
return Ok(true)
}
}
'repeat: for item in &item_impl.items {
match item {
syn::ImplItem::Fn(fn_item) => {
if !ir::contains_ink_attributes(&fn_item.attrs) {
continue 'repeat
}
let attr = ir::first_ink_attribute(&fn_item.attrs)?
.expect("missing expected ink! attribute for struct");
match attr.first().kind() {
ir::AttributeArg::Constructor | ir::AttributeArg::Message => {
return Ok(true)
}
_ => continue 'repeat,
}
}
_ => continue 'repeat,
}
}
Ok(false)
}
pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
extract_cfg_attributes(self.attrs(), span)
}
}
impl TryFrom<syn::ItemImpl> for ItemImpl {
type Error = syn::Error;
fn try_from(item_impl: syn::ItemImpl) -> Result<Self, Self::Error> {
let impl_block_span = item_impl.span();
if !Self::is_ink_impl_block(&item_impl)? {
return Err(format_err_spanned!(
item_impl,
"missing ink! annotations on implementation block or on any of its items"
))
}
if let Some(defaultness) = item_impl.defaultness {
return Err(format_err_spanned!(
defaultness,
"default implementations are unsupported for ink! implementation blocks",
))
}
if let Some(unsafety) = item_impl.unsafety {
return Err(format_err_spanned!(
unsafety,
"unsafe ink! implementation blocks are not supported",
))
}
if !item_impl.generics.params.is_empty() {
return Err(format_err_spanned!(
item_impl.generics.params,
"generic ink! implementation blocks are not supported",
))
}
let impl_items = item_impl
.items
.into_iter()
.map(<ImplItem as TryFrom<_>>::try_from)
.collect::<Result<Vec<_>, syn::Error>>()?;
let is_trait_impl = item_impl.trait_.is_some();
for impl_item in &impl_items {
fn ensure_valid_visibility(
vis: ir::Visibility,
span: Span,
what: &str,
is_trait_impl: bool,
) -> Result<(), syn::Error> {
let requires_pub = !is_trait_impl;
if requires_pub != vis.is_pub() {
return Err(format_err!(
span,
"ink! {} in {} impl blocks must have {} visibility",
what,
if is_trait_impl { "trait" } else { "inherent" },
if requires_pub { "public" } else { "inherited" },
))
}
Ok(())
}
match impl_item {
ir::ImplItem::Message(message) => {
ensure_valid_visibility(
message.visibility(),
message.item.span(),
"message",
is_trait_impl,
)?;
}
ir::ImplItem::Constructor(constructor) => {
ensure_valid_visibility(
constructor.visibility(),
constructor.item.span(),
"constructor",
is_trait_impl,
)?;
}
_ => (),
}
}
let (ink_attrs, other_attrs) = ir::partition_attributes(item_impl.attrs)?;
let mut namespace: Option<ir::Namespace> = None;
if !ink_attrs.is_empty() {
let normalized =
ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| {
err.into_combine(format_err!(impl_block_span, "at this invocation",))
})?;
normalized.ensure_no_conflicts(|arg| {
match arg.kind() {
ir::AttributeArg::Implementation | ir::AttributeArg::Namespace(_) => {
Ok(())
}
_ => Err(None),
}
})?;
namespace = normalized.namespace();
}
if namespace.is_some() && is_trait_impl {
return Err(format_err!(
impl_block_span,
"namespace ink! property is not allowed on ink! trait implementation blocks",
));
}
Ok(Self {
attrs: other_attrs,
defaultness: item_impl.defaultness,
unsafety: item_impl.unsafety,
impl_token: item_impl.impl_token,
generics: item_impl.generics,
trait_: item_impl.trait_,
self_ty: item_impl.self_ty,
brace_token: item_impl.brace_token,
items: impl_items,
namespace,
})
}
}
impl ItemImpl {
pub fn attrs(&self) -> &[syn::Attribute] {
&self.attrs
}
pub fn self_type(&self) -> &syn::Type {
self.self_ty.as_ref()
}
pub fn trait_path(&self) -> Option<&syn::Path> {
self.trait_.as_ref().map(|(_, path, _)| path)
}
pub fn trait_ident(&self) -> Option<&Ident> {
self.trait_path()
.and_then(|trait_path| trait_path.segments.last())
.map(|segment| &segment.ident)
}
pub fn namespace(&self) -> Option<&ir::Namespace> {
self.namespace.as_ref()
}
pub fn iter_messages(&self) -> IterMessages {
IterMessages::new(self)
}
pub fn iter_constructors(&self) -> IterConstructors {
IterConstructors::new(self)
}
pub fn items(&self) -> &[ir::ImplItem] {
&self.items
}
}