ink_analyzer_macro/
lib.rs

1//! Procedural macros for [ink! analyzer](https://docs.rs/ink-analyzer/latest/ink_analyzer/).
2//!
3//! # Example
4//! Using `ink_analyzer_macro::entity` proc-macro to create a `Contract` type.
5//!
6//! ```
7//! use ink_analyzer_ir::{Event, Message, Storage};
8//! use ink_analyzer_ir::ast;
9//!
10//! #[ink_analyzer_macro::entity(macro_kind = Contract)]
11//! #[derive(Debug, Clone, PartialEq, Eq)]
12//! struct Contract {
13//!     ast: ast::Module,
14//!     storage: Option<Storage>,
15//!     events: Vec<Event>,
16//!     #[initializer(call = ink_analyzer_ir::ink_callable_closest_descendants)]
17//!     messages: Vec<Message>,
18//!     // --snip--
19//! }
20//! ```
21
22mod entity;
23mod error;
24mod utils;
25
26use proc_macro::TokenStream;
27
28/// proc-macro that implements the `InkEntity` trait for any `struct` with an `ast` field,
29/// where the type for `ast` is `T: ASTNode`.
30///
31/// **NOTE:** Any additional macros should be applied after this macro
32/// because it modifies the fields of the struct to which it is applied.
33///
34/// # Arguments
35/// The `entity` macro takes one argument that represents the casting precondition as follows:
36///
37/// - `ast` - accepts only syntax nodes whose kind matches the type of the `ast` field.
38///   This is the default and be skipped.
39/// - `macro_kind = T` - accepts only items annotated with an ink! attribute macro of the kind `T`
40///   (e.g. `Contract` for `#[ink::contract]`).
41/// - `arg_kind = T` - accepts only items annotated with an ink! attribute argument of the kind `T`
42///   (e.g. `Storage` for `#[ink(storage)]`).
43/// - `call = F` - accepts only syntax nodes for which the function `F` returns true
44///   where `F: Fn(&SyntaxNode) -> bool`.
45///
46/// # Attributes
47/// Apart from the `ast` field which is required and must be `T: ASTNode`,
48/// all other fields are optional and can use any valid identifier.
49/// However, if present, they must be either `Vec`s or `Option`s of an ink! entity type
50/// (e.g. `Contract`, `ChainExtension` for ink! attribute macro types e.t.c
51/// or `Storage`, `Event`, `Constructor` for ink! attribute argument types)
52/// because they represent ink! entity descendants.
53///
54/// These ink! entity descendant fields (i.e. all fields apart from the `ast` field)
55/// can (optionally) be annotated with a single `#[initializer(...)]` attribute
56/// that defines their initialization strategy.
57///
58/// The `initializer` attribute takes one argument that represents the initialization strategy
59/// as follows:
60///
61/// - `closest` - collects all descendant ink! entities of the field's type that
62///   don't have any other ink! entity ancestors between them and the this entity.
63///   This is the default and be skipped.
64/// - `peek_macro = T` - collects all descendant ink! entities of the field's type that,
65///   either don't have any other ink! entity ancestors
66///   or only have one ink! attribute macro entity of type T
67///   (e.g. `Contract` for `#[ink::contract]`), between them and the this entity.
68/// - `peek_arg = T` - collects all descendant ink! entities of the field's type that,
69///   either don't have any other ink! entity ancestors
70///   or only have one ink! attribute argument entity of type T
71///   (e.g. `Storage` for `#[ink(storage)]`), between them and the this entity.
72/// - `call = F` - collects all ink! entities returned by the function `F`
73///   where `F: Fn(&SyntaxNode) -> impl Iterator<Item = T>` and `T: InkEntity`
74///   and `T` also matches the ink! entity type for the field.
75///
76/// # Example
77/// ```
78/// use ink_analyzer_ir::{Event, Message, Storage};
79/// use ink_analyzer_ir::ast;
80///
81/// #[ink_analyzer_macro::entity(macro_kind = Contract)]
82/// #[derive(Debug, Clone, PartialEq, Eq)]
83/// struct Contract {
84///     ast: ast::Module,
85///     storage: Option<Storage>,
86///     events: Vec<Event>,
87///     #[initializer(call = ink_analyzer_ir::ink_callable_closest_descendants)]
88///     messages: Vec<Message>,
89///     // --snip--
90/// }
91/// ```
92#[proc_macro_attribute]
93pub fn entity(args: TokenStream, item: TokenStream) -> TokenStream {
94    entity::impl_entity(args.into(), item.into())
95        .unwrap_or_else(error::Error::into_compile_error)
96        .into()
97}