lang: Allow the `cfg` attribute above the instructions (#2339)
This commit is contained in:
parent
4866680cb5
commit
e25918c609
|
@ -72,6 +72,7 @@ The minor version will be incremented upon a breaking change and the patch versi
|
|||
- idl: Fix using `address` constraint with non-const expressions ([#3216](https://github.com/coral-xyz/anchor/pull/3216)).
|
||||
- idl: Fix using full path types with `Program` ([#3228](https://github.com/coral-xyz/anchor/pull/3228)).
|
||||
- lang: Use closures for `init` constraints to reduce the stack usage of `try_accounts` ([#2939](https://github.com/coral-xyz/anchor/pull/2939)).
|
||||
- lang: Allow the `cfg` attribute above the instructions ([#2339](https://github.com/coral-xyz/anchor/pull/2339)).
|
||||
|
||||
### Breaking
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use heck::SnakeCase;
|
|||
use quote::quote;
|
||||
|
||||
pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||
let mut accounts = std::collections::HashSet::new();
|
||||
let mut accounts = std::collections::HashMap::new();
|
||||
|
||||
// Go through instruction accounts.
|
||||
for ix in &program.ixs {
|
||||
|
@ -13,15 +13,16 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
"__client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
accounts.insert(macro_name, ix.cfgs.as_slice());
|
||||
}
|
||||
|
||||
// Build the tokens from all accounts
|
||||
let account_structs: Vec<proc_macro2::TokenStream> = accounts
|
||||
.iter()
|
||||
.map(|macro_name: &String| {
|
||||
.map(|(macro_name, cfgs)| {
|
||||
let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
|
||||
quote! {
|
||||
#(#cfgs)*
|
||||
pub use crate::#macro_name::*;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -19,6 +19,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
let args: Vec<&syn::PatType> = ix.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let discriminator = gen_discriminator(SIGHASH_GLOBAL_NAMESPACE, name);
|
||||
let ret_type = &ix.returns.ty.to_token_stream();
|
||||
let ix_cfgs = &ix.cfgs;
|
||||
let (method_ret, maybe_return) = match ret_type.to_string().as_str() {
|
||||
"()" => (quote! {anchor_lang::Result<()> }, quote! { Ok(()) }),
|
||||
_ => (
|
||||
|
@ -28,6 +29,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
};
|
||||
|
||||
quote! {
|
||||
#(#ix_cfgs)*
|
||||
pub fn #method_name<'a, 'b, 'c, 'info>(
|
||||
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
|
||||
#(#args),*
|
||||
|
@ -91,7 +93,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
}
|
||||
|
||||
pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
|
||||
let mut accounts = std::collections::HashSet::new();
|
||||
let mut accounts = std::collections::HashMap::new();
|
||||
|
||||
// Go through instruction accounts.
|
||||
for ix in &program.ixs {
|
||||
|
@ -101,15 +103,17 @@ pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
|
|||
"__cpi_client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
let cfgs = &ix.cfgs;
|
||||
accounts.insert(macro_name, cfgs.as_slice());
|
||||
}
|
||||
|
||||
// Build the tokens from all accounts
|
||||
let account_structs: Vec<proc_macro2::TokenStream> = accounts
|
||||
.iter()
|
||||
.map(|macro_name: &String| {
|
||||
.map(|(macro_name, cfgs)| {
|
||||
let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
|
||||
quote! {
|
||||
#(#cfgs)*
|
||||
pub use crate::#macro_name::*;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,8 +12,10 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
.parse()
|
||||
.expect("Failed to parse ix method name in camel as `TokenStream`");
|
||||
let discriminator = quote! { instruction::#ix_name_camel::DISCRIMINATOR };
|
||||
let ix_cfgs = &ix.cfgs;
|
||||
|
||||
quote! {
|
||||
#(#ix_cfgs)*
|
||||
if data.starts_with(#discriminator) {
|
||||
return __private::__global::#ix_method_name(
|
||||
program_id,
|
||||
|
|
|
@ -104,6 +104,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
let variant_arm = generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args);
|
||||
let ix_name_log = format!("Instruction: {ix_name}");
|
||||
let ret_type = &ix.returns.ty.to_token_stream();
|
||||
let cfgs = &ix.cfgs;
|
||||
let maybe_set_return_data = match ret_type.to_string().as_str() {
|
||||
"()" => quote! {},
|
||||
_ => quote! {
|
||||
|
@ -113,6 +114,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
},
|
||||
};
|
||||
quote! {
|
||||
#(#cfgs)*
|
||||
#[inline(never)]
|
||||
pub fn #ix_method_name<'info>(
|
||||
__program_id: &Pubkey,
|
||||
|
|
|
@ -10,6 +10,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
.iter()
|
||||
.map(|ix| {
|
||||
let name = &ix.raw_method.sig.ident.to_string();
|
||||
let ix_cfgs = &ix.cfgs;
|
||||
let ix_name_camel =
|
||||
proc_macro2::Ident::new(&name.to_camel_case(), ix.raw_method.sig.ident.span());
|
||||
let raw_args: Vec<proc_macro2::TokenStream> = ix
|
||||
|
@ -34,10 +35,13 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
};
|
||||
|
||||
quote! {
|
||||
#(#ix_cfgs)*
|
||||
impl anchor_lang::Discriminator for #ix_name_camel {
|
||||
const DISCRIMINATOR: &'static [u8] = #discriminator;
|
||||
}
|
||||
#(#ix_cfgs)*
|
||||
impl anchor_lang::InstructionData for #ix_name_camel {}
|
||||
#(#ix_cfgs)*
|
||||
impl anchor_lang::Owner for #ix_name_camel {
|
||||
fn owner() -> Pubkey {
|
||||
ID
|
||||
|
@ -48,6 +52,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
// If no args, output a "unit" variant instead of a struct variant.
|
||||
if ix.args.is_empty() {
|
||||
quote! {
|
||||
#(#ix_cfgs)*
|
||||
/// Instruction.
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #ix_name_camel;
|
||||
|
@ -56,6 +61,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
}
|
||||
} else {
|
||||
quote! {
|
||||
#(#ix_cfgs)*
|
||||
/// Instruction.
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #ix_name_camel {
|
||||
|
|
|
@ -32,6 +32,7 @@ pub fn gen_idl_print_fn_program(program: &Program) -> TokenStream {
|
|||
let name = ix.ident.to_string();
|
||||
let name_pascal = format_ident!("{}", name.to_camel_case());
|
||||
let ctx_ident = &ix.anchor_ident;
|
||||
let cfgs = &ix.cfgs;
|
||||
|
||||
let docs = match &ix.docs {
|
||||
Some(docs) if !no_docs => quote! { vec![#(#docs.into()),*] },
|
||||
|
@ -74,6 +75,7 @@ pub fn gen_idl_print_fn_program(program: &Program) -> TokenStream {
|
|||
|
||||
Ok((
|
||||
quote! {
|
||||
#(#cfgs)*
|
||||
#idl::IdlInstruction {
|
||||
name: #name.into(),
|
||||
docs: #docs,
|
||||
|
|
|
@ -25,6 +25,7 @@ use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}
|
|||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Comma;
|
||||
use syn::Attribute;
|
||||
use syn::Lit;
|
||||
use syn::{
|
||||
Expr, Generics, Ident, ItemEnum, ItemFn, ItemMod, ItemStruct, LitInt, PatType, Token, Type,
|
||||
|
@ -64,6 +65,7 @@ pub struct Ix {
|
|||
pub raw_method: ItemFn,
|
||||
pub ident: Ident,
|
||||
pub docs: Option<Vec<String>>,
|
||||
pub cfgs: Vec<Attribute>,
|
||||
pub args: Vec<IxArg>,
|
||||
pub returns: IxReturn,
|
||||
// The ident for the struct deriving Accounts.
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::parser::spl_interface;
|
|||
use crate::{FallbackFn, Ix, IxArg, IxReturn, Overrides};
|
||||
use syn::parse::{Error as ParseError, Result as ParseResult};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::Attribute;
|
||||
|
||||
// Parse all non-state ix handlers from the program mod definition.
|
||||
pub fn parse(program_mod: &syn::ItemMod) -> ParseResult<(Vec<Ix>, Option<FallbackFn>)> {
|
||||
|
@ -28,12 +29,14 @@ pub fn parse(program_mod: &syn::ItemMod) -> ParseResult<(Vec<Ix>, Option<Fallbac
|
|||
let overrides = parse_overrides(&method.attrs)?;
|
||||
let interface_discriminator = spl_interface::parse(&method.attrs);
|
||||
let docs = docs::parse(&method.attrs);
|
||||
let cfgs = parse_cfg(method);
|
||||
let returns = parse_return(method)?;
|
||||
let anchor_ident = ctx_accounts_ident(&ctx.raw_arg)?;
|
||||
Ok(Ix {
|
||||
raw_method: method.clone(),
|
||||
ident: method.sig.ident.clone(),
|
||||
docs,
|
||||
cfgs,
|
||||
args,
|
||||
anchor_ident,
|
||||
returns,
|
||||
|
@ -146,3 +149,14 @@ pub fn parse_return(method: &syn::ItemFn) -> ParseResult<IxReturn> {
|
|||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_cfg(method: &syn::ItemFn) -> Vec<Attribute> {
|
||||
method
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|attr| match attr.path.is_ident("cfg") {
|
||||
true => Some(attr.to_owned()),
|
||||
false => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ no-idl = []
|
|||
cpi = ["no-entrypoint"]
|
||||
default = []
|
||||
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
|
||||
my-feature = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
|
||||
|
|
|
@ -812,3 +812,7 @@ pub struct TestBoxedOwnerConstraint<'info> {
|
|||
pub my_account: Box<Account<'info, Data>>,
|
||||
pub program: Program<'info, Misc>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "my-feature")]
|
||||
#[derive(Accounts)]
|
||||
pub struct Empty {}
|
||||
|
|
|
@ -396,4 +396,9 @@ pub mod misc {
|
|||
pub fn test_boxed_owner_constraint(_ctx: Context<TestBoxedOwnerConstraint>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "my-feature")]
|
||||
pub fn only_my_feature(_ctx: Context<Empty>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue