Skip to content

Commit

Permalink
feat: improved error reporting for near macro
Browse files Browse the repository at this point in the history
  • Loading branch information
akorchyn committed Feb 6, 2025
1 parent 5ddafdc commit ea10576
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 0 deletions.
47 changes: 47 additions & 0 deletions near-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ struct NearMacroArgs {
inside_nearsdk: Option<bool>,
}

fn has_nested_near_macros(item: TokenStream) -> bool {
syn::parse::<syn::Item>(item)
.ok()
.and_then(|item_ast| {
let attrs = match item_ast {
syn::Item::Struct(s) => s.attrs,
syn::Item::Enum(e) => e.attrs,
syn::Item::Impl(i) => i.attrs,
_ => vec![], // Other cases don't support near macros anyway

Check warning on line 54 in near-sdk-macros/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

near-sdk-macros/src/lib.rs#L53-L54

Added lines #L53 - L54 were not covered by tests
};

attrs.into_iter().find(|attr| {
let path_str = attr.path().to_token_stream().to_string();
path_str == "near" || path_str == "near_bindgen"
})
})
.is_some()
}

fn check_duplicate_contract_state() -> bool {

Check warning on line 65 in near-sdk-macros/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

near-sdk-macros/src/lib.rs#L65

Added line #L65 was not covered by tests
static CONTRACT_STATE_DEFINED: ::std::sync::atomic::AtomicBool =
::std::sync::atomic::AtomicBool::new(false);

CONTRACT_STATE_DEFINED.swap(true, ::std::sync::atomic::Ordering::SeqCst)
}

Check warning on line 70 in near-sdk-macros/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

near-sdk-macros/src/lib.rs#L69-L70

Added lines #L69 - L70 were not covered by tests

#[proc_macro_attribute]
pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
if attr.to_string().contains("event_json") {
Expand All @@ -68,12 +94,33 @@ pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
} else {
quote! {::near_sdk}
};

// Check for nested near macros by parsing the input and examining actual attributes
if has_nested_near_macros(item.clone()) {
return TokenStream::from(
syn::Error::new(
Span::call_site(),
"The `near` macro cannot be nested with other `near` or `near_bindgen` macros. Please comma-separate the attributes instead of nesting them",
)
.to_compile_error(),
);

Check warning on line 106 in near-sdk-macros/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

near-sdk-macros/src/lib.rs#L100-L106

Added lines #L100 - L106 were not covered by tests
}
let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
let string_serde_crate = quote! {#near_sdk_crate::serde}.to_string();

let mut expanded: proc_macro2::TokenStream = quote! {};

if near_macro_args.contract_state.unwrap_or(false) {
if check_duplicate_contract_state() {
return TokenStream::from(
syn::Error::new(
Span::call_site(),
"Contract state can only be defined once per crate",
)
.to_compile_error(),
);
}

Check warning on line 122 in near-sdk-macros/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

near-sdk-macros/src/lib.rs#L114-L122

Added lines #L114 - L122 were not covered by tests

if let Some(metadata) = near_macro_args.contract_metadata {
expanded = quote! {#[#near_sdk_crate::near_bindgen(#metadata)]}
} else {
Expand Down
2 changes: 2 additions & 0 deletions near-sdk/compilation_tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ fn compilation_tests() {
t.compile_fail("compilation_tests/contract_metadata_fn_name.rs");
t.pass("compilation_tests/contract_metadata_bindgen.rs");
t.pass("compilation_tests/types.rs");
t.compile_fail("compilation_tests/nested_near_error.rs");
t.compile_fail("compilation_tests/double_contract_state_error.rs");
}
13 changes: 13 additions & 0 deletions near-sdk/compilation_tests/double_contract_state_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use near_sdk::near;

#[near(contract_state)]
pub struct Contract {}

pub mod mod1 {
use near_sdk::near;

#[near(contract_state)]
struct Contract {}
}

fn main() {}
7 changes: 7 additions & 0 deletions near-sdk/compilation_tests/double_contract_state_error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: Contract state can only be defined once per crate
--> compilation_tests/double_contract_state_error.rs:9:5
|
9 | #[near(contract_state)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `near` (in Nightly builds, run with -Z macro-backtrace for more info)
14 changes: 14 additions & 0 deletions near-sdk/compilation_tests/nested_near_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Method signature uses lifetime.
use near_sdk::near;

#[near(contract_state)]
#[near(contract_metadata(
version = "39f2d2646f2f60e18ab53337501370dc02a5661c",
link = "https://github.com/near-examples/nft-tutorial",
standard(standard = "nep171", version = "1.0.0"),
standard(standard = "nep177", version = "2.0.0"),
))]
struct CompileFailure {}

fn main() {}
7 changes: 7 additions & 0 deletions near-sdk/compilation_tests/nested_near_error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: The `near` macro cannot be nested with other `near` or `near_bindgen` macros. Please comma-separate the attributes instead of nesting them
--> compilation_tests/nested_near_error.rs:5:1
|
5 | #[near(contract_state)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `near` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit ea10576

Please sign in to comment.