2011-07-06 23:28:07 +00:00
|
|
|
// Code that generates a test runner to run all the tests in a crate
|
|
|
|
|
2024-11-18 02:01:01 +00:00
|
|
|
use std::mem;
|
2024-07-28 22:13:50 +00:00
|
|
|
|
2020-04-27 17:56:11 +00:00
|
|
|
use rustc_ast as ast;
|
2020-07-30 01:27:50 +00:00
|
|
|
use rustc_ast::entry::EntryPointType;
|
2023-11-10 02:11:24 +00:00
|
|
|
use rustc_ast::mut_visit::*;
|
2020-02-29 17:37:32 +00:00
|
|
|
use rustc_ast::ptr::P;
|
2024-11-08 21:51:28 +00:00
|
|
|
use rustc_ast::visit::{Visitor, walk_item};
|
2021-02-16 21:56:07 +00:00
|
|
|
use rustc_ast::{ModKind, attr};
|
2024-06-18 10:35:56 +00:00
|
|
|
use rustc_errors::DiagCtxtHandle;
|
2020-06-27 20:51:28 +00:00
|
|
|
use rustc_expand::base::{ExtCtxt, ResolverExpand};
|
2019-12-29 14:23:55 +00:00
|
|
|
use rustc_expand::expand::{AstFragment, ExpansionConfig};
|
2019-11-29 23:23:38 +00:00
|
|
|
use rustc_feature::Features;
|
2024-04-14 20:11:14 +00:00
|
|
|
use rustc_lint_defs::BuiltinLintDiag;
|
2020-07-30 01:27:50 +00:00
|
|
|
use rustc_session::Session;
|
2023-08-03 12:43:42 +00:00
|
|
|
use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS;
|
2019-12-31 17:15:40 +00:00
|
|
|
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
|
2024-12-12 23:29:23 +00:00
|
|
|
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
|
2019-09-12 00:13:34 +00:00
|
|
|
use rustc_target::spec::PanicStrategy;
|
2024-11-18 02:01:01 +00:00
|
|
|
use smallvec::smallvec;
|
2022-11-23 00:55:16 +00:00
|
|
|
use thin_vec::{ThinVec, thin_vec};
|
|
|
|
use tracing::debug;
|
2019-07-18 18:29:15 +00:00
|
|
|
|
2023-04-10 15:04:14 +00:00
|
|
|
use crate::errors;
|
|
|
|
|
2022-07-26 21:40:48 +00:00
|
|
|
#[derive(Clone)]
|
2013-02-19 07:40:42 +00:00
|
|
|
struct Test {
|
2013-08-31 16:13:04 +00:00
|
|
|
span: Span,
|
2019-08-25 20:03:24 +00:00
|
|
|
ident: Ident,
|
2022-07-26 21:40:48 +00:00
|
|
|
name: Symbol,
|
2013-02-19 07:40:42 +00:00
|
|
|
}
|
2013-02-04 22:02:01 +00:00
|
|
|
|
2013-12-25 18:10:33 +00:00
|
|
|
struct TestCtxt<'a> {
|
|
|
|
ext_cx: ExtCtxt<'a>,
|
2019-09-12 00:13:34 +00:00
|
|
|
panic_strategy: PanicStrategy,
|
2019-08-28 21:47:52 +00:00
|
|
|
def_site: Span,
|
2018-07-21 01:04:02 +00:00
|
|
|
test_cases: Vec<Test>,
|
2016-11-16 10:52:37 +00:00
|
|
|
reexport_test_harness_main: Option<Symbol>,
|
2018-07-21 01:04:02 +00:00
|
|
|
test_runner: Option<ast::Path>,
|
2013-02-04 22:02:01 +00:00
|
|
|
}
|
2011-07-06 21:29:50 +00:00
|
|
|
|
2022-11-27 11:15:06 +00:00
|
|
|
/// Traverse the crate, collecting all the test functions, eliding any
|
|
|
|
/// existing main functions, and synthesizing a main test harness
|
2023-08-09 12:28:00 +00:00
|
|
|
pub fn inject(
|
|
|
|
krate: &mut ast::Crate,
|
|
|
|
sess: &Session,
|
|
|
|
features: &Features,
|
|
|
|
resolver: &mut dyn ResolverExpand,
|
|
|
|
) {
|
2023-12-18 00:15:13 +00:00
|
|
|
let dcx = sess.dcx();
|
2020-07-30 01:27:50 +00:00
|
|
|
let panic_strategy = sess.panic_strategy();
|
2020-11-08 11:27:51 +00:00
|
|
|
let platform_panic_strategy = sess.target.panic_strategy;
|
2020-07-30 01:27:50 +00:00
|
|
|
|
2019-08-25 20:03:24 +00:00
|
|
|
// Check for #![reexport_test_harness_main = "some_name"] which gives the
|
|
|
|
// main test function the name `some_name` without hygiene. This needs to be
|
2014-08-08 14:01:05 +00:00
|
|
|
// unconditional, so that the attribute is still marked as used in
|
|
|
|
// non-test builds.
|
|
|
|
let reexport_test_harness_main =
|
2023-03-19 17:32:34 +00:00
|
|
|
attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
|
2014-08-08 14:01:05 +00:00
|
|
|
|
2018-07-21 01:04:02 +00:00
|
|
|
// Do this here so that the test_runner crate attribute gets marked as used
|
|
|
|
// even in non-test builds
|
2023-12-18 00:15:13 +00:00
|
|
|
let test_runner = get_test_runner(dcx, krate);
|
2018-07-21 01:04:02 +00:00
|
|
|
|
2023-04-09 19:37:31 +00:00
|
|
|
if sess.is_test_crate() {
|
2022-07-06 12:44:47 +00:00
|
|
|
let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
|
2019-09-20 02:33:38 +00:00
|
|
|
(PanicStrategy::Abort, true) => PanicStrategy::Abort,
|
|
|
|
(PanicStrategy::Abort, false) => {
|
2020-07-30 01:27:50 +00:00
|
|
|
if panic_strategy == platform_panic_strategy {
|
|
|
|
// Silently allow compiling with panic=abort on these platforms,
|
|
|
|
// but with old behavior (abort if a test fails).
|
|
|
|
} else {
|
2023-12-18 00:15:13 +00:00
|
|
|
dcx.emit_err(errors::TestsNotSupport {});
|
2020-07-30 01:27:50 +00:00
|
|
|
}
|
2019-09-20 02:33:38 +00:00
|
|
|
PanicStrategy::Unwind
|
|
|
|
}
|
|
|
|
(PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
|
|
|
|
};
|
2018-02-08 22:16:39 +00:00
|
|
|
generate_test_harness(
|
|
|
|
sess,
|
|
|
|
resolver,
|
|
|
|
reexport_test_harness_main,
|
2019-09-12 00:13:34 +00:00
|
|
|
krate,
|
2023-08-09 12:28:00 +00:00
|
|
|
features,
|
2019-09-12 00:13:34 +00:00
|
|
|
panic_strategy,
|
|
|
|
test_runner,
|
|
|
|
)
|
2012-01-06 01:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-25 18:10:33 +00:00
|
|
|
struct TestHarnessGenerator<'a> {
|
|
|
|
cx: TestCtxt<'a>,
|
2019-08-25 20:03:24 +00:00
|
|
|
tests: Vec<Test>,
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2021-10-17 16:32:34 +00:00
|
|
|
impl TestHarnessGenerator<'_> {
|
|
|
|
fn add_test_cases(&mut self, node_id: ast::NodeId, span: Span, prev_tests: Vec<Test>) {
|
|
|
|
let mut tests = mem::replace(&mut self.tests, prev_tests);
|
|
|
|
|
|
|
|
if !tests.is_empty() {
|
|
|
|
// Create an identifier that will hygienically resolve the test
|
|
|
|
// case name, even in another module.
|
|
|
|
let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass(
|
|
|
|
span,
|
|
|
|
AstPass::TestHarness,
|
|
|
|
&[],
|
|
|
|
Some(node_id),
|
|
|
|
);
|
|
|
|
for test in &mut tests {
|
|
|
|
// See the comment on `mk_main` for why we're using
|
|
|
|
// `apply_mark` directly.
|
|
|
|
test.ident.span =
|
|
|
|
test.ident.span.apply_mark(expn_id.to_expn_id(), Transparency::Opaque);
|
|
|
|
}
|
|
|
|
self.cx.test_cases.extend(tests);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Overhaul `syntax::fold::Folder`.
This commit changes `syntax::fold::Folder` from a functional style
(where most methods take a `T` and produce a new `T`) to a more
imperative style (where most methods take and modify a `&mut T`), and
renames it `syntax::mut_visit::MutVisitor`.
The first benefit is speed. The functional style does not require any
reallocations, due to the use of `P::map` and
`MoveMap::move_{,flat_}map`. However, every field in the AST must be
overwritten; even those fields that are unchanged are overwritten with
the same value. This causes a lot of unnecessary memory writes. The
imperative style reduces instruction counts by 1--3% across a wide range
of workloads, particularly incremental workloads.
The second benefit is conciseness; the imperative style is usually more
concise. E.g. compare the old functional style:
```
fn fold_abc(&mut self, abc: ABC) {
ABC {
a: fold_a(abc.a),
b: fold_b(abc.b),
c: abc.c,
}
}
```
with the imperative style:
```
fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
visit_a(a);
visit_b(b);
}
```
(The reductions get larger in more complex examples.)
Overall, the patch removes over 200 lines of code -- even though the new
code has more comments -- and a lot of the remaining lines have fewer
characters.
Some notes:
- The old style used methods called `fold_*`. The new style mostly uses
methods called `visit_*`, but there are a few methods that map a `T`
to something other than a `T`, which are called `flat_map_*` (`T` maps
to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s).
- `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed
`map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to
reflect their slightly changed signatures.
- Although this commit renames the `fold` module as `mut_visit`, it
keeps it in the `fold.rs` file, so as not to confuse git. The next
commit will rename the file.
2019-02-05 04:20:55 +00:00
|
|
|
impl<'a> MutVisitor for TestHarnessGenerator<'a> {
|
|
|
|
fn visit_crate(&mut self, c: &mut ast::Crate) {
|
2021-10-17 16:32:34 +00:00
|
|
|
let prev_tests = mem::take(&mut self.tests);
|
2024-07-17 11:23:35 +00:00
|
|
|
walk_crate(self, c);
|
2022-03-03 23:45:25 +00:00
|
|
|
self.add_test_cases(ast::CRATE_NODE_ID, c.spans.inner_span, prev_tests);
|
2013-08-29 19:10:02 +00:00
|
|
|
|
2018-07-21 01:04:02 +00:00
|
|
|
// Create a main function to run our tests
|
2021-02-14 18:14:12 +00:00
|
|
|
c.items.push(mk_main(&mut self.cx));
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2025-05-27 07:17:17 +00:00
|
|
|
fn visit_item(&mut self, item: &mut ast::Item) {
|
2023-03-19 17:32:34 +00:00
|
|
|
if let Some(name) = get_test_name(&item) {
|
2018-07-21 01:04:02 +00:00
|
|
|
debug!("this is a test item");
|
2018-05-17 05:55:18 +00:00
|
|
|
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
// `unwrap` is ok because only functions, consts, and static should reach here.
|
|
|
|
let test = Test { span: item.span, ident: item.kind.ident().unwrap(), name };
|
2019-08-25 20:03:24 +00:00
|
|
|
self.tests.push(test);
|
2016-09-23 07:23:01 +00:00
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
|
2014-07-21 05:10:11 +00:00
|
|
|
// We don't want to recurse into anything other than mods, since
|
|
|
|
// mods or tests inside of functions will break things
|
2024-12-05 21:19:08 +00:00
|
|
|
if let ast::ItemKind::Mod(
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
_,
|
2024-12-05 21:19:08 +00:00
|
|
|
_,
|
|
|
|
ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _),
|
|
|
|
) = item.kind
|
2022-12-06 13:22:36 +00:00
|
|
|
{
|
2021-10-17 16:32:34 +00:00
|
|
|
let prev_tests = mem::take(&mut self.tests);
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
walk_item_kind(&mut item.kind, item.span, item.id, &mut item.vis, (), self);
|
2021-10-17 16:32:34 +00:00
|
|
|
self.add_test_cases(item.id, span, prev_tests);
|
2023-08-03 12:43:42 +00:00
|
|
|
} else {
|
|
|
|
// But in those cases, we emit a lint to warn the user of these missing tests.
|
|
|
|
walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item);
|
2016-09-23 07:23:01 +00:00
|
|
|
}
|
2014-07-21 05:10:11 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
|
2023-08-03 12:43:42 +00:00
|
|
|
struct InnerItemLinter<'a> {
|
|
|
|
sess: &'a Session,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Visitor<'a> for InnerItemLinter<'_> {
|
|
|
|
fn visit_item(&mut self, i: &'a ast::Item) {
|
|
|
|
if let Some(attr) = attr::find_by_name(&i.attrs, sym::rustc_test_marker) {
|
2024-05-20 17:47:54 +00:00
|
|
|
self.sess.psess.buffer_lint(
|
2023-08-03 12:43:42 +00:00
|
|
|
UNNAMEABLE_TEST_ITEMS,
|
|
|
|
attr.span,
|
|
|
|
i.id,
|
2024-04-14 20:11:14 +00:00
|
|
|
BuiltinLintDiag::UnnameableTestItems,
|
2023-08-03 12:43:42 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-29 06:47:47 +00:00
|
|
|
fn entry_point_type(item: &ast::Item, at_root: bool) -> EntryPointType {
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
match &item.kind {
|
|
|
|
ast::ItemKind::Fn(fn_) => {
|
|
|
|
rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(fn_.ident.name))
|
2020-07-30 01:27:50 +00:00
|
|
|
}
|
|
|
|
_ => EntryPointType::None,
|
|
|
|
}
|
|
|
|
}
|
2023-09-29 06:54:48 +00:00
|
|
|
|
2018-07-21 01:04:02 +00:00
|
|
|
/// A folder used to remove any entry points (like fn main) because the harness
|
2023-10-19 21:46:28 +00:00
|
|
|
/// coroutine will provide its own
|
2020-07-30 01:27:50 +00:00
|
|
|
struct EntryPointCleaner<'a> {
|
2015-08-24 15:34:04 +00:00
|
|
|
// Current depth in the ast
|
2020-07-30 01:27:50 +00:00
|
|
|
sess: &'a Session,
|
2015-08-24 15:34:04 +00:00
|
|
|
depth: usize,
|
2019-08-28 21:47:52 +00:00
|
|
|
def_site: Span,
|
2015-08-24 15:34:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 01:27:50 +00:00
|
|
|
impl<'a> MutVisitor for EntryPointCleaner<'a> {
|
2025-05-27 07:17:17 +00:00
|
|
|
fn visit_item(&mut self, item: &mut ast::Item) {
|
2015-08-24 15:34:04 +00:00
|
|
|
self.depth += 1;
|
2024-11-18 02:01:01 +00:00
|
|
|
ast::mut_visit::walk_item(self, item);
|
2015-08-24 15:34:04 +00:00
|
|
|
self.depth -= 1;
|
|
|
|
|
2024-12-14 08:13:12 +00:00
|
|
|
// Remove any #[rustc_main] from the AST so it doesn't
|
2015-08-24 18:33:22 +00:00
|
|
|
// clash with the one we're going to add, but mark it as
|
2015-08-24 15:34:04 +00:00
|
|
|
// #[allow(dead_code)] to avoid printing warnings.
|
2024-11-18 02:01:01 +00:00
|
|
|
match entry_point_type(&item, self.depth == 0) {
|
2024-12-14 08:13:12 +00:00
|
|
|
EntryPointType::MainNamed | EntryPointType::RustcMainAttr => {
|
2024-11-18 02:01:01 +00:00
|
|
|
let allow_dead_code = attr::mk_attr_nested_word(
|
|
|
|
&self.sess.psess.attr_id_generator,
|
|
|
|
ast::AttrStyle::Outer,
|
|
|
|
ast::Safety::Default,
|
|
|
|
sym::allow,
|
|
|
|
sym::dead_code,
|
|
|
|
self.def_site,
|
|
|
|
);
|
2024-12-14 08:13:12 +00:00
|
|
|
item.attrs.retain(|attr| !attr.has_name(sym::rustc_main));
|
2024-11-18 02:01:01 +00:00
|
|
|
item.attrs.push(allow_dead_code);
|
2022-06-22 16:23:21 +00:00
|
|
|
}
|
2024-11-18 02:01:01 +00:00
|
|
|
EntryPointType::None | EntryPointType::OtherMain => {}
|
2015-08-24 15:34:04 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-21 01:04:02 +00:00
|
|
|
/// Crawl over the crate, inserting test reexports and the test main function
|
2014-07-25 02:44:24 +00:00
|
|
|
fn generate_test_harness(
|
2020-07-30 01:27:50 +00:00
|
|
|
sess: &Session,
|
2020-06-27 20:51:28 +00:00
|
|
|
resolver: &mut dyn ResolverExpand,
|
2016-11-16 10:52:37 +00:00
|
|
|
reexport_test_harness_main: Option<Symbol>,
|
Overhaul `syntax::fold::Folder`.
This commit changes `syntax::fold::Folder` from a functional style
(where most methods take a `T` and produce a new `T`) to a more
imperative style (where most methods take and modify a `&mut T`), and
renames it `syntax::mut_visit::MutVisitor`.
The first benefit is speed. The functional style does not require any
reallocations, due to the use of `P::map` and
`MoveMap::move_{,flat_}map`. However, every field in the AST must be
overwritten; even those fields that are unchanged are overwritten with
the same value. This causes a lot of unnecessary memory writes. The
imperative style reduces instruction counts by 1--3% across a wide range
of workloads, particularly incremental workloads.
The second benefit is conciseness; the imperative style is usually more
concise. E.g. compare the old functional style:
```
fn fold_abc(&mut self, abc: ABC) {
ABC {
a: fold_a(abc.a),
b: fold_b(abc.b),
c: abc.c,
}
}
```
with the imperative style:
```
fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
visit_a(a);
visit_b(b);
}
```
(The reductions get larger in more complex examples.)
Overall, the patch removes over 200 lines of code -- even though the new
code has more comments -- and a lot of the remaining lines have fewer
characters.
Some notes:
- The old style used methods called `fold_*`. The new style mostly uses
methods called `visit_*`, but there are a few methods that map a `T`
to something other than a `T`, which are called `flat_map_*` (`T` maps
to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s).
- `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed
`map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to
reflect their slightly changed signatures.
- Although this commit renames the `fold` module as `mut_visit`, it
keeps it in the `fold.rs` file, so as not to confuse git. The next
commit will rename the file.
2019-02-05 04:20:55 +00:00
|
|
|
krate: &mut ast::Crate,
|
2018-07-21 01:04:02 +00:00
|
|
|
features: &Features,
|
2019-09-12 00:13:34 +00:00
|
|
|
panic_strategy: PanicStrategy,
|
Overhaul `syntax::fold::Folder`.
This commit changes `syntax::fold::Folder` from a functional style
(where most methods take a `T` and produce a new `T`) to a more
imperative style (where most methods take and modify a `&mut T`), and
renames it `syntax::mut_visit::MutVisitor`.
The first benefit is speed. The functional style does not require any
reallocations, due to the use of `P::map` and
`MoveMap::move_{,flat_}map`. However, every field in the AST must be
overwritten; even those fields that are unchanged are overwritten with
the same value. This causes a lot of unnecessary memory writes. The
imperative style reduces instruction counts by 1--3% across a wide range
of workloads, particularly incremental workloads.
The second benefit is conciseness; the imperative style is usually more
concise. E.g. compare the old functional style:
```
fn fold_abc(&mut self, abc: ABC) {
ABC {
a: fold_a(abc.a),
b: fold_b(abc.b),
c: abc.c,
}
}
```
with the imperative style:
```
fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
visit_a(a);
visit_b(b);
}
```
(The reductions get larger in more complex examples.)
Overall, the patch removes over 200 lines of code -- even though the new
code has more comments -- and a lot of the remaining lines have fewer
characters.
Some notes:
- The old style used methods called `fold_*`. The new style mostly uses
methods called `visit_*`, but there are a few methods that map a `T`
to something other than a `T`, which are called `flat_map_*` (`T` maps
to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s).
- `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed
`map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to
reflect their slightly changed signatures.
- Although this commit renames the `fold` module as `mut_visit`, it
keeps it in the `fold.rs` file, so as not to confuse git. The next
commit will rename the file.
2019-02-05 04:20:55 +00:00
|
|
|
test_runner: Option<ast::Path>,
|
|
|
|
) {
|
2023-08-09 12:28:00 +00:00
|
|
|
let econfig = ExpansionConfig::default("test".to_string(), features);
|
2020-03-15 23:43:37 +00:00
|
|
|
let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);
|
2019-08-28 21:47:52 +00:00
|
|
|
|
|
|
|
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
|
|
|
|
DUMMY_SP,
|
|
|
|
AstPass::TestHarness,
|
2023-08-24 19:15:41 +00:00
|
|
|
&[sym::test, sym::rustc_attrs, sym::coverage_attribute],
|
2019-08-28 21:47:52 +00:00
|
|
|
None,
|
|
|
|
);
|
2021-06-25 18:43:04 +00:00
|
|
|
let def_site = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
|
2019-08-28 21:47:52 +00:00
|
|
|
|
|
|
|
// Remove the entry points
|
2020-07-30 01:27:50 +00:00
|
|
|
let mut cleaner = EntryPointCleaner { sess, depth: 0, def_site };
|
2019-08-28 21:47:52 +00:00
|
|
|
cleaner.visit_crate(krate);
|
|
|
|
|
2017-12-06 18:50:55 +00:00
|
|
|
let cx = TestCtxt {
|
2019-08-28 21:47:52 +00:00
|
|
|
ext_cx,
|
2019-09-12 00:13:34 +00:00
|
|
|
panic_strategy,
|
2019-08-28 21:47:52 +00:00
|
|
|
def_site,
|
2018-07-21 01:04:02 +00:00
|
|
|
test_cases: Vec::new(),
|
2017-08-07 05:54:09 +00:00
|
|
|
reexport_test_harness_main,
|
2018-07-21 01:04:02 +00:00
|
|
|
test_runner,
|
2013-02-04 22:02:01 +00:00
|
|
|
};
|
2011-07-27 12:19:39 +00:00
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
TestHarnessGenerator { cx, tests: Vec::new() }.visit_crate(krate);
|
2011-07-06 21:29:50 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 01:04:02 +00:00
|
|
|
/// Creates a function item for use as the main function of a test build.
|
|
|
|
/// This function will call the `test_runner` as specified by the crate attribute
|
2019-08-28 21:47:52 +00:00
|
|
|
///
|
|
|
|
/// By default this expands to
|
|
|
|
///
|
2024-05-01 15:37:22 +00:00
|
|
|
/// ```ignore (messes with test internals)
|
2021-04-08 13:37:38 +00:00
|
|
|
/// #[rustc_main]
|
2019-08-28 21:47:52 +00:00
|
|
|
/// pub fn main() {
|
|
|
|
/// extern crate test;
|
|
|
|
/// test::test_main_static(&[
|
|
|
|
/// &test_const1,
|
|
|
|
/// &test_const2,
|
|
|
|
/// &test_const3,
|
|
|
|
/// ]);
|
|
|
|
/// }
|
2020-05-01 20:32:33 +00:00
|
|
|
/// ```
|
2019-08-28 21:47:52 +00:00
|
|
|
///
|
|
|
|
/// Most of the Ident have the usual def-site hygiene for the AST pass. The
|
|
|
|
/// exception is the `test_const`s. These have a syntax context that has two
|
|
|
|
/// opaque marks: one from the expansion of `test` or `test_case`, and one
|
2024-11-18 02:01:01 +00:00
|
|
|
/// generated in `TestHarnessGenerator::visit_item`. When resolving this
|
2019-08-28 21:47:52 +00:00
|
|
|
/// identifier after failing to find a matching identifier in the root module
|
|
|
|
/// we remove the outer mark, and try resolving at its def-site, which will
|
|
|
|
/// then resolve to `test_const`.
|
|
|
|
///
|
|
|
|
/// The expansion here can be controlled by two attributes:
|
|
|
|
///
|
2020-05-01 20:32:33 +00:00
|
|
|
/// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main`
|
|
|
|
/// function and [`TestCtxt::test_runner`] provides a path that replaces
|
2019-08-28 21:47:52 +00:00
|
|
|
/// `test::test_main_static`.
|
2019-02-06 17:33:01 +00:00
|
|
|
fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
|
2019-08-28 21:47:52 +00:00
|
|
|
let sp = cx.def_site;
|
2015-01-22 02:13:08 +00:00
|
|
|
let ecx = &cx.ext_cx;
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
let test_ident = Ident::new(sym::test, sp);
|
2016-11-16 08:21:52 +00:00
|
|
|
|
2019-09-12 00:13:34 +00:00
|
|
|
let runner_name = match cx.panic_strategy {
|
|
|
|
PanicStrategy::Unwind => "test_main_static",
|
|
|
|
PanicStrategy::Abort => "test_main_static_abort",
|
|
|
|
};
|
|
|
|
|
2015-01-22 02:13:08 +00:00
|
|
|
// test::test_main_static(...)
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| {
|
|
|
|
ecx.path(sp, vec![test_ident, Ident::from_str_and_span(runner_name, sp)])
|
|
|
|
});
|
2018-07-21 01:04:02 +00:00
|
|
|
|
|
|
|
test_runner.span = sp;
|
|
|
|
|
2018-10-25 18:11:11 +00:00
|
|
|
let test_main_path_expr = ecx.expr_path(test_runner);
|
2022-11-23 00:55:16 +00:00
|
|
|
let call_test_main = ecx.expr_call(sp, test_main_path_expr, thin_vec![mk_tests_slice(cx, sp)]);
|
2015-01-22 02:13:08 +00:00
|
|
|
let call_test_main = ecx.stmt_expr(call_test_main);
|
2018-07-21 01:04:02 +00:00
|
|
|
|
2019-08-25 20:03:24 +00:00
|
|
|
// extern crate test
|
2022-08-17 02:34:33 +00:00
|
|
|
let test_extern_stmt = ecx.stmt_item(
|
|
|
|
sp,
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
ecx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)),
|
2022-08-17 02:34:33 +00:00
|
|
|
);
|
2018-07-21 01:04:02 +00:00
|
|
|
|
2021-04-08 13:37:38 +00:00
|
|
|
// #[rustc_main]
|
Avoid more `MetaItem`-to-`Attribute` conversions.
There is code for converting `Attribute` (syntactic) to `MetaItem`
(semantic). There is also code for the reverse direction. The reverse
direction isn't really necessary; it's currently only used when
generating attributes, e.g. in `derive` code.
This commit adds some new functions for creating `Attributes`s directly,
without involving `MetaItem`s: `mk_attr_word`, `mk_attr_name_value_str`,
`mk_attr_nested_word`, and
`ExtCtxt::attr_{word,name_value_str,nested_word}`.
These new methods replace the old functions for creating `Attribute`s:
`mk_attr_inner`, `mk_attr_outer`, and `ExtCtxt::attribute`. Those
functions took `MetaItem`s as input, and relied on many other functions
that created `MetaItems`, which are also removed: `mk_name_value_item`,
`mk_list_item`, `mk_word_item`, `mk_nested_word_item`,
`{MetaItem,MetaItemKind,NestedMetaItem}::token_trees`,
`MetaItemKind::attr_args`, `MetaItemLit::{from_lit_kind,to_token}`,
`ExtCtxt::meta_word`.
Overall this cuts more than 100 lines of code and makes thing simpler.
2022-11-29 07:43:44 +00:00
|
|
|
let main_attr = ecx.attr_word(sym::rustc_main, sp);
|
2023-08-09 14:57:16 +00:00
|
|
|
// #[coverage(off)]
|
|
|
|
let coverage_attr = ecx.attr_nested_word(sym::coverage, sym::off, sp);
|
2024-09-11 10:08:14 +00:00
|
|
|
// #[doc(hidden)]
|
|
|
|
let doc_hidden_attr = ecx.attr_nested_word(sym::doc, sym::hidden, sp);
|
2019-08-28 21:47:52 +00:00
|
|
|
|
2015-01-22 02:13:08 +00:00
|
|
|
// pub fn main() { ... }
|
2022-11-23 00:55:16 +00:00
|
|
|
let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(ThinVec::new()));
|
2018-07-21 01:04:02 +00:00
|
|
|
|
|
|
|
// If no test runner is provided we need to import the test crate
|
|
|
|
let main_body = if cx.test_runner.is_none() {
|
2023-01-30 03:13:27 +00:00
|
|
|
ecx.block(sp, thin_vec![test_extern_stmt, call_test_main])
|
2018-07-21 01:04:02 +00:00
|
|
|
} else {
|
2023-01-30 03:13:27 +00:00
|
|
|
ecx.block(sp, thin_vec![call_test_main])
|
2018-07-21 01:04:02 +00:00
|
|
|
};
|
|
|
|
|
2022-11-23 00:55:16 +00:00
|
|
|
let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty));
|
2020-08-12 21:02:14 +00:00
|
|
|
let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp };
|
2021-11-07 08:43:49 +00:00
|
|
|
let defaultness = ast::Defaultness::Final;
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
|
|
|
|
// Honor the reexport_test_harness_main attribute
|
|
|
|
let main_ident = match cx.reexport_test_harness_main {
|
|
|
|
Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
|
|
|
|
None => Ident::new(sym::main, sp),
|
|
|
|
};
|
|
|
|
|
2021-11-07 08:43:49 +00:00
|
|
|
let main = ast::ItemKind::Fn(Box::new(ast::Fn {
|
|
|
|
defaultness,
|
2021-08-05 01:53:21 +00:00
|
|
|
sig,
|
Move `ast::Item::ident` into `ast::ItemKind`.
`ast::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
`Trait`, `TraitAlias`, `MacroDef`, `Delegation`.
- It's always empty for these item kinds: `Use`, `ForeignMod`,
`GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`.
There is a similar story for `AssocItemKind` and `ForeignItemKind`.
Some sites that handle items check for an empty ident, some don't. This
is a very C-like way of doing things, but this is Rust, we have sum
types, we can do this properly and never forget to check for the
exceptional case and never YOLO possibly empty identifiers (or possibly
dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- `ast::Item` got 8 bytes bigger. This could be avoided by boxing the
fields within some of the `ast::ItemKind` variants (specifically:
`Struct`, `Union`, `Enum`). I might do that in a follow-up; this
commit is big enough already.
- For the visitors: `FnKind` no longer needs an `ident` field because
the `Fn` within how has one.
- In the parser, the `ItemInfo` typedef is no longer needed. It was used
in various places to return an `Ident` alongside an `ItemKind`, but
now the `Ident` (if present) is within the `ItemKind`.
- In a few places I renamed identifier variables called `name` (or
`foo_name`) as `ident` (or `foo_ident`), to better match the type, and
because `name` is normally used for `Symbol`s. It's confusing to see
something like `foo_name.name`.
2025-03-20 22:47:43 +00:00
|
|
|
ident: main_ident,
|
2021-11-07 08:43:49 +00:00
|
|
|
generics: ast::Generics::default(),
|
2025-01-09 00:38:25 +00:00
|
|
|
contract: None,
|
2021-11-07 08:43:49 +00:00
|
|
|
body: Some(main_body),
|
2024-07-26 10:04:02 +00:00
|
|
|
define_opaque: None,
|
2021-11-07 08:43:49 +00:00
|
|
|
}));
|
2018-07-21 01:04:02 +00:00
|
|
|
|
2019-08-13 23:30:09 +00:00
|
|
|
let main = P(ast::Item {
|
2024-09-11 10:08:14 +00:00
|
|
|
attrs: thin_vec![main_attr, coverage_attr, doc_hidden_attr],
|
2015-01-22 02:13:08 +00:00
|
|
|
id: ast::DUMMY_NODE_ID,
|
2019-09-26 16:51:36 +00:00
|
|
|
kind: main,
|
2020-08-21 23:11:00 +00:00
|
|
|
vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
|
2017-07-11 00:44:46 +00:00
|
|
|
span: sp,
|
|
|
|
tokens: None,
|
2019-08-13 23:30:09 +00:00
|
|
|
});
|
2013-08-15 06:06:33 +00:00
|
|
|
|
2019-08-13 23:30:09 +00:00
|
|
|
// Integrate the new item into existing module structures.
|
|
|
|
let main = AstFragment::Items(smallvec![main]);
|
|
|
|
cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap()
|
2012-12-28 01:53:04 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 01:04:02 +00:00
|
|
|
/// Creates a slice containing every test like so:
|
2019-08-28 21:47:52 +00:00
|
|
|
/// &[&test1, &test2]
|
2019-08-25 20:03:24 +00:00
|
|
|
fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
|
2018-07-21 01:04:02 +00:00
|
|
|
debug!("building test vector from {} tests", cx.test_cases.len());
|
2020-03-05 12:56:01 +00:00
|
|
|
let ecx = &cx.ext_cx;
|
2018-07-21 01:04:02 +00:00
|
|
|
|
2022-07-26 21:40:48 +00:00
|
|
|
let mut tests = cx.test_cases.clone();
|
2023-11-21 19:07:32 +00:00
|
|
|
tests.sort_by(|a, b| a.name.as_str().cmp(b.name.as_str()));
|
2022-07-26 21:40:48 +00:00
|
|
|
|
2022-06-21 22:38:24 +00:00
|
|
|
ecx.expr_array_ref(
|
2019-08-25 20:03:24 +00:00
|
|
|
sp,
|
2022-07-26 21:40:48 +00:00
|
|
|
tests
|
2018-07-21 01:04:02 +00:00
|
|
|
.iter()
|
|
|
|
.map(|test| {
|
2019-08-25 20:03:24 +00:00
|
|
|
ecx.expr_addr_of(test.span, ecx.expr_path(ecx.path(test.span, vec![test.ident])))
|
2018-07-21 01:04:02 +00:00
|
|
|
})
|
|
|
|
.collect(),
|
2019-12-22 22:42:04 +00:00
|
|
|
)
|
2013-08-15 06:06:33 +00:00
|
|
|
}
|
2013-03-14 18:22:51 +00:00
|
|
|
|
2023-03-19 17:32:34 +00:00
|
|
|
fn get_test_name(i: &ast::Item) -> Option<Symbol> {
|
|
|
|
attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
|
2018-07-21 01:04:02 +00:00
|
|
|
}
|
2014-07-25 00:01:42 +00:00
|
|
|
|
2024-06-18 10:35:56 +00:00
|
|
|
fn get_test_runner(dcx: DiagCtxtHandle<'_>, krate: &ast::Crate) -> Option<ast::Path> {
|
2023-03-19 17:32:34 +00:00
|
|
|
let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
|
2020-03-17 12:19:06 +00:00
|
|
|
let meta_list = test_attr.meta_item_list()?;
|
|
|
|
let span = test_attr.span;
|
|
|
|
match &*meta_list {
|
|
|
|
[single] => match single.meta_item() {
|
|
|
|
Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
|
2022-01-27 09:44:25 +00:00
|
|
|
_ => {
|
2023-12-17 23:15:45 +00:00
|
|
|
dcx.emit_err(errors::TestRunnerInvalid { span });
|
2022-01-27 09:44:25 +00:00
|
|
|
}
|
2020-03-17 12:19:06 +00:00
|
|
|
},
|
2022-01-27 09:44:25 +00:00
|
|
|
_ => {
|
2023-12-17 23:15:45 +00:00
|
|
|
dcx.emit_err(errors::TestRunnerNargs { span });
|
2022-01-27 09:44:25 +00:00
|
|
|
}
|
2020-03-17 12:19:06 +00:00
|
|
|
}
|
|
|
|
None
|
2013-08-15 06:06:33 +00:00
|
|
|
}
|