ink_ir/ir/trait_def/item/
trait_item.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::super::InkAttribute;
16use crate::{
17    InputsIter,
18    Receiver,
19    ir::{
20        self,
21        attrs::SelectorOrWildcard,
22        utils,
23        utils::extract_cfg_attributes,
24    },
25};
26use proc_macro2::{
27    Span,
28    TokenStream,
29};
30use syn::{
31    Result,
32    spanned::Spanned as _,
33};
34
35/// An ink! item within an ink! trait definition.
36#[derive(Debug, Clone)]
37pub enum InkTraitItem<'a> {
38    Message(InkTraitMessage<'a>),
39}
40
41impl<'a> InkTraitItem<'a> {
42    /// Returns the Rust identifier of the ink! trait item.
43    pub fn ident(&self) -> &syn::Ident {
44        match self {
45            Self::Message(message) => message.ident(),
46        }
47    }
48
49    /// Returns the ink! attributes of the ink! trait item.
50    pub fn ink_attrs(&self) -> InkAttribute {
51        match self {
52            Self::Message(message) => message.ink_attrs(),
53        }
54    }
55
56    /// Returns `Some` if the ink! trait item is a message.
57    pub fn filter_map_message(self) -> Option<InkTraitMessage<'a>> {
58        match self {
59            Self::Message(ink_trait_message) => Some(ink_trait_message),
60        }
61    }
62}
63
64/// A checked ink! message of an ink! trait definition.
65#[derive(Debug, Clone)]
66pub struct InkTraitMessage<'a> {
67    item: &'a syn::TraitItemFn,
68}
69
70impl<'a> InkTraitMessage<'a> {
71    /// Panic message in case a user encounters invalid attributes.
72    const INVALID_ATTRIBUTES_ERRSTR: &'static str =
73        "encountered invalid attributes for ink! trait message";
74
75    /// Creates a new ink! trait definition message.
76    pub(super) fn new(item: &'a syn::TraitItemFn) -> Self {
77        Self { item }
78    }
79
80    /// Analyses and extracts the ink! and non-ink! attributes of an ink! trait message.
81    pub(super) fn extract_attributes(
82        span: Span,
83        attrs: &[syn::Attribute],
84    ) -> Result<(InkAttribute, Vec<syn::Attribute>)> {
85        let (ink_attrs, non_ink_attrs) = ir::sanitize_attributes(
86            span,
87            attrs.iter().cloned(),
88            &ir::AttributeArgKind::Message,
89            |arg| {
90                match arg.kind() {
91                    ir::AttributeArg::Selector(SelectorOrWildcard::Wildcard) => {
92                        Err(Some(format_err!(
93                            arg.span(),
94                            "wildcard selectors are only supported for inherent ink! messages or constructors, not for traits."
95                        )))
96                    }
97                    ir::AttributeArg::Message
98                    | ir::AttributeArg::Payable
99                    | ir::AttributeArg::Default
100                    | ir::AttributeArg::Selector(_) => Ok(()),
101                    _ => Err(None),
102                }
103            },
104        )?;
105        Ok((ink_attrs, non_ink_attrs))
106    }
107
108    /// Returns all non-ink! attributes.
109    pub fn attrs(&self) -> Vec<syn::Attribute> {
110        let (_, rust_attrs) = Self::extract_attributes(self.span(), &self.item.attrs)
111            .expect(Self::INVALID_ATTRIBUTES_ERRSTR);
112        rust_attrs
113    }
114
115    /// Returns a list of `cfg` attributes if any.
116    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
117        extract_cfg_attributes(&self.attrs(), span)
118    }
119
120    /// Returns all ink! attributes.
121    pub fn ink_attrs(&self) -> InkAttribute {
122        let (ink_attrs, _) = Self::extract_attributes(self.span(), &self.item.attrs)
123            .expect(Self::INVALID_ATTRIBUTES_ERRSTR);
124        ink_attrs
125    }
126
127    /// Returns the original signature of the ink! message.
128    pub fn sig(&self) -> &syn::Signature {
129        &self.item.sig
130    }
131
132    /// Returns the `self` receiver of the ink! trait message.
133    ///
134    /// Returns `Ref` for `&self` messages and `RefMut` for `&mut self` messages.
135    pub fn receiver(&self) -> Receiver {
136        match self.item.sig.inputs.iter().next() {
137            Some(syn::FnArg::Receiver(receiver)) => {
138                debug_assert!(receiver.reference.is_some());
139                if receiver.mutability.is_some() {
140                    Receiver::RefMut
141                } else {
142                    Receiver::Ref
143                }
144            }
145            _ => unreachable!("encountered invalid receiver argument for ink! message"),
146        }
147    }
148
149    /// Returns an iterator over the inputs of the ink! trait message.
150    pub fn inputs(&self) -> InputsIter<'_> {
151        InputsIter::from(self)
152    }
153
154    /// Returns the return type of the ink! message if any.
155    pub fn output(&self) -> Option<&syn::Type> {
156        match &self.item.sig.output {
157            syn::ReturnType::Default => None,
158            syn::ReturnType::Type(_, return_type) => Some(return_type),
159        }
160    }
161
162    /// Returns the Rust identifier of the ink! message.
163    pub fn ident(&self) -> &syn::Ident {
164        &self.item.sig.ident
165    }
166
167    /// Returns a local ID unique to the ink! trait definition of the ink! trait message.
168    ///
169    /// # Note
170    ///
171    /// It is a compile error if two ink! trait messages share the same local ID.
172    /// Although the above scenario is very unlikely since the local ID is computed
173    /// solely by the identifier of the ink! message.
174    pub fn local_id(&self) -> u32 {
175        utils::local_message_id(self.ident())
176    }
177
178    /// Returns the span of the ink! message.
179    pub fn span(&self) -> Span {
180        self.item.span()
181    }
182
183    /// Returns `true` if the ink! message may mutate the contract storage.
184    pub fn mutates(&self) -> bool {
185        self.sig()
186            .receiver()
187            .map(|receiver| receiver.mutability.is_some())
188            .expect("encountered missing receiver for ink! message")
189    }
190}
191
192impl<'a> From<&'a InkTraitMessage<'a>> for InputsIter<'a> {
193    fn from(message: &'a InkTraitMessage) -> Self {
194        Self::new(&message.item.sig.inputs)
195    }
196}