Skip to content

Commit 52d87e6

Browse files
committed
feat: Pass attributes through #[test] macro
This is done by recursively parsing the 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 ec6d78f commit 52d87e6

2 files changed

Lines changed: 107 additions & 19 deletions

File tree

crates/libtest2/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ pub mod _private {
6060
pub use libtest2_harness::TestKind;
6161

6262
pub use crate::_main_parse as main_parse;
63-
pub use crate::_parse_ignore as parse_ignore;
6463
pub use crate::_test_parse as test_parse;
6564
pub use crate::case::DynCase;
6665
}

crates/libtest2/src/macros.rs

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,115 @@ macro_rules! _main_parse {
1313
}
1414

1515
#[macro_export]
16-
macro_rules! _parse_ignore {
17-
(ignore) => {
18-
::std::option::Option::<&'static str>::None
16+
#[allow(clippy::crate_in_macro_def)] // accessing item defined by `_main_parse`
17+
macro_rules! _test_parse {
18+
// Entry point
19+
(#[test] $(#[$($attr:tt)+])* fn $name:ident $($item:tt)*) => {
20+
$crate::_private::test_parse! {
21+
name=$name
22+
body=[$($item)*]
23+
unparsed_attrs=[$(#[$($attr)+])*]
24+
parsed_attrs=[]
25+
}
1926
};
20-
(ignore = $reason:expr) => {
21-
::std::option::Option::<&'static str>::Some($reason)
27+
28+
// Recursively handle attributes
29+
(name=$name:ident body=[$($item:tt)*] unparsed_attrs=[] parsed_attrs=[$(#[$parsed_attr:meta])*] $(ignore=$state:expr)?) => {
30+
$crate::_private::test_parse! {
31+
name=$name
32+
body=[$($item)*]
33+
attrs=[$(#[$parsed_attr])*]
34+
$(ignore=$state)?
35+
}
2236
};
23-
($($attr:tt)*) => {
24-
compile_error!(concat!("unknown attribute '", stringify!($($attr)*), "'"));
37+
(name=$name:ident body=[$($item:tt)*] unparsed_attrs=[#[ignore] $(#[$($unparsed_attr:tt)+])*] parsed_attrs=[$(#[$parsed_attr:meta])*] $(ignore=$state:expr)?) => {
38+
$crate::_private::test_parse! {
39+
name=$name
40+
body=[$($item)*]
41+
unparsed_attrs=[$(#[$($unparsed_attr)*])*]
42+
parsed_attrs=[$(#[$parsed_attr])*]
43+
ignore=()
44+
}
45+
};
46+
(name=$name:ident body=[$($item:tt)*] unparsed_attrs=[#[ignore = $reason:literal] $(#[$($unparsed_attr:tt)+])*] parsed_attrs=[$(#[$parsed_attr:meta])*] $(ignore=$state:expr)?) => {
47+
$crate::_private::test_parse! {
48+
name=$name
49+
body=[$($item)*]
50+
unparsed_attrs=[$(#[$($unparsed_attr)*])*]
51+
parsed_attrs=[$(#[$parsed_attr])*]
52+
ignore=$reason
53+
}
54+
};
55+
(name=$name:ident body=[$($item:tt)*] unparsed_attrs=[#[$($attr:tt)+] $(#[$($unparsed_attr:tt)+])*] parsed_attrs=[$(#[$parsed_attr:meta])*] $(ignore=$state:expr)?) => {
56+
$crate::_private::test_parse! {
57+
name=$name
58+
body=[$($item)*]
59+
unparsed_attrs=[$(#[$($unparsed_attr)*])*]
60+
parsed_attrs=[#[$($attr)* $(#[$($parsed_attr)*])*]]
61+
$(ignore=$state)?
62+
}
2563
};
26-
}
2764

28-
#[macro_export]
29-
#[allow(clippy::crate_in_macro_def)] // accessing item defined by `_main_parse`
30-
macro_rules! _test_parse {
31-
(#[test] $(#[$($attr:tt)*])* fn $name:ident $($item:tt)*) => {
65+
// End result
66+
(name=$name:ident body=[$($item:tt)*] attrs=[$(#[$attr:meta])*]) => {
67+
#[allow(non_camel_case_types)]
68+
struct $name;
69+
70+
impl $crate::_private::Case for $name {
71+
fn name(&self) -> &str {
72+
$crate::_private::push!(crate::TESTS, _: $crate::_private::DynCase = $crate::_private::DynCase(&$name));
73+
74+
stringify!($name)
75+
}
76+
fn kind(&self) -> $crate::_private::TestKind {
77+
Default::default()
78+
}
79+
fn source(&self) -> Option<&$crate::_private::Source> {
80+
None
81+
}
82+
fn exclusive(&self, _: &$crate::TestContext) -> bool {
83+
false
84+
}
85+
86+
fn run(&self, context: &$crate::TestContext) -> $crate::RunResult {
87+
$(#[$attr])*
88+
fn run $($item)*
89+
90+
run(context)
91+
}
92+
}
93+
};
94+
(name=$name:ident body=[$($item:tt)*] attrs=[$(#[$attr:meta])*] ignore=$reason:literal) => {
95+
#[allow(non_camel_case_types)]
96+
struct $name;
97+
98+
impl $crate::_private::Case for $name {
99+
fn name(&self) -> &str {
100+
$crate::_private::push!(crate::TESTS, _: $crate::_private::DynCase = $crate::_private::DynCase(&$name));
101+
102+
stringify!($name)
103+
}
104+
fn kind(&self) -> $crate::_private::TestKind {
105+
Default::default()
106+
}
107+
fn source(&self) -> Option<&$crate::_private::Source> {
108+
None
109+
}
110+
fn exclusive(&self, _: &$crate::TestContext) -> bool {
111+
false
112+
}
113+
114+
fn run(&self, context: &$crate::TestContext) -> $crate::RunResult {
115+
$(#[$attr])*
116+
fn run $($item)*
117+
118+
context.ignore_for($reason)?;
119+
120+
run(context)
121+
}
122+
}
123+
};
124+
(name=$name:ident body=[$($item:tt)*] attrs=[$(#[$attr:meta])*] ignore=$unused:expr) => {
32125
#[allow(non_camel_case_types)]
33126
struct $name;
34127

@@ -49,14 +142,10 @@ macro_rules! _test_parse {
49142
}
50143

51144
fn run(&self, context: &$crate::TestContext) -> $crate::RunResult {
145+
$(#[$attr])*
52146
fn run $($item)*
53147

54-
$(
55-
match $crate::_private::parse_ignore!($($attr)*) {
56-
::std::option::Option::None => context.ignore()?,
57-
::std::option::Option::Some(reason) => context.ignore_for(reason)?,
58-
}
59-
)*
148+
context.ignore()?;
60149

61150
use $crate::IntoRunResult;
62151
let result = run(context);

0 commit comments

Comments
 (0)