Skip to content

Commit

Permalink
lang: give better errors for init when programs are missing (#1257)
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-schaaf committed Jan 7, 2022
1 parent e2b9281 commit 01ff03b
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,10 @@ incremented for features.

## [Unreleased]

#### Fixes

*lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257))

## [0.20.0] - 2022-01-06

### Fixes
Expand Down
9 changes: 9 additions & 0 deletions lang/syn/src/lib.rs
Expand Up @@ -154,6 +154,15 @@ pub enum AccountField {
CompositeField(CompositeField),
}

impl AccountField {
fn ident(&self) -> &Ident {
match self {
AccountField::Field(field) => &field.ident,
AccountField::CompositeField(c_field) => &c_field.ident,
}
}
}

#[derive(Debug)]
pub struct Field {
pub ident: Ident,
Expand Down
62 changes: 62 additions & 0 deletions lang/syn/src/parser/accounts/mod.rs
Expand Up @@ -31,9 +31,71 @@ pub fn parse(strct: &syn::ItemStruct) -> ParseResult<AccountsStruct> {
))
}
};

let _ = constraints_cross_checks(&fields)?;

Ok(AccountsStruct::new(strct.clone(), fields, instruction_api))
}

fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> {
// INIT
let init_field = fields.iter().find(|f| {
if let AccountField::Field(field) = f {
field.constraints.init.is_some()
} else {
false
}
});
if let Some(init_field) = init_field {
// init needs system program.
if fields.iter().all(|f| f.ident() != "system_program") {
return Err(ParseError::new(
init_field.ident().span(),
"the init constraint requires \
the system_program field to exist in the account \
validation struct. Use the program type to add \
the system_program field to your validation struct.",
));
}
if let AccountField::Field(field) = init_field {
let kind = &field.constraints.init.as_ref().unwrap().kind;
// init token/a_token/mint needs token program.
match kind {
InitKind::Program { .. } => (),
InitKind::Token { .. }
| InitKind::AssociatedToken { .. }
| InitKind::Mint { .. } => {
if fields.iter().all(|f| f.ident() != "token_program") {
return Err(ParseError::new(
init_field.ident().span(),
"the init constraint requires \
the token_program field to exist in the account \
validation struct. Use the program type to add \
the token_program field to your validation struct.",
));
}
}
}
// a_token needs associated token program.
if let InitKind::AssociatedToken { .. } = kind {
if fields
.iter()
.all(|f| f.ident() != "associated_token_program")
{
return Err(ParseError::new(
init_field.ident().span(),
"the init constraint requires \
the associated_token_program field to exist in the account \
validation struct. Use the program type to add \
the associated_token_program field to your validation struct.",
));
}
}
}
}
Ok(())
}

pub fn parse_account_field(f: &syn::Field, has_instruction_api: bool) -> ParseResult<AccountField> {
let ident = f.ident.clone().unwrap();
let account_field = match is_field_primitive(f)? {
Expand Down

0 comments on commit 01ff03b

Please sign in to comment.