1use std::iter;
5
6use rustc_ast::{self as ast, GenericParamKind, HasNodeId, attr, join_path_idents};
7use rustc_ast_pretty::pprust;
8use rustc_attr_parsing::AttributeParser;
9use rustc_data_structures::assert_matches;
10use rustc_errors::{Applicability, Diag, Level};
11use rustc_expand::base::*;
12use rustc_hir::Attribute;
13use rustc_hir::attrs::AttributeKind;
14use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym};
15use thin_vec::{ThinVec, thin_vec};
16use tracing::debug;
17
18use crate::errors;
19use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
20
21pub(crate) fn expand_test_case(
29 ecx: &mut ExtCtxt<'_>,
30 attr_sp: Span,
31 meta_item: &ast::MetaItem,
32 anno_item: Annotatable,
33) -> Vec<Annotatable> {
34 check_builtin_macro_attribute(ecx, meta_item, sym::test_case);
35 warn_on_duplicate_attribute(ecx, &anno_item, sym::test_case);
36
37 let sp = ecx.with_def_site_ctxt(attr_sp);
38 let (mut item, is_stmt) = match anno_item {
39 Annotatable::Item(item) => (item, false),
40 Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => {
41 if let ast::StmtKind::Item(i) = stmt.kind {
42 (i, true)
43 } else {
44 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
45 }
46 }
47 _ => {
48 ecx.dcx().emit_err(errors::TestCaseNonItem { span: anno_item.span() });
49 return ::alloc::vec::Vec::new()vec![];
50 }
51 };
52
53 if !ecx.ecfg.should_test {
54 return ::alloc::vec::Vec::new()vec![];
55 }
56
57 match &mut item.kind {
60 ast::ItemKind::Fn(box ast::Fn { ident, .. })
61 | ast::ItemKind::Const(box ast::ConstItem { ident, .. })
62 | ast::ItemKind::Static(box ast::StaticItem { ident, .. }) => {
63 ident.span = ident.span.with_ctxt(sp.ctxt());
64 let test_path_symbol = Symbol::intern(&item_path(
65 &ecx.current_expansion.module.mod_path[1..],
67 ident,
68 ));
69 item.vis = ast::Visibility {
70 span: item.vis.span,
71 kind: ast::VisibilityKind::Public,
72 tokens: None,
73 };
74 item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp));
75 }
76 _ => {}
77 }
78
79 let ret = if is_stmt {
80 Annotatable::Stmt(Box::new(ecx.stmt_item(item.span, item)))
81 } else {
82 Annotatable::Item(item)
83 };
84
85 <[_]>::into_vec(::alloc::boxed::box_new([ret]))vec![ret]
86}
87
88pub(crate) fn expand_test(
89 cx: &mut ExtCtxt<'_>,
90 attr_sp: Span,
91 meta_item: &ast::MetaItem,
92 item: Annotatable,
93) -> Vec<Annotatable> {
94 check_builtin_macro_attribute(cx, meta_item, sym::test);
95 warn_on_duplicate_attribute(cx, &item, sym::test);
96 expand_test_or_bench(cx, attr_sp, item, false)
97}
98
99pub(crate) fn expand_bench(
100 cx: &mut ExtCtxt<'_>,
101 attr_sp: Span,
102 meta_item: &ast::MetaItem,
103 item: Annotatable,
104) -> Vec<Annotatable> {
105 check_builtin_macro_attribute(cx, meta_item, sym::bench);
106 warn_on_duplicate_attribute(cx, &item, sym::bench);
107 expand_test_or_bench(cx, attr_sp, item, true)
108}
109
110pub(crate) fn expand_test_or_bench(
111 cx: &ExtCtxt<'_>,
112 attr_sp: Span,
113 item: Annotatable,
114 is_bench: bool,
115) -> Vec<Annotatable> {
116 let (item, is_stmt) = match item {
117 Annotatable::Item(i) => (i, false),
118 Annotatable::Stmt(box ast::Stmt { kind: ast::StmtKind::Item(i), .. }) => (i, true),
119 other => {
120 not_testable_error(cx, is_bench, attr_sp, None);
121 return <[_]>::into_vec(::alloc::boxed::box_new([other]))vec![other];
122 }
123 };
124
125 let ast::ItemKind::Fn(fn_) = &item.kind else {
126 not_testable_error(cx, is_bench, attr_sp, Some(&item));
127 return if is_stmt {
128 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Stmt(Box::new(cx.stmt_item(item.span,
item)))]))vec![Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]
129 } else {
130 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Item(item)]))vec![Annotatable::Item(item)]
131 };
132 };
133
134 if !cx.ecfg.should_test {
136 return ::alloc::vec::Vec::new()vec![];
137 }
138
139 if let Some(attr) = attr::find_by_name(&item.attrs, sym::naked) {
140 cx.dcx().emit_err(errors::NakedFunctionTestingAttribute {
141 testing_span: attr_sp,
142 naked_span: attr.span,
143 });
144 return <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Item(item)]))vec![Annotatable::Item(item)];
145 }
146
147 let check_result = if is_bench {
151 check_bench_signature(cx, &item, fn_)
152 } else {
153 check_test_signature(cx, &item, fn_)
154 };
155 if check_result.is_err() {
156 return if is_stmt {
157 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Stmt(Box::new(cx.stmt_item(item.span,
item)))]))vec![Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]
158 } else {
159 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Item(item)]))vec![Annotatable::Item(item)]
160 };
161 }
162
163 let sp = cx.with_def_site_ctxt(item.span);
164 let ret_ty_sp = cx.with_def_site_ctxt(fn_.sig.decl.output.span());
165 let attr_sp = cx.with_def_site_ctxt(attr_sp);
166
167 let test_ident = Ident::new(sym::test, attr_sp);
168
169 let test_path = |name| cx.path(ret_ty_sp, <[_]>::into_vec(::alloc::boxed::box_new([test_ident,
Ident::from_str_and_span(name, sp)]))vec![test_ident, Ident::from_str_and_span(name, sp)]);
171
172 let should_panic_path = |name| {
174 cx.path(
175 sp,
176 <[_]>::into_vec(::alloc::boxed::box_new([test_ident,
Ident::from_str_and_span("ShouldPanic", sp),
Ident::from_str_and_span(name, sp)]))vec![
177 test_ident,
178 Ident::from_str_and_span("ShouldPanic", sp),
179 Ident::from_str_and_span(name, sp),
180 ],
181 )
182 };
183
184 let test_type_path = |name| {
186 cx.path(
187 sp,
188 <[_]>::into_vec(::alloc::boxed::box_new([test_ident,
Ident::from_str_and_span("TestType", sp),
Ident::from_str_and_span(name, sp)]))vec![
189 test_ident,
190 Ident::from_str_and_span("TestType", sp),
191 Ident::from_str_and_span(name, sp),
192 ],
193 )
194 };
195
196 let field = |name, expr| cx.field_imm(sp, Ident::from_str_and_span(name, sp), expr);
198
199 let coverage_off = |mut expr: Box<ast::Expr>| {
204 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(_));
205 expr.attrs.push(cx.attr_nested_word(sym::coverage, sym::off, sp));
206 expr
207 };
208
209 let test_fn = if is_bench {
210 let bencher_param =
212 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);
213 cx.expr_call(
214 sp,
215 cx.expr_path(test_path("StaticBenchFn")),
216 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(coverage_off(cx.lambda1(sp,
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,
<[_]>::into_vec(::alloc::boxed::box_new([fn_.ident])))),
{
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_ident(sp, bencher_param));
vec
}));
vec
}), bencher_param)));
vec
}thin_vec![
217 coverage_off(cx.lambda1(
220 sp,
221 cx.expr_call(
222 sp,
223 cx.expr_path(test_path("assert_test_result")),
224 thin_vec![
225 cx.expr_call(
227 ret_ty_sp,
228 cx.expr_path(cx.path(sp, vec![fn_.ident])),
229 thin_vec![cx.expr_ident(sp, bencher_param)],
230 ),
231 ],
232 ),
233 bencher_param,
234 )), ],
236 )
237 } else {
238 cx.expr_call(
239 sp,
240 cx.expr_path(test_path("StaticTestFn")),
241 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(coverage_off(cx.lambda0(sp,
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,
<[_]>::into_vec(::alloc::boxed::box_new([fn_.ident])))),
ThinVec::new()));
vec
}))));
vec
}thin_vec![
242 coverage_off(cx.lambda0(
245 sp,
246 cx.expr_call(
248 sp,
249 cx.expr_path(test_path("assert_test_result")),
250 thin_vec![
251 cx.expr_call(
253 ret_ty_sp,
254 cx.expr_path(cx.path(sp, vec![fn_.ident])),
255 ThinVec::new(),
256 ), ],
258 ), )), ],
261 )
262 };
263
264 let test_path_symbol = Symbol::intern(&item_path(
265 &cx.current_expansion.module.mod_path[1..],
267 &fn_.ident,
268 ));
269
270 let location_info = get_location_info(cx, &fn_);
271
272 let mut test_const =
273 cx.item(
274 sp,
275 {
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![
276 cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
278 cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp),
280 cx.attr_nested_word(sym::doc, sym::hidden, attr_sp),
282 ],
283 ast::ItemKind::Const(
285 ast::ConstItem {
286 defaultness: ast::Defaultness::Final,
287 ident: Ident::new(fn_.ident.name, sp),
288 generics: ast::Generics::default(),
289 ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
290 define_opaque: None,
291 rhs: Some(ast::ConstItemRhs::Body(
293 cx.expr_struct(
294 sp,
295 test_path("TestDescAndFn"),
296 {
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![
297 field(
299 "desc",
300 cx.expr_struct(sp, test_path("TestDesc"), thin_vec![
301 field(
303 "name",
304 cx.expr_call(
305 sp,
306 cx.expr_path(test_path("StaticTestName")),
307 thin_vec![cx.expr_str(sp, test_path_symbol)],
308 ),
309 ),
310 field("ignore", cx.expr_bool(sp, should_ignore(&item)),),
312 field(
314 "ignore_message",
315 if let Some(msg) = should_ignore_message(&item) {
316 cx.expr_some(sp, cx.expr_str(sp, msg))
317 } else {
318 cx.expr_none(sp)
319 },
320 ),
321 field("source_file", cx.expr_str(sp, location_info.0)),
323 field("start_line", cx.expr_usize(sp, location_info.1)),
325 field("start_col", cx.expr_usize(sp, location_info.2)),
327 field("end_line", cx.expr_usize(sp, location_info.3)),
329 field("end_col", cx.expr_usize(sp, location_info.4)),
331 field("compile_fail", cx.expr_bool(sp, false)),
333 field("no_run", cx.expr_bool(sp, false)),
335 field("should_panic", match should_panic(cx, &item) {
337 ShouldPanic::No => {
339 cx.expr_path(should_panic_path("No"))
340 }
341 ShouldPanic::Yes(None) => {
343 cx.expr_path(should_panic_path("Yes"))
344 }
345 ShouldPanic::Yes(Some(sym)) => cx.expr_call(
347 sp,
348 cx.expr_path(should_panic_path("YesWithMessage")),
349 thin_vec![cx.expr_str(sp, sym)],
350 ),
351 },),
352 field("test_type", match test_type(cx) {
354 TestType::UnitTest => {
356 cx.expr_path(test_type_path("UnitTest"))
357 }
358 TestType::IntegrationTest => {
360 cx.expr_path(test_type_path("IntegrationTest"))
361 }
362 TestType::Unknown => {
364 cx.expr_path(test_type_path("Unknown"))
365 }
366 },),
367 ],),
369 ),
370 field("testfn", test_fn), ],
373 ), )),
375 }
376 .into(),
377 ),
378 );
379 test_const.vis.kind = ast::VisibilityKind::Public;
380
381 let test_extern =
383 cx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident));
384
385 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_builtin_macros/src/test.rs:385",
"rustc_builtin_macros::test", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_builtin_macros/src/test.rs"),
::tracing_core::__macro_support::Option::Some(385u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("synthetic test item:\n{0}\n",
pprust::item_to_string(&test_const)) as &dyn Value))])
});
} else { ; }
};debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
386
387 if is_stmt {
388 <[_]>::into_vec(::alloc::boxed::box_new([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![
389 Annotatable::Stmt(Box::new(cx.stmt_item(sp, test_extern))),
391 Annotatable::Stmt(Box::new(cx.stmt_item(sp, test_const))),
393 Annotatable::Stmt(Box::new(cx.stmt_item(sp, item))),
395 ]
396 } else {
397 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Item(test_extern),
Annotatable::Item(test_const), Annotatable::Item(item)]))vec![
398 Annotatable::Item(test_extern),
400 Annotatable::Item(test_const),
402 Annotatable::Item(item),
404 ]
405 }
406}
407
408fn not_testable_error(cx: &ExtCtxt<'_>, is_bench: bool, attr_sp: Span, item: Option<&ast::Item>) {
409 let dcx = cx.dcx();
410 let name = if is_bench { "bench" } else { "test" };
411 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");
412 let level = match item.map(|i| &i.kind) {
413 Some(ast::ItemKind::MacCall(_)) => Level::Warning,
416 _ => Level::Error,
417 };
418 let mut err = Diag::<()>::new(dcx, level, msg);
419 err.span(attr_sp);
420 if let Some(item) = item {
421 err.span_label(
422 item.span,
423 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected a non-associated function, found {0} {1}",
item.kind.article(), item.kind.descr()))
})format!(
424 "expected a non-associated function, found {} {}",
425 item.kind.article(),
426 item.kind.descr()
427 ),
428 );
429 }
430 err.span_label(attr_sp, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the `#[{0}]` macro causes a function to be run as a test and has no effect on non-functions",
name))
})format!("the `#[{name}]` macro causes a function to be run as a test and has no effect on non-functions"));
431
432 if !is_bench {
433 err.with_span_suggestion(attr_sp,
434 "replace with conditional compilation to make the item only exist when tests are being run",
435 "#[cfg(test)]",
436 Applicability::MaybeIncorrect).emit();
437 } else {
438 err.emit();
439 }
440}
441
442fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, usize, usize) {
443 let span = fn_.ident.span;
444 let (source_file, lo_line, lo_col, hi_line, hi_col) =
445 cx.sess.source_map().span_to_location_info(span);
446
447 let file_name = match source_file {
448 Some(sf) => sf.name.display(RemapPathScopeComponents::MACRO).to_string(),
449 None => "no-location".to_string(),
450 };
451
452 (Symbol::intern(&file_name), lo_line, lo_col, hi_line, hi_col)
453}
454
455fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String {
456 join_path_idents(mod_path.iter().chain(iter::once(item_ident)))
457}
458
459enum ShouldPanic {
460 No,
461 Yes(Option<Symbol>),
462}
463
464fn should_ignore(i: &ast::Item) -> bool {
465 attr::contains_name(&i.attrs, sym::ignore)
466}
467
468fn should_ignore_message(i: &ast::Item) -> Option<Symbol> {
469 match attr::find_by_name(&i.attrs, sym::ignore) {
470 Some(attr) => {
471 match attr.meta_item_list() {
472 Some(_) => None,
474 None => attr.value_str(),
476 }
477 }
478 None => None,
479 }
480}
481
482fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
483 if let Some(Attribute::Parsed(AttributeKind::ShouldPanic { reason, .. })) =
484 AttributeParser::parse_limited(
485 cx.sess,
486 &i.attrs,
487 sym::should_panic,
488 i.span,
489 i.node_id(),
490 None,
491 )
492 {
493 ShouldPanic::Yes(reason)
494 } else {
495 ShouldPanic::No
496 }
497}
498
499enum TestType {
500 UnitTest,
501 IntegrationTest,
502 Unknown,
503}
504
505fn test_type(cx: &ExtCtxt<'_>) -> TestType {
509 let crate_path = cx.root_path.as_path();
514
515 if crate_path.ends_with("src") {
516 TestType::UnitTest
518 } else if crate_path.ends_with("tests") {
519 TestType::IntegrationTest
521 } else {
522 TestType::Unknown
524 }
525}
526
527fn check_test_signature(
528 cx: &ExtCtxt<'_>,
529 i: &ast::Item,
530 f: &ast::Fn,
531) -> Result<(), ErrorGuaranteed> {
532 let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
533 let dcx = cx.dcx();
534
535 if let ast::Safety::Unsafe(span) = f.sig.header.safety {
536 return Err(dcx.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
537 }
538
539 if let Some(coroutine_kind) = f.sig.header.coroutine_kind {
540 match coroutine_kind {
541 ast::CoroutineKind::Async { span, .. } => {
542 return Err(dcx.emit_err(errors::TestBadFn {
543 span: i.span,
544 cause: span,
545 kind: "async",
546 }));
547 }
548 ast::CoroutineKind::Gen { span, .. } => {
549 return Err(dcx.emit_err(errors::TestBadFn {
550 span: i.span,
551 cause: span,
552 kind: "gen",
553 }));
554 }
555 ast::CoroutineKind::AsyncGen { span, .. } => {
556 return Err(dcx.emit_err(errors::TestBadFn {
557 span: i.span,
558 cause: span,
559 kind: "async gen",
560 }));
561 }
562 }
563 }
564
565 let has_output = match &f.sig.decl.output {
568 ast::FnRetTy::Default(..) => false,
569 ast::FnRetTy::Ty(t) if t.kind.is_unit() => false,
570 _ => true,
571 };
572
573 if !f.sig.decl.inputs.is_empty() {
574 return Err(dcx.span_err(i.span, "functions used as tests can not have any arguments"));
575 }
576
577 if has_should_panic_attr && has_output {
578 return Err(dcx.span_err(i.span, "functions using `#[should_panic]` must return `()`"));
579 }
580
581 if f.generics.params.iter().any(|param| !#[allow(non_exhaustive_omitted_patterns)] match param.kind {
GenericParamKind::Lifetime => true,
_ => false,
}matches!(param.kind, GenericParamKind::Lifetime)) {
582 return Err(dcx.span_err(
583 i.span,
584 "functions used as tests can not have any non-lifetime generic parameters",
585 ));
586 }
587
588 Ok(())
589}
590
591fn check_bench_signature(
592 cx: &ExtCtxt<'_>,
593 i: &ast::Item,
594 f: &ast::Fn,
595) -> Result<(), ErrorGuaranteed> {
596 if f.sig.decl.inputs.len() != 1 {
599 return Err(cx.dcx().emit_err(errors::BenchSig { span: i.span }));
600 }
601 Ok(())
602}