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    /// Returns the function name override (if any).
64    pub fn name(&self) -> Option<String> {
65        match self {
66            Self::Message(message) => message.name(),
67        }
68    }
69
70    /// Returns the "normalized" function name
71    ///
72    /// # Note
73    /// This returns the name override (if provided), otherwise the identifier is
74    /// returned.
75    pub fn normalized_name(&self) -> String {
76        match self {
77            Self::Message(message) => message.normalized_name(),
78        }
79    }
80}
81
82/// A checked ink! message of an ink! trait definition.
83#[derive(Debug, Clone)]
84pub struct InkTraitMessage<'a> {
85    item: &'a syn::TraitItemFn,
86}
87
88impl<'a> InkTraitMessage<'a> {
89    /// Panic message in case a user encounters invalid attributes.
90    const INVALID_ATTRIBUTES_ERRSTR: &'static str =
91        "encountered invalid attributes for ink! trait message";
92
93    /// Creates a new ink! trait definition message.
94    pub(super) fn new(item: &'a syn::TraitItemFn) -> Self {
95        Self { item }
96    }
97
98    /// Analyses and extracts the ink! and non-ink! attributes of an ink! trait message.
99    pub(super) fn extract_attributes(
100        span: Span,
101        attrs: &[syn::Attribute],
102    ) -> Result<(InkAttribute, Vec<syn::Attribute>)> {
103        let (ink_attrs, non_ink_attrs) = ir::sanitize_attributes(
104            span,
105            attrs.iter().cloned(),
106            &ir::AttributeArgKind::Message,
107            |arg| {
108                match arg.kind() {
109                    ir::AttributeArg::Selector(SelectorOrWildcard::Wildcard) => {
110                        Err(Some(format_err!(
111                            arg.span(),
112                            "wildcard selectors are only supported for inherent ink! messages or constructors, not for traits."
113                        )))
114                    }
115                    ir::AttributeArg::Message
116                    | ir::AttributeArg::Payable
117                    | ir::AttributeArg::Default
118                    | ir::AttributeArg::Selector(_)
119                    | ir::AttributeArg::Name(_) => Ok(()),
120                    _ => Err(None),
121                }
122            },
123        )?;
124        Ok((ink_attrs, non_ink_attrs))
125    }
126
127    /// Returns all non-ink! attributes.
128    pub fn attrs(&self) -> Vec<syn::Attribute> {
129        let (_, rust_attrs) = Self::extract_attributes(self.span(), &self.item.attrs)
130            .expect(Self::INVALID_ATTRIBUTES_ERRSTR);
131        rust_attrs
132    }
133
134    /// Returns a list of `cfg` attributes if any.
135    pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
136        extract_cfg_attributes(&self.attrs(), span)
137    }
138
139    /// Returns all ink! attributes.
140    pub fn ink_attrs(&self) -> InkAttribute {
141        let (ink_attrs, _) = Self::extract_attributes(self.span(), &self.item.attrs)
142            .expect(Self::INVALID_ATTRIBUTES_ERRSTR);
143        ink_attrs
144    }
145
146    /// Returns the original signature of the ink! message.
147    pub fn sig(&self) -> &syn::Signature {
148        &self.item.sig
149    }
150
151    /// Returns the `self` receiver of the ink! trait message.
152    ///
153    /// Returns `Ref` for `&self` messages and `RefMut` for `&mut self` messages.
154    pub fn receiver(&self) -> Receiver {
155        match self.item.sig.inputs.iter().next() {
156            Some(syn::FnArg::Receiver(receiver)) => {
157                debug_assert!(receiver.reference.is_some());
158                if receiver.mutability.is_some() {
159                    Receiver::RefMut
160                } else {
161                    Receiver::Ref
162                }
163            }
164            _ => unreachable!("encountered invalid receiver argument for ink! message"),
165        }
166    }
167
168    /// Returns an iterator over the inputs of the ink! trait message.
169    pub fn inputs(&self) -> InputsIter<'_> {
170        InputsIter::from(self)
171    }
172
173    /// Returns the return type of the ink! message if any.
174    pub fn output(&self) -> Option<&syn::Type> {
175        match &self.item.sig.output {
176            syn::ReturnType::Default => None,
177            syn::ReturnType::Type(_, return_type) => Some(return_type),
178        }
179    }
180
181    /// Returns the Rust identifier of the ink! message.
182    pub fn ident(&self) -> &syn::Ident {
183        &self.item.sig.ident
184    }
185
186    /// Returns a local ID unique to the ink! trait definition of the ink! trait message.
187    ///
188    /// # Note
189    ///
190    /// It is a compile error if two ink! trait messages share the same local ID.
191    /// Although the above scenario is very unlikely since the local ID is computed
192    /// solely by the identifier of the ink! message.
193    pub fn local_id(&self) -> u32 {
194        utils::local_message_id(&self.normalized_name())
195    }
196
197    /// Returns the span of the ink! message.
198    pub fn span(&self) -> Span {
199        self.item.span()
200    }
201
202    /// Returns `true` if the ink! message may mutate the contract storage.
203    pub fn mutates(&self) -> bool {
204        self.sig()
205            .receiver()
206            .map(|receiver| receiver.mutability.is_some())
207            .expect("encountered missing receiver for ink! message")
208    }
209
210    /// Returns the function name override (if any).
211    pub fn name(&self) -> Option<String> {
212        self.ink_attrs().name()
213    }
214
215    /// Returns the "normalized" function name
216    ///
217    /// # Note
218    /// This returns the name override (if provided), otherwise the identifier is
219    /// returned.
220    pub fn normalized_name(&self) -> String {
221        self.name().unwrap_or_else(|| self.ident().to_string())
222    }
223}
224
225impl<'a> From<&'a InkTraitMessage<'a>> for InputsIter<'a> {
226    fn from(message: &'a InkTraitMessage) -> Self {
227        Self::new(&message.item.sig.inputs)
228    }
229}