1use crate::{
16 error::ExtError as _,
17 ir,
18 ir::attrs::Attrs as _,
19};
20use proc_macro2::{
21 Ident,
22 Span,
23 TokenStream,
24};
25
26mod callable;
27mod constructor;
28mod impl_item;
29mod iter;
30mod message;
31
32#[cfg(test)]
33mod tests;
34
35use self::callable::ensure_callable_invariants;
36pub use self::{
37 callable::{
38 Callable,
39 CallableKind,
40 CallableWithSelector,
41 InputsIter,
42 Visibility,
43 },
44 constructor::Constructor,
45 impl_item::ImplItem,
46 iter::{
47 IterConstructors,
48 IterMessages,
49 },
50 message::{
51 Message,
52 Receiver,
53 },
54};
55use quote::TokenStreamExt as _;
56use syn::spanned::Spanned;
57
58use super::utils::extract_cfg_attributes;
59
60#[derive(Debug, PartialEq, Eq)]
71pub struct ItemImpl {
72 attrs: Vec<syn::Attribute>,
73 defaultness: Option<syn::token::Default>,
74 unsafety: Option<syn::token::Unsafe>,
75 impl_token: syn::token::Impl,
76 generics: syn::Generics,
77 trait_: Option<(Option<syn::Token![!]>, syn::Path, syn::token::For)>,
78 self_ty: Box<syn::Type>,
79 brace_token: syn::token::Brace,
80 items: Vec<ImplItem>,
81 namespace: Option<ir::Namespace>,
85}
86
87impl quote::ToTokens for ItemImpl {
88 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
91 tokens.append_all(
92 self.attrs
93 .iter()
94 .filter(|attr| matches!(attr.style, syn::AttrStyle::Outer)),
95 );
96 self.defaultness.to_tokens(tokens);
97 self.unsafety.to_tokens(tokens);
98 self.impl_token.to_tokens(tokens);
99 self.generics.to_tokens(tokens);
100 if let Some((polarity, path, for_token)) = &self.trait_ {
101 polarity.to_tokens(tokens);
102 path.to_tokens(tokens);
103 for_token.to_tokens(tokens);
104 }
105 self.self_ty.to_tokens(tokens);
106 self.generics.where_clause.to_tokens(tokens);
107 self.brace_token.surround(tokens, |tokens| {
108 tokens.append_all(
109 self.attrs
110 .iter()
111 .filter(|attr| matches!(attr.style, syn::AttrStyle::Inner(_))),
112 );
113 tokens.append_all(&self.items);
114 });
115 }
116}
117
118impl ItemImpl {
119 pub(super) fn is_ink_impl_block(
161 item_impl: &syn::ItemImpl,
162 ) -> Result<bool, syn::Error> {
163 if !ir::contains_ink_attributes(&item_impl.attrs)
166 && item_impl
167 .items
168 .iter()
169 .all(|item| !ir::contains_ink_attributes(item.attrs()))
170 {
171 return Ok(false)
172 }
173 let (ink_attrs, _) = ir::partition_attributes(item_impl.attrs.clone())?;
176 let impl_block_span = item_impl.span();
177 if !ink_attrs.is_empty() {
178 let normalized =
179 ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| {
180 err.into_combine(format_err!(impl_block_span, "at this invocation",))
181 })?;
182 if normalized
183 .ensure_first(&ir::AttributeArgKind::Implementation)
184 .is_ok()
185 {
186 return Ok(true)
187 }
188 }
189 'repeat: for item in &item_impl.items {
192 match item {
193 syn::ImplItem::Fn(fn_item) => {
194 if !ir::contains_ink_attributes(&fn_item.attrs) {
195 continue 'repeat
196 }
197 let attr = ir::first_ink_attribute(&fn_item.attrs)?
198 .expect("missing expected ink! attribute for struct");
199 match attr.first().kind() {
200 ir::AttributeArg::Constructor | ir::AttributeArg::Message => {
201 return Ok(true)
202 }
203 _ => continue 'repeat,
204 }
205 }
206 _ => continue 'repeat,
207 }
208 }
209 Ok(false)
210 }
211
212 pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream> {
214 extract_cfg_attributes(self.attrs(), span)
215 }
216}
217
218impl TryFrom<syn::ItemImpl> for ItemImpl {
219 type Error = syn::Error;
220
221 fn try_from(item_impl: syn::ItemImpl) -> Result<Self, Self::Error> {
222 let impl_block_span = item_impl.span();
223 if !Self::is_ink_impl_block(&item_impl)? {
224 return Err(format_err_spanned!(
225 item_impl,
226 "missing ink! annotations on implementation block or on any of its items"
227 ))
228 }
229 if let Some(defaultness) = item_impl.defaultness {
230 return Err(format_err_spanned!(
231 defaultness,
232 "default implementations are unsupported for ink! implementation blocks",
233 ))
234 }
235 if let Some(unsafety) = item_impl.unsafety {
236 return Err(format_err_spanned!(
237 unsafety,
238 "unsafe ink! implementation blocks are not supported",
239 ))
240 }
241 if !item_impl.generics.params.is_empty() {
242 return Err(format_err_spanned!(
243 item_impl.generics.params,
244 "generic ink! implementation blocks are not supported",
245 ))
246 }
247 let impl_items = item_impl
248 .items
249 .into_iter()
250 .map(<ImplItem as TryFrom<_>>::try_from)
251 .collect::<Result<Vec<_>, syn::Error>>()?;
252 let is_trait_impl = item_impl.trait_.is_some();
253 for impl_item in &impl_items {
254 fn ensure_valid_visibility(
262 vis: ir::Visibility,
263 span: Span,
264 what: &str,
265 is_trait_impl: bool,
266 ) -> Result<(), syn::Error> {
267 let requires_pub = !is_trait_impl;
268 if requires_pub != vis.is_pub() {
269 return Err(format_err!(
270 span,
271 "ink! {} in {} impl blocks must have {} visibility",
272 what,
273 if is_trait_impl { "trait" } else { "inherent" },
274 if requires_pub { "public" } else { "inherited" },
275 ))
276 }
277 Ok(())
278 }
279 match impl_item {
280 ir::ImplItem::Message(message) => {
281 ensure_valid_visibility(
282 message.visibility(),
283 message.item.span(),
284 "message",
285 is_trait_impl,
286 )?;
287 }
288 ir::ImplItem::Constructor(constructor) => {
289 ensure_valid_visibility(
290 constructor.visibility(),
291 constructor.item.span(),
292 "constructor",
293 is_trait_impl,
294 )?;
295 }
296 _ => (),
297 }
298 }
299 let (ink_attrs, other_attrs) = ir::partition_attributes(item_impl.attrs)?;
300 let mut namespace: Option<ir::Namespace> = None;
301 if !ink_attrs.is_empty() {
302 let normalized =
303 ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| {
304 err.into_combine(format_err!(impl_block_span, "at this invocation",))
305 })?;
306 normalized.ensure_no_conflicts(|arg| {
307 match arg.kind() {
308 ir::AttributeArg::Implementation | ir::AttributeArg::Namespace(_) => {
309 Ok(())
310 }
311 _ => Err(None),
312 }
313 })?;
314 namespace = normalized.namespace();
315 }
316 if namespace.is_some() && is_trait_impl {
317 return Err(format_err!(
318 impl_block_span,
319 "namespace ink! property is not allowed on ink! trait implementation blocks",
320 ));
321 }
322 Ok(Self {
323 attrs: other_attrs,
324 defaultness: item_impl.defaultness,
325 unsafety: item_impl.unsafety,
326 impl_token: item_impl.impl_token,
327 generics: item_impl.generics,
328 trait_: item_impl.trait_,
329 self_ty: item_impl.self_ty,
330 brace_token: item_impl.brace_token,
331 items: impl_items,
332 namespace,
333 })
334 }
335}
336
337impl ItemImpl {
338 pub fn attrs(&self) -> &[syn::Attribute] {
340 &self.attrs
341 }
342
343 pub fn self_type(&self) -> &syn::Type {
345 self.self_ty.as_ref()
346 }
347
348 pub fn trait_path(&self) -> Option<&syn::Path> {
352 self.trait_.as_ref().map(|(_, path, _)| path)
353 }
354
355 pub fn trait_ident(&self) -> Option<&Ident> {
359 self.trait_path()
360 .and_then(|trait_path| trait_path.segments.last())
361 .map(|segment| &segment.ident)
362 }
363
364 pub fn namespace(&self) -> Option<&ir::Namespace> {
366 self.namespace.as_ref()
367 }
368
369 pub fn iter_messages(&self) -> IterMessages {
371 IterMessages::new(self)
372 }
373
374 pub fn iter_constructors(&self) -> IterConstructors {
376 IterConstructors::new(self)
377 }
378
379 pub fn items(&self) -> &[ir::ImplItem] {
381 &self.items
382 }
383}