From f08e56a4f0a45d6a0a43cb411e850fb185491391 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 20 Sep 2024 17:06:31 +0200 Subject: [PATCH 1/5] Directly specify and handle supported arguments --- crates/snforge-scarb-plugin/src/args.rs | 34 +++++++++++++++++-- crates/snforge-scarb-plugin/src/attributes.rs | 20 ++++++++++- .../src/attributes/available_gas.rs | 5 +++ .../src/attributes/fork.rs | 12 +++++++ .../src/attributes/fuzzer.rs | 7 +++- .../src/attributes/ignore.rs | 6 +++- .../src/attributes/should_panic.rs | 6 ++++ 7 files changed, 84 insertions(+), 6 deletions(-) diff --git a/crates/snforge-scarb-plugin/src/args.rs b/crates/snforge-scarb-plugin/src/args.rs index 924bc83756..04f25d5cf1 100644 --- a/crates/snforge-scarb-plugin/src/args.rs +++ b/crates/snforge-scarb-plugin/src/args.rs @@ -1,5 +1,5 @@ use self::{named::NamedArgs, unnamed::UnnamedArgs}; -use crate::attributes::{AttributeInfo, ErrorExt}; +use crate::attributes::{AttributeInfo, ErrorExt, ValidArgs, ValidArgsTypes, ValidNamedArgs}; use cairo_lang_macro::Diagnostic; use cairo_lang_syntax::node::{ ast::{ArgClause, Expr, OptionArgListParenthesized}, @@ -67,8 +67,30 @@ impl Arguments { } #[inline] - pub fn named_only(&self) -> Result<&NamedArgs, Diagnostic> { + pub fn named_only(&self) -> Result<&NamedArgs, Diagnostic> { if self.shorthand.is_empty() && self.unnamed.is_empty() { + match T::VALID_ARGS { + ValidArgsTypes::Named(valid_named_args) + | ValidArgsTypes::Both { valid_named_args } => match valid_named_args { + ValidNamedArgs::All => {} + ValidNamedArgs::Restricted(valid_named_args) => { + for (arg, _) in self.named.iter() { + if !valid_named_args.contains(&arg.as_str()) { + return Err(T::error(format!( + "unsupported named argument \"{arg}\" provided", + ))); + } + } + } + }, + ValidArgsTypes::Unnamed => panic!( + "`named_only` arguments requested where only `Unnamed` arguments are valid" + ), + ValidArgsTypes::None => { + panic!("`named_only` arguments requested where no arguments are valid") + } + } + Ok(&self.named) } else { Err(T::error("can be used with named attributes only")) @@ -76,8 +98,14 @@ impl Arguments { } #[inline] - pub fn unnamed_only(&self) -> Result { + pub fn unnamed_only(&self) -> Result { if self.shorthand.is_empty() && self.named.is_empty() { + match T::VALID_ARGS { + ValidArgsTypes::Named(_) => panic!("`unnamed_arguments` arguments requested where only `Named` arguments are valid"), + ValidArgsTypes::Unnamed | ValidArgsTypes::Both { .. } => {}, + ValidArgsTypes::None => panic!("`named_only` arguments requested where no arguments are valid") + } + Ok(UnnamedArgs::new(&self.unnamed)) } else { Err(T::error("can be used with unnamed attributes only")) diff --git a/crates/snforge-scarb-plugin/src/attributes.rs b/crates/snforge-scarb-plugin/src/attributes.rs index c90ac8097e..90b292bc91 100644 --- a/crates/snforge-scarb-plugin/src/attributes.rs +++ b/crates/snforge-scarb-plugin/src/attributes.rs @@ -18,7 +18,25 @@ pub trait AttributeTypeData { const CHEATCODE_NAME: &'static str; } -pub trait AttributeCollector: AttributeInfo + AttributeTypeData { +pub enum ValidNamedArgs<'a> { + All, + Restricted(&'a [&'a str]), +} + +pub enum ValidArgsTypes<'a> { + Named(ValidNamedArgs<'a>), + Unnamed, + Both { + valid_named_args: ValidNamedArgs<'a>, + }, + None, +} + +pub trait ValidArgs { + const VALID_ARGS: ValidArgsTypes<'_>; +} + +pub trait AttributeCollector: AttributeInfo + AttributeTypeData + ValidArgs { fn args_into_config_expression( db: &dyn SyntaxGroup, args: Arguments, diff --git a/crates/snforge-scarb-plugin/src/attributes/available_gas.rs b/crates/snforge-scarb-plugin/src/attributes/available_gas.rs index 38c7fc06e8..7a6511e922 100644 --- a/crates/snforge-scarb-plugin/src/attributes/available_gas.rs +++ b/crates/snforge-scarb-plugin/src/attributes/available_gas.rs @@ -1,3 +1,4 @@ +use crate::attributes::{ValidArgs, ValidArgsTypes}; use crate::{ args::Arguments, attributes::{AttributeCollector, AttributeInfo, AttributeTypeData}, @@ -18,6 +19,10 @@ impl AttributeTypeData for AvailableGasCollector { const CHEATCODE_NAME: &'static str = "set_config_available_gas"; } +impl ValidArgs for AvailableGasCollector { + const VALID_ARGS: ValidArgsTypes<'_> = ValidArgsTypes::Unnamed; +} + impl AttributeCollector for AvailableGasCollector { fn args_into_config_expression( db: &dyn SyntaxGroup, diff --git a/crates/snforge-scarb-plugin/src/attributes/fork.rs b/crates/snforge-scarb-plugin/src/attributes/fork.rs index e6a8d5aa61..c9ae3d74e8 100644 --- a/crates/snforge-scarb-plugin/src/attributes/fork.rs +++ b/crates/snforge-scarb-plugin/src/attributes/fork.rs @@ -1,4 +1,5 @@ use self::block_id::{BlockId, BlockIdVariants}; +use crate::attributes::{ValidArgs, ValidArgsTypes, ValidNamedArgs}; use crate::{ args::Arguments, attributes::{AttributeCollector, AttributeInfo, AttributeTypeData}, @@ -24,6 +25,17 @@ impl AttributeTypeData for ForkCollector { const CHEATCODE_NAME: &'static str = "set_config_fork"; } +impl ValidArgs for ForkCollector { + const VALID_ARGS: ValidArgsTypes<'_> = ValidArgsTypes::Both { + valid_named_args: ValidNamedArgs::Restricted(&[ + "block_hash", + "block_number", + "block_tag", + "url", + ]), + }; +} + impl AttributeCollector for ForkCollector { fn args_into_config_expression( db: &dyn SyntaxGroup, diff --git a/crates/snforge-scarb-plugin/src/attributes/fuzzer.rs b/crates/snforge-scarb-plugin/src/attributes/fuzzer.rs index 81527856f9..9f17bbe780 100644 --- a/crates/snforge-scarb-plugin/src/attributes/fuzzer.rs +++ b/crates/snforge-scarb-plugin/src/attributes/fuzzer.rs @@ -1,4 +1,4 @@ -use super::{AttributeInfo, AttributeTypeData}; +use super::{AttributeInfo, AttributeTypeData, ValidArgs, ValidArgsTypes, ValidNamedArgs}; use crate::{ args::Arguments, attributes::{AttributeCollector, ErrorExt}, @@ -20,6 +20,11 @@ impl AttributeTypeData for FuzzerCollector { const CHEATCODE_NAME: &'static str = "set_config_fuzzer"; } +impl ValidArgs for FuzzerCollector { + const VALID_ARGS: ValidArgsTypes<'_> = + ValidArgsTypes::Named(ValidNamedArgs::Restricted(&["seed", "runs"])); +} + impl AttributeCollector for FuzzerCollector { fn args_into_config_expression( db: &dyn SyntaxGroup, diff --git a/crates/snforge-scarb-plugin/src/attributes/ignore.rs b/crates/snforge-scarb-plugin/src/attributes/ignore.rs index ae4d633173..5bae5b7c79 100644 --- a/crates/snforge-scarb-plugin/src/attributes/ignore.rs +++ b/crates/snforge-scarb-plugin/src/attributes/ignore.rs @@ -1,4 +1,4 @@ -use super::{AttributeInfo, AttributeTypeData}; +use super::{AttributeInfo, AttributeTypeData, ValidArgs, ValidArgsTypes}; use crate::{ args::Arguments, attributes::AttributeCollector, config_statement::extend_with_config_cheatcodes, @@ -16,6 +16,10 @@ impl AttributeTypeData for IgnoreCollector { const CHEATCODE_NAME: &'static str = "set_config_ignore"; } +impl ValidArgs for IgnoreCollector { + const VALID_ARGS: ValidArgsTypes<'_> = ValidArgsTypes::None; +} + impl AttributeCollector for IgnoreCollector { fn args_into_config_expression( _db: &dyn SyntaxGroup, diff --git a/crates/snforge-scarb-plugin/src/attributes/should_panic.rs b/crates/snforge-scarb-plugin/src/attributes/should_panic.rs index e1751370fe..684db44bfa 100644 --- a/crates/snforge-scarb-plugin/src/attributes/should_panic.rs +++ b/crates/snforge-scarb-plugin/src/attributes/should_panic.rs @@ -1,4 +1,5 @@ use self::expected::Expected; +use crate::attributes::{ValidArgs, ValidArgsTypes, ValidNamedArgs}; use crate::{ args::Arguments, attributes::{AttributeCollector, AttributeInfo, AttributeTypeData}, @@ -21,6 +22,11 @@ impl AttributeTypeData for ShouldPanicCollector { const CHEATCODE_NAME: &'static str = "set_config_should_panic"; } +impl ValidArgs for ShouldPanicCollector { + const VALID_ARGS: ValidArgsTypes<'_> = + ValidArgsTypes::Named(ValidNamedArgs::Restricted(&["expected"])); +} + impl AttributeCollector for ShouldPanicCollector { fn args_into_config_expression( db: &dyn SyntaxGroup, From bdbc77c09a9dbc30be5e9047da8bd39d4c4f758c Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 20 Sep 2024 17:13:46 +0200 Subject: [PATCH 2/5] Add tests --- .../single_attributes/available_gas.rs | 15 +++++++++++++ .../integration/single_attributes/fork.rs | 22 +++++++++++++++++++ .../integration/single_attributes/fuzzer.rs | 15 +++++++++++++ .../single_attributes/should_panic.rs | 15 +++++++++++++ 4 files changed, 67 insertions(+) diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs index 8f2c978de0..aca3263514 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs @@ -48,6 +48,21 @@ fn fails_with_non_number_literal() { ); } +#[test] +fn fails_with_named() { + let item = TokenStream::new(EMPTY_FN.into()); + let args = TokenStream::new(r#"(abc: "123")"#.into()); + + let result = available_gas(args, item); + + assert_diagnostics( + &result, + &[Diagnostic::error( + "#[available_gas] can be used with unnamed attributes only", + )], + ); +} + #[test] fn work_with_number() { let item = TokenStream::new(EMPTY_FN.into()); diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs index 9aacc2ab67..c9ac044964 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs @@ -86,6 +86,28 @@ fn fails_with_invalid_url() { ); } +#[test] +fn fails_with_unknown_argument() { + let item = TokenStream::new(EMPTY_FN.into()); + let args = TokenStream::new( + r#"(url: "http://example.com", block_number: 23, unknow_arg: "value")"#.into(), + ); + + let result = fork(args, item); + + assert_diagnostics( + &result, + &[Diagnostic::error(formatdoc!( + " + Both options failed + First variant: #[fork] unsupported named argument \"unknow_arg\" provided + Second variant: #[fork] can be used with unnamed attributes only + Resolve at least one of them + " + ))], + ); +} + #[test] fn accepts_string() { let item = TokenStream::new(EMPTY_FN.into()); diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fuzzer.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fuzzer.rs index d2fed46a3d..fc82f2b1f2 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fuzzer.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fuzzer.rs @@ -142,6 +142,21 @@ fn fail_with_invalid_args() { ); } +#[test] +fn fail_with_unknown_arg() { + let item = TokenStream::new(EMPTY_FN.into()); + let args = TokenStream::new("(seed: 1234, runs: 20, unknown_arg: 50)".into()); + + let result = fuzzer(args, item); + + assert_diagnostics( + &result, + &[Diagnostic::error( + "#[fuzzer] unsupported named argument \"unknown_arg\" provided", + )], + ); +} + #[test] fn is_used_once() { let item = TokenStream::new(formatdoc!( diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs index 223d919123..b1e96fa480 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs @@ -148,6 +148,21 @@ fn work_with_expected_tuple() { ); } +#[test] +fn fail_with_unknown_args() { + let item = TokenStream::new(EMPTY_FN.into()); + let args = TokenStream::new("(unknown_arg: 'value')".into()); + + let result = should_panic(args, item); + + assert_diagnostics( + &result, + &[Diagnostic::error( + "#[should_panic] unsupported named argument \"unknown_arg\" provided", + )], + ); +} + #[test] fn is_used_once() { let item = TokenStream::new(formatdoc!( From 472f8189d90f5ddc3553138d903355211ca5f5bf Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 23 Sep 2024 09:51:56 +0200 Subject: [PATCH 3/5] Extend test --- .../tests/integration/single_attributes/should_panic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs index b1e96fa480..0f564bba78 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/should_panic.rs @@ -151,7 +151,7 @@ fn work_with_expected_tuple() { #[test] fn fail_with_unknown_args() { let item = TokenStream::new(EMPTY_FN.into()); - let args = TokenStream::new("(unknown_arg: 'value')".into()); + let args = TokenStream::new("(expected: 'abc', unknown_arg: 'value')".into()); let result = should_panic(args, item); From e9c5415dc48b27e5a7c34ce200b7ef7feff1a07d Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Wed, 13 Nov 2024 17:35:29 +0100 Subject: [PATCH 4/5] Use iterator instead of loop --- crates/snforge-scarb-plugin/src/args.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/snforge-scarb-plugin/src/args.rs b/crates/snforge-scarb-plugin/src/args.rs index 103e1c9df5..92a0773197 100644 --- a/crates/snforge-scarb-plugin/src/args.rs +++ b/crates/snforge-scarb-plugin/src/args.rs @@ -74,12 +74,15 @@ impl Arguments { | ValidArgsTypes::Both { valid_named_args } => match valid_named_args { ValidNamedArgs::All => {} ValidNamedArgs::Restricted(valid_named_args) => { - for (arg, _) in self.named.iter() { - if !valid_named_args.contains(&arg.as_str()) { - return Err(T::error(format!( - "unsupported named argument \"{arg}\" provided", - ))); - } + if let Some(arg) = self + .named + .iter() + .map(|(arg, _)| arg) + .find(|arg| !valid_named_args.contains(&arg.as_str())) + { + return Err(T::error(format!( + "unsupported named argument \"{arg}\" provided", + ))); } } }, From 8961bb7e191a8a9df0a1f99433b18baa01c118fa Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Wed, 13 Nov 2024 17:45:43 +0100 Subject: [PATCH 5/5] Fix failing test --- .../tests/integration/single_attributes/fork.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs index 9655b86ce6..4f2f5a46fa 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs @@ -102,9 +102,10 @@ fn fails_with_unknown_argument() { &result, &[Diagnostic::error(formatdoc!( " - Both options failed - First variant: #[fork] unsupported named argument \"unknow_arg\" provided - Second variant: #[fork] can be used with unnamed attributes only + All options failed + - variant: #[fork] unsupported named argument \"unknow_arg\" provided + - variant: #[fork] expected 1 arguments, got: 0 + - variant: #[fork] can be used with unnamed attributes only Resolve at least one of them " ))],