Skip to content

Commit eeb4fb4

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 c61a9e5 commit eeb4fb4

File tree

2 files changed

+107
-19
lines changed

2 files changed

+107
-19
lines changed

crates/libtest2/src/lib.rs

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

4646
pub use crate::_main_parse as main_parse;
47-
pub use crate::_parse_ignore as parse_ignore;
4847
pub use crate::_test_parse as test_parse;
4948
pub use crate::case::DynCase;
5049
}

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
run(context)
62151
}

0 commit comments

Comments
 (0)