lib.rs 3.9 KB

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