Skip to content

Commit 1cefabf

Browse files
committed
fix: Recursive parsing of attributes
The `test_parse` macro is changed to recursively parse attributes, filtering out the `#[ignore]` macro. This should also make it fairly trivial to add additional macros, such as `#[should_ignore]`. There is a fair amount of complexity added to the macro with this change, but on the other hand, it also succeeds in generating only the necessary code. You win some, you lose some. TODO: Move out should_panic code
1 parent e08a5b9 commit 1cefabf

1 file changed

Lines changed: 50 additions & 17 deletions

File tree

crates/libtest2/src/macros.rs

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,48 @@ macro_rules! _main_parse {
1414
}
1515

1616
#[macro_export]
17-
macro_rules! _parse_ignore {
18-
(ignore) => {
19-
::std::option::Option::<&'static str>::None
17+
#[allow(clippy::crate_in_macro_def)] // accessing item defined by `_main_parse`
18+
macro_rules! _test_parse {
19+
// Entry point
20+
(#[test] $(#[$($attr:tt)+])* fn $name:ident $($item:tt)*) => {
21+
$crate::_private::test_parse!(continue:
22+
name=$name
23+
body=[$($item)*]
24+
attrs=[$(#[$($attr)+])*]
25+
);
26+
};
27+
28+
// Recursively handle attributes:
29+
30+
// Edge condition (no more attributes to parse)
31+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[] $(ignore=$ignore:tt)?) => {
32+
$crate::_private::test_parse!(break:
33+
name=$name
34+
body=[$($item)*]
35+
$(ignore=$ignore)?
36+
);
2037
};
21-
(ignore = $reason:expr) => {
22-
::std::option::Option::<&'static str>::Some($reason)
38+
// Process `#[ignore]` macro (NOTE: This will only match if an `#[ignore]` macro has not already been parsed)
39+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[ignore $(= $reason:literal)?] $(#[$($attr:tt)+])*]) => {
40+
$crate::_private::test_parse!(continue:
41+
name=$name
42+
body=[$($item)*]
43+
attrs=[$(#[$($attr)*])*]
44+
ignore=[$($reason)?]
45+
);
2346
};
24-
($($attr:tt)*) => {
25-
compile_error!(concat!("unknown attribute '", stringify!($($attr)*), "'"));
47+
// Discard unknown attributes
48+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[$($unknown_attr:tt)+] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)?) => {
49+
$crate::_private::test_parse!(continue:
50+
name=$name
51+
body=[$($item)*]
52+
attrs=[$(#[$($attr)*])*]
53+
$(ignore=$ignore)?
54+
);
2655
};
27-
}
2856

29-
#[macro_export]
30-
#[allow(clippy::crate_in_macro_def)] // accessing item defined by `_main_parse`
31-
macro_rules! _test_parse {
32-
(#[test] $(#[$($attr:tt)*])* fn $name:ident $($item:tt)*) => {
57+
// End result
58+
(break: name=$name:ident body=[$($item:tt)*] $(ignore=$ignore:tt)? $(should_panic=$should_panic:tt)?) => {
3359
#[allow(non_camel_case_types)]
3460
struct $name;
3561

@@ -53,11 +79,8 @@ macro_rules! _test_parse {
5379
fn run $($item)*
5480

5581
$(
56-
match $crate::_private::parse_ignore!($($attr)*) {
57-
::std::option::Option::None => context.ignore()?,
58-
::std::option::Option::Some(reason) => context.ignore_for(reason)?,
59-
}
60-
)*
82+
$crate::_private::parse_ignore!(context, $ignore)?;
83+
)?
6184

6285
use $crate::IntoRunResult;
6386
let result = run(context);
@@ -66,3 +89,13 @@ macro_rules! _test_parse {
6689
}
6790
};
6891
}
92+
93+
#[macro_export]
94+
macro_rules! _parse_ignore {
95+
($context:expr, [$reason:literal]) => {
96+
$context.ignore_for($reason)
97+
};
98+
($context:expr, []) => {
99+
$context.ignore()
100+
};
101+
}

0 commit comments

Comments
 (0)