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}