Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improved error reporting for near macro #1301

Merged
merged 2 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
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 @@
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"
akorchyn marked this conversation as resolved.
Show resolved Hide resolved
})
})
.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::AcqRel)
}

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 @@
} 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(),
"#[near] or #[near_bindgen] attributes are not allowed to be nested inside of the outmost #[near] attribute. Only a single #[near] attribute is allowed",
)
.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() {}
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: #[near] or #[near_bindgen] attributes are not allowed to be nested inside of the outmost #[near] attribute. Only a single #[near] attribute is allowed
--> 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)
Loading