ink_ir/ir/trait_def/item/
mod.rs1mod iter;
16mod trait_item;
17
18use self::iter::IterInkTraitItemsRaw;
19pub use self::{
20 iter::IterInkTraitItems,
21 trait_item::{
22 InkTraitItem,
23 InkTraitMessage,
24 },
25};
26use super::TraitDefinitionConfig;
27use crate::{
28 ir,
29 ir::{
30 attrs::SelectorOrWildcard,
31 idents_lint,
32 },
33 Selector,
34};
35use ir::TraitPrefix;
36use proc_macro2::{
37 Ident,
38 Span,
39};
40use std::collections::HashMap;
41use syn::{
42 spanned::Spanned as _,
43 Result,
44};
45
46#[derive(Debug, PartialEq, Eq)]
48pub struct InkItemTrait {
49 item: syn::ItemTrait,
50 message_selectors: HashMap<syn::Ident, Selector>,
51}
52
53#[cfg(test)]
54impl TryFrom<syn::ItemTrait> for InkItemTrait {
55 type Error = syn::Error;
56
57 fn try_from(item_trait: syn::ItemTrait) -> core::result::Result<Self, Self::Error> {
58 let config = TraitDefinitionConfig::default();
59 Self::new(&config, item_trait)
60 }
61}
62
63impl InkItemTrait {
64 pub fn new(
66 config: &TraitDefinitionConfig,
67 item_trait: syn::ItemTrait,
68 ) -> Result<Self> {
69 idents_lint::ensure_no_ink_identifiers(&item_trait)?;
70 Self::analyse_properties(&item_trait)?;
71 Self::analyse_items(&item_trait)?;
72 let mut message_selectors = <HashMap<syn::Ident, Selector>>::new();
73 Self::extract_selectors(config, &item_trait, &mut message_selectors)?;
74 if message_selectors.is_empty() {
75 return Err(format_err!(
76 item_trait.span(),
77 "encountered invalid empty ink! trait definition"
78 ))
79 }
80 Ok(Self {
81 item: item_trait,
82 message_selectors,
83 })
84 }
85}
86
87impl InkItemTrait {
88 pub fn span(&self) -> Span {
90 self.item.span()
91 }
92
93 pub fn attrs(&self) -> &[syn::Attribute] {
95 &self.item.attrs
96 }
97
98 pub fn ident(&self) -> &Ident {
100 &self.item.ident
101 }
102
103 pub fn iter_items(&self) -> IterInkTraitItems {
105 IterInkTraitItems::new(self)
106 }
107
108 fn analyse_properties(item_trait: &syn::ItemTrait) -> Result<()> {
117 if let Some(unsafety) = &item_trait.unsafety {
118 return Err(format_err_spanned!(
119 unsafety,
120 "ink! trait definitions cannot be unsafe"
121 ))
122 }
123 if let Some(auto) = &item_trait.auto_token {
124 return Err(format_err_spanned!(
125 auto,
126 "ink! trait definitions cannot be automatically implemented traits"
127 ))
128 }
129 if !item_trait.generics.params.is_empty() {
130 return Err(format_err_spanned!(
131 item_trait.generics.params,
132 "ink! trait definitions must not be generic"
133 ))
134 }
135 if !matches!(item_trait.vis, syn::Visibility::Public(_)) {
136 return Err(format_err_spanned!(
137 item_trait.vis,
138 "ink! trait definitions must have public visibility"
139 ))
140 }
141 if !item_trait.supertraits.is_empty() {
142 return Err(format_err_spanned!(
143 item_trait.supertraits,
144 "ink! trait definitions with supertraits are not supported, yet"
145 ))
146 }
147 Ok(())
148 }
149
150 fn analyse_items(item_trait: &syn::ItemTrait) -> Result<()> {
170 for trait_item in &item_trait.items {
171 match trait_item {
172 syn::TraitItem::Const(const_trait_item) => {
173 return Err(format_err_spanned!(
174 const_trait_item,
175 "associated constants in ink! trait definitions are not supported, yet"
176 ))
177 }
178 syn::TraitItem::Macro(macro_trait_item) => {
179 return Err(format_err_spanned!(
180 macro_trait_item,
181 "macros in ink! trait definitions are not supported"
182 ))
183 }
184 syn::TraitItem::Type(type_trait_item) => {
185 return Err(format_err_spanned!(
186 type_trait_item,
187 "associated types in ink! trait definitions are not supported, yet"
188 ))
189 }
190 syn::TraitItem::Verbatim(verbatim) => {
191 return Err(format_err_spanned!(
192 verbatim,
193 "encountered unsupported item in ink! trait definition"
194 ))
195 }
196 syn::TraitItem::Fn(fn_trait_item) => {
197 Self::analyse_trait_fn(fn_trait_item)?;
198 }
199 unknown => {
200 return Err(format_err_spanned!(
201 unknown,
202 "encountered unknown or unsupported item in ink! trait definition"
203 ))
204 }
205 }
206 }
207 Ok(())
208 }
209
210 fn analyse_trait_fn(method: &syn::TraitItemFn) -> Result<()> {
220 if let Some(default_impl) = &method.default {
221 return Err(format_err_spanned!(
222 default_impl,
223 "ink! trait methods with default implementations are not supported"
224 ))
225 }
226 if let Some(constness) = &method.sig.constness {
227 return Err(format_err_spanned!(
228 constness,
229 "const ink! trait methods are not supported"
230 ))
231 }
232 if let Some(asyncness) = &method.sig.asyncness {
233 return Err(format_err_spanned!(
234 asyncness,
235 "async ink! trait methods are not supported"
236 ))
237 }
238 if let Some(unsafety) = &method.sig.unsafety {
239 return Err(format_err_spanned!(
240 unsafety,
241 "unsafe ink! trait methods are not supported"
242 ))
243 }
244 if let Some(abi) = &method.sig.abi {
245 return Err(format_err_spanned!(
246 abi,
247 "ink! trait methods with non default ABI are not supported"
248 ))
249 }
250 if let Some(variadic) = &method.sig.variadic {
251 return Err(format_err_spanned!(
252 variadic,
253 "variadic ink! trait methods are not supported"
254 ))
255 }
256 if !method.sig.generics.params.is_empty() {
257 return Err(format_err_spanned!(
258 method.sig.generics.params,
259 "generic ink! trait methods are not supported"
260 ))
261 }
262 match ir::first_ink_attribute(&method.attrs) {
263 Ok(Some(ink_attr)) => {
264 match ink_attr.first().kind() {
265 ir::AttributeArg::Message => {
266 Self::analyse_trait_message(method)?;
267 }
268 ir::AttributeArg::Constructor => {
269 Self::analyse_trait_constructor(method)?;
270 }
271 _unsupported => {
272 return Err(format_err_spanned!(
273 method,
274 "encountered unsupported ink! attribute for ink! trait method",
275 ))
276 }
277 }
278 }
279 Ok(None) => {
280 return Err(format_err_spanned!(
281 method,
282 "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method"
283 ))
284 }
285 Err(err) => return Err(err),
286 }
287 Ok(())
288 }
289
290 fn analyse_trait_constructor(constructor: &syn::TraitItemFn) -> Result<()> {
292 Err(format_err!(
293 constructor.span(),
294 "ink! trait definitions must not have constructors",
295 ))
296 }
297
298 fn analyse_trait_message(message: &syn::TraitItemFn) -> Result<()> {
304 InkTraitMessage::extract_attributes(message.span(), &message.attrs)?;
305 match message.sig.receiver() {
306 None => {
307 return Err(format_err_spanned!(
308 message.sig,
309 "missing `&self` or `&mut self` receiver for ink! message",
310 ))
311 }
312 Some(receiver) => {
313 if receiver.reference.is_none() {
314 return Err(format_err_spanned!(
315 receiver,
316 "self receiver of ink! message must be `&self` or `&mut self`"
317 ))
318 }
319 }
320 }
321 Ok(())
322 }
323
324 fn extract_selectors(
339 config: &TraitDefinitionConfig,
340 item_trait: &syn::ItemTrait,
341 message_selectors: &mut HashMap<syn::Ident, Selector>,
342 ) -> Result<()> {
343 let mut seen_message_selectors = <HashMap<Selector, syn::Ident>>::new();
344 let (_ink_attrs, _) = ir::sanitize_optional_attributes(
345 item_trait.span(),
346 item_trait.attrs.iter().cloned(),
347 |arg| {
348 match arg.kind() {
349 ir::AttributeArg::Namespace(_) => Ok(()),
350 _ => Err(None),
351 }
352 },
353 )
354 .unwrap_or_else(|err| {
355 panic!("encountered unexpected invalid attributes on ink! trait definition: {err}")
356 });
357 let namespace = config.namespace();
358 let ident = &item_trait.ident;
359 let trait_prefix = TraitPrefix::new(ident, namespace);
360 for callable in IterInkTraitItemsRaw::from_raw(item_trait) {
361 let ident = callable.ident();
362 let ink_attrs = callable.ink_attrs();
363 let selector = match ink_attrs.selector() {
364 Some(SelectorOrWildcard::UserProvided(manual_selector)) => {
365 manual_selector
366 }
367 _ => Selector::compose(trait_prefix, ident),
368 };
369 let (duplicate_selector, duplicate_ident) = match callable {
370 InkTraitItem::Message(_) => {
371 let duplicate_selector =
372 seen_message_selectors.insert(selector, ident.clone());
373 let duplicate_ident =
374 message_selectors.insert(ident.clone(), selector);
375 (duplicate_selector, duplicate_ident)
376 }
377 };
378 if let Some(duplicate_selector) = duplicate_selector {
379 use crate::error::ExtError as _;
380 return Err(format_err_spanned!(
381 ident,
382 "encountered duplicate selector ({:x?}) in the same ink! trait definition",
383 selector.to_bytes(),
384 ).into_combine(format_err_spanned!(
385 duplicate_selector,
386 "first ink! trait constructor or message with same selector found here",
387 )));
388 }
389 assert!(
390 duplicate_ident.is_none(),
391 "encountered unexpected overlapping ink! trait constructor or message identifier",
392 );
393 }
394 Ok(())
395 }
396}