From a9d504463650969e13daee52f024c0422cceb362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Tue, 30 Jul 2024 16:42:22 +0200 Subject: [PATCH 1/3] feat: Emit error if contract macro is above entry_points --- sylvia-derive/src/input.rs | 44 ++++++++++++++++-- sylvia-derive/src/lib.rs | 14 +++--- sylvia-derive/src/message.rs | 4 +- sylvia-derive/src/parser/entry_point.rs | 12 +---- sylvia/src/into_response.rs | 2 - sylvia/tests/ui/macros/entry_points.rs | 19 ++++++++ sylvia/tests/ui/macros/entry_points.stderr | 52 ++++++++++++++++++++++ 7 files changed, 121 insertions(+), 26 deletions(-) diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 148ae7c5..582df26b 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use proc_macro_error::{emit_error, emit_warning}; use quote::quote; use syn::{GenericParam, Ident, ItemImpl, ItemTrait, TraitItem}; @@ -7,13 +7,13 @@ use crate::associated_types::{AssociatedTypes, ItemType, EXEC_TYPE, QUERY_TYPE}; use crate::executor::{ContractExecutor, InterfaceExecutor}; use crate::interfaces::Interfaces; use crate::message::{ - ContractApi, ContractEnumMessage, EnumMessage, GlueMessage, InterfaceApi, MsgVariants, - StructMessage, + ContractApi, ContractEnumMessage, EntryPoints, EnumMessage, GlueMessage, InterfaceApi, + MsgVariants, StructMessage, }; use crate::multitest::{ContractMtHelpers, TraitMtHelpers}; use crate::parser::attributes::msg::MsgType; use crate::parser::{ - assert_new_method_defined, ContractErrorAttr, Custom, OverrideEntryPoint, + assert_new_method_defined, ContractErrorAttr, Custom, EntryPointArgs, OverrideEntryPoint, ParsedSylviaAttributes, }; use crate::querier::{ContractQuerier, InterfaceQuerier}; @@ -339,3 +339,39 @@ impl<'a> ImplInput<'a> { ContractMtHelpers::new(item, generic_params, custom, override_entry_points.clone()).emit() } } + +pub struct EntryPointInput<'a> { + item: &'a ItemImpl, + args: EntryPointArgs, +} + +impl<'a> EntryPointInput<'a> { + pub fn new(item: &'a ItemImpl, args: EntryPointArgs, attr_span: Span) -> Self { + let instantiate = + MsgVariants::::new(item.as_variants(), MsgType::Instantiate, &[], &None); + + if args.generics.len() != item.generics.params.len() { + emit_error!( + attr_span, + "Missing concrete types."; + note = "For every generic type in the contract, a concrete type must be provided in `#[entry_points(generics)]`."; + ); + } + + if instantiate.get_only_variant().is_none() { + emit_error!( + attr_span, "Missing instantiation message."; + note = "`sylvia::entry_points` requires exactly one method marked with `#[sv::msg(instantiation)]` attribute."; + note = "Make sure you implemented the `entry_points` macro above the `contract` macro." + ); + } + + Self { item, args } + } + + pub fn process(&self) -> TokenStream { + let Self { item, args } = self; + + EntryPoints::new(item, args).emit() + } +} diff --git a/sylvia-derive/src/lib.rs b/sylvia-derive/src/lib.rs index b51149fc..2fe5b9fc 100644 --- a/sylvia-derive/src/lib.rs +++ b/sylvia-derive/src/lib.rs @@ -2,12 +2,15 @@ //! //! Please refer to the [Sylvia-book](https://cosmwasm.github.io/sylvia-book/index.html) on how to use these macros. -use input::{ImplInput, TraitInput}; +use crate::parser::EntryPointArgs; +use input::{EntryPointInput, ImplInput, TraitInput}; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use proc_macro_error::proc_macro_error; use quote::quote; +use strip_input::StripInput; use syn::fold::Fold; +use syn::spanned::Spanned; use syn::{parse2, parse_quote, ItemImpl, ItemTrait, Path}; mod associated_types; @@ -26,11 +29,6 @@ mod strip_self_path; mod utils; mod variant_descs; -use strip_input::StripInput; - -use crate::message::EntryPoints; -use crate::parser::EntryPointArgs; - #[cfg(not(test))] pub(crate) fn crate_module() -> Path { use proc_macro_crate::{crate_name, FoundCrate}; @@ -759,8 +757,8 @@ pub fn entry_points(attr: TokenStream, item: TokenStream) -> TokenStream { fn entry_points_impl(attr: TokenStream2, item: TokenStream2) -> TokenStream2 { fn inner(attr: TokenStream2, item: TokenStream2) -> syn::Result { let input: ItemImpl = parse2(item)?; - let attrs = EntryPointArgs::new(&attr, &input)?; - let expanded = EntryPoints::new(&input, attrs).emit(); + let args = EntryPointArgs::new(&attr)?; + let expanded = EntryPointInput::new(&input, args, attr.span()).process(); Ok(quote! { #input diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index a3a69aea..d44ac63a 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -1264,11 +1264,11 @@ pub struct EntryPoints<'a> { override_entry_points: Vec, generics: Vec<&'a GenericParam>, where_clause: &'a Option, - attrs: EntryPointArgs, + attrs: &'a EntryPointArgs, } impl<'a> EntryPoints<'a> { - pub fn new(source: &'a ItemImpl, attrs: EntryPointArgs) -> Self { + pub fn new(source: &'a ItemImpl, attrs: &'a EntryPointArgs) -> Self { let name = StripGenerics.fold_type(*source.self_ty.clone()); let parsed_attrs = ParsedSylviaAttributes::new(source.attrs.iter()); let override_entry_points = parsed_attrs.override_entry_point_attrs; diff --git a/sylvia-derive/src/parser/entry_point.rs b/sylvia-derive/src/parser/entry_point.rs index 87bceff3..584eb926 100644 --- a/sylvia-derive/src/parser/entry_point.rs +++ b/sylvia-derive/src/parser/entry_point.rs @@ -3,7 +3,7 @@ use proc_macro_error::emit_error; use syn::parse::{Error, Nothing, Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{parse2, GenericArgument, ItemImpl, Path, Result, Token}; +use syn::{parse2, GenericArgument, Path, Result, Token}; use super::extract_generics_from_path; @@ -15,20 +15,12 @@ pub struct EntryPointArgs { } impl EntryPointArgs { - pub fn new(attr: &TokenStream2, source: &ItemImpl) -> Result { + pub fn new(attr: &TokenStream2) -> Result { let args: Self = parse2(attr.clone()).map_err(|err| { emit_error!(attr, err); err })?; - if args.generics.len() != source.generics.params.len() { - emit_error!( - attr.span(), - "Missing concrete types."; - note = "For every generic type in the contract, a concrete type must be provided in `#[entry_points(generics)]`."; - ); - } - Ok(args) } } diff --git a/sylvia/src/into_response.rs b/sylvia/src/into_response.rs index 8311ce4b..a9f84cb9 100644 --- a/sylvia/src/into_response.rs +++ b/sylvia/src/into_response.rs @@ -11,9 +11,7 @@ impl IntoMsg for SubMsg { let msg = match self.msg { CosmosMsg::Wasm(wasm) => CosmosMsg::Wasm(wasm), CosmosMsg::Bank(bank) => CosmosMsg::Bank(bank), - #[cfg(feature = "staking")] CosmosMsg::Staking(staking) => CosmosMsg::Staking(staking), - #[cfg(feature = "staking")] CosmosMsg::Distribution(distribution) => CosmosMsg::Distribution(distribution), CosmosMsg::Custom(_) => Err(StdError::generic_err( "Custom Empty message should not be sent", diff --git a/sylvia/tests/ui/macros/entry_points.rs b/sylvia/tests/ui/macros/entry_points.rs index 36ceabb6..84a1e9a3 100644 --- a/sylvia/tests/ui/macros/entry_points.rs +++ b/sylvia/tests/ui/macros/entry_points.rs @@ -60,4 +60,23 @@ pub mod missing_generics { } } +pub mod wrong_order { + use super::*; + + pub struct Contract; + + #[contract] + #[entry_points] + impl Contract { + pub fn new() -> Self { + Self + } + + #[sv::msg(instantiate)] + fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + Ok(Response::new()) + } + } +} + fn main() {} diff --git a/sylvia/tests/ui/macros/entry_points.stderr b/sylvia/tests/ui/macros/entry_points.stderr index 8bf7e60f..7548a3d8 100644 --- a/sylvia/tests/ui/macros/entry_points.stderr +++ b/sylvia/tests/ui/macros/entry_points.stderr @@ -17,3 +17,55 @@ error: Missing concrete types. | 42 | #[entry_points(generics)] | ^^^^^^^^ + +error: Missing instantiation message. + + = note: `sylvia::entry_points` requires exactly one method marked with `#[sv::msg(instantiation)]` attribute. + = note: Make sure you implemented the `entry_points` macro above the `contract` macro. + + --> tests/ui/macros/entry_points.rs:69:5 + | +69 | #[entry_points] + | ^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `entry_points` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: no method named `instantiate` found for reference `&wrong_order::Contract` in the current scope + --> tests/ui/macros/entry_points.rs:76:12 + | +76 | fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + | ^^^^^^^^^^^ method not found in `&Contract` + | + = help: items from traits can only be used if the trait is in scope +help: trait `Contract` which provides `instantiate` is implemented but not in scope; perhaps you want to import it + | +68 + use sylvia::cw_multi_test::Contract; + | + +error[E0599]: no function or associated item named `new` found for struct `wrong_order::Contract` in the current scope + --> tests/ui/macros/entry_points.rs:68:5 + | +66 | pub struct Contract; + | ------------------- function or associated item `new` not found for this struct +67 | +68 | #[contract] + | ^^^^^^^^^^^ function or associated item not found in `Contract` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `new`, perhaps you need to implement one of them: + candidate #1: `ark_ec::hashing::HashToCurve` + candidate #2: `ark_ec::hashing::map_to_curve_hasher::MapToCurve` + candidate #3: `ark_ff::fields::field_hashers::HashToField` + candidate #4: `ark_poly::domain::EvaluationDomain` + candidate #5: `ark_poly::polynomial::multivariate::Term` + candidate #6: `crypto_common::KeyInit` + candidate #7: `crypto_common::KeyIvInit` + candidate #8: `curve25519_dalek::traits::VartimePrecomputedMultiscalarMul` + candidate #9: `digest::VariableOutput` + candidate #10: `digest::core_api::VariableOutputCore` + candidate #11: `digest::digest::Digest` + candidate #12: `digest::mac::Mac` + candidate #13: `itertools::adaptors::coalesce::CountItem` + candidate #14: `rand::distributions::uniform::UniformSampler` + candidate #15: `typenum::marker_traits::Bit` + = note: this error originates in the attribute macro `contract` (in Nightly builds, run with -Z macro-backtrace for more info) From 9bacb4769dfd7a32fc7a1e45607bab2c0c20a684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 31 Jul 2024 15:51:29 +0200 Subject: [PATCH 2/3] test: Mark ui tests as ignored and run them with latest stable on CI --- .github/workflows/ci.yml | 39 ++++++++++++++++++++++++++++ Cargo.lock | 56 ++++++++++++++++++++++++---------------- sylvia/tests/ui.rs | 1 + 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91c9c189..2813198e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,6 +101,45 @@ jobs: path: | examples/contracts/**/schema/*.json + # Errors differ between Rust versions. We test only the latest output. + test_ui: + name: ${{ matrix.build }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - build: macOS + os: macOS-latest + - build: Windows + os: windows-latest + defaults: + run: + shell: bash + working-directory: ./ + steps: + - name: Checkout sources + uses: actions/checkout@v2 + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown + profile: minimal + override: true + - name: Test project + run: cargo test --locked -- --ignored + coverage: name: Code coverage runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index b3ebf948..924cde87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,11 +186,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" [[package]] name = "cfg-if" @@ -797,9 +803,13 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" +dependencies = [ + "zerocopy", + "zerocopy-derive", +] [[package]] name = "primeorder" @@ -1052,20 +1062,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -1210,21 +1221,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "7a44eede9b727419af8095cb2d72fab15487a541f54647ad4414b34096ee4631" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.15", + "toml_edit 0.22.18", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -1242,22 +1253,22 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.16", ] [[package]] name = "trybuild" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1e5645f2ee8025c2f1d75e1138f2dd034d74e6ba54620f3c569ba2a2a1ea06" +checksum = "b55265878356bdd85c9baa15859c87de93b2bf1f33acf752040a561e4a228f62" dependencies = [ "glob", "serde", @@ -1308,9 +1319,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1411,9 +1422,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" dependencies = [ "memchr", ] @@ -1424,6 +1435,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/sylvia/tests/ui.rs b/sylvia/tests/ui.rs index bd72ecdd..04bfa981 100644 --- a/sylvia/tests/ui.rs +++ b/sylvia/tests/ui.rs @@ -1,4 +1,5 @@ #[test] +#[ignore] fn ui() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/**/*.rs"); From e2c4bfae279591617437414b78602d47acdef0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 31 Jul 2024 15:54:15 +0200 Subject: [PATCH 3/3] test: Change name of test_ui --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2813198e..2c305087 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: # Errors differ between Rust versions. We test only the latest output. test_ui: - name: ${{ matrix.build }} + name: Test UI runs-on: ${{ matrix.os }} strategy: fail-fast: false