Skip to content

Commit a68a7e5

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.
1 parent e08a5b9 commit a68a7e5

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)