rust/compiler/rustc_builtin_macros/src/test_harness.rs

385 lines
14 KiB
Rust
Raw Normal View History

// Code that generates a test runner to run all the tests in a crate
2020-04-27 17:56:11 +00:00
use rustc_ast as ast;
use rustc_ast::entry::EntryPointType;
use rustc_ast::mut_visit::{ExpectOne, *};
use rustc_ast::ptr::P;
use rustc_ast::{attr, ModKind};
2020-06-27 20:51:28 +00:00
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
use rustc_feature::Features;
use rustc_session::Session;
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
2020-04-19 11:00:18 +00:00
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::PanicStrategy;
2019-12-22 22:42:04 +00:00
use smallvec::{smallvec, SmallVec};
2020-08-05 11:35:53 +00:00
use tracing::debug;
use std::{iter, mem};
struct Test {
span: Span,
2019-08-25 20:03:24 +00:00
ident: Ident,
}
2013-12-25 18:10:33 +00:00
struct TestCtxt<'a> {
ext_cx: ExtCtxt<'a>,
panic_strategy: PanicStrategy,
def_site: Span,
2018-07-21 01:04:02 +00:00
test_cases: Vec<Test>,
reexport_test_harness_main: Option<Symbol>,
2018-07-21 01:04:02 +00:00
test_runner: Option<ast::Path>,
}
// Traverse the crate, collecting all the test functions, eliding any
// existing main functions, and synthesizing a main test harness
pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) {
let span_diagnostic = sess.diagnostic();
let panic_strategy = sess.panic_strategy();
let platform_panic_strategy = sess.target.panic_strategy;
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
// unconditional, so that the attribute is still marked as used in
// non-test builds.
let reexport_test_harness_main =
sess.first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
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
let test_runner = get_test_runner(sess, span_diagnostic, &krate);
2018-07-21 01:04:02 +00:00
if sess.opts.test {
let panic_strategy = match (panic_strategy, sess.opts.debugging_opts.panic_abort_tests) {
2019-12-22 22:42:04 +00:00
(PanicStrategy::Abort, true) => PanicStrategy::Abort,
(PanicStrategy::Abort, false) => {
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 {
span_diagnostic.err(
"building tests with panic=abort is not supported \
without `-Zpanic_abort_tests`",
);
}
PanicStrategy::Unwind
}
(PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
};
2019-12-22 22:42:04 +00:00
generate_test_harness(
sess,
resolver,
reexport_test_harness_main,
krate,
&sess.features_untracked(),
2019-12-22 22:42:04 +00:00
panic_strategy,
test_runner,
)
}
}
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>,
}
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) {
let prev_tests = mem::take(&mut self.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
noop_visit_crate(c, self);
self.add_test_cases(ast::CRATE_NODE_ID, c.span, prev_tests);
2018-07-21 01:04:02 +00:00
// Create a main function to run our tests
c.items.push(mk_main(&mut self.cx));
}
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
fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
2018-07-21 01:04:02 +00:00
let mut item = i.into_inner();
if is_test_case(&self.cx.ext_cx.sess, &item) {
2018-07-21 01:04:02 +00:00
debug!("this is a test item");
2019-12-22 22:42:04 +00:00
let test = Test { span: item.span, ident: item.ident };
2019-08-25 20:03:24 +00:00
self.tests.push(test);
}
// We don't want to recurse into anything other than mods, since
// mods or tests inside of functions will break things
if let ast::ItemKind::Mod(_, ModKind::Loaded(.., span)) = item.kind {
let prev_tests = mem::take(&mut self.tests);
noop_visit_item_kind(&mut item.kind, self);
self.add_test_cases(item.id, span, prev_tests);
}
2018-08-13 19:15:16 +00:00
smallvec![P(item)]
}
}
// Beware, this is duplicated in librustc_passes/entry.rs (with
// `rustc_hir::Item`), so make sure to keep them in sync.
fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPointType {
match item.kind {
ast::ItemKind::Fn(..) => {
if sess.contains_name(&item.attrs, sym::start) {
EntryPointType::Start
2021-04-08 13:37:38 +00:00
} else if sess.contains_name(&item.attrs, sym::rustc_main) {
EntryPointType::MainAttr
} else if item.ident.name == sym::main {
if depth == 0 {
// This is a top-level function so can be 'main'
EntryPointType::MainNamed
} else {
EntryPointType::OtherMain
}
} else {
EntryPointType::None
}
}
_ => EntryPointType::None,
}
}
2018-07-21 01:04:02 +00:00
/// A folder used to remove any entry points (like fn main) because the harness
/// generator will provide its own
struct EntryPointCleaner<'a> {
// Current depth in the ast
sess: &'a Session,
depth: usize,
def_site: Span,
}
impl<'a> MutVisitor for EntryPointCleaner<'a> {
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
fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
self.depth += 1;
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
let item = noop_flat_map_item(i, self).expect_one("noop did something");
self.depth -= 1;
// Remove any #[main] or #[start] from the AST so it doesn't
// clash with the one we're going to add, but mark it as
// #[allow(dead_code)] to avoid printing warnings.
let item = match entry_point_type(self.sess, &item, self.depth) {
2019-12-22 22:42:04 +00:00
EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item
.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
let allow_ident = Ident::new(sym::allow, self.def_site);
let dc_nested =
attr::mk_nested_word_item(Ident::new(sym::dead_code, self.def_site));
2019-08-04 21:59:06 +00:00
let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
let attrs = attrs
.into_iter()
.filter(|attr| {
!attr.has_name(sym::rustc_main) && !attr.has_name(sym::start)
})
.chain(iter::once(allow_dead_code))
.collect();
ast::Item { id, ident, attrs, kind, vis, span, tokens }
}),
2019-12-22 22:42:04 +00:00
EntryPointType::None | EntryPointType::OtherMain => item,
};
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
smallvec![item]
}
}
2018-07-21 01:04:02 +00:00
/// Crawl over the crate, inserting test reexports and the test main function
2019-12-22 22:42:04 +00:00
fn generate_test_harness(
sess: &Session,
2020-06-27 20:51:28 +00:00
resolver: &mut dyn ResolverExpand,
2019-12-22 22:42:04 +00:00
reexport_test_harness_main: Option<Symbol>,
krate: &mut ast::Crate,
features: &Features,
panic_strategy: PanicStrategy,
test_runner: Option<ast::Path>,
) {
let mut econfig = ExpansionConfig::default("test".to_string());
econfig.features = Some(features);
2020-03-15 23:43:37 +00:00
let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
DUMMY_SP,
AstPass::TestHarness,
2021-04-08 13:37:38 +00:00
&[sym::test, sym::rustc_attrs],
None,
);
2021-06-25 18:43:04 +00:00
let def_site = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
// Remove the entry points
let mut cleaner = EntryPointCleaner { sess, depth: 0, def_site };
cleaner.visit_crate(krate);
let cx = TestCtxt {
ext_cx,
panic_strategy,
def_site,
2018-07-21 01:04:02 +00:00
test_cases: Vec::new(),
reexport_test_harness_main,
2019-12-22 22:42:04 +00:00
test_runner,
};
2011-07-27 12:19:39 +00:00
2019-12-22 22:42:04 +00:00
TestHarnessGenerator { cx, tests: Vec::new() }.visit_crate(krate);
}
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
///
/// By default this expands to
///
2020-05-01 20:32:33 +00:00
/// ```
2021-04-08 13:37:38 +00:00
/// #[rustc_main]
/// pub fn main() {
/// extern crate test;
/// test::test_main_static(&[
/// &test_const1,
/// &test_const2,
/// &test_const3,
/// ]);
/// }
2020-05-01 20:32:33 +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
/// generated in `TestHarnessGenerator::flat_map_item`. When resolving this
/// 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
/// `test::test_main_static`.
2019-02-06 17:33:01 +00:00
fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
let sp = cx.def_site;
let ecx = &cx.ext_cx;
2019-08-25 20:03:24 +00:00
let test_id = Ident::new(sym::test, sp);
let runner_name = match cx.panic_strategy {
PanicStrategy::Unwind => "test_main_static",
PanicStrategy::Abort => "test_main_static_abort",
};
// test::test_main_static(...)
2019-12-22 22:42:04 +00:00
let mut test_runner = cx
.test_runner
.clone()
.unwrap_or_else(|| ecx.path(sp, vec![test_id, 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);
2019-12-22 22:42:04 +00:00
let call_test_main = ecx.expr_call(sp, test_main_path_expr, vec![mk_tests_slice(cx, sp)]);
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
2019-12-22 22:42:04 +00:00
let test_extern_stmt =
ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None)));
2018-07-21 01:04:02 +00:00
2021-04-08 13:37:38 +00:00
// #[rustc_main]
let main_meta = ecx.meta_word(sp, sym::rustc_main);
let main_attr = ecx.attribute(main_meta);
// pub fn main() { ... }
let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
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() {
ecx.block(sp, vec![test_extern_stmt, call_test_main])
} else {
ecx.block(sp, vec![call_test_main])
};
2020-02-15 03:10:59 +00:00
let decl = ecx.fn_decl(vec![], ast::FnRetTy::Ty(main_ret_ty));
Use smaller def span for functions Currently, the def span of a funtion encompasses the entire function signature and body. However, this is usually unnecessarily verbose - when we are pointing at an entire function in a diagnostic, we almost always want to point at the signature. The actual contents of the body tends to be irrelevant to the diagnostic we are emitting, and just takes up additional screen space. This commit changes the `def_span` of all function items (freestanding functions, `impl`-block methods, and `trait`-block methods) to be the span of the signature. For example, the function ```rust pub fn foo<T>(val: T) -> T { val } ``` now has a `def_span` corresponding to `pub fn foo<T>(val: T) -> T` (everything before the opening curly brace). Trait methods without a body have a `def_span` which includes the trailing semicolon. For example: ```rust trait Foo { fn bar(); }``` the function definition `Foo::bar` has a `def_span` of `fn bar();` This makes our diagnostic output much shorter, and emphasizes information that is relevant to whatever diagnostic we are reporting. We continue to use the full span (including the body) in a few of places: * MIR building uses the full span when building source scopes. * 'Outlives suggestions' use the full span to sort the diagnostics being emitted. * The `#[rustc_on_unimplemented(enclosing_scope="in this scope")]` attribute points the entire scope body. * The 'unconditional recursion' lint uses the full span to show additional context for the recursive call. All of these cases work only with local items, so we don't need to add anything extra to crate metadata.
2020-08-12 21:02:14 +00:00
let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp };
let defaultness = ast::Defaultness::Final;
let main = ast::ItemKind::Fn(Box::new(ast::Fn {
defaultness,
sig,
generics: ast::Generics::default(),
body: Some(main_body),
}));
2018-07-21 01:04:02 +00:00
// Honor the reexport_test_harness_main attribute
2019-05-17 00:44:51 +00:00
let main_id = match cx.reexport_test_harness_main {
2019-08-25 20:03:24 +00:00
Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
None => Ident::new(sym::main, sp),
2019-05-17 00:44:51 +00:00
};
2018-07-21 01:04:02 +00:00
let main = P(ast::Item {
2018-07-21 01:04:02 +00:00
ident: main_id,
attrs: vec![main_attr],
id: ast::DUMMY_NODE_ID,
2019-09-26 16:51:36 +00:00
kind: main,
vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
span: sp,
tokens: None,
});
// 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()
}
2018-07-21 01:04:02 +00:00
/// Creates a slice containing every test like so:
/// &[&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());
let ecx = &cx.ext_cx;
2018-07-21 01:04:02 +00:00
2019-12-22 22:42:04 +00:00
ecx.expr_vec_slice(
sp,
cx.test_cases
.iter()
.map(|test| {
ecx.expr_addr_of(test.span, ecx.expr_path(ecx.path(test.span, vec![test.ident])))
})
.collect(),
)
}
fn is_test_case(sess: &Session, i: &ast::Item) -> bool {
sess.contains_name(&i.attrs, sym::rustc_test_marker)
2018-07-21 01:04:02 +00:00
}
fn get_test_runner(
sess: &Session,
sd: &rustc_errors::Handler,
krate: &ast::Crate,
) -> Option<ast::Path> {
let test_attr = sess.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()),
_ => sd.struct_span_err(span, "`test_runner` argument must be a path").emit(),
},
_ => sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit(),
}
None
}