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 `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)).
|
- 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: 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
|
### Breaking
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use heck::SnakeCase;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
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.
|
// Go through instruction accounts.
|
||||||
for ix in &program.ixs {
|
for ix in &program.ixs {
|
||||||
|
@ -13,15 +13,16 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
"__client_accounts_{}",
|
"__client_accounts_{}",
|
||||||
anchor_ident.to_string().to_snake_case()
|
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
|
// Build the tokens from all accounts
|
||||||
let account_structs: Vec<proc_macro2::TokenStream> = accounts
|
let account_structs: Vec<proc_macro2::TokenStream> = accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|macro_name: &String| {
|
.map(|(macro_name, cfgs)| {
|
||||||
let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
|
let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#cfgs)*
|
||||||
pub use crate::#macro_name::*;
|
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 args: Vec<&syn::PatType> = ix.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||||
let discriminator = gen_discriminator(SIGHASH_GLOBAL_NAMESPACE, name);
|
let discriminator = gen_discriminator(SIGHASH_GLOBAL_NAMESPACE, name);
|
||||||
let ret_type = &ix.returns.ty.to_token_stream();
|
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() {
|
let (method_ret, maybe_return) = match ret_type.to_string().as_str() {
|
||||||
"()" => (quote! {anchor_lang::Result<()> }, quote! { Ok(()) }),
|
"()" => (quote! {anchor_lang::Result<()> }, quote! { Ok(()) }),
|
||||||
_ => (
|
_ => (
|
||||||
|
@ -28,6 +29,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#ix_cfgs)*
|
||||||
pub fn #method_name<'a, 'b, 'c, 'info>(
|
pub fn #method_name<'a, 'b, 'c, 'info>(
|
||||||
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
|
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
|
||||||
#(#args),*
|
#(#args),*
|
||||||
|
@ -91,7 +93,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_accounts(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.
|
// Go through instruction accounts.
|
||||||
for ix in &program.ixs {
|
for ix in &program.ixs {
|
||||||
|
@ -101,15 +103,17 @@ pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
|
||||||
"__cpi_client_accounts_{}",
|
"__cpi_client_accounts_{}",
|
||||||
anchor_ident.to_string().to_snake_case()
|
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
|
// Build the tokens from all accounts
|
||||||
let account_structs: Vec<proc_macro2::TokenStream> = accounts
|
let account_structs: Vec<proc_macro2::TokenStream> = accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|macro_name: &String| {
|
.map(|(macro_name, cfgs)| {
|
||||||
let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
|
let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap();
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#cfgs)*
|
||||||
pub use crate::#macro_name::*;
|
pub use crate::#macro_name::*;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,8 +12,10 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Failed to parse ix method name in camel as `TokenStream`");
|
.expect("Failed to parse ix method name in camel as `TokenStream`");
|
||||||
let discriminator = quote! { instruction::#ix_name_camel::DISCRIMINATOR };
|
let discriminator = quote! { instruction::#ix_name_camel::DISCRIMINATOR };
|
||||||
|
let ix_cfgs = &ix.cfgs;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#ix_cfgs)*
|
||||||
if data.starts_with(#discriminator) {
|
if data.starts_with(#discriminator) {
|
||||||
return __private::__global::#ix_method_name(
|
return __private::__global::#ix_method_name(
|
||||||
program_id,
|
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 variant_arm = generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args);
|
||||||
let ix_name_log = format!("Instruction: {ix_name}");
|
let ix_name_log = format!("Instruction: {ix_name}");
|
||||||
let ret_type = &ix.returns.ty.to_token_stream();
|
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() {
|
let maybe_set_return_data = match ret_type.to_string().as_str() {
|
||||||
"()" => quote! {},
|
"()" => quote! {},
|
||||||
_ => quote! {
|
_ => quote! {
|
||||||
|
@ -113,6 +114,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#cfgs)*
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn #ix_method_name<'info>(
|
pub fn #ix_method_name<'info>(
|
||||||
__program_id: &Pubkey,
|
__program_id: &Pubkey,
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ix| {
|
.map(|ix| {
|
||||||
let name = &ix.raw_method.sig.ident.to_string();
|
let name = &ix.raw_method.sig.ident.to_string();
|
||||||
|
let ix_cfgs = &ix.cfgs;
|
||||||
let ix_name_camel =
|
let ix_name_camel =
|
||||||
proc_macro2::Ident::new(&name.to_camel_case(), ix.raw_method.sig.ident.span());
|
proc_macro2::Ident::new(&name.to_camel_case(), ix.raw_method.sig.ident.span());
|
||||||
let raw_args: Vec<proc_macro2::TokenStream> = ix
|
let raw_args: Vec<proc_macro2::TokenStream> = ix
|
||||||
|
@ -34,10 +35,13 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#ix_cfgs)*
|
||||||
impl anchor_lang::Discriminator for #ix_name_camel {
|
impl anchor_lang::Discriminator for #ix_name_camel {
|
||||||
const DISCRIMINATOR: &'static [u8] = #discriminator;
|
const DISCRIMINATOR: &'static [u8] = #discriminator;
|
||||||
}
|
}
|
||||||
|
#(#ix_cfgs)*
|
||||||
impl anchor_lang::InstructionData for #ix_name_camel {}
|
impl anchor_lang::InstructionData for #ix_name_camel {}
|
||||||
|
#(#ix_cfgs)*
|
||||||
impl anchor_lang::Owner for #ix_name_camel {
|
impl anchor_lang::Owner for #ix_name_camel {
|
||||||
fn owner() -> Pubkey {
|
fn owner() -> Pubkey {
|
||||||
ID
|
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 no args, output a "unit" variant instead of a struct variant.
|
||||||
if ix.args.is_empty() {
|
if ix.args.is_empty() {
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#ix_cfgs)*
|
||||||
/// Instruction.
|
/// Instruction.
|
||||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||||
pub struct #ix_name_camel;
|
pub struct #ix_name_camel;
|
||||||
|
@ -56,6 +61,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#ix_cfgs)*
|
||||||
/// Instruction.
|
/// Instruction.
|
||||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||||
pub struct #ix_name_camel {
|
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 = ix.ident.to_string();
|
||||||
let name_pascal = format_ident!("{}", name.to_camel_case());
|
let name_pascal = format_ident!("{}", name.to_camel_case());
|
||||||
let ctx_ident = &ix.anchor_ident;
|
let ctx_ident = &ix.anchor_ident;
|
||||||
|
let cfgs = &ix.cfgs;
|
||||||
|
|
||||||
let docs = match &ix.docs {
|
let docs = match &ix.docs {
|
||||||
Some(docs) if !no_docs => quote! { vec![#(#docs.into()),*] },
|
Some(docs) if !no_docs => quote! { vec![#(#docs.into()),*] },
|
||||||
|
@ -74,6 +75,7 @@ pub fn gen_idl_print_fn_program(program: &Program) -> TokenStream {
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#cfgs)*
|
||||||
#idl::IdlInstruction {
|
#idl::IdlInstruction {
|
||||||
name: #name.into(),
|
name: #name.into(),
|
||||||
docs: #docs,
|
docs: #docs,
|
||||||
|
|
|
@ -25,6 +25,7 @@ use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::token::Comma;
|
use syn::token::Comma;
|
||||||
|
use syn::Attribute;
|
||||||
use syn::Lit;
|
use syn::Lit;
|
||||||
use syn::{
|
use syn::{
|
||||||
Expr, Generics, Ident, ItemEnum, ItemFn, ItemMod, ItemStruct, LitInt, PatType, Token, Type,
|
Expr, Generics, Ident, ItemEnum, ItemFn, ItemMod, ItemStruct, LitInt, PatType, Token, Type,
|
||||||
|
@ -64,6 +65,7 @@ pub struct Ix {
|
||||||
pub raw_method: ItemFn,
|
pub raw_method: ItemFn,
|
||||||
pub ident: Ident,
|
pub ident: Ident,
|
||||||
pub docs: Option<Vec<String>>,
|
pub docs: Option<Vec<String>>,
|
||||||
|
pub cfgs: Vec<Attribute>,
|
||||||
pub args: Vec<IxArg>,
|
pub args: Vec<IxArg>,
|
||||||
pub returns: IxReturn,
|
pub returns: IxReturn,
|
||||||
// The ident for the struct deriving Accounts.
|
// The ident for the struct deriving Accounts.
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::parser::spl_interface;
|
||||||
use crate::{FallbackFn, Ix, IxArg, IxReturn, Overrides};
|
use crate::{FallbackFn, Ix, IxArg, IxReturn, Overrides};
|
||||||
use syn::parse::{Error as ParseError, Result as ParseResult};
|
use syn::parse::{Error as ParseError, Result as ParseResult};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
use syn::Attribute;
|
||||||
|
|
||||||
// Parse all non-state ix handlers from the program mod definition.
|
// Parse all non-state ix handlers from the program mod definition.
|
||||||
pub fn parse(program_mod: &syn::ItemMod) -> ParseResult<(Vec<Ix>, Option<FallbackFn>)> {
|
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 overrides = parse_overrides(&method.attrs)?;
|
||||||
let interface_discriminator = spl_interface::parse(&method.attrs);
|
let interface_discriminator = spl_interface::parse(&method.attrs);
|
||||||
let docs = docs::parse(&method.attrs);
|
let docs = docs::parse(&method.attrs);
|
||||||
|
let cfgs = parse_cfg(method);
|
||||||
let returns = parse_return(method)?;
|
let returns = parse_return(method)?;
|
||||||
let anchor_ident = ctx_accounts_ident(&ctx.raw_arg)?;
|
let anchor_ident = ctx_accounts_ident(&ctx.raw_arg)?;
|
||||||
Ok(Ix {
|
Ok(Ix {
|
||||||
raw_method: method.clone(),
|
raw_method: method.clone(),
|
||||||
ident: method.sig.ident.clone(),
|
ident: method.sig.ident.clone(),
|
||||||
docs,
|
docs,
|
||||||
|
cfgs,
|
||||||
args,
|
args,
|
||||||
anchor_ident,
|
anchor_ident,
|
||||||
returns,
|
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"]
|
cpi = ["no-entrypoint"]
|
||||||
default = []
|
default = []
|
||||||
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
|
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
|
||||||
|
my-feature = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
|
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
|
||||||
|
|
|
@ -812,3 +812,7 @@ pub struct TestBoxedOwnerConstraint<'info> {
|
||||||
pub my_account: Box<Account<'info, Data>>,
|
pub my_account: Box<Account<'info, Data>>,
|
||||||
pub program: Program<'info, Misc>,
|
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<()> {
|
pub fn test_boxed_owner_constraint(_ctx: Context<TestBoxedOwnerConstraint>) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "my-feature")]
|
||||||
|
pub fn only_my_feature(_ctx: Context<Empty>) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue