pub(crate) fn attribute_marks_test(body: &str) -> bool {
let matches_test = |s: &str| {
matches!(s, "test" | "rstest" | "wasm_bindgen_test" | "test_case")
|| s.ends_with("::test")
|| s.contains("::test(")
|| cfg_inner(s).is_some_and(cfg_predicate_marks_test)
};
let trimmed = body.trim();
if matches_test(trimmed) {
return true;
}
if trimmed.bytes().any(|b| b.is_ascii_whitespace()) {
return matches_test(&strip_whitespace(trimmed));
}
false
}
fn strip_whitespace(s: &str) -> String {
s.chars().filter(|c| !c.is_whitespace()).collect()
}
fn cfg_inner(body: &str) -> Option<&str> {
let rest = body.trim_start().strip_prefix("cfg")?.trim_start();
let after_open = rest.strip_prefix('(')?;
let inner = after_open.strip_suffix(')')?;
Some(inner)
}
fn cfg_predicate_marks_test(pred: &str) -> bool {
let mut stack = vec![pred];
while let Some(operand) = stack.pop() {
let trimmed = operand.trim();
if trimmed == "test" {
return true;
}
if cfg_split_top_level_args(trimmed).nth(1).is_some() {
stack.extend(cfg_split_top_level_args(trimmed));
continue;
}
if let Some(rest) = trimmed.strip_prefix("not").map(str::trim_start)
&& rest.starts_with('(')
&& rest.ends_with(')')
{
continue;
}
if let Some(rest) = trimmed
.strip_prefix("all")
.or_else(|| trimmed.strip_prefix("any"))
&& let Some(args) = rest.trim_start().strip_prefix('(')
&& let Some(args) = args.strip_suffix(')')
{
stack.extend(cfg_split_top_level_args(args));
}
}
false
}
fn cfg_split_top_level_args(args: &str) -> impl Iterator<Item = &str> {
let mut depth = 0_i32;
let mut start = 0_usize;
let mut done = false;
let bytes = args.as_bytes();
std::iter::from_fn(move || {
if done {
return None;
}
let mut i = start;
while i < bytes.len() {
match bytes[i] {
b'(' => depth += 1,
b')' => depth -= 1,
b',' if depth == 0 => {
let slice = &args[start..i];
start = i + 1;
return Some(slice);
}
_ => {}
}
i += 1;
}
done = true;
Some(&args[start..])
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rust_attr_test_marks_bare_test_attribute() {
assert!(attribute_marks_test("test"));
assert!(attribute_marks_test("rstest"));
assert!(attribute_marks_test("wasm_bindgen_test"));
assert!(attribute_marks_test("test_case"));
assert!(attribute_marks_test("tokio::test"));
assert!(attribute_marks_test(
"tokio::test(flavor = \"current_thread\")"
));
}
#[test]
fn rust_attr_test_marks_cfg_test_variants() {
assert!(attribute_marks_test("cfg(test)"));
assert!(attribute_marks_test("cfg(test, foo)"));
assert!(attribute_marks_test("cfg(all(test, unix))"));
assert!(attribute_marks_test("cfg(any(test, foo))"));
}
#[test]
fn rust_attr_test_marks_cfg_with_test_not_first() {
assert!(
attribute_marks_test("cfg(all(unix, test))"),
"test as second all() operand must mark test-only"
);
assert!(
attribute_marks_test("cfg(any(feature = \"x\", test))"),
"test as second any() operand must mark test-only"
);
assert!(attribute_marks_test(
"cfg(all(unix, any(test, feature = \"x\")))"
));
}
#[test]
fn rust_attr_test_skips_not_test_and_feature_named_test() {
assert!(!attribute_marks_test("cfg(not(test))"));
assert!(!attribute_marks_test("cfg(all(unix, not(test)))"));
assert!(!attribute_marks_test("cfg(feature = \"test\")"));
assert!(!attribute_marks_test("cfg(all(unix, feature = \"test\"))"));
assert!(!attribute_marks_test("cfg(unix)"));
assert!(!attribute_marks_test("derive(Debug)"));
assert!(!attribute_marks_test(
"cfg(all(unix, target_os = \"linux\"))"
));
assert!(!attribute_marks_test("cfg(any(unix, windows))"));
assert!(!attribute_marks_test(
"cfg(all(unix, any(feature = \"x\", feature = \"y\")))"
));
assert!(!attribute_marks_test("cfg(any(unix, not(test)))"));
}
#[test]
fn rust_attr_test_not_led_comma_list_keeps_later_test_operand() {
assert!(
attribute_marks_test("cfg(not(foo), all(test))"),
"not(foo), all(test) list must still see the trailing test"
);
assert!(
attribute_marks_test("cfg(not(unix), any(test))"),
"not(unix), any(test) list must still see the trailing test"
);
assert!(attribute_marks_test("cfg(all(not(foo), all(test)))"));
assert!(attribute_marks_test("cfg(not(foo), test)"));
assert!(!attribute_marks_test("cfg(not(test))"));
assert!(!attribute_marks_test("cfg(not(foo, bar))"));
assert!(!attribute_marks_test("cfg(not(test, unix))"));
assert!(attribute_marks_test("cfg(all(test, unix))"));
}
#[test]
fn rust_attr_test_tolerates_internal_whitespace() {
assert!(attribute_marks_test("cfg( all( unix , test ) )"));
assert!(!attribute_marks_test("cfg( not ( test ) )"));
}
#[test]
fn rust_attr_test_handles_deeply_nested_cfg_without_overflow() {
const DEPTH: usize = 50_000;
fn nest(comb: &str, inner: &str) -> String {
let mut s = String::with_capacity(DEPTH * (comb.len() + 1) + inner.len() + DEPTH + 5);
s.push_str("cfg(");
for _ in 0..DEPTH {
s.push_str(comb);
s.push('(');
}
s.push_str(inner);
for _ in 0..DEPTH {
s.push(')');
}
s.push(')');
s
}
assert!(
attribute_marks_test(&nest("all", "test")),
"deeply nested all(...) wrapping `test` must mark test-only"
);
assert!(
!attribute_marks_test(&nest("any", "unix")),
"deeply nested any(...) without `test` must not mark test-only"
);
assert!(
!attribute_marks_test(&nest("all", "not(test)")),
"deeply nested not(test) must remain production-only"
);
}
#[test]
fn strip_whitespace_preserves_non_ascii_utf8() {
assert_eq!(strip_whitespace("é test"), "étest");
assert_eq!(strip_whitespace("crate ::ñ::test"), "crate::ñ::test");
assert_eq!(strip_whitespace(" 日本語 test"), "日本語test");
assert_eq!(
strip_whitespace("cfg( all( unix , test ) )"),
"cfg(all(unix,test))"
);
}
}