diff --git a/crates/snforge-scarb-plugin/src/args.rs b/crates/snforge-scarb-plugin/src/args.rs index 2f4c2af7db..92a0773197 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,33 @@ 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) => { + 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", + ))); + } + } + }, + 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 +101,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 01413f01e2..dc44558e32 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 691e929bc6..982e6c8c5b 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, 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 a92a4a02b4..4f2f5a46fa 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs @@ -89,6 +89,29 @@ 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!( + " + 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 + " + ))], + ); +} + #[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 9552cd048b..a9936c4d27 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 @@ -177,6 +177,21 @@ fn work_with_expected_tuple() { ); } +#[test] +fn fail_with_unknown_args() { + let item = TokenStream::new(EMPTY_FN.into()); + let args = TokenStream::new("(expected: 'abc', 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!(