浏览代码

Add `IdlBuild` trait (#2629)

acheron 2 年之前
父节点
当前提交
b5f4796156

+ 2 - 0
lang/Cargo.toml

@@ -43,8 +43,10 @@ anchor-attribute-program = { path = "./attribute/program", version = "0.28.0" }
 anchor-derive-accounts = { path = "./derive/accounts", version = "0.28.0" }
 anchor-derive-serde = { path = "./derive/serde", version = "0.28.0" }
 anchor-derive-space = { path = "./derive/space", version = "0.28.0" }
+
 # `anchor-syn` should only be included with `idl-build` feature
 anchor-syn = { path = "./syn", version = "0.28.0", optional = true }
+
 arrayref = "0.3"
 base64 = "0.13"
 bincode = "1"

+ 2 - 2
lang/attribute/account/src/lib.rs

@@ -405,10 +405,10 @@ pub fn zero_copy(
     #[cfg(feature = "idl-build")]
     {
         let no_docs = get_no_docs();
-        let idl_gen_impl = gen_idl_gen_impl_for_struct(&account_strct, no_docs);
+        let idl_build_impl = gen_idl_build_impl_for_struct(&account_strct, no_docs);
         return proc_macro::TokenStream::from(quote! {
             #ret
-            #idl_gen_impl
+            #idl_build_impl
         });
     }
 

+ 2 - 2
lang/attribute/event/src/lib.rs

@@ -48,10 +48,10 @@ pub fn event(
 
     #[cfg(feature = "idl-build")]
     {
-        let idl_gen = anchor_syn::idl::build::gen_idl_print_function_for_event(&event_strct);
+        let idl_build = anchor_syn::idl::build::gen_idl_print_function_for_event(&event_strct);
         return proc_macro::TokenStream::from(quote! {
             #ret
-            #idl_gen
+            #idl_build
         });
     }
 

+ 5 - 5
lang/derive/serde/src/lib.rs

@@ -37,12 +37,12 @@ pub fn anchor_serialize(input: TokenStream) -> TokenStream {
     {
         let no_docs = get_no_docs();
 
-        let idl_gen_impl = match syn::parse(input).unwrap() {
-            Item::Struct(item) => gen_idl_gen_impl_for_struct(&item, no_docs),
-            Item::Enum(item) => gen_idl_gen_impl_for_enum(item, no_docs),
+        let idl_build_impl = match syn::parse(input).unwrap() {
+            Item::Struct(item) => gen_idl_build_impl_for_struct(&item, no_docs),
+            Item::Enum(item) => gen_idl_build_impl_for_enum(item, no_docs),
             Item::Union(item) => {
                 // unions are not included in the IDL - TODO print a warning
-                idl_gen_impl_skeleton(quote! {None}, quote! {}, &item.ident, &item.generics)
+                idl_build_impl_skeleton(quote! {None}, quote! {}, &item.ident, &item.generics)
             }
             // Derive macros can only be defined on structs, enums, and unions.
             _ => unreachable!(),
@@ -50,7 +50,7 @@ pub fn anchor_serialize(input: TokenStream) -> TokenStream {
 
         return TokenStream::from(quote! {
             #ret
-            #idl_gen_impl
+            #idl_build_impl
         });
     };
 

+ 11 - 5
lang/src/lib.rs

@@ -50,19 +50,21 @@ pub use anchor_attribute_account::{account, declare_id, zero_copy};
 pub use anchor_attribute_constant::constant;
 pub use anchor_attribute_error::*;
 pub use anchor_attribute_event::{emit, event};
-#[cfg(feature = "event-cpi")]
-pub use anchor_attribute_event::{emit_cpi, event_cpi};
 pub use anchor_attribute_program::program;
 pub use anchor_derive_accounts::Accounts;
 pub use anchor_derive_serde::{AnchorDeserialize, AnchorSerialize};
 pub use anchor_derive_space::InitSpace;
+
 /// Borsh is the default serialization format for instructions and accounts.
 pub use borsh::de::BorshDeserialize as AnchorDeserialize;
 pub use borsh::ser::BorshSerialize as AnchorSerialize;
 pub use solana_program;
 
+#[cfg(feature = "event-cpi")]
+pub use anchor_attribute_event::{emit_cpi, event_cpi};
+
 #[cfg(feature = "idl-build")]
-pub use anchor_syn;
+pub use anchor_syn::{self, idl::build::IdlBuild};
 
 pub type Result<T> = std::result::Result<T, error::Error>;
 
@@ -353,8 +355,6 @@ pub mod prelude {
         AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, InitSpace, Key,
         Lamports, Owner, ProgramData, Result, Space, ToAccountInfo, ToAccountInfos, ToAccountMetas,
     };
-    #[cfg(feature = "event-cpi")]
-    pub use super::{emit_cpi, event_cpi};
     pub use anchor_attribute_error::*;
     pub use borsh;
     pub use error::*;
@@ -373,6 +373,12 @@ pub mod prelude {
     pub use solana_program::sysvar::stake_history::StakeHistory;
     pub use solana_program::sysvar::Sysvar as SolanaSysvar;
     pub use thiserror;
+
+    #[cfg(feature = "event-cpi")]
+    pub use super::{emit_cpi, event_cpi};
+
+    #[cfg(feature = "idl-build")]
+    pub use super::IdlBuild;
 }
 
 /// Internal module used by macros and unstable apis.

+ 3 - 2
lang/syn/src/codegen/accounts/mod.rs

@@ -36,10 +36,11 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
     {
         #![allow(warnings)]
         let no_docs = crate::idl::build::get_no_docs();
-        let idl_gen_impl = crate::idl::build::gen_idl_gen_impl_for_accounts_strct(&accs, no_docs);
+        let idl_build_impl =
+            crate::idl::build::gen_idl_build_impl_for_accounts_struct(&accs, no_docs);
         return quote! {
             #ret
-            #idl_gen_impl
+            #idl_build_impl
         };
     }
 

+ 2 - 2
lang/syn/src/codegen/error.rs

@@ -103,10 +103,10 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
 
     #[cfg(feature = "idl-build")]
     {
-        let idl_gen = gen_idl_print_function_for_error(&error);
+        let idl_build = gen_idl_print_function_for_error(&error);
         return quote! {
             #ret
-            #idl_gen
+            #idl_build
         };
     };
 

+ 2 - 2
lang/syn/src/codegen/program/mod.rs

@@ -40,11 +40,11 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
     #[cfg(feature = "idl-build")]
     {
         let no_docs = crate::idl::build::get_no_docs();
-        let idl_gen = crate::idl::build::gen_idl_print_function_for_program(program, no_docs);
+        let idl_build = crate::idl::build::gen_idl_print_function_for_program(program, no_docs);
 
         return quote! {
             #ret
-            #idl_gen
+            #idl_build
         };
     };
 

+ 43 - 13
lang/syn/src/idl/build.rs

@@ -1,10 +1,39 @@
+pub use serde_json;
+
 use crate::{parser::docs, AccountField, AccountsStruct, Error, Program};
 use heck::MixedCase;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
-pub use serde_json;
 use syn::{Ident, ItemEnum, ItemStruct};
 
+/// A trait that types must implement in order to generate the IDL via compilation.
+///
+/// This trait is automatically implemented for Anchor all types that use the `AnchorSerialize`
+/// proc macro. Note that manually implementing the `AnchorSerialize` trait will **NOT** have the
+/// same effect.
+///
+/// Types that don't implement this trait will cause a compile error during the IDL generation.
+///
+/// The methods have default implementation that allows the program to compile but the type will
+/// **NOT** be included in the IDL.
+pub trait IdlBuild {
+    /// Returns the full module path of the type.
+    fn __anchor_private_full_path() -> String {
+        String::default()
+    }
+
+    /// Returns the IDL type definition of the type or `None` if it doesn't exist.
+    fn __anchor_private_gen_idl_type() -> Option<super::types::IdlTypeDefinition> {
+        None
+    }
+
+    /// Insert the type definition to the defined types hashmap.
+    fn __anchor_private_insert_idl_defined(
+        _defined_types: &mut std::collections::HashMap<String, super::types::IdlTypeDefinition>,
+    ) {
+    }
+}
+
 #[inline(always)]
 fn get_module_paths() -> (TokenStream, TokenStream) {
     (
@@ -439,7 +468,7 @@ pub fn idl_type_definition_ts_from_syn_enum(
     ))
 }
 
-pub fn idl_gen_impl_skeleton(
+pub fn idl_build_impl_skeleton(
     idl_type_definition_ts: TokenStream,
     insert_defined_ts: TokenStream,
     ident: &Ident,
@@ -448,18 +477,19 @@ pub fn idl_gen_impl_skeleton(
     let (idl, _) = get_module_paths();
     let name = ident.to_string();
     let (impl_generics, ty_generics, where_clause) = input_generics.split_for_impl();
+    let idl_build_trait = quote! {anchor_lang::anchor_syn::idl::build::IdlBuild};
 
     quote! {
-        impl #impl_generics #ident #ty_generics #where_clause {
-            pub fn __anchor_private_full_path() -> String {
+        impl #impl_generics #idl_build_trait for #ident #ty_generics #where_clause {
+            fn __anchor_private_full_path() -> String {
                 format!("{}::{}", std::module_path!(), #name)
             }
 
-            pub fn __anchor_private_gen_idl_type() -> Option<#idl::IdlTypeDefinition> {
+            fn __anchor_private_gen_idl_type() -> Option<#idl::IdlTypeDefinition> {
                 #idl_type_definition_ts
             }
 
-            pub fn __anchor_private_insert_idl_defined(
+            fn __anchor_private_insert_idl_defined(
                 defined_types: &mut std::collections::HashMap<String, #idl::IdlTypeDefinition>
             ) {
                 #insert_defined_ts
@@ -469,7 +499,7 @@ pub fn idl_gen_impl_skeleton(
 }
 
 // generates the IDL generation impl for for a struct
-pub fn gen_idl_gen_impl_for_struct(strct: &ItemStruct, no_docs: bool) -> TokenStream {
+pub fn gen_idl_build_impl_for_struct(strct: &ItemStruct, no_docs: bool) -> TokenStream {
     let idl_type_definition_ts: TokenStream;
     let insert_defined_ts: TokenStream;
 
@@ -492,7 +522,7 @@ pub fn gen_idl_gen_impl_for_struct(strct: &ItemStruct, no_docs: bool) -> TokenSt
     let ident = &strct.ident;
     let input_generics = &strct.generics;
 
-    idl_gen_impl_skeleton(
+    idl_build_impl_skeleton(
         idl_type_definition_ts,
         insert_defined_ts,
         ident,
@@ -501,7 +531,7 @@ pub fn gen_idl_gen_impl_for_struct(strct: &ItemStruct, no_docs: bool) -> TokenSt
 }
 
 // generates the IDL generation impl for for an enum
-pub fn gen_idl_gen_impl_for_enum(enm: ItemEnum, no_docs: bool) -> TokenStream {
+pub fn gen_idl_build_impl_for_enum(enm: ItemEnum, no_docs: bool) -> TokenStream {
     let idl_type_definition_ts: TokenStream;
     let insert_defined_ts: TokenStream;
 
@@ -524,7 +554,7 @@ pub fn gen_idl_gen_impl_for_enum(enm: ItemEnum, no_docs: bool) -> TokenStream {
     let ident = &enm.ident;
     let input_generics = &enm.generics;
 
-    idl_gen_impl_skeleton(
+    idl_build_impl_skeleton(
         idl_type_definition_ts,
         insert_defined_ts,
         ident,
@@ -533,7 +563,7 @@ pub fn gen_idl_gen_impl_for_enum(enm: ItemEnum, no_docs: bool) -> TokenStream {
 }
 
 // generates the IDL generation impl for for an event
-pub fn gen_idl_gen_impl_for_event(event_strct: &ItemStruct) -> TokenStream {
+pub fn gen_idl_build_impl_for_event(event_strct: &ItemStruct) -> TokenStream {
     fn parse_fields(
         fields: &syn::FieldsNamed,
     ) -> Result<(Vec<TokenStream>, Vec<syn::TypePath>), ()> {
@@ -601,7 +631,7 @@ pub fn gen_idl_gen_impl_for_event(event_strct: &ItemStruct) -> TokenStream {
 }
 
 // generates the IDL generation impl for the Accounts struct
-pub fn gen_idl_gen_impl_for_accounts_strct(
+pub fn gen_idl_build_impl_for_accounts_struct(
     accs_strct: &AccountsStruct,
     no_docs: bool,
 ) -> TokenStream {
@@ -817,7 +847,7 @@ pub fn gen_idl_print_function_for_event(event: &ItemStruct) -> TokenStream {
 
     let ident = &event.ident;
     let fn_name = format_ident!("__anchor_private_print_idl_event_{}", ident.to_string());
-    let impl_gen = gen_idl_gen_impl_for_event(event);
+    let impl_gen = gen_idl_build_impl_for_event(event);
 
     quote! {
         #impl_gen

+ 1 - 0
spl/Cargo.toml

@@ -13,6 +13,7 @@ associated_token = ["spl-associated-token-account"]
 dex = ["serum_dex"]
 devnet = []
 governance = []
+idl-build = ["anchor-lang/idl-build"]
 metadata = ["mpl-token-metadata"]
 mint = []
 shmem = []

+ 3 - 0
spl/src/governance.rs

@@ -54,5 +54,8 @@ macro_rules! vote_weight_record {
                 &mut self.0
             }
         }
+
+        #[cfg(feature = "idl-build")]
+        impl anchor_lang::IdlBuild for VoterWeightRecord {}
     };
 }

+ 9 - 0
spl/src/metadata.rs

@@ -782,6 +782,9 @@ impl Deref for MetadataAccount {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for MetadataAccount {}
+
 #[derive(Clone, Debug, PartialEq)]
 pub struct MasterEditionAccount(mpl_token_metadata::state::MasterEditionV2);
 
@@ -819,6 +822,9 @@ impl anchor_lang::Owner for MasterEditionAccount {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for MasterEditionAccount {}
+
 #[derive(Clone, Debug, PartialEq)]
 pub struct TokenRecordAccount(mpl_token_metadata::state::TokenRecord);
 
@@ -855,6 +861,9 @@ impl Deref for TokenRecordAccount {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for TokenRecordAccount {}
+
 #[derive(Clone)]
 pub struct Metadata;
 

+ 3 - 0
spl/src/stake.rs

@@ -156,6 +156,9 @@ impl Deref for StakeAccount {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for StakeAccount {}
+
 #[derive(Clone)]
 pub struct Stake;
 

+ 6 - 0
spl/src/token.rs

@@ -486,6 +486,9 @@ impl Deref for TokenAccount {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for TokenAccount {}
+
 #[derive(Clone, Debug, Default, PartialEq)]
 pub struct Mint(spl_token::state::Mint);
 
@@ -517,6 +520,9 @@ impl Deref for Mint {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for Mint {}
+
 #[derive(Clone)]
 pub struct Token;
 

+ 6 - 0
spl/src/token_interface.rs

@@ -32,6 +32,9 @@ impl Deref for TokenAccount {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for TokenAccount {}
+
 #[derive(Clone, Debug, Default, PartialEq)]
 pub struct Mint(spl_token_2022::state::Mint);
 
@@ -59,6 +62,9 @@ impl Deref for Mint {
     }
 }
 
+#[cfg(feature = "idl-build")]
+impl anchor_lang::IdlBuild for Mint {}
+
 #[derive(Clone)]
 pub struct TokenInterface;
 

+ 40 - 0
tests/idl/idls/build.json

@@ -61,6 +61,26 @@
           "isMut": false,
           "isSigner": false
         },
+        {
+          "name": "tokenAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "tokenInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
         {
           "name": "payer",
           "isMut": true,
@@ -111,6 +131,26 @@
           "isMut": false,
           "isSigner": false
         },
+        {
+          "name": "tokenAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "tokenInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
         {
           "name": "payer",
           "isMut": true,

+ 40 - 0
tests/idl/idls/parse.json

@@ -61,6 +61,26 @@
           "isMut": false,
           "isSigner": false
         },
+        {
+          "name": "tokenAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "tokenInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
         {
           "name": "payer",
           "isMut": true,
@@ -111,6 +131,26 @@
           "isMut": false,
           "isSigner": false
         },
+        {
+          "name": "tokenAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "tokenInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
+        {
+          "name": "mintInterfaceAccount",
+          "isMut": false,
+          "isSigner": false
+        },
         {
           "name": "payer",
           "isMut": true,

+ 2 - 2
tests/idl/programs/idl/Cargo.toml

@@ -13,11 +13,11 @@ no-entrypoint = []
 no-idl = []
 no-log-ix-name = []
 cpi = ["no-entrypoint"]
-idl-build = ["anchor-lang/idl-build", "external/idl-build"]
+idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build", "external/idl-build"]
 default = []
 
 [dependencies]
 anchor-lang = { path = "../../../../lang" }
 anchor-spl = { path = "../../../../spl" }
-bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]}
+bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"] }
 external = { path = "../external", features = ["no-entrypoint"] }

+ 6 - 0
tests/idl/programs/idl/src/lib.rs

@@ -1,4 +1,5 @@
 use anchor_lang::prelude::*;
+use anchor_spl::{token, token_interface};
 use std::str::FromStr;
 
 declare_id!("id11111111111111111111111111111111111111111");
@@ -274,6 +275,11 @@ pub struct Initialize<'info> {
     nested: NestedAccounts<'info>,
     zc_account: AccountLoader<'info, SomeZcAccount>,
 
+    token_account: Account<'info, token::TokenAccount>,
+    mint_account: Account<'info, token::Mint>,
+    token_interface_account: InterfaceAccount<'info, token_interface::TokenAccount>,
+    mint_interface_account: InterfaceAccount<'info, token_interface::Mint>,
+
     #[account(mut)]
     payer: Signer<'info>,
     system_program: Program<'info, System>,