mod iter;
mod trait_item;
use self::iter::IterInkTraitItemsRaw;
pub use self::{
iter::IterInkTraitItems,
trait_item::{
InkTraitItem,
InkTraitMessage,
},
};
use super::TraitDefinitionConfig;
use crate::{
ir,
ir::{
attrs::SelectorOrWildcard,
idents_lint,
},
Selector,
};
use ir::TraitPrefix;
use proc_macro2::{
Ident,
Span,
};
use std::collections::HashMap;
use syn::{
spanned::Spanned as _,
Result,
};
#[derive(Debug, PartialEq, Eq)]
pub struct InkItemTrait {
item: syn::ItemTrait,
message_selectors: HashMap<syn::Ident, Selector>,
}
#[cfg(test)]
impl TryFrom<syn::ItemTrait> for InkItemTrait {
type Error = syn::Error;
fn try_from(item_trait: syn::ItemTrait) -> core::result::Result<Self, Self::Error> {
let config = TraitDefinitionConfig::default();
Self::new(&config, item_trait)
}
}
impl InkItemTrait {
pub fn new(
config: &TraitDefinitionConfig,
item_trait: syn::ItemTrait,
) -> Result<Self> {
idents_lint::ensure_no_ink_identifiers(&item_trait)?;
Self::analyse_properties(&item_trait)?;
Self::analyse_items(&item_trait)?;
let mut message_selectors = <HashMap<syn::Ident, Selector>>::new();
Self::extract_selectors(config, &item_trait, &mut message_selectors)?;
if message_selectors.is_empty() {
return Err(format_err!(
item_trait.span(),
"encountered invalid empty ink! trait definition"
))
}
Ok(Self {
item: item_trait,
message_selectors,
})
}
}
impl InkItemTrait {
pub fn span(&self) -> Span {
self.item.span()
}
pub fn attrs(&self) -> &[syn::Attribute] {
&self.item.attrs
}
pub fn ident(&self) -> &Ident {
&self.item.ident
}
pub fn iter_items(&self) -> IterInkTraitItems {
IterInkTraitItems::new(self)
}
fn analyse_properties(item_trait: &syn::ItemTrait) -> Result<()> {
if let Some(unsafety) = &item_trait.unsafety {
return Err(format_err_spanned!(
unsafety,
"ink! trait definitions cannot be unsafe"
))
}
if let Some(auto) = &item_trait.auto_token {
return Err(format_err_spanned!(
auto,
"ink! trait definitions cannot be automatically implemented traits"
))
}
if !item_trait.generics.params.is_empty() {
return Err(format_err_spanned!(
item_trait.generics.params,
"ink! trait definitions must not be generic"
))
}
if !matches!(item_trait.vis, syn::Visibility::Public(_)) {
return Err(format_err_spanned!(
item_trait.vis,
"ink! trait definitions must have public visibility"
))
}
if !item_trait.supertraits.is_empty() {
return Err(format_err_spanned!(
item_trait.supertraits,
"ink! trait definitions with supertraits are not supported, yet"
))
}
Ok(())
}
fn analyse_items(item_trait: &syn::ItemTrait) -> Result<()> {
for trait_item in &item_trait.items {
match trait_item {
syn::TraitItem::Const(const_trait_item) => {
return Err(format_err_spanned!(
const_trait_item,
"associated constants in ink! trait definitions are not supported, yet"
))
}
syn::TraitItem::Macro(macro_trait_item) => {
return Err(format_err_spanned!(
macro_trait_item,
"macros in ink! trait definitions are not supported"
))
}
syn::TraitItem::Type(type_trait_item) => {
return Err(format_err_spanned!(
type_trait_item,
"associated types in ink! trait definitions are not supported, yet"
))
}
syn::TraitItem::Verbatim(verbatim) => {
return Err(format_err_spanned!(
verbatim,
"encountered unsupported item in ink! trait definition"
))
}
syn::TraitItem::Fn(fn_trait_item) => {
Self::analyse_trait_fn(fn_trait_item)?;
}
unknown => {
return Err(format_err_spanned!(
unknown,
"encountered unknown or unsupported item in ink! trait definition"
))
}
}
}
Ok(())
}
fn analyse_trait_fn(method: &syn::TraitItemFn) -> Result<()> {
if let Some(default_impl) = &method.default {
return Err(format_err_spanned!(
default_impl,
"ink! trait methods with default implementations are not supported"
))
}
if let Some(constness) = &method.sig.constness {
return Err(format_err_spanned!(
constness,
"const ink! trait methods are not supported"
))
}
if let Some(asyncness) = &method.sig.asyncness {
return Err(format_err_spanned!(
asyncness,
"async ink! trait methods are not supported"
))
}
if let Some(unsafety) = &method.sig.unsafety {
return Err(format_err_spanned!(
unsafety,
"unsafe ink! trait methods are not supported"
))
}
if let Some(abi) = &method.sig.abi {
return Err(format_err_spanned!(
abi,
"ink! trait methods with non default ABI are not supported"
))
}
if let Some(variadic) = &method.sig.variadic {
return Err(format_err_spanned!(
variadic,
"variadic ink! trait methods are not supported"
))
}
if !method.sig.generics.params.is_empty() {
return Err(format_err_spanned!(
method.sig.generics.params,
"generic ink! trait methods are not supported"
))
}
match ir::first_ink_attribute(&method.attrs) {
Ok(Some(ink_attr)) => {
match ink_attr.first().kind() {
ir::AttributeArg::Message => {
Self::analyse_trait_message(method)?;
}
ir::AttributeArg::Constructor => {
Self::analyse_trait_constructor(method)?;
}
_unsupported => {
return Err(format_err_spanned!(
method,
"encountered unsupported ink! attribute for ink! trait method",
))
}
}
}
Ok(None) => {
return Err(format_err_spanned!(
method,
"missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method"
))
}
Err(err) => return Err(err),
}
Ok(())
}
fn analyse_trait_constructor(constructor: &syn::TraitItemFn) -> Result<()> {
Err(format_err!(
constructor.span(),
"ink! trait definitions must not have constructors",
))
}
fn analyse_trait_message(message: &syn::TraitItemFn) -> Result<()> {
InkTraitMessage::extract_attributes(message.span(), &message.attrs)?;
match message.sig.receiver() {
None => {
return Err(format_err_spanned!(
message.sig,
"missing `&self` or `&mut self` receiver for ink! message",
))
}
Some(receiver) => {
if receiver.reference.is_none() {
return Err(format_err_spanned!(
receiver,
"self receiver of ink! message must be `&self` or `&mut self`"
))
}
}
}
Ok(())
}
fn extract_selectors(
config: &TraitDefinitionConfig,
item_trait: &syn::ItemTrait,
message_selectors: &mut HashMap<syn::Ident, Selector>,
) -> Result<()> {
let mut seen_message_selectors = <HashMap<Selector, syn::Ident>>::new();
let (_ink_attrs, _) = ir::sanitize_optional_attributes(
item_trait.span(),
item_trait.attrs.iter().cloned(),
|arg| {
match arg.kind() {
ir::AttributeArg::Namespace(_) => Ok(()),
_ => Err(None),
}
},
)
.unwrap_or_else(|err| {
panic!("encountered unexpected invalid attributes on ink! trait definition: {err}")
});
let namespace = config.namespace();
let ident = &item_trait.ident;
let trait_prefix = TraitPrefix::new(ident, namespace);
for callable in IterInkTraitItemsRaw::from_raw(item_trait) {
let ident = callable.ident();
let ink_attrs = callable.ink_attrs();
let selector = match ink_attrs.selector() {
Some(SelectorOrWildcard::UserProvided(manual_selector)) => {
manual_selector
}
_ => Selector::compose(trait_prefix, ident),
};
let (duplicate_selector, duplicate_ident) = match callable {
InkTraitItem::Message(_) => {
let duplicate_selector =
seen_message_selectors.insert(selector, ident.clone());
let duplicate_ident =
message_selectors.insert(ident.clone(), selector);
(duplicate_selector, duplicate_ident)
}
};
if let Some(duplicate_selector) = duplicate_selector {
use crate::error::ExtError as _;
return Err(format_err_spanned!(
ident,
"encountered duplicate selector ({:x?}) in the same ink! trait definition",
selector.to_bytes(),
).into_combine(format_err_spanned!(
duplicate_selector,
"first ink! trait constructor or message with same selector found here",
)));
}
assert!(
duplicate_ident.is_none(),
"encountered unexpected overlapping ink! trait constructor or message identifier",
);
}
Ok(())
}
}