1use std::{assert_matches, iter};
5
6use rustc_ast::{self as ast, GenericParamKind, Mutability, attr, join_path_idents};
7use rustc_ast_pretty::pprust;
8use rustc_attr_ir::{Attribute, AttributeKind};
9use rustc_attr_parsing::AttributeParser;
10use rustc_errors::{Applicability, Diag, Level};
11use rustc_expand::base::*;
12use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym};
13use thin_vec::{ThinVec, thin_vec};
14use tracing::debug;
15
16use crate::diagnostics;
17use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
18
19pub(crate) fn expand_test_case(
27 ecx: &mut ExtCtxt<'_>,
28 attr_sp: Span,
29 meta_item: &ast::MetaItem,
30 anno_item: Annotatable,
31) -> Vec<Annotatable> {
32 check_builtin_macro_attribute(ecx, meta_item, sym::test_case);
33 warn_on_duplicate_attribute(ecx, &anno_item, sym::test_case);
34
35 let sp = ecx.with_def_site_ctxt(attr_sp);
36 let (mut item, is_stmt) = match anno_item {
37 Annotatable::Item(item) => (item, false),
38 Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => {
39 if let ast::StmtKind::Item(i) = stmt.kind {
40 (i, true)
41 } else {
42 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
43 }
44 }
45 _ => {
46 ecx.dcx().emit_err(diagnostics::TestCaseNonItem { span: anno_item.span() });
47 return ::alloc::vec::Vec::new()vec![];
48 }
49 };
50
51 if !ecx.ecfg.should_test {
52 return ::alloc::vec::Vec::new()vec![];
53 }
54
55 match &mut item.kind {
58 ast::ItemKind::Fn(ast::Fn { ident, .. })
59 | ast::ItemKind::Const(ast::ConstItem { ident, .. })
60 | ast::ItemKind::Static(ast::StaticItem { ident, .. }) => {
61 ident.span = ident.span.with_ctxt(sp.ctxt());
62 let test_path_symbol = Symbol::intern(&item_path(
63 &ecx.current_expansion.module.mod_path[1..],
65 ident,
66 ));
67 item.vis = ast::Visibility { span: item.vis.span, kind: ast::VisibilityKind::Public };
68 item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp));
69 }
70 _ => {}
71 }
72
73 let ret = if is_stmt {
74 Annotatable::Stmt(Box::new(ecx.stmt_item(item.span, item)))
75 } else {
76 Annotatable::Item(item)
77 };
78
79 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ret]))vec![ret]
80}
81
82pub(crate) fn expand_test(
83 cx: &mut ExtCtxt<'_>,
84 attr_sp: Span,
85 meta_item: &ast::MetaItem,
86 item: Annotatable,
87) -> Vec<Annotatable> {
88 check_builtin_macro_attribute(cx, meta_item, sym::test);
89 warn_on_duplicate_attribute(cx, &item, sym::test);
90 expand_test_or_bench(cx, attr_sp, item, false)
91}
92
93pub(crate) fn expand_bench(
94 cx: &mut ExtCtxt<'_>,
95 attr_path_sp: Span,
96 meta_item: &ast::MetaItem,
97 item: Annotatable,
98) -> Vec<Annotatable> {
99 check_builtin_macro_attribute(cx, meta_item, sym::bench);
100 warn_on_duplicate_attribute(cx, &item, sym::bench);
101 expand_test_or_bench(cx, attr_path_sp, item, true)
102}
103
104pub(crate) fn expand_test_or_bench(
105 cx: &ExtCtxt<'_>,
106 attr_sp: Span,
107 item: Annotatable,
108 is_bench: bool,
109) -> Vec<Annotatable> {
110 let (item, is_stmt) = match item {
111 Annotatable::Item(i) => (i, false),
112 Annotatable::Stmt(ast::Stmt { kind: ast::StmtKind::Item(i), .. }) => (i, true),
113 other => {
114 not_testable_error(cx, is_bench, attr_sp, None);
115 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[other]))vec![other];
116 }
117 };
118
119 let ast::ItemKind::Fn(fn_) = &item.kind else {
120 not_testable_error(cx, is_bench, attr_sp, Some(&item));
121 return if is_stmt {
122 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]))vec![Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]
123 } else {
124 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)]
125 };
126 };
127
128 if !cx.ecfg.should_test {
130 return ::alloc::vec::Vec::new()vec![];
131 }
132
133 if let Some(attr) = attr::find_by_name(&item.attrs, sym::naked) {
134 cx.dcx().emit_err(diagnostics::NakedFunctionTestingAttribute {
135 testing_span: attr_sp,
136 naked_span: attr.span,
137 });
138 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)];
139 }
140
141 let check_result = if is_bench {
145 check_bench_signature(cx, &item, fn_)
146 } else {
147 check_test_signature(cx, &item, fn_)
148 };
149 if check_result.is_err() {
150 return if is_stmt {
151 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]))vec![Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]
152 } else {
153 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)]
154 };
155 }
156
157 let sp = cx.with_def_site_ctxt(item.span);
158 let ret_ty_sp = cx.with_def_site_ctxt(fn_.sig.decl.output.span());
159 let attr_sp = cx.with_def_site_ctxt(attr_sp);
160
161 let test_ident = Ident::new(sym::test, attr_sp);
162
163 let test_path = |name| cx.path(ret_ty_sp, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[test_ident, Ident::from_str_and_span(name, sp)]))vec![test_ident, Ident::from_str_and_span(name, sp)]);
165
166 let should_panic_path = |name| {
168 cx.path(
169 sp,
170 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[test_ident, Ident::new(sym::ShouldPanic, sp),
Ident::from_str_and_span(name, sp)]))vec![test_ident, Ident::new(sym::ShouldPanic, sp), Ident::from_str_and_span(name, sp)],
171 )
172 };
173
174 let test_type_path = |name| {
176 cx.path(
177 sp,
178 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[test_ident, Ident::new(sym::TestType, sp),
Ident::from_str_and_span(name, sp)]))vec![test_ident, Ident::new(sym::TestType, sp), Ident::from_str_and_span(name, sp)],
179 )
180 };
181
182 let field = |name, expr| cx.field_imm(sp, Ident::from_str_and_span(name, sp), expr);
184
185 let coverage_off = |mut expr: Box<ast::Expr>| {
190 {
match expr.kind {
ast::ExprKind::Closure(_) => {}
ref left_val => {
::core::panicking::assert_matches_failed(left_val,
"ast::ExprKind::Closure(_)", ::core::option::Option::None);
}
}
};assert_matches!(expr.kind, ast::ExprKind::Closure(_));
191 expr.attrs.push(cx.attr_nested_word(sym::coverage, sym::off, sp));
192 expr
193 };
194
195 let test_fn = if is_bench {
196 let bencher_param =
198 Ident::from_str_and_span(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("__bench_{0}", fn_.ident.name))
})format!("__bench_{}", fn_.ident.name), attr_sp);
199 cx.expr_call(
200 sp,
201 cx.expr_path(test_path("StaticBenchFn")),
202 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(coverage_off(cx.closure(sp,
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[bencher_param])),
cx.expr_call(sp,
cx.expr_path(test_path("assert_test_result")),
{
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_call(ret_ty_sp,
cx.expr_path(cx.path(sp,
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[fn_.ident])))),
{
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_ident(sp, bencher_param));
vec
}));
vec
}))));
vec
}thin_vec![
203 coverage_off(cx.closure(
206 sp,
207 vec![bencher_param],
208 cx.expr_call(
209 sp,
210 cx.expr_path(test_path("assert_test_result")),
211 thin_vec![
212 cx.expr_call(
214 ret_ty_sp,
215 cx.expr_path(cx.path(sp, vec![fn_.ident])),
216 thin_vec![cx.expr_ident(sp, bencher_param)],
217 ),
218 ],
219 ),
220 )), ],
222 )
223 } else {
224 cx.expr_call(
225 sp,
226 cx.expr_path(test_path("StaticTestFn")),
227 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(coverage_off(cx.closure(sp, ::alloc::vec::Vec::new(),
cx.expr_call(sp,
cx.expr_path(test_path("assert_test_result")),
{
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_call(ret_ty_sp,
cx.expr_path(cx.path(sp,
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[fn_.ident])))), ThinVec::new()));
vec
}))));
vec
}thin_vec![
228 coverage_off(cx.closure(
231 sp,
232 vec![],
233 cx.expr_call(
235 sp,
236 cx.expr_path(test_path("assert_test_result")),
237 thin_vec![
238 cx.expr_call(
240 ret_ty_sp,
241 cx.expr_path(cx.path(sp, vec![fn_.ident])),
242 ThinVec::new(),
243 ), ],
245 ), )), ],
248 )
249 };
250
251 let test_path_symbol = Symbol::intern(&item_path(
252 &cx.current_expansion.module.mod_path[1..],
254 &fn_.ident,
255 ));
256
257 let location_info = get_location_info(cx, fn_);
258
259 let mut test_const = cx.item_static(
265 sp,
266 {
let len = [(), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_nested_word(sym::cfg, sym::test, attr_sp));
vec.push(cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol,
attr_sp));
vec.push(cx.attr_nested_word(sym::doc, sym::hidden, attr_sp));
vec
}thin_vec![
267 cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
269 cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp),
271 cx.attr_nested_word(sym::doc, sym::hidden, attr_sp),
273 ],
274 Ident::new(fn_.ident.name, sp),
275 cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
276 Mutability::Not,
277 cx.expr_struct(
279 sp,
280 test_path("TestDescAndFn"),
281 {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(field("desc",
cx.expr_struct(sp, test_path("TestDesc"),
{
let len =
[(), (), (), (), (), (), (), (), (), (), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(field("name",
cx.expr_call(sp, cx.expr_path(test_path("StaticTestName")),
{
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_str(sp, test_path_symbol));
vec
})));
vec.push(field("ignore",
cx.expr_bool(sp, should_ignore(&item))));
vec.push(field("ignore_message",
if let Some(msg) = should_ignore_message(&item) {
cx.expr_some(sp, cx.expr_str(sp, msg))
} else { cx.expr_none(sp) }));
vec.push(field("source_file",
cx.expr_str(sp, location_info.0)));
vec.push(field("start_line",
cx.expr_usize(sp, location_info.1)));
vec.push(field("start_col",
cx.expr_usize(sp, location_info.2)));
vec.push(field("end_line",
cx.expr_usize(sp, location_info.3)));
vec.push(field("end_col",
cx.expr_usize(sp, location_info.4)));
vec.push(field("compile_fail", cx.expr_bool(sp, false)));
vec.push(field("no_run", cx.expr_bool(sp, false)));
vec.push(field("should_panic",
match should_panic(cx, &item) {
ShouldPanic::No => { cx.expr_path(should_panic_path("No")) }
ShouldPanic::Yes(None) => {
cx.expr_path(should_panic_path("Yes"))
}
ShouldPanic::Yes(Some(sym)) =>
cx.expr_call(sp,
cx.expr_path(should_panic_path("YesWithMessage")),
{
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_str(sp, sym));
vec
}),
}));
vec.push(field("test_type",
match test_type(cx) {
TestType::UnitTest => {
cx.expr_path(test_type_path("UnitTest"))
}
TestType::IntegrationTest => {
cx.expr_path(test_type_path("IntegrationTest"))
}
TestType::Unknown => {
cx.expr_path(test_type_path("Unknown"))
}
}));
vec
})));
vec.push(field("testfn", test_fn));
vec
}thin_vec![
282 field(
284 "desc",
285 cx.expr_struct(
286 sp,
287 test_path("TestDesc"),
288 thin_vec![
289 field(
291 "name",
292 cx.expr_call(
293 sp,
294 cx.expr_path(test_path("StaticTestName")),
295 thin_vec![cx.expr_str(sp, test_path_symbol)],
296 ),
297 ),
298 field("ignore", cx.expr_bool(sp, should_ignore(&item)),),
300 field(
302 "ignore_message",
303 if let Some(msg) = should_ignore_message(&item) {
304 cx.expr_some(sp, cx.expr_str(sp, msg))
305 } else {
306 cx.expr_none(sp)
307 },
308 ),
309 field("source_file", cx.expr_str(sp, location_info.0)),
311 field("start_line", cx.expr_usize(sp, location_info.1)),
313 field("start_col", cx.expr_usize(sp, location_info.2)),
315 field("end_line", cx.expr_usize(sp, location_info.3)),
317 field("end_col", cx.expr_usize(sp, location_info.4)),
319 field("compile_fail", cx.expr_bool(sp, false)),
321 field("no_run", cx.expr_bool(sp, false)),
323 field(
325 "should_panic",
326 match should_panic(cx, &item) {
327 ShouldPanic::No => {
329 cx.expr_path(should_panic_path("No"))
330 }
331 ShouldPanic::Yes(None) => {
333 cx.expr_path(should_panic_path("Yes"))
334 }
335 ShouldPanic::Yes(Some(sym)) => cx.expr_call(
337 sp,
338 cx.expr_path(should_panic_path("YesWithMessage")),
339 thin_vec![cx.expr_str(sp, sym)],
340 ),
341 },
342 ),
343 field(
345 "test_type",
346 match test_type(cx) {
347 TestType::UnitTest => {
349 cx.expr_path(test_type_path("UnitTest"))
350 }
351 TestType::IntegrationTest => {
353 cx.expr_path(test_type_path("IntegrationTest"))
354 }
355 TestType::Unknown => {
357 cx.expr_path(test_type_path("Unknown"))
358 }
359 },
360 ),
361 ],
363 ),
364 ),
365 field("testfn", test_fn), ],
368 ),
370 );
371 test_const.vis.kind = ast::VisibilityKind::Public;
372
373 let test_extern =
375 cx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident));
376
377 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/test.rs:377",
"rustc_builtin_macros::test", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/test.rs"),
::tracing_core::__macro_support::Option::Some(377u32),
::tracing_core::__macro_support::Option::Some("rustc_builtin_macros::test"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("synthetic test item:\n{0}\n",
pprust::item_to_string(&test_const)) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
378
379 if is_stmt {
380 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Stmt(Box::new(cx.stmt_item(sp, test_extern))),
Annotatable::Stmt(Box::new(cx.stmt_item(sp, test_const))),
Annotatable::Stmt(Box::new(cx.stmt_item(sp, item)))]))vec![
381 Annotatable::Stmt(Box::new(cx.stmt_item(sp, test_extern))),
383 Annotatable::Stmt(Box::new(cx.stmt_item(sp, test_const))),
385 Annotatable::Stmt(Box::new(cx.stmt_item(sp, item))),
387 ]
388 } else {
389 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(test_extern), Annotatable::Item(test_const),
Annotatable::Item(item)]))vec![
390 Annotatable::Item(test_extern),
392 Annotatable::Item(test_const),
394 Annotatable::Item(item),
396 ]
397 }
398}
399
400fn not_testable_error(cx: &ExtCtxt<'_>, is_bench: bool, attr_sp: Span, item: Option<&ast::Item>) {
401 let dcx = cx.dcx();
402 let name = if is_bench { "bench" } else { "test" };
403 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the `{0}` attribute may only be used on a free function",
name))
})format!("the `{name}` attribute may only be used on a free function");
404 let level = match item.map(|i| &i.kind) {
405 Some(ast::ItemKind::MacCall(_)) => Level::Warning(None),
408 _ => Level::Error,
409 };
410 let mut err = Diag::new(dcx, level, msg);
411 err.span(attr_sp);
412 if let Some(item) = item {
413 err.span_label(
414 item.span,
415 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected a non-associated function, found {0} {1}",
item.kind.article(), item.kind.descr()))
})format!(
416 "expected a non-associated function, found {} {}",
417 item.kind.article(),
418 item.kind.descr()
419 ),
420 );
421 }
422 err.span_label(attr_sp, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the `{0}` attribute causes a function to be run as a test and has no effect on non-functions",
name))
})format!("the `{name}` attribute causes a function to be run as a test and has no effect on non-functions"));
423
424 if !is_bench {
425 err.with_span_suggestion(attr_sp,
426 "replace with conditional compilation to make the item only exist when tests are being run",
427 "#[cfg(test)]",
428 Applicability::MaybeIncorrect).emit();
429 } else {
430 err.emit();
431 }
432}
433
434fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, usize, usize) {
435 let span = fn_.ident.span;
436 let (source_file, lo_line, lo_col, hi_line, hi_col) =
437 cx.sess.source_map().span_to_location_info(span);
438
439 let file_name = match source_file {
440 Some(sf) => sf.name.display(RemapPathScopeComponents::MACRO).to_string(),
441 None => "no-location".to_string(),
442 };
443
444 (Symbol::intern(&file_name), lo_line, lo_col, hi_line, hi_col)
445}
446
447fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String {
448 join_path_idents(mod_path.iter().chain(iter::once(item_ident)))
449}
450
451enum ShouldPanic {
452 No,
453 Yes(Option<Symbol>),
454}
455
456fn should_ignore(i: &ast::Item) -> bool {
457 attr::contains_name(&i.attrs, sym::ignore)
458}
459
460fn should_ignore_message(i: &ast::Item) -> Option<Symbol> {
461 match attr::find_by_name(&i.attrs, sym::ignore) {
462 Some(attr) => {
463 match attr.meta_item_list() {
464 Some(_) => None,
466 None => attr.value_str(),
468 }
469 }
470 None => None,
471 }
472}
473
474fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
475 if let Some(Attribute::Parsed(AttributeKind::ShouldPanic { reason, .. })) =
476 AttributeParser::parse_limited_sym(cx.sess, &i.attrs, &[sym::should_panic])
477 {
478 ShouldPanic::Yes(reason)
479 } else {
480 ShouldPanic::No
481 }
482}
483
484enum TestType {
485 UnitTest,
486 IntegrationTest,
487 Unknown,
488}
489
490fn test_type(cx: &ExtCtxt<'_>) -> TestType {
494 let crate_path = cx.root_path.as_path();
499
500 if crate_path.ends_with("src") {
501 TestType::UnitTest
503 } else if crate_path.ends_with("tests") {
504 TestType::IntegrationTest
506 } else {
507 TestType::Unknown
509 }
510}
511
512fn check_test_signature(
513 cx: &ExtCtxt<'_>,
514 i: &ast::Item,
515 f: &ast::Fn,
516) -> Result<(), ErrorGuaranteed> {
517 let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
518 let dcx = cx.dcx();
519
520 if let ast::Safety::Unsafe(span) = f.sig.header.safety {
521 return Err(dcx.emit_err(diagnostics::TestBadFn {
522 span: i.span,
523 cause: span,
524 kind: "unsafe",
525 }));
526 }
527
528 if let Some(coroutine_marker) = f.sig.header.coroutine_marker {
529 return Err(dcx.emit_err(diagnostics::TestBadFn {
530 span: i.span,
531 cause: coroutine_marker.span,
532 kind: coroutine_marker.kind.as_str(),
533 }));
534 }
535
536 let has_output = match &f.sig.decl.output {
539 ast::FnRetTy::Default(..) => false,
540 ast::FnRetTy::Ty(t) if t.kind.is_unit() => false,
541 _ => true,
542 };
543
544 if !f.sig.decl.inputs.is_empty() {
545 return Err(dcx.span_err(i.span, "functions used as tests can not have any arguments"));
546 }
547
548 if has_should_panic_attr && has_output {
549 return Err(dcx.span_err(i.span, "functions using `#[should_panic]` must return `()`"));
550 }
551
552 if f.generics.params.iter().any(|param| !#[allow(non_exhaustive_omitted_patterns)] match param.kind {
GenericParamKind::Lifetime => true,
_ => false,
}matches!(param.kind, GenericParamKind::Lifetime)) {
553 return Err(dcx.span_err(
554 i.span,
555 "functions used as tests can not have any non-lifetime generic parameters",
556 ));
557 }
558
559 Ok(())
560}
561
562fn check_bench_signature(
563 cx: &ExtCtxt<'_>,
564 i: &ast::Item,
565 f: &ast::Fn,
566) -> Result<(), ErrorGuaranteed> {
567 if f.sig.decl.inputs.len() != 1 {
570 return Err(cx.dcx().emit_err(diagnostics::BenchSig { span: i.span }));
571 }
572 Ok(())
573}