Rollup merge of #48143 - nikomatsakis:termination_trait_in_tests, r=eddyb
Termination trait in tests Support the `Termination` trait in unit tests (cc https://github.com/rust-lang/rust/issues/43301) Also, a drive-by fix for #47075. This is joint work with @bkchr.
This commit is contained in:
commit
b52b33a386
@ -818,7 +818,8 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session,
|
|||||||
&mut resolver,
|
&mut resolver,
|
||||||
sess.opts.test,
|
sess.opts.test,
|
||||||
krate,
|
krate,
|
||||||
sess.diagnostic())
|
sess.diagnostic(),
|
||||||
|
&sess.features.borrow())
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we're actually rustdoc then there's no need to actually compile
|
// If we're actually rustdoc then there's no need to actually compile
|
||||||
|
@ -500,11 +500,6 @@ mod memchr;
|
|||||||
// The runtime entry point and a few unstable public functions used by the
|
// The runtime entry point and a few unstable public functions used by the
|
||||||
// compiler
|
// compiler
|
||||||
pub mod rt;
|
pub mod rt;
|
||||||
// The trait to support returning arbitrary types in the main function
|
|
||||||
mod termination;
|
|
||||||
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
pub use self::termination::Termination;
|
|
||||||
|
|
||||||
// Include a number of private modules that exist solely to provide
|
// Include a number of private modules that exist solely to provide
|
||||||
// the rustdoc documentation for primitive types. Using `include!`
|
// the rustdoc documentation for primitive types. Using `include!`
|
||||||
|
@ -1392,6 +1392,73 @@ pub fn id() -> u32 {
|
|||||||
::sys::os::getpid()
|
::sys::os::getpid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod exit {
|
||||||
|
pub const SUCCESS: i32 = 0;
|
||||||
|
pub const FAILURE: i32 = 1;
|
||||||
|
}
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod exit {
|
||||||
|
use libc;
|
||||||
|
pub const SUCCESS: i32 = libc::EXIT_SUCCESS;
|
||||||
|
pub const FAILURE: i32 = libc::EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for implementing arbitrary return types in the `main` function.
|
||||||
|
///
|
||||||
|
/// The c-main function only supports to return integers as return type.
|
||||||
|
/// So, every type implementing the `Termination` trait has to be converted
|
||||||
|
/// to an integer.
|
||||||
|
///
|
||||||
|
/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate
|
||||||
|
/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned.
|
||||||
|
#[cfg_attr(not(test), lang = "termination")]
|
||||||
|
#[unstable(feature = "termination_trait_lib", issue = "43301")]
|
||||||
|
#[rustc_on_unimplemented =
|
||||||
|
"`main` can only return types that implement {Termination}, not `{Self}`"]
|
||||||
|
pub trait Termination {
|
||||||
|
/// Is called to get the representation of the value as status code.
|
||||||
|
/// This status code is returned to the operating system.
|
||||||
|
fn report(self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "termination_trait_lib", issue = "43301")]
|
||||||
|
impl Termination for () {
|
||||||
|
fn report(self) -> i32 { exit::SUCCESS }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "termination_trait_lib", issue = "43301")]
|
||||||
|
impl<T: Termination, E: fmt::Debug> Termination for Result<T, E> {
|
||||||
|
fn report(self) -> i32 {
|
||||||
|
match self {
|
||||||
|
Ok(val) => val.report(),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error: {:?}", err);
|
||||||
|
exit::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "termination_trait_lib", issue = "43301")]
|
||||||
|
impl Termination for ! {
|
||||||
|
fn report(self) -> i32 { unreachable!(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "termination_trait_lib", issue = "43301")]
|
||||||
|
impl Termination for bool {
|
||||||
|
fn report(self) -> i32 {
|
||||||
|
if self { exit::SUCCESS } else { exit::FAILURE }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "termination_trait_lib", issue = "43301")]
|
||||||
|
impl Termination for i32 {
|
||||||
|
fn report(self) -> i32 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))]
|
#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use io::prelude::*;
|
use io::prelude::*;
|
||||||
|
@ -68,7 +68,7 @@ fn lang_start_internal(main: &(Fn() -> i32 + Sync + ::panic::RefUnwindSafe),
|
|||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
#[lang = "start"]
|
#[lang = "start"]
|
||||||
fn lang_start<T: ::termination::Termination + 'static>
|
fn lang_start<T: ::process::Termination + 'static>
|
||||||
(main: fn() -> T, argc: isize, argv: *const *const u8) -> isize
|
(main: fn() -> T, argc: isize, argv: *const *const u8) -> isize
|
||||||
{
|
{
|
||||||
lang_start_internal(&move || main().report(), argc, argv)
|
lang_start_internal(&move || main().report(), argc, argv)
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
use fmt::Debug;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
mod exit {
|
|
||||||
pub const SUCCESS: i32 = 0;
|
|
||||||
pub const FAILURE: i32 = 1;
|
|
||||||
}
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
mod exit {
|
|
||||||
use libc;
|
|
||||||
pub const SUCCESS: i32 = libc::EXIT_SUCCESS;
|
|
||||||
pub const FAILURE: i32 = libc::EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for implementing arbitrary return types in the `main` function.
|
|
||||||
///
|
|
||||||
/// The c-main function only supports to return integers as return type.
|
|
||||||
/// So, every type implementing the `Termination` trait has to be converted
|
|
||||||
/// to an integer.
|
|
||||||
///
|
|
||||||
/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate
|
|
||||||
/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned.
|
|
||||||
#[cfg_attr(not(test), lang = "termination")]
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
#[rustc_on_unimplemented =
|
|
||||||
"`main` can only return types that implement {Termination}, not `{Self}`"]
|
|
||||||
pub trait Termination {
|
|
||||||
/// Is called to get the representation of the value as status code.
|
|
||||||
/// This status code is returned to the operating system.
|
|
||||||
fn report(self) -> i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
impl Termination for () {
|
|
||||||
fn report(self) -> i32 { exit::SUCCESS }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
impl<T: Termination, E: Debug> Termination for Result<T, E> {
|
|
||||||
fn report(self) -> i32 {
|
|
||||||
match self {
|
|
||||||
Ok(val) => val.report(),
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Error: {:?}", err);
|
|
||||||
exit::FAILURE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
impl Termination for ! {
|
|
||||||
fn report(self) -> i32 { unreachable!(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
impl Termination for bool {
|
|
||||||
fn report(self) -> i32 {
|
|
||||||
if self { exit::SUCCESS } else { exit::FAILURE }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "termination_trait", issue = "43301")]
|
|
||||||
impl Termination for i32 {
|
|
||||||
fn report(self) -> i32 {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
@ -319,14 +319,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
|||||||
types: Vec<P<ast::Ty>>,
|
types: Vec<P<ast::Ty>>,
|
||||||
bindings: Vec<ast::TypeBinding> )
|
bindings: Vec<ast::TypeBinding> )
|
||||||
-> ast::Path {
|
-> ast::Path {
|
||||||
use syntax::parse::token;
|
|
||||||
|
|
||||||
let last_identifier = idents.pop().unwrap();
|
let last_identifier = idents.pop().unwrap();
|
||||||
let mut segments: Vec<ast::PathSegment> = Vec::new();
|
let mut segments: Vec<ast::PathSegment> = Vec::new();
|
||||||
if global &&
|
|
||||||
!idents.first().map_or(false, |&ident| token::Ident(ident).is_path_segment_keyword()) {
|
|
||||||
segments.push(ast::PathSegment::crate_root(span));
|
|
||||||
}
|
|
||||||
|
|
||||||
segments.extend(idents.into_iter().map(|i| ast::PathSegment::from_ident(i, span)));
|
segments.extend(idents.into_iter().map(|i| ast::PathSegment::from_ident(i, span)));
|
||||||
let parameters = if !lifetimes.is_empty() || !types.is_empty() || !bindings.is_empty() {
|
let parameters = if !lifetimes.is_empty() || !types.is_empty() || !bindings.is_empty() {
|
||||||
@ -335,7 +329,9 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
segments.push(ast::PathSegment { identifier: last_identifier, span, parameters });
|
segments.push(ast::PathSegment { identifier: last_identifier, span, parameters });
|
||||||
ast::Path { span, segments }
|
let path = ast::Path { span, segments };
|
||||||
|
|
||||||
|
if global { path.default_to_global() } else { path }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a qualified path.
|
/// Constructs a qualified path.
|
||||||
|
@ -32,6 +32,7 @@ use ext::build::AstBuilder;
|
|||||||
use ext::expand::ExpansionConfig;
|
use ext::expand::ExpansionConfig;
|
||||||
use ext::hygiene::{Mark, SyntaxContext};
|
use ext::hygiene::{Mark, SyntaxContext};
|
||||||
use fold::Folder;
|
use fold::Folder;
|
||||||
|
use feature_gate::Features;
|
||||||
use util::move_map::MoveMap;
|
use util::move_map::MoveMap;
|
||||||
use fold;
|
use fold;
|
||||||
use parse::{token, ParseSess};
|
use parse::{token, ParseSess};
|
||||||
@ -63,6 +64,7 @@ struct TestCtxt<'a> {
|
|||||||
reexport_test_harness_main: Option<Symbol>,
|
reexport_test_harness_main: Option<Symbol>,
|
||||||
is_libtest: bool,
|
is_libtest: bool,
|
||||||
ctxt: SyntaxContext,
|
ctxt: SyntaxContext,
|
||||||
|
features: &'a Features,
|
||||||
|
|
||||||
// top-level re-export submodule, filled out after folding is finished
|
// top-level re-export submodule, filled out after folding is finished
|
||||||
toplevel_reexport: Option<Ident>,
|
toplevel_reexport: Option<Ident>,
|
||||||
@ -74,7 +76,8 @@ pub fn modify_for_testing(sess: &ParseSess,
|
|||||||
resolver: &mut Resolver,
|
resolver: &mut Resolver,
|
||||||
should_test: bool,
|
should_test: bool,
|
||||||
krate: ast::Crate,
|
krate: ast::Crate,
|
||||||
span_diagnostic: &errors::Handler) -> ast::Crate {
|
span_diagnostic: &errors::Handler,
|
||||||
|
features: &Features) -> ast::Crate {
|
||||||
// Check for #[reexport_test_harness_main = "some_name"] which
|
// Check for #[reexport_test_harness_main = "some_name"] which
|
||||||
// creates a `use some_name = __test::main;`. This needs to be
|
// creates a `use some_name = __test::main;`. This needs to be
|
||||||
// unconditional, so that the attribute is still marked as used in
|
// unconditional, so that the attribute is still marked as used in
|
||||||
@ -84,7 +87,8 @@ pub fn modify_for_testing(sess: &ParseSess,
|
|||||||
"reexport_test_harness_main");
|
"reexport_test_harness_main");
|
||||||
|
|
||||||
if should_test {
|
if should_test {
|
||||||
generate_test_harness(sess, resolver, reexport_test_harness_main, krate, span_diagnostic)
|
generate_test_harness(sess, resolver, reexport_test_harness_main,
|
||||||
|
krate, span_diagnostic, features)
|
||||||
} else {
|
} else {
|
||||||
krate
|
krate
|
||||||
}
|
}
|
||||||
@ -265,16 +269,20 @@ fn generate_test_harness(sess: &ParseSess,
|
|||||||
resolver: &mut Resolver,
|
resolver: &mut Resolver,
|
||||||
reexport_test_harness_main: Option<Symbol>,
|
reexport_test_harness_main: Option<Symbol>,
|
||||||
krate: ast::Crate,
|
krate: ast::Crate,
|
||||||
sd: &errors::Handler) -> ast::Crate {
|
sd: &errors::Handler,
|
||||||
|
features: &Features) -> ast::Crate {
|
||||||
// Remove the entry points
|
// Remove the entry points
|
||||||
let mut cleaner = EntryPointCleaner { depth: 0 };
|
let mut cleaner = EntryPointCleaner { depth: 0 };
|
||||||
let krate = cleaner.fold_crate(krate);
|
let krate = cleaner.fold_crate(krate);
|
||||||
|
|
||||||
let mark = Mark::fresh(Mark::root());
|
let mark = Mark::fresh(Mark::root());
|
||||||
|
|
||||||
|
let mut econfig = ExpansionConfig::default("test".to_string());
|
||||||
|
econfig.features = Some(features);
|
||||||
|
|
||||||
let cx = TestCtxt {
|
let cx = TestCtxt {
|
||||||
span_diagnostic: sd,
|
span_diagnostic: sd,
|
||||||
ext_cx: ExtCtxt::new(sess, ExpansionConfig::default("test".to_string()), resolver),
|
ext_cx: ExtCtxt::new(sess, econfig, resolver),
|
||||||
path: Vec::new(),
|
path: Vec::new(),
|
||||||
testfns: Vec::new(),
|
testfns: Vec::new(),
|
||||||
reexport_test_harness_main,
|
reexport_test_harness_main,
|
||||||
@ -282,6 +290,7 @@ fn generate_test_harness(sess: &ParseSess,
|
|||||||
is_libtest: attr::find_crate_name(&krate.attrs).map(|s| s == "test").unwrap_or(false),
|
is_libtest: attr::find_crate_name(&krate.attrs).map(|s| s == "test").unwrap_or(false),
|
||||||
toplevel_reexport: None,
|
toplevel_reexport: None,
|
||||||
ctxt: SyntaxContext::empty().apply_mark(mark),
|
ctxt: SyntaxContext::empty().apply_mark(mark),
|
||||||
|
features,
|
||||||
};
|
};
|
||||||
|
|
||||||
mark.set_expn_info(ExpnInfo {
|
mark.set_expn_info(ExpnInfo {
|
||||||
@ -318,17 +327,24 @@ enum HasTestSignature {
|
|||||||
fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
|
fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
|
||||||
let has_test_attr = attr::contains_name(&i.attrs, "test");
|
let has_test_attr = attr::contains_name(&i.attrs, "test");
|
||||||
|
|
||||||
fn has_test_signature(i: &ast::Item) -> HasTestSignature {
|
fn has_test_signature(cx: &TestCtxt, i: &ast::Item) -> HasTestSignature {
|
||||||
match i.node {
|
match i.node {
|
||||||
ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
|
ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
|
||||||
|
// If the termination trait is active, the compiler will check that the output
|
||||||
|
// type implements the `Termination` trait as `libtest` enforces that.
|
||||||
|
let output_matches = if cx.features.termination_trait {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
let no_output = match decl.output {
|
let no_output = match decl.output {
|
||||||
ast::FunctionRetTy::Default(..) => true,
|
ast::FunctionRetTy::Default(..) => true,
|
||||||
ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
|
ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
|
||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
if decl.inputs.is_empty()
|
|
||||||
&& no_output
|
no_output && !generics.is_parameterized()
|
||||||
&& !generics.is_parameterized() {
|
};
|
||||||
|
|
||||||
|
if decl.inputs.is_empty() && output_matches {
|
||||||
Yes
|
Yes
|
||||||
} else {
|
} else {
|
||||||
No
|
No
|
||||||
@ -338,26 +354,43 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_test_attr {
|
let has_test_signature = if has_test_attr {
|
||||||
let diag = cx.span_diagnostic;
|
let diag = cx.span_diagnostic;
|
||||||
match has_test_signature(i) {
|
match has_test_signature(cx, i) {
|
||||||
Yes => {},
|
Yes => true,
|
||||||
No => diag.span_err(i.span, "functions used as tests must have signature fn() -> ()"),
|
No => {
|
||||||
NotEvenAFunction => diag.span_err(i.span,
|
if cx.features.termination_trait {
|
||||||
"only functions may be used as tests"),
|
diag.span_err(i.span, "functions used as tests can not have any arguments");
|
||||||
|
} else {
|
||||||
|
diag.span_err(i.span, "functions used as tests must have signature fn() -> ()");
|
||||||
}
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
NotEvenAFunction => {
|
||||||
|
diag.span_err(i.span, "only functions may be used as tests");
|
||||||
|
false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
has_test_attr && has_test_signature(i) == Yes
|
has_test_attr && has_test_signature
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
|
fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
|
||||||
let has_bench_attr = attr::contains_name(&i.attrs, "bench");
|
let has_bench_attr = attr::contains_name(&i.attrs, "bench");
|
||||||
|
|
||||||
fn has_test_signature(i: &ast::Item) -> bool {
|
fn has_bench_signature(cx: &TestCtxt, i: &ast::Item) -> bool {
|
||||||
match i.node {
|
match i.node {
|
||||||
ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
|
ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
|
||||||
let input_cnt = decl.inputs.len();
|
let input_cnt = decl.inputs.len();
|
||||||
|
|
||||||
|
// If the termination trait is active, the compiler will check that the output
|
||||||
|
// type implements the `Termination` trait as `libtest` enforces that.
|
||||||
|
let output_matches = if cx.features.termination_trait {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
let no_output = match decl.output {
|
let no_output = match decl.output {
|
||||||
ast::FunctionRetTy::Default(..) => true,
|
ast::FunctionRetTy::Default(..) => true,
|
||||||
ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
|
ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
|
||||||
@ -367,22 +400,32 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
|
|||||||
.filter(|param| param.is_type_param())
|
.filter(|param| param.is_type_param())
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
|
no_output && tparm_cnt == 0
|
||||||
|
};
|
||||||
|
|
||||||
// NB: inadequate check, but we're running
|
// NB: inadequate check, but we're running
|
||||||
// well before resolve, can't get too deep.
|
// well before resolve, can't get too deep.
|
||||||
input_cnt == 1
|
input_cnt == 1 && output_matches
|
||||||
&& no_output && tparm_cnt == 0
|
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_bench_attr && !has_test_signature(i) {
|
let has_bench_signature = has_bench_signature(cx, i);
|
||||||
|
|
||||||
|
if has_bench_attr && !has_bench_signature {
|
||||||
let diag = cx.span_diagnostic;
|
let diag = cx.span_diagnostic;
|
||||||
|
|
||||||
|
if cx.features.termination_trait {
|
||||||
|
diag.span_err(i.span, "functions used as benches must have signature \
|
||||||
|
`fn(&mut Bencher) -> impl Termination`");
|
||||||
|
} else {
|
||||||
diag.span_err(i.span, "functions used as benches must have signature \
|
diag.span_err(i.span, "functions used as benches must have signature \
|
||||||
`fn(&mut Bencher) -> ()`");
|
`fn(&mut Bencher) -> ()`");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
has_bench_attr && has_test_signature(i)
|
has_bench_attr && has_bench_signature
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_ignored(i: &ast::Item) -> bool {
|
fn is_ignored(i: &ast::Item) -> bool {
|
||||||
@ -690,9 +733,12 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
|
|||||||
field("should_panic", fail_expr),
|
field("should_panic", fail_expr),
|
||||||
field("allow_fail", allow_fail_expr)]);
|
field("allow_fail", allow_fail_expr)]);
|
||||||
|
|
||||||
|
let mut visible_path = vec![];
|
||||||
let mut visible_path = match cx.toplevel_reexport {
|
if cx.features.extern_absolute_paths {
|
||||||
Some(id) => vec![id],
|
visible_path.push(keywords::Crate.ident());
|
||||||
|
}
|
||||||
|
match cx.toplevel_reexport {
|
||||||
|
Some(id) => visible_path.push(id),
|
||||||
None => {
|
None => {
|
||||||
let diag = cx.span_diagnostic;
|
let diag = cx.span_diagnostic;
|
||||||
diag.bug("expected to find top-level re-export name, but found None");
|
diag.bug("expected to find top-level re-export name, but found None");
|
||||||
@ -700,9 +746,64 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
|
|||||||
};
|
};
|
||||||
visible_path.extend(path);
|
visible_path.extend(path);
|
||||||
|
|
||||||
let fn_expr = ecx.expr_path(ecx.path_global(span, visible_path));
|
// Rather than directly give the test function to the test
|
||||||
|
// harness, we create a wrapper like one of the following:
|
||||||
|
//
|
||||||
|
// || test::assert_test_result(real_function()) // for test
|
||||||
|
// |b| test::assert_test_result(real_function(b)) // for bench
|
||||||
|
//
|
||||||
|
// this will coerce into a fn pointer that is specialized to the
|
||||||
|
// actual return type of `real_function` (Typically `()`, but not always).
|
||||||
|
let fn_expr = {
|
||||||
|
// construct `real_function()` (this will be inserted into the overall expr)
|
||||||
|
let real_function_expr = ecx.expr_path(ecx.path_global(span, visible_path));
|
||||||
|
// construct path `test::assert_test_result`
|
||||||
|
let assert_test_result = test_path("assert_test_result");
|
||||||
|
if test.bench {
|
||||||
|
// construct `|b| {..}`
|
||||||
|
let b_ident = Ident::with_empty_ctxt(Symbol::gensym("b"));
|
||||||
|
let b_expr = ecx.expr_ident(span, b_ident);
|
||||||
|
ecx.lambda(
|
||||||
|
span,
|
||||||
|
vec![b_ident],
|
||||||
|
// construct `assert_test_result(..)`
|
||||||
|
ecx.expr_call(
|
||||||
|
span,
|
||||||
|
ecx.expr_path(assert_test_result),
|
||||||
|
vec![
|
||||||
|
// construct `real_function(b)`
|
||||||
|
ecx.expr_call(
|
||||||
|
span,
|
||||||
|
real_function_expr,
|
||||||
|
vec![b_expr],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// construct `|| {..}`
|
||||||
|
ecx.lambda(
|
||||||
|
span,
|
||||||
|
vec![],
|
||||||
|
// construct `assert_test_result(..)`
|
||||||
|
ecx.expr_call(
|
||||||
|
span,
|
||||||
|
ecx.expr_path(assert_test_result),
|
||||||
|
vec![
|
||||||
|
// construct `real_function()`
|
||||||
|
ecx.expr_call(
|
||||||
|
span,
|
||||||
|
real_function_expr,
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let variant_name = if test.bench { "StaticBenchFn" } else { "StaticTestFn" };
|
let variant_name = if test.bench { "StaticBenchFn" } else { "StaticTestFn" };
|
||||||
|
|
||||||
// self::test::$variant_name($fn_expr)
|
// self::test::$variant_name($fn_expr)
|
||||||
let testfn_expr = ecx.expr_call(span, ecx.expr_path(test_path(variant_name)), vec![fn_expr]);
|
let testfn_expr = ecx.expr_call(span, ecx.expr_path(test_path(variant_name)), vec![fn_expr]);
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#![feature(set_stdio)]
|
#![feature(set_stdio)]
|
||||||
#![feature(panic_unwind)]
|
#![feature(panic_unwind)]
|
||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
|
#![feature(termination_trait_lib)]
|
||||||
|
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
extern crate term;
|
extern crate term;
|
||||||
@ -67,6 +68,7 @@ use std::io::prelude::*;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::process::Termination;
|
||||||
use std::sync::mpsc::{channel, Sender};
|
use std::sync::mpsc::{channel, Sender};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@ -81,8 +83,8 @@ const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in qu
|
|||||||
pub mod test {
|
pub mod test {
|
||||||
pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
|
pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
|
||||||
TrFailedMsg, TrIgnored, TrOk, Metric, MetricMap, StaticTestFn, StaticTestName,
|
TrFailedMsg, TrIgnored, TrOk, Metric, MetricMap, StaticTestFn, StaticTestName,
|
||||||
DynTestName, DynTestFn, run_test, test_main, test_main_static, filter_tests,
|
DynTestName, DynTestFn, assert_test_result, run_test, test_main, test_main_static,
|
||||||
parse_opts, StaticBenchFn, ShouldPanic, Options};
|
filter_tests, parse_opts, StaticBenchFn, ShouldPanic, Options};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
@ -322,6 +324,13 @@ pub fn test_main_static(tests: &[TestDescAndFn]) {
|
|||||||
test_main(&args, owned_tests, Options::new())
|
test_main(&args, owned_tests, Options::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invoked when unit tests terminate. Should panic if the unit
|
||||||
|
/// test is considered a failure. By default, invokes `report()`
|
||||||
|
/// and checks for a `0` result.
|
||||||
|
pub fn assert_test_result<T: Termination>(result: T) {
|
||||||
|
assert_eq!(result.report(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum ColorConfig {
|
pub enum ColorConfig {
|
||||||
AutoColor,
|
AutoColor,
|
||||||
|
@ -15,6 +15,3 @@
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bar(x: isize) { }
|
fn bar(x: isize) { }
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~| expected type `for<'r> fn(&'r mut __test::test::Bencher)`
|
|
||||||
//~| found type `fn(isize) {bar}`
|
|
||||||
//~| expected mutable reference, found isize
|
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
#![feature(termination_trait)]
|
#![feature(termination_trait)]
|
||||||
|
|
||||||
fn main() -> char {
|
fn main() -> char {
|
||||||
//~^ ERROR: the trait bound `char: std::Termination` is not satisfied
|
//~^ ERROR: the trait bound `char: std::process::Termination` is not satisfied
|
||||||
' '
|
' '
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,6 @@
|
|||||||
|
|
||||||
struct ReturnType {}
|
struct ReturnType {}
|
||||||
|
|
||||||
fn main() -> ReturnType { //~ ERROR `ReturnType: std::Termination` is not satisfied
|
fn main() -> ReturnType { //~ ERROR `ReturnType: std::process::Termination` is not satisfied
|
||||||
ReturnType {}
|
ReturnType {}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
// file at the top-level directory of this distribution and at
|
// file at the top-level directory of this distribution and at
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
//
|
//
|
||||||
@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
#![feature(termination_trait)]
|
#![feature(termination_trait)]
|
||||||
|
|
||||||
use std::io::Error;
|
// error-pattern:oh, dear
|
||||||
|
|
||||||
fn main() -> Result<(), Box<Error>> {
|
fn main() -> ! {
|
||||||
Ok(())
|
panic!("oh, dear");
|
||||||
}
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// compile-flags: --test
|
||||||
|
|
||||||
|
#![feature(termination_trait)]
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_a_num() -> Result<(), ParseIntError> {
|
||||||
|
let _: u32 = "22".parse()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn not_a_num() -> Result<(), ParseIntError> {
|
||||||
|
let _: u32 = "abc".parse()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn test_a_positive_bench(_: &mut Bencher) -> Result<(), ParseIntError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_a_neg_bench(_: &mut Bencher) -> Result<(), ParseIntError> {
|
||||||
|
let _: u32 = "abc".parse()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
21
src/test/run-pass/rfc-2126-extern-absolute-paths/test.rs
Normal file
21
src/test/run-pass/rfc-2126-extern-absolute-paths/test.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Check that `#[test]` works with extern-absolute-paths enabled.
|
||||||
|
//
|
||||||
|
// Regression test for #47075.
|
||||||
|
|
||||||
|
// compile-flags: --test
|
||||||
|
|
||||||
|
#![feature(extern_absolute_paths)]
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user