Move #[test_case] to a syntax extension
This commit is contained in:
parent
e5ed105716
commit
0593dc7e3c
15 changed files with 108 additions and 82 deletions
|
@ -828,7 +828,6 @@ where
|
||||||
let (mut krate, features) = syntax::config::features(
|
let (mut krate, features) = syntax::config::features(
|
||||||
krate,
|
krate,
|
||||||
&sess.parse_sess,
|
&sess.parse_sess,
|
||||||
sess.opts.test,
|
|
||||||
sess.edition(),
|
sess.edition(),
|
||||||
);
|
);
|
||||||
// these need to be set "early" so that expansion sees `quote` if enabled.
|
// these need to be set "early" so that expansion sees `quote` if enabled.
|
||||||
|
|
|
@ -1872,7 +1872,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(attr) = attr::find_by_name(&it.attrs, "test_case") {
|
if let Some(attr) = attr::find_by_name(&it.attrs, "rustc_test_marker") {
|
||||||
cx.struct_span_lint(
|
cx.struct_span_lint(
|
||||||
UNNAMEABLE_TEST_ITEMS,
|
UNNAMEABLE_TEST_ITEMS,
|
||||||
attr.span,
|
attr.span,
|
||||||
|
|
|
@ -479,7 +479,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
if kind == MacroKind::Attr && path.len() == 1 {
|
if kind == MacroKind::Attr {
|
||||||
if let Some(ext) = self.unshadowable_attrs.get(&path[0].name) {
|
if let Some(ext) = self.unshadowable_attrs.get(&path[0].name) {
|
||||||
return Ok(ext.def());
|
return Ok(ext.def());
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,18 +21,16 @@ use ptr::P;
|
||||||
|
|
||||||
/// A folder that strips out items that do not belong in the current configuration.
|
/// A folder that strips out items that do not belong in the current configuration.
|
||||||
pub struct StripUnconfigured<'a> {
|
pub struct StripUnconfigured<'a> {
|
||||||
pub should_test: bool,
|
|
||||||
pub sess: &'a ParseSess,
|
pub sess: &'a ParseSess,
|
||||||
pub features: Option<&'a Features>,
|
pub features: Option<&'a Features>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// `cfg_attr`-process the crate's attributes and compute the crate's features.
|
// `cfg_attr`-process the crate's attributes and compute the crate's features.
|
||||||
pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool, edition: Edition)
|
pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition)
|
||||||
-> (ast::Crate, Features) {
|
-> (ast::Crate, Features) {
|
||||||
let features;
|
let features;
|
||||||
{
|
{
|
||||||
let mut strip_unconfigured = StripUnconfigured {
|
let mut strip_unconfigured = StripUnconfigured {
|
||||||
should_test,
|
|
||||||
sess,
|
sess,
|
||||||
features: None,
|
features: None,
|
||||||
};
|
};
|
||||||
|
@ -118,11 +116,6 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
// Determine if a node with the given attributes should be included in this configuration.
|
// Determine if a node with the given attributes should be included in this configuration.
|
||||||
pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
|
pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||||
attrs.iter().all(|attr| {
|
attrs.iter().all(|attr| {
|
||||||
// When not compiling with --test we should not compile the #[test] functions
|
|
||||||
if !self.should_test && is_test(attr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mis = if !is_cfg(attr) {
|
let mis = if !is_cfg(attr) {
|
||||||
return true;
|
return true;
|
||||||
} else if let Some(mis) = attr.meta_item_list() {
|
} else if let Some(mis) = attr.meta_item_list() {
|
||||||
|
@ -249,7 +242,7 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
//
|
//
|
||||||
// NB: This is intentionally not part of the fold_expr() function
|
// NB: This is intentionally not part of the fold_expr() function
|
||||||
// in order for fold_opt_expr() to be able to avoid this check
|
// in order for fold_opt_expr() to be able to avoid this check
|
||||||
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a) || is_test(a)) {
|
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
|
||||||
let msg = "removing an expression is not supported in this position";
|
let msg = "removing an expression is not supported in this position";
|
||||||
self.sess.span_diagnostic.span_err(attr.span, msg);
|
self.sess.span_diagnostic.span_err(attr.span, msg);
|
||||||
}
|
}
|
||||||
|
@ -352,7 +345,3 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
|
||||||
fn is_cfg(attr: &ast::Attribute) -> bool {
|
fn is_cfg(attr: &ast::Attribute) -> bool {
|
||||||
attr.check_name("cfg")
|
attr.check_name("cfg")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_test(att: &ast::Attribute) -> bool {
|
|
||||||
att.check_name("test_case")
|
|
||||||
}
|
|
||||||
|
|
|
@ -450,14 +450,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
let (fragment_with_placeholders, invocations) = {
|
let (fragment_with_placeholders, invocations) = {
|
||||||
let mut collector = InvocationCollector {
|
let mut collector = InvocationCollector {
|
||||||
cfg: StripUnconfigured {
|
cfg: StripUnconfigured {
|
||||||
should_test: self.cx.ecfg.should_test,
|
|
||||||
sess: self.cx.parse_sess,
|
sess: self.cx.parse_sess,
|
||||||
features: self.cx.ecfg.features,
|
features: self.cx.ecfg.features,
|
||||||
},
|
},
|
||||||
cx: self.cx,
|
cx: self.cx,
|
||||||
invocations: Vec::new(),
|
invocations: Vec::new(),
|
||||||
monotonic: self.monotonic,
|
monotonic: self.monotonic,
|
||||||
tests_nameable: true,
|
|
||||||
};
|
};
|
||||||
(fragment.fold_with(&mut collector), collector.invocations)
|
(fragment.fold_with(&mut collector), collector.invocations)
|
||||||
};
|
};
|
||||||
|
@ -475,7 +473,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
|
|
||||||
fn fully_configure(&mut self, item: Annotatable) -> Annotatable {
|
fn fully_configure(&mut self, item: Annotatable) -> Annotatable {
|
||||||
let mut cfg = StripUnconfigured {
|
let mut cfg = StripUnconfigured {
|
||||||
should_test: self.cx.ecfg.should_test,
|
|
||||||
sess: self.cx.parse_sess,
|
sess: self.cx.parse_sess,
|
||||||
features: self.cx.ecfg.features,
|
features: self.cx.ecfg.features,
|
||||||
};
|
};
|
||||||
|
@ -1047,11 +1044,6 @@ struct InvocationCollector<'a, 'b: 'a> {
|
||||||
cfg: StripUnconfigured<'a>,
|
cfg: StripUnconfigured<'a>,
|
||||||
invocations: Vec<Invocation>,
|
invocations: Vec<Invocation>,
|
||||||
monotonic: bool,
|
monotonic: bool,
|
||||||
|
|
||||||
/// Test functions need to be nameable. Tests inside functions or in other
|
|
||||||
/// unnameable locations need to be ignored. `tests_nameable` tracks whether
|
|
||||||
/// any test functions found in the current context would be nameable.
|
|
||||||
tests_nameable: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> InvocationCollector<'a, 'b> {
|
impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||||
|
@ -1069,20 +1061,6 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||||
placeholder(fragment_kind, NodeId::placeholder_from_mark(mark))
|
placeholder(fragment_kind, NodeId::placeholder_from_mark(mark))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Folds the item allowing tests to be expanded because they are still nameable.
|
|
||||||
/// This should probably only be called with module items
|
|
||||||
fn fold_nameable(&mut self, item: P<ast::Item>) -> OneVector<P<ast::Item>> {
|
|
||||||
fold::noop_fold_item(item, self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds the item but doesn't allow tests to occur within it
|
|
||||||
fn fold_unnameable(&mut self, item: P<ast::Item>) -> OneVector<P<ast::Item>> {
|
|
||||||
let was_nameable = mem::replace(&mut self.tests_nameable, false);
|
|
||||||
let items = fold::noop_fold_item(item, self);
|
|
||||||
self.tests_nameable = was_nameable;
|
|
||||||
items
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: AstFragmentKind) -> AstFragment {
|
fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: AstFragmentKind) -> AstFragment {
|
||||||
self.collect(kind, InvocationKind::Bang { mac: mac, ident: None, span: span })
|
self.collect(kind, InvocationKind::Bang { mac: mac, ident: None, span: span })
|
||||||
}
|
}
|
||||||
|
@ -1297,7 +1275,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
|
||||||
fn fold_item(&mut self, item: P<ast::Item>) -> OneVector<P<ast::Item>> {
|
fn fold_item(&mut self, item: P<ast::Item>) -> OneVector<P<ast::Item>> {
|
||||||
let item = configure!(self, item);
|
let item = configure!(self, item);
|
||||||
|
|
||||||
let (attr, traits, mut item) = self.classify_item(item);
|
let (attr, traits, item) = self.classify_item(item);
|
||||||
if attr.is_some() || !traits.is_empty() {
|
if attr.is_some() || !traits.is_empty() {
|
||||||
let item = Annotatable::Item(item);
|
let item = Annotatable::Item(item);
|
||||||
return self.collect_attr(attr, traits, item, AstFragmentKind::Items).make_items();
|
return self.collect_attr(attr, traits, item, AstFragmentKind::Items).make_items();
|
||||||
|
@ -1319,7 +1297,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
|
||||||
}
|
}
|
||||||
ast::ItemKind::Mod(ast::Mod { inner, .. }) => {
|
ast::ItemKind::Mod(ast::Mod { inner, .. }) => {
|
||||||
if item.ident == keywords::Invalid.ident() {
|
if item.ident == keywords::Invalid.ident() {
|
||||||
return self.fold_nameable(item);
|
return noop_fold_item(item, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_directory_ownership = self.cx.current_expansion.directory_ownership;
|
let orig_directory_ownership = self.cx.current_expansion.directory_ownership;
|
||||||
|
@ -1359,32 +1337,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
|
||||||
|
|
||||||
let orig_module =
|
let orig_module =
|
||||||
mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
|
mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
|
||||||
let result = self.fold_nameable(item);
|
let result = noop_fold_item(item, self);
|
||||||
self.cx.current_expansion.module = orig_module;
|
self.cx.current_expansion.module = orig_module;
|
||||||
self.cx.current_expansion.directory_ownership = orig_directory_ownership;
|
self.cx.current_expansion.directory_ownership = orig_directory_ownership;
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that test items can be exported by the harness generator.
|
_ => noop_fold_item(item, self),
|
||||||
// #[test] fn foo() {}
|
|
||||||
// becomes:
|
|
||||||
// #[test] pub fn foo_gensym(){}
|
|
||||||
ast::ItemKind::Const(..)
|
|
||||||
| ast::ItemKind::Static(..)
|
|
||||||
| ast::ItemKind::Fn(..) if self.cx.ecfg.should_test => {
|
|
||||||
if self.tests_nameable && attr::contains_name(&item.attrs, "test_case") {
|
|
||||||
// Publicize the item under gensymed name to avoid pollution
|
|
||||||
// This means #[test_case] items can't be referenced by user code
|
|
||||||
item = item.map(|mut item| {
|
|
||||||
item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
|
|
||||||
item.ident = item.ident.gensym();
|
|
||||||
item
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fold_unnameable(item)
|
|
||||||
}
|
|
||||||
_ => self.fold_unnameable(item),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1609,6 +1568,7 @@ impl<'feat> ExpansionConfig<'feat> {
|
||||||
feature_tests! {
|
feature_tests! {
|
||||||
fn enable_quotes = quote,
|
fn enable_quotes = quote,
|
||||||
fn enable_asm = asm,
|
fn enable_asm = asm,
|
||||||
|
fn enable_custom_test_frameworks = custom_test_frameworks,
|
||||||
fn enable_global_asm = global_asm,
|
fn enable_global_asm = global_asm,
|
||||||
fn enable_log_syntax = log_syntax,
|
fn enable_log_syntax = log_syntax,
|
||||||
fn enable_concat_idents = concat_idents,
|
fn enable_concat_idents = concat_idents,
|
||||||
|
|
|
@ -777,10 +777,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||||
("no_link", Normal, Ungated),
|
("no_link", Normal, Ungated),
|
||||||
("derive", Normal, Ungated),
|
("derive", Normal, Ungated),
|
||||||
("should_panic", Normal, Ungated),
|
("should_panic", Normal, Ungated),
|
||||||
("test_case", Normal, Gated(Stability::Unstable,
|
|
||||||
"custom_test_frameworks",
|
|
||||||
"Custom test frameworks are experimental",
|
|
||||||
cfg_fn!(custom_test_frameworks))),
|
|
||||||
("ignore", Normal, Ungated),
|
("ignore", Normal, Ungated),
|
||||||
("no_implicit_prelude", Normal, Ungated),
|
("no_implicit_prelude", Normal, Ungated),
|
||||||
("reexport_test_harness_main", Normal, Ungated),
|
("reexport_test_harness_main", Normal, Ungated),
|
||||||
|
@ -965,6 +961,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||||
attribute is just used for rustc unit \
|
attribute is just used for rustc unit \
|
||||||
tests and will never be stable",
|
tests and will never be stable",
|
||||||
cfg_fn!(rustc_attrs))),
|
cfg_fn!(rustc_attrs))),
|
||||||
|
("rustc_test_marker", Normal, Gated(Stability::Unstable,
|
||||||
|
"rustc_attrs",
|
||||||
|
"the `#[rustc_test_marker]` attribute \
|
||||||
|
is used internally to track tests",
|
||||||
|
cfg_fn!(rustc_attrs))),
|
||||||
|
|
||||||
// RFC #2094
|
// RFC #2094
|
||||||
("nll", Whitelisted, Gated(Stability::Unstable,
|
("nll", Whitelisted, Gated(Stability::Unstable,
|
||||||
|
@ -1164,7 +1165,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||||
("type_length_limit", CrateLevel, Ungated),
|
("type_length_limit", CrateLevel, Ungated),
|
||||||
("test_runner", CrateLevel, Gated(Stability::Unstable,
|
("test_runner", CrateLevel, Gated(Stability::Unstable,
|
||||||
"custom_test_frameworks",
|
"custom_test_frameworks",
|
||||||
"Custom Test Frameworks is an unstable feature",
|
EXPLAIN_CUSTOM_TEST_FRAMEWORKS,
|
||||||
cfg_fn!(custom_test_frameworks))),
|
cfg_fn!(custom_test_frameworks))),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1382,6 +1383,9 @@ pub const EXPLAIN_ASM: &'static str =
|
||||||
pub const EXPLAIN_GLOBAL_ASM: &'static str =
|
pub const EXPLAIN_GLOBAL_ASM: &'static str =
|
||||||
"`global_asm!` is not stable enough for use and is subject to change";
|
"`global_asm!` is not stable enough for use and is subject to change";
|
||||||
|
|
||||||
|
pub const EXPLAIN_CUSTOM_TEST_FRAMEWORKS: &'static str =
|
||||||
|
"custom test frameworks are an unstable feature";
|
||||||
|
|
||||||
pub const EXPLAIN_LOG_SYNTAX: &'static str =
|
pub const EXPLAIN_LOG_SYNTAX: &'static str =
|
||||||
"`log_syntax!` is not stable enough for use and is subject to change";
|
"`log_syntax!` is not stable enough for use and is subject to change";
|
||||||
|
|
||||||
|
|
|
@ -6272,7 +6272,6 @@ impl<'a> Parser<'a> {
|
||||||
let (in_cfg, outer_attrs) = {
|
let (in_cfg, outer_attrs) = {
|
||||||
let mut strip_unconfigured = ::config::StripUnconfigured {
|
let mut strip_unconfigured = ::config::StripUnconfigured {
|
||||||
sess: self.sess,
|
sess: self.sess,
|
||||||
should_test: false, // irrelevant
|
|
||||||
features: None, // don't perform gated feature checking
|
features: None, // don't perform gated feature checking
|
||||||
};
|
};
|
||||||
let outer_attrs = strip_unconfigured.process_cfg_attrs(outer_attrs.to_owned());
|
let outer_attrs = strip_unconfigured.process_cfg_attrs(outer_attrs.to_owned());
|
||||||
|
|
|
@ -433,7 +433,7 @@ fn visible_path(cx: &TestCtxt, path: &[Ident]) -> Vec<Ident>{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_test_case(i: &ast::Item) -> bool {
|
fn is_test_case(i: &ast::Item) -> bool {
|
||||||
attr::contains_name(&i.attrs, "test_case")
|
attr::contains_name(&i.attrs, "rustc_test_marker")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
|
fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
|
||||||
|
|
|
@ -54,6 +54,7 @@ mod global_asm;
|
||||||
mod log_syntax;
|
mod log_syntax;
|
||||||
mod trace_macros;
|
mod trace_macros;
|
||||||
mod test;
|
mod test;
|
||||||
|
mod test_case;
|
||||||
|
|
||||||
pub mod proc_macro_registrar;
|
pub mod proc_macro_registrar;
|
||||||
|
|
||||||
|
@ -145,6 +146,7 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
|
||||||
assert: assert::expand_assert,
|
assert: assert::expand_assert,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(Symbol::intern("test_case"), MultiModifier(Box::new(test_case::expand)));
|
||||||
|
|
||||||
// format_args uses `unstable` things internally.
|
// format_args uses `unstable` things internally.
|
||||||
register(Symbol::intern("format_args"),
|
register(Symbol::intern("format_args"),
|
||||||
|
|
|
@ -135,8 +135,14 @@ pub fn expand_test_or_bench(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut test_const = cx.item(sp, item.ident.gensym(),
|
let mut test_const = cx.item(sp, item.ident.gensym(),
|
||||||
// #[test_case]
|
vec![
|
||||||
vec![cx.attribute(attr_sp, cx.meta_word(attr_sp, Symbol::intern("test_case")))],
|
// #[cfg(test)]
|
||||||
|
cx.attribute(attr_sp, cx.meta_list(attr_sp, Symbol::intern("cfg"), vec![
|
||||||
|
cx.meta_list_item_word(attr_sp, Symbol::intern("test"))
|
||||||
|
])),
|
||||||
|
// #[rustc_test_marker]
|
||||||
|
cx.attribute(attr_sp, cx.meta_word(attr_sp, Symbol::intern("rustc_test_marker")))
|
||||||
|
],
|
||||||
// const $ident: test::TestDescAndFn =
|
// const $ident: test::TestDescAndFn =
|
||||||
ast::ItemKind::Const(cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
|
ast::ItemKind::Const(cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
|
||||||
// test::TestDescAndFn {
|
// test::TestDescAndFn {
|
||||||
|
|
75
src/libsyntax_ext/test_case.rs
Normal file
75
src/libsyntax_ext/test_case.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
|
||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// #[test_case] is used by custom test authors to mark tests
|
||||||
|
// When building for test, it needs to make the item public and gensym the name
|
||||||
|
// Otherwise, we'll omit the item. This behavior means that any item annotated
|
||||||
|
// with #[test_case] is never addressable.
|
||||||
|
//
|
||||||
|
// We mark item with an inert attribute "rustc_test_marker" which the test generation
|
||||||
|
// logic will pick up on.
|
||||||
|
|
||||||
|
use syntax::ext::base::*;
|
||||||
|
use syntax::ext::build::AstBuilder;
|
||||||
|
use syntax::ext::hygiene::{self, Mark, SyntaxContext};
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::source_map::respan;
|
||||||
|
use syntax::symbol::Symbol;
|
||||||
|
use syntax_pos::{DUMMY_SP, Span};
|
||||||
|
use syntax::source_map::{ExpnInfo, MacroAttribute};
|
||||||
|
use syntax::feature_gate;
|
||||||
|
|
||||||
|
pub fn expand(
|
||||||
|
ecx: &mut ExtCtxt,
|
||||||
|
attr_sp: Span,
|
||||||
|
_meta_item: &ast::MetaItem,
|
||||||
|
anno_item: Annotatable
|
||||||
|
) -> Vec<Annotatable> {
|
||||||
|
if !ecx.ecfg.enable_custom_test_frameworks() {
|
||||||
|
feature_gate::emit_feature_err(&ecx.parse_sess,
|
||||||
|
"custom_test_frameworks",
|
||||||
|
attr_sp,
|
||||||
|
feature_gate::GateIssue::Language,
|
||||||
|
feature_gate::EXPLAIN_CUSTOM_TEST_FRAMEWORKS);
|
||||||
|
|
||||||
|
return vec![anno_item];
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ecx.ecfg.should_test { return vec![]; }
|
||||||
|
|
||||||
|
let sp = {
|
||||||
|
let mark = Mark::fresh(Mark::root());
|
||||||
|
mark.set_expn_info(ExpnInfo {
|
||||||
|
call_site: DUMMY_SP,
|
||||||
|
def_site: None,
|
||||||
|
format: MacroAttribute(Symbol::intern("test_case")),
|
||||||
|
allow_internal_unstable: true,
|
||||||
|
allow_internal_unsafe: false,
|
||||||
|
local_inner_macros: false,
|
||||||
|
edition: hygiene::default_edition(),
|
||||||
|
});
|
||||||
|
attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(mark))
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut item = anno_item.expect_item();
|
||||||
|
|
||||||
|
item = item.map(|mut item| {
|
||||||
|
item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
|
||||||
|
item.ident = item.ident.gensym();
|
||||||
|
item.attrs.push(
|
||||||
|
ecx.attribute(sp,
|
||||||
|
ecx.meta_word(sp, Symbol::intern("rustc_test_marker")))
|
||||||
|
);
|
||||||
|
item
|
||||||
|
});
|
||||||
|
|
||||||
|
return vec![Annotatable::Item(item)]
|
||||||
|
}
|
|
@ -18,6 +18,4 @@ fn main() {
|
||||||
//~^ ERROR removing an expression is not supported in this position
|
//~^ ERROR removing an expression is not supported in this position
|
||||||
let _ = [1, 2, 3][#[cfg(unset)] 1];
|
let _ = [1, 2, 3][#[cfg(unset)] 1];
|
||||||
//~^ ERROR removing an expression is not supported in this position
|
//~^ ERROR removing an expression is not supported in this position
|
||||||
let _ = #[test_case] ();
|
|
||||||
//~^ ERROR removing an expression is not supported in this position
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,5 @@ error: removing an expression is not supported in this position
|
||||||
LL | let _ = [1, 2, 3][#[cfg(unset)] 1];
|
LL | let _ = [1, 2, 3][#[cfg(unset)] 1];
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: removing an expression is not supported in this position
|
error: aborting due to 3 previous errors
|
||||||
--> $DIR/cfg-non-opt-expr.rs:21:13
|
|
||||||
|
|
|
||||||
LL | let _ = #[test_case] ();
|
|
||||||
| ^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![test_runner(main)] //~ ERROR Custom Test Frameworks is an unstable feature
|
#![test_runner(main)] //~ ERROR custom test frameworks are an unstable feature
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
error[E0658]: Custom Test Frameworks is an unstable feature (see issue #50297)
|
error[E0658]: custom test frameworks are an unstable feature (see issue #50297)
|
||||||
--> $DIR/feature-gate-custom_test_frameworks.rs:11:1
|
--> $DIR/feature-gate-custom_test_frameworks.rs:11:1
|
||||||
|
|
|
|
||||||
LL | #![test_runner(main)] //~ ERROR Custom Test Frameworks is an unstable feature
|
LL | #![test_runner(main)] //~ ERROR custom test frameworks are an unstable feature
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: add #![feature(custom_test_frameworks)] to the crate attributes to enable
|
= help: add #![feature(custom_test_frameworks)] to the crate attributes to enable
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue