Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The minor version will be incremented upon a breaking change and the patch versi

### Fixes

- lang: Make idl build time way faster by caching `CrateContext` ([#4325](https://github.com/solana-foundation/anchor/pull/4325)).
- lang: Add missing `Lazy` bound on generics ([#4240](https://github.com/solana-foundation/anchor/pull/4240)).
- lang: Fix wrong generated error code in declare_program! ([#4129](https://github.com/solana-foundation/anchor/pull/4129)).
- idl: Fix defined types with unsupported fields not producing an error ([#4088](https://github.com/solana-foundation/anchor/pull/4088)).
Expand Down
54 changes: 46 additions & 8 deletions lang/syn/src/idl/defined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,15 +494,59 @@ pub fn gen_idl_type(
use super::{common::find_path, external::get_external_type};
use crate::parser::context::CrateContext;
use quote::ToTokens;
use std::{
collections::{HashMap, HashSet},
sync::OnceLock,
};

struct CachedCrateData {
/// Names of all structs and enums defined in the crate
defined_names: HashSet<String>,
/// Type aliases stored as (name, source_text) for re-parsing
type_aliases: HashMap<String, String>,
}

static CRATE_DATA_CACHE: OnceLock<CachedCrateData> = OnceLock::new();

// If no path was found, just return an empty path and let the find_path function handle it
let source_path = proc_macro2::Span::call_site()
.local_file()
.unwrap_or_default();

if let Ok(Ok(ctx)) = find_path("lib.rs", &source_path).map(CrateContext::parse) {
if let Ok(lib_path) = find_path("lib.rs", &source_path) {
let name = path.path.segments.last().unwrap().ident.to_string();
let alias = ctx.type_aliases().find(|ty| ty.ident == name);

let cache =
CRATE_DATA_CACHE.get_or_init(|| match CrateContext::parse(&lib_path) {
Ok(ctx) => {
let defined_names: HashSet<String> = ctx
.structs()
.map(|s| s.ident.to_string())
.chain(ctx.enums().map(|e| e.ident.to_string()))
.collect();
let type_aliases: HashMap<String, String> = ctx
.type_aliases()
.map(|ty| {
(ty.ident.to_string(), ty.to_token_stream().to_string())
})
.collect();
CachedCrateData {
defined_names,
type_aliases,
}
}
Err(_) => CachedCrateData {
defined_names: HashSet::new(),
type_aliases: HashMap::new(),
},
});

let alias_src = cache.type_aliases.get(&name).cloned();
let is_external = !cache.defined_names.contains(&name);

let alias: Option<syn::ItemType> =
alias_src.and_then(|src| syn::parse_str(&src).ok());

if let Some(alias) = alias {
if let Some(segment) = path.path.segments.last() {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
Expand Down Expand Up @@ -563,12 +607,6 @@ pub fn gen_idl_type(
}

// Handle external types
let is_external = ctx
.structs()
.map(|s| s.ident.to_string())
.chain(ctx.enums().map(|e| e.ident.to_string()))
.find(|defined| defined == &name)
.is_none();
if is_external {
if let Ok(Some(ty)) = get_external_type(&name, source_path) {
return gen_idl_type(&ty, generic_params);
Expand Down
Loading