Skip to content

Commit

Permalink
lang: Parse #[account] attribute arguments with syn (#3140)
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto authored Aug 1, 2024
1 parent f6a8042 commit 9fa2509
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The minor version will be incremented upon a breaking change and the patch versi
- lang: Make discriminator type unsized ([#3098](https://github.com/coral-xyz/anchor/pull/3098)).
- lang: Require `Discriminator` trait impl when using the `zero` constraint ([#3118](https://github.com/coral-xyz/anchor/pull/3118)).
- ts: Remove `DISCRIMINATOR_SIZE` constant ([#3120](https://github.com/coral-xyz/anchor/pull/3120)).
- lang: `#[account]` attribute arguments no longer parses identifiers as namespaces ([#3140](https://github.com/coral-xyz/anchor/pull/3140)).

## [0.30.1] - 2024-06-20

Expand Down
105 changes: 78 additions & 27 deletions lang/attribute/account/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
extern crate proc_macro;

use quote::quote;
use syn::parse_macro_input;
use quote::{quote, ToTokens};
use syn::{
parenthesized,
parse::{Parse, ParseStream},
parse_macro_input,
token::{Comma, Paren},
Ident, LitStr,
};

mod id;

Expand Down Expand Up @@ -67,31 +73,10 @@ pub fn account(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let mut namespace = "".to_string();
let mut is_zero_copy = false;
let mut unsafe_bytemuck = false;
let args_str = args.to_string();
let args: Vec<&str> = args_str.split(',').collect();
if args.len() > 2 {
panic!("Only two args are allowed to the account attribute.")
}
for arg in args {
let ns = arg
.to_string()
.replace('\"', "")
.chars()
.filter(|c| !c.is_whitespace())
.collect();
if ns == "zero_copy" {
is_zero_copy = true;
unsafe_bytemuck = false;
} else if ns == "zero_copy(unsafe)" {
is_zero_copy = true;
unsafe_bytemuck = true;
} else {
namespace = ns;
}
}
let args = parse_macro_input!(args as AccountArgs);
let namespace = args.namespace.unwrap_or_default();
let is_zero_copy = args.zero_copy.is_some();
let unsafe_bytemuck = args.zero_copy.unwrap_or_default();

let account_strct = parse_macro_input!(input as syn::ItemStruct);
let account_name = &account_strct.ident;
Expand Down Expand Up @@ -248,6 +233,72 @@ pub fn account(
})
}

#[derive(Debug, Default)]
struct AccountArgs {
/// `bool` is for deciding whether to use `unsafe` e.g. `Some(true)` for `zero_copy(unsafe)`
zero_copy: Option<bool>,
namespace: Option<String>,
}

impl Parse for AccountArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut parsed = Self::default();
let args = input.parse_terminated::<_, Comma>(AccountArg::parse)?;
for arg in args {
match arg {
AccountArg::ZeroCopy { is_unsafe } => {
parsed.zero_copy.replace(is_unsafe);
}
AccountArg::Namespace(ns) => {
parsed.namespace.replace(ns);
}
}
}

Ok(parsed)
}
}

enum AccountArg {
ZeroCopy { is_unsafe: bool },
Namespace(String),
}

impl Parse for AccountArg {
fn parse(input: ParseStream) -> syn::Result<Self> {
// Namespace
if let Ok(ns) = input.parse::<LitStr>() {
return Ok(Self::Namespace(
ns.to_token_stream().to_string().replace('\"', ""),
));
}

// Zero copy
let ident = input.parse::<Ident>()?;
if ident == "zero_copy" {
let is_unsafe = if input.peek(Paren) {
let content;
parenthesized!(content in input);
let content = content.parse::<proc_macro2::TokenStream>()?;
if content.to_string().as_str().trim() != "unsafe" {
return Err(syn::Error::new(
syn::spanned::Spanned::span(&content),
"Expected `unsafe`",
));
}

true
} else {
false
};

return Ok(Self::ZeroCopy { is_unsafe });
};

Err(syn::Error::new(ident.span(), "Unexpected argument"))
}
}

#[proc_macro_derive(ZeroCopyAccessor, attributes(accessor))]
pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let account_strct = parse_macro_input!(item as syn::ItemStruct);
Expand Down

0 comments on commit 9fa2509

Please sign in to comment.