file.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. use crate::idl::*;
  2. use crate::parser::context::CrateContext;
  3. use crate::parser::{self, accounts, docs, error, program};
  4. use crate::Ty;
  5. use crate::{AccountField, AccountsStruct, StateIx};
  6. use anyhow::Result;
  7. use heck::MixedCase;
  8. use quote::ToTokens;
  9. use std::collections::{HashMap, HashSet};
  10. use std::path::Path;
  11. const DERIVE_NAME: &str = "Accounts";
  12. // TODO: share this with `anchor_lang` crate.
  13. const ERROR_CODE_OFFSET: u32 = 6000;
  14. // Parse an entire interface file.
  15. pub fn parse(
  16. filename: impl AsRef<Path>,
  17. version: String,
  18. seeds_feature: bool,
  19. no_docs: bool,
  20. safety_checks: bool,
  21. ) -> Result<Option<Idl>> {
  22. let ctx = CrateContext::parse(filename)?;
  23. if safety_checks {
  24. ctx.safety_checks()?;
  25. }
  26. let program_mod = match parse_program_mod(&ctx) {
  27. None => return Ok(None),
  28. Some(m) => m,
  29. };
  30. let mut p = program::parse(program_mod)?;
  31. if no_docs {
  32. p.docs = None;
  33. for ix in &mut p.ixs {
  34. ix.docs = None;
  35. }
  36. }
  37. let accs = parse_account_derives(&ctx);
  38. let state = match p.state {
  39. None => None,
  40. Some(state) => match state.ctor_and_anchor {
  41. None => None, // State struct defined but no implementation
  42. Some((ctor, anchor_ident)) => {
  43. let mut methods = state
  44. .impl_block_and_methods
  45. .map(|(_impl_block, methods)| {
  46. methods
  47. .iter()
  48. .map(|method: &StateIx| {
  49. let name = method.ident.to_string().to_mixed_case();
  50. let args = method
  51. .args
  52. .iter()
  53. .map(|arg| {
  54. let mut tts = proc_macro2::TokenStream::new();
  55. arg.raw_arg.ty.to_tokens(&mut tts);
  56. let doc = if !no_docs {
  57. docs::parse(&arg.raw_arg.attrs)
  58. } else {
  59. None
  60. };
  61. let ty = tts.to_string().parse().unwrap();
  62. IdlField {
  63. name: arg.name.to_string().to_mixed_case(),
  64. docs: doc,
  65. ty,
  66. }
  67. })
  68. .collect::<Vec<_>>();
  69. let accounts_strct =
  70. accs.get(&method.anchor_ident.to_string()).unwrap();
  71. let accounts = idl_accounts(
  72. &ctx,
  73. accounts_strct,
  74. &accs,
  75. seeds_feature,
  76. no_docs,
  77. );
  78. IdlInstruction {
  79. name,
  80. docs: None,
  81. accounts,
  82. args,
  83. returns: None,
  84. }
  85. })
  86. .collect::<Vec<_>>()
  87. })
  88. .unwrap_or_default();
  89. let ctor = {
  90. let name = "new".to_string();
  91. let args = ctor
  92. .sig
  93. .inputs
  94. .iter()
  95. .filter(|arg| match arg {
  96. syn::FnArg::Typed(pat_ty) => {
  97. // TODO: this filtering should be done in the parser.
  98. let mut arg_str = parser::tts_to_string(&pat_ty.ty);
  99. arg_str.retain(|c| !c.is_whitespace());
  100. !arg_str.starts_with("Context<")
  101. }
  102. _ => false,
  103. })
  104. .map(|arg: &syn::FnArg| match arg {
  105. syn::FnArg::Typed(arg_typed) => {
  106. let mut tts = proc_macro2::TokenStream::new();
  107. arg_typed.ty.to_tokens(&mut tts);
  108. let doc = if !no_docs {
  109. docs::parse(&arg_typed.attrs)
  110. } else {
  111. None
  112. };
  113. let ty = tts.to_string().parse().unwrap();
  114. IdlField {
  115. name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(),
  116. docs: doc,
  117. ty,
  118. }
  119. }
  120. _ => panic!("Invalid syntax"),
  121. })
  122. .collect();
  123. let accounts_strct = accs.get(&anchor_ident.to_string()).unwrap();
  124. let accounts =
  125. idl_accounts(&ctx, accounts_strct, &accs, seeds_feature, no_docs);
  126. IdlInstruction {
  127. name,
  128. docs: None,
  129. accounts,
  130. args,
  131. returns: None,
  132. }
  133. };
  134. methods.insert(0, ctor);
  135. let strct = {
  136. let fields = match state.strct.fields {
  137. syn::Fields::Named(f_named) => f_named
  138. .named
  139. .iter()
  140. .map(|f: &syn::Field| {
  141. let mut tts = proc_macro2::TokenStream::new();
  142. f.ty.to_tokens(&mut tts);
  143. let doc = if !no_docs {
  144. docs::parse(&f.attrs)
  145. } else {
  146. None
  147. };
  148. let ty = tts.to_string().parse().unwrap();
  149. IdlField {
  150. name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
  151. docs: doc,
  152. ty,
  153. }
  154. })
  155. .collect::<Vec<IdlField>>(),
  156. _ => panic!("State must be a struct"),
  157. };
  158. IdlTypeDefinition {
  159. name: state.name,
  160. docs: None,
  161. ty: IdlTypeDefinitionTy::Struct { fields },
  162. }
  163. };
  164. Some(IdlState { strct, methods })
  165. }
  166. },
  167. };
  168. let error = parse_error_enum(&ctx).map(|mut e| error::parse(&mut e, None));
  169. let error_codes = error.as_ref().map(|e| {
  170. e.codes
  171. .iter()
  172. .map(|code| IdlErrorCode {
  173. code: ERROR_CODE_OFFSET + code.id,
  174. name: code.ident.to_string(),
  175. msg: code.msg.clone(),
  176. })
  177. .collect::<Vec<IdlErrorCode>>()
  178. });
  179. let instructions = p
  180. .ixs
  181. .iter()
  182. .map(|ix| {
  183. let args = ix
  184. .args
  185. .iter()
  186. .map(|arg| {
  187. let doc = if !no_docs {
  188. docs::parse(&arg.raw_arg.attrs)
  189. } else {
  190. None
  191. };
  192. IdlField {
  193. name: arg.name.to_string().to_mixed_case(),
  194. docs: doc,
  195. ty: to_idl_type(&ctx, &arg.raw_arg.ty),
  196. }
  197. })
  198. .collect::<Vec<_>>();
  199. // todo: don't unwrap
  200. let accounts_strct = accs.get(&ix.anchor_ident.to_string()).unwrap();
  201. let accounts = idl_accounts(&ctx, accounts_strct, &accs, seeds_feature, no_docs);
  202. let ret_type_str = ix.returns.ty.to_token_stream().to_string();
  203. let returns = match ret_type_str.as_str() {
  204. "()" => None,
  205. _ => Some(ret_type_str.parse().unwrap()),
  206. };
  207. IdlInstruction {
  208. name: ix.ident.to_string().to_mixed_case(),
  209. docs: ix.docs.clone(),
  210. accounts,
  211. args,
  212. returns,
  213. }
  214. })
  215. .collect::<Vec<_>>();
  216. let events = parse_events(&ctx)
  217. .iter()
  218. .map(|e: &&syn::ItemStruct| {
  219. let fields = match &e.fields {
  220. syn::Fields::Named(n) => n,
  221. _ => panic!("Event fields must be named"),
  222. };
  223. let fields = fields
  224. .named
  225. .iter()
  226. .map(|f: &syn::Field| {
  227. let index = match f.attrs.get(0) {
  228. None => false,
  229. Some(i) => parser::tts_to_string(&i.path) == "index",
  230. };
  231. IdlEventField {
  232. name: f.ident.clone().unwrap().to_string().to_mixed_case(),
  233. ty: to_idl_type(&ctx, &f.ty),
  234. index,
  235. }
  236. })
  237. .collect::<Vec<IdlEventField>>();
  238. IdlEvent {
  239. name: e.ident.to_string(),
  240. fields,
  241. }
  242. })
  243. .collect::<Vec<IdlEvent>>();
  244. // All user defined types.
  245. let mut accounts = vec![];
  246. let mut types = vec![];
  247. let ty_defs = parse_ty_defs(&ctx, no_docs)?;
  248. let account_structs = parse_accounts(&ctx);
  249. let account_names: HashSet<String> = account_structs
  250. .iter()
  251. .map(|a| a.ident.to_string())
  252. .collect::<HashSet<_>>();
  253. let error_name = error.map(|e| e.name).unwrap_or_else(|| "".to_string());
  254. // All types that aren't in the accounts section, are in the types section.
  255. for ty_def in ty_defs {
  256. // Don't add the error type to the types or accounts sections.
  257. if ty_def.name != error_name {
  258. if account_names.contains(&ty_def.name) {
  259. accounts.push(ty_def);
  260. } else if !events.iter().any(|e| e.name == ty_def.name) {
  261. types.push(ty_def);
  262. }
  263. }
  264. }
  265. let constants = parse_consts(&ctx)
  266. .iter()
  267. .map(|c: &&syn::ItemConst| IdlConst {
  268. name: c.ident.to_string(),
  269. ty: c.ty.to_token_stream().to_string().parse().unwrap(),
  270. value: c.expr.to_token_stream().to_string().parse().unwrap(),
  271. })
  272. .collect::<Vec<IdlConst>>();
  273. Ok(Some(Idl {
  274. version,
  275. name: p.name.to_string(),
  276. docs: p.docs.clone(),
  277. state,
  278. instructions,
  279. types,
  280. accounts,
  281. events: if events.is_empty() {
  282. None
  283. } else {
  284. Some(events)
  285. },
  286. errors: error_codes,
  287. metadata: None,
  288. constants,
  289. }))
  290. }
  291. // Parse the main program mod.
  292. fn parse_program_mod(ctx: &CrateContext) -> Option<syn::ItemMod> {
  293. let root = ctx.root_module();
  294. let mods = root
  295. .items()
  296. .filter_map(|i| match i {
  297. syn::Item::Mod(item_mod) => {
  298. let mod_count = item_mod
  299. .attrs
  300. .iter()
  301. .filter(|attr| attr.path.segments.last().unwrap().ident == "program")
  302. .count();
  303. if mod_count != 1 {
  304. return None;
  305. }
  306. Some(item_mod)
  307. }
  308. _ => None,
  309. })
  310. .collect::<Vec<_>>();
  311. if mods.len() != 1 {
  312. return None;
  313. }
  314. Some(mods[0].clone())
  315. }
  316. fn parse_error_enum(ctx: &CrateContext) -> Option<syn::ItemEnum> {
  317. ctx.enums()
  318. .filter_map(|item_enum| {
  319. let attrs_count = item_enum
  320. .attrs
  321. .iter()
  322. .filter(|attr| {
  323. let segment = attr.path.segments.last().unwrap();
  324. segment.ident == "error_code"
  325. })
  326. .count();
  327. match attrs_count {
  328. 0 => None,
  329. 1 => Some(item_enum),
  330. _ => panic!("Invalid syntax: one error attribute allowed"),
  331. }
  332. })
  333. .next()
  334. .cloned()
  335. }
  336. fn parse_events(ctx: &CrateContext) -> Vec<&syn::ItemStruct> {
  337. ctx.structs()
  338. .filter_map(|item_strct| {
  339. let attrs_count = item_strct
  340. .attrs
  341. .iter()
  342. .filter(|attr| {
  343. let segment = attr.path.segments.last().unwrap();
  344. segment.ident == "event"
  345. })
  346. .count();
  347. match attrs_count {
  348. 0 => None,
  349. 1 => Some(item_strct),
  350. _ => panic!("Invalid syntax: one event attribute allowed"),
  351. }
  352. })
  353. .collect()
  354. }
  355. fn parse_accounts(ctx: &CrateContext) -> Vec<&syn::ItemStruct> {
  356. ctx.structs()
  357. .filter_map(|item_strct| {
  358. let attrs_count = item_strct
  359. .attrs
  360. .iter()
  361. .filter(|attr| {
  362. let segment = attr.path.segments.last().unwrap();
  363. segment.ident == "account" || segment.ident == "associated"
  364. })
  365. .count();
  366. match attrs_count {
  367. 0 => None,
  368. 1 => Some(item_strct),
  369. _ => panic!("Invalid syntax: one event attribute allowed"),
  370. }
  371. })
  372. .collect()
  373. }
  374. // Parse all structs implementing the `Accounts` trait.
  375. fn parse_account_derives(ctx: &CrateContext) -> HashMap<String, AccountsStruct> {
  376. // TODO: parse manual implementations. Currently we only look
  377. // for derives.
  378. ctx.structs()
  379. .filter_map(|i_strct| {
  380. for attr in &i_strct.attrs {
  381. if attr.path.is_ident("derive") && attr.tokens.to_string().contains(DERIVE_NAME) {
  382. let strct = accounts::parse(i_strct).expect("Code not parseable");
  383. return Some((strct.ident.to_string(), strct));
  384. }
  385. }
  386. None
  387. })
  388. .collect()
  389. }
  390. fn parse_consts(ctx: &CrateContext) -> Vec<&syn::ItemConst> {
  391. ctx.consts()
  392. .filter(|item_strct| {
  393. for attr in &item_strct.attrs {
  394. if attr.path.segments.last().unwrap().ident == "constant" {
  395. return true;
  396. }
  397. }
  398. false
  399. })
  400. .collect()
  401. }
  402. // Parse all user defined types in the file.
  403. fn parse_ty_defs(ctx: &CrateContext, no_docs: bool) -> Result<Vec<IdlTypeDefinition>> {
  404. ctx.structs()
  405. .filter_map(|item_strct| {
  406. // Only take serializable types
  407. let serializable = item_strct.attrs.iter().any(|attr| {
  408. let attr_string = attr.tokens.to_string();
  409. let attr_name = attr.path.segments.last().unwrap().ident.to_string();
  410. let attr_serializable = ["account", "associated", "event", "zero_copy"];
  411. let derived_serializable = attr_name == "derive"
  412. && attr_string.contains("AnchorSerialize")
  413. && attr_string.contains("AnchorDeserialize");
  414. attr_serializable.iter().any(|a| *a == attr_name) || derived_serializable
  415. });
  416. if !serializable {
  417. return None;
  418. }
  419. // Only take public types
  420. match &item_strct.vis {
  421. syn::Visibility::Public(_) => (),
  422. _ => return None,
  423. }
  424. let name = item_strct.ident.to_string();
  425. let doc = if !no_docs {
  426. docs::parse(&item_strct.attrs)
  427. } else {
  428. None
  429. };
  430. let fields = match &item_strct.fields {
  431. syn::Fields::Named(fields) => fields
  432. .named
  433. .iter()
  434. .map(|f: &syn::Field| {
  435. let doc = if !no_docs {
  436. docs::parse(&f.attrs)
  437. } else {
  438. None
  439. };
  440. Ok(IdlField {
  441. name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
  442. docs: doc,
  443. ty: to_idl_type(ctx, &f.ty),
  444. })
  445. })
  446. .collect::<Result<Vec<IdlField>>>(),
  447. syn::Fields::Unnamed(_) => return None,
  448. _ => panic!("Empty structs are allowed."),
  449. };
  450. Some(fields.map(|fields| IdlTypeDefinition {
  451. name,
  452. docs: doc,
  453. ty: IdlTypeDefinitionTy::Struct { fields },
  454. }))
  455. })
  456. .chain(ctx.enums().map(|enm| {
  457. let name = enm.ident.to_string();
  458. let doc = if !no_docs {
  459. docs::parse(&enm.attrs)
  460. } else {
  461. None
  462. };
  463. let variants = enm
  464. .variants
  465. .iter()
  466. .map(|variant: &syn::Variant| {
  467. let name = variant.ident.to_string();
  468. let fields = match &variant.fields {
  469. syn::Fields::Unit => None,
  470. syn::Fields::Unnamed(fields) => {
  471. let fields: Vec<IdlType> = fields
  472. .unnamed
  473. .iter()
  474. .map(|f| to_idl_type(ctx, &f.ty))
  475. .collect();
  476. Some(EnumFields::Tuple(fields))
  477. }
  478. syn::Fields::Named(fields) => {
  479. let fields: Vec<IdlField> = fields
  480. .named
  481. .iter()
  482. .map(|f: &syn::Field| {
  483. let name = f.ident.as_ref().unwrap().to_string();
  484. let doc = if !no_docs {
  485. docs::parse(&f.attrs)
  486. } else {
  487. None
  488. };
  489. let ty = to_idl_type(ctx, &f.ty);
  490. IdlField {
  491. name,
  492. docs: doc,
  493. ty,
  494. }
  495. })
  496. .collect();
  497. Some(EnumFields::Named(fields))
  498. }
  499. };
  500. IdlEnumVariant { name, fields }
  501. })
  502. .collect::<Vec<IdlEnumVariant>>();
  503. Ok(IdlTypeDefinition {
  504. name,
  505. docs: doc,
  506. ty: IdlTypeDefinitionTy::Enum { variants },
  507. })
  508. }))
  509. .collect()
  510. }
  511. // Replace variable array lengths with values
  512. fn resolve_variable_array_lengths(ctx: &CrateContext, mut tts_string: String) -> String {
  513. for constant in ctx.consts().filter(|c| match *c.ty {
  514. // Filter to only those consts that are of type usize or could be cast to usize
  515. syn::Type::Path(ref p) => {
  516. let segment = p.path.segments.last().unwrap();
  517. matches!(
  518. segment.ident.to_string().as_str(),
  519. "usize"
  520. | "u8"
  521. | "u16"
  522. | "u32"
  523. | "u64"
  524. | "u128"
  525. | "isize"
  526. | "i8"
  527. | "i16"
  528. | "i32"
  529. | "i64"
  530. | "i128"
  531. )
  532. }
  533. _ => false,
  534. }) {
  535. let mut check_string = tts_string.clone();
  536. // Strip whitespace to handle accidental double whitespaces
  537. check_string.retain(|c| !c.is_whitespace());
  538. let size_string = format!("{}]", &constant.ident.to_string());
  539. let cast_size_string = format!("{}asusize]", &constant.ident.to_string());
  540. // Check for something to replace
  541. let mut replacement_string = None;
  542. if check_string.contains(cast_size_string.as_str()) {
  543. replacement_string = Some(cast_size_string);
  544. } else if check_string.contains(size_string.as_str()) {
  545. replacement_string = Some(size_string);
  546. }
  547. if let Some(replacement_string) = replacement_string {
  548. // Check for the existence of consts existing elsewhere in the
  549. // crate which have the same name, are usize, and have a
  550. // different value. We can't know which was intended for the
  551. // array size from ctx.
  552. if ctx.consts().any(|c| {
  553. c != constant
  554. && c.ident == constant.ident
  555. && c.ty == constant.ty
  556. && c.expr != constant.expr
  557. }) {
  558. panic!("Crate wide unique name required for array size const.");
  559. }
  560. // Replace the match, don't break because there might be multiple replacements to be
  561. // made in the case of multidimensional arrays
  562. tts_string = check_string.replace(
  563. &replacement_string,
  564. format!("{}]", &constant.expr.to_token_stream()).as_str(),
  565. );
  566. }
  567. }
  568. tts_string
  569. }
  570. fn to_idl_type(ctx: &CrateContext, ty: &syn::Type) -> IdlType {
  571. let mut tts_string = parser::tts_to_string(&ty);
  572. if tts_string.starts_with('[') {
  573. tts_string = resolve_variable_array_lengths(ctx, tts_string);
  574. }
  575. // Box<FooType> -> FooType
  576. tts_string = tts_string
  577. .strip_prefix("Box < ")
  578. .and_then(|t| t.strip_suffix(" >"))
  579. .unwrap_or(&tts_string)
  580. .into();
  581. tts_string.parse().unwrap()
  582. }
  583. fn idl_accounts(
  584. ctx: &CrateContext,
  585. accounts: &AccountsStruct,
  586. global_accs: &HashMap<String, AccountsStruct>,
  587. seeds_feature: bool,
  588. no_docs: bool,
  589. ) -> Vec<IdlAccountItem> {
  590. accounts
  591. .fields
  592. .iter()
  593. .map(|acc: &AccountField| match acc {
  594. AccountField::CompositeField(comp_f) => {
  595. let accs_strct = global_accs.get(&comp_f.symbol).unwrap_or_else(|| {
  596. panic!("Could not resolve Accounts symbol {}", comp_f.symbol)
  597. });
  598. let accounts = idl_accounts(ctx, accs_strct, global_accs, seeds_feature, no_docs);
  599. IdlAccountItem::IdlAccounts(IdlAccounts {
  600. name: comp_f.ident.to_string().to_mixed_case(),
  601. accounts,
  602. })
  603. }
  604. AccountField::Field(acc) => IdlAccountItem::IdlAccount(IdlAccount {
  605. name: acc.ident.to_string().to_mixed_case(),
  606. is_mut: acc.constraints.is_mutable(),
  607. is_signer: match acc.ty {
  608. Ty::Signer => true,
  609. _ => acc.constraints.is_signer(),
  610. },
  611. docs: if !no_docs { acc.docs.clone() } else { None },
  612. pda: pda::parse(ctx, accounts, acc, seeds_feature),
  613. relations: relations::parse(acc, seeds_feature),
  614. }),
  615. })
  616. .collect::<Vec<_>>()
  617. }