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