1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use anchor_syn::codegen;
use anchor_syn::parser::error::{self as error_parser, ErrorInput};
use anchor_syn::ErrorArgs;
use syn::{parse_macro_input, Expr};
/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
/// used as return types from Anchor instruction handlers. Importantly, the
/// attribute implements
/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) on the
/// `ErrorCode` to support converting from the user defined error enum *into*
/// the generated `Error`.
///
/// # Example
///
/// ```ignore
/// use anchor_lang::prelude::*;
///
/// #[program]
/// mod errors {
/// use super::*;
/// pub fn hello(_ctx: Context<Hello>) -> Result<()> {
/// Err(error!(MyError::Hello))
/// }
/// }
///
/// #[derive(Accounts)]
/// pub struct Hello {}
///
/// #[error_code]
/// pub enum MyError {
/// #[msg("This is an error message clients will automatically display")]
/// Hello,
/// }
/// ```
///
/// Note that we generate a new `Error` type so that we can return either the
/// user defined error enum *or* a
/// [`ProgramError`](../solana_program/enum.ProgramError.html), which is used
/// pervasively, throughout solana program crates. The generated `Error` type
/// should almost never be used directly, as the user defined error is
/// preferred. In the example above, `error!(MyError::Hello)`.
///
/// # Msg
///
/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that
/// parsers and IDLs can map error codes to error messages.
#[proc_macro_attribute]
pub fn error_code(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args = match args.is_empty() {
true => None,
false => Some(parse_macro_input!(args as ErrorArgs)),
};
let mut error_enum = parse_macro_input!(input as syn::ItemEnum);
let error = codegen::error::generate(error_parser::parse(&mut error_enum, args));
proc_macro::TokenStream::from(error)
}
/// Generates an [`Error::AnchorError`](../../anchor_lang/error/enum.Error.html) that includes file and line information.
///
/// # Example
/// ```rust,ignore
/// #[program]
/// mod errors {
/// use super::*;
/// pub fn example(_ctx: Context<Example>) -> Result<()> {
/// Err(error!(MyError::Hello))
/// }
/// }
///
/// #[error_code]
/// pub enum MyError {
/// #[msg("This is an error message clients will automatically display")]
/// Hello,
/// }
/// ```
#[proc_macro]
pub fn error(ts: proc_macro::TokenStream) -> TokenStream {
let input = parse_macro_input!(ts as ErrorInput);
let error_code = input.error_code;
create_error(error_code, true, None)
}
fn create_error(error_code: Expr, source: bool, account_name: Option<Expr>) -> TokenStream {
let error_origin = match (source, account_name) {
(false, None) => quote! { None },
(false, Some(account_name)) => quote! {
Some(anchor_lang::error::ErrorOrigin::AccountName(#account_name.to_string()))
},
(true, _) => quote! {
Some(anchor_lang::error::ErrorOrigin::Source(anchor_lang::error::Source {
filename: file!(),
line: line!()
}))
},
};
TokenStream::from(quote! {
anchor_lang::error::Error::from(
anchor_lang::error::AnchorError {
error_name: #error_code.name(),
error_code_number: #error_code.into(),
error_msg: #error_code.to_string(),
error_origin: #error_origin,
compared_values: None
}
)
})
}