Skip to content

Commit

Permalink
fix: Fixed contract_source_metadata compilation issue when multiple…
Browse files Browse the repository at this point in the history
… impl blocks are there (#1118)
  • Loading branch information
ruseinov authored Dec 20, 2023
1 parent d5c9936 commit 5f47ad6
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 44 deletions.
17 changes: 17 additions & 0 deletions near-sdk-macros/src/core_impl/code_generator/metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use proc_macro2::Ident;
use quote::quote;
use syn::Generics;

/// Generates a view method to retrieve the source metadata.
pub(crate) fn generate_contract_metadata_method(
ident: &Ident,
generics: &Generics,
) -> proc_macro2::TokenStream {
quote! {
impl #generics #ident #generics {
pub fn contract_source_metadata() {
near_sdk::env::value_return(CONTRACT_SOURCE_METADATA.as_bytes())
}
}
}
}
1 change: 1 addition & 0 deletions near-sdk-macros/src/core_impl/code_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ mod item_impl_info;
pub use item_impl_info::*;

pub(crate) mod ext;
pub(crate) mod metadata;

pub(crate) mod serializer;
109 changes: 67 additions & 42 deletions near-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ extern crate proc_macro;

mod core_impl;

use core_impl::ext::generate_ext_structs;
use core_impl::{ext::generate_ext_structs, metadata::generate_contract_metadata_method};

use proc_macro::TokenStream;

use self::core_impl::*;
use proc_macro2::Span;
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use syn::{parse_quote, ImplItem, ItemEnum, ItemImpl, ItemStruct, ItemTrait, WhereClause};

Expand Down Expand Up @@ -114,8 +115,25 @@ pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
return core_impl::near_events(attr, item);
}

let generate_metadata = |ident: &Ident,
generics: &syn::Generics|
-> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
let metadata_impl_gen = generate_contract_metadata_method(ident, generics).into();
let metadata_impl_gen = syn::parse::<ItemImpl>(metadata_impl_gen)
.expect("failed to generate contract metadata");
process_impl_block(metadata_impl_gen)
};

if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
let metadata = core_impl::contract_source_metadata_const(attr);

let metadata_impl_gen = generate_metadata(&input.ident, &input.generics);

let metadata_impl_gen = match metadata_impl_gen {
Ok(metadata) => metadata,
Err(err) => return err.into(),
};

let ext_gen = generate_ext_structs(&input.ident, Some(&input.generics));
#[cfg(feature = "__abi-embed-checked")]
let abi_embedded = abi::embed();
Expand All @@ -126,9 +144,17 @@ pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
#ext_gen
#abi_embedded
#metadata
#metadata_impl_gen
})
} else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
let metadata = core_impl::contract_source_metadata_const(attr);
let metadata_impl_gen = generate_metadata(&input.ident, &input.generics);

let metadata_impl_gen = match metadata_impl_gen {
Ok(metadata) => metadata,
Err(err) => return err.into(),
};

let ext_gen = generate_ext_structs(&input.ident, Some(&input.generics));
#[cfg(feature = "__abi-embed-checked")]
let abi_embedded = abi::embed();
Expand All @@ -139,8 +165,9 @@ pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
#ext_gen
#abi_embedded
#metadata
#metadata_impl_gen
})
} else if let Ok(mut input) = syn::parse::<ItemImpl>(item) {
} else if let Ok(input) = syn::parse::<ItemImpl>(item) {
for method in &input.items {
if let ImplItem::Fn(m) = method {
let ident = &m.sig.ident;
Expand All @@ -155,46 +182,11 @@ pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
}
}
}

if input.trait_.is_none() {
let contract_source_metadata = quote! {
pub fn contract_source_metadata() {
near_sdk::env::value_return(CONTRACT_SOURCE_METADATA.as_bytes())
}
};

match syn::parse2::<ImplItem>(contract_source_metadata) {
Ok(x) => {
input.items.push(x);
}
Err(err) => {
return err.to_compile_error().into();
}
}
match process_impl_block(input) {
Ok(output) => output,
Err(output) => output,
}

let item_impl_info = match ItemImplInfo::new(&mut input) {
Ok(x) => x,
Err(err) => {
return err.to_compile_error().into();
}
};

#[cfg(not(feature = "__abi-generate"))]
let abi_generated = quote! {};
#[cfg(feature = "__abi-generate")]
let abi_generated = abi::generate(&item_impl_info);

let generated_code = item_impl_info.wrapper_code();

// Add wrapper methods for ext call API
let ext_generated_code = item_impl_info.generate_ext_wrapper_code();
TokenStream::from(quote! {
#ext_generated_code
#input
#generated_code
#abi_generated
})
.into()
} else {
TokenStream::from(
syn::Error::new(
Expand All @@ -206,6 +198,39 @@ pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
}
}

// This function deals with impl block processing, generating wrappers and ABI.
//
// # Arguments
// * input - impl block to process.
//
// The Result has a TokenStream error type, because those need to be propagated to the compiler.
fn process_impl_block(
mut input: ItemImpl,
) -> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
let item_impl_info = match ItemImplInfo::new(&mut input) {
Ok(x) => x,
Err(err) => return Err(err.to_compile_error()),
};

#[cfg(not(feature = "__abi-generate"))]
let abi_generated = quote! {};
#[cfg(feature = "__abi-generate")]
let abi_generated = abi::generate(&item_impl_info);

let generated_code = item_impl_info.wrapper_code();

// Add wrapper methods for ext call API
let ext_generated_code = item_impl_info.generate_ext_wrapper_code();

Ok(TokenStream::from(quote! {
#ext_generated_code
#input
#generated_code
#abi_generated
})
.into())
}

/// `ext_contract` takes a Rust Trait and converts it to a module with static methods.
/// Each of these static methods takes positional arguments defined by the Trait,
/// then the receiver_id, the attached deposit and the amount of gas and returns a new Promise.
Expand Down
1 change: 1 addition & 0 deletions near-sdk/compilation_tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ fn compilation_tests() {
t.pass("compilation_tests/handle_result_alias.rs");
t.pass("compilation_tests/contract_metadata.rs");
t.compile_fail("compilation_tests/contract_metadata_fn_name.rs");
t.pass("compilation_tests/contract_metadata_bindgen.rs");
}
20 changes: 20 additions & 0 deletions near-sdk/compilation_tests/contract_metadata_bindgen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use near_account_id::AccountIdRef;
use near_sdk::near_bindgen;

#[near_bindgen]
struct Contract {}

#[near_bindgen]
impl Contract {
pub fn anything() {}
}

#[near_bindgen]
impl Contract {
pub fn anything_else() {}
}

fn main() {
let ext = Contract::ext(AccountIdRef::new_or_panic("0000").into());
ext.contract_source_metadata();
}
1 change: 1 addition & 0 deletions near-sdk/compilation_tests/impl_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::near_bindgen;
#[allow(unused_imports)]
use std::marker::PhantomData;

#[near_bindgen]
Expand Down
10 changes: 8 additions & 2 deletions near-sdk/compilation_tests/impl_generic.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
error: Impl type parameters are not supported for smart contracts.
--> $DIR/impl_generic.rs:15:6
--> compilation_tests/impl_generic.rs:10:20
|
15 | impl<'a, T: 'a + std::fmt::Display> Incrementer<T> {
10 | struct Incrementer<T> {
| ^

error: Impl type parameters are not supported for smart contracts.
--> compilation_tests/impl_generic.rs:16:6
|
16 | impl<'a, T: 'a + std::fmt::Display> Incrementer<T> {
| ^^

0 comments on commit 5f47ad6

Please sign in to comment.