diff options
| author | John Renner <john@jrenner.net> | 2018-09-02 09:03:24 -0700 |
|---|---|---|
| committer | John Renner <john@jrenner.net> | 2018-09-04 22:33:23 -0700 |
| commit | 0593dc7e3c9783f1c0bbbc8f017f9e914114e057 (patch) | |
| tree | 851bd551452a99115a72773b871f0d0f1433fcc4 /src | |
| parent | e5ed10571690b2ee4fc64319967973b2e50b517f (diff) | |
| download | rust-0593dc7e3c9783f1c0bbbc8f017f9e914114e057.tar.gz rust-0593dc7e3c9783f1c0bbbc8f017f9e914114e057.zip | |
Move #[test_case] to a syntax extension
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_driver/driver.rs | 1 | ||||
| -rw-r--r-- | src/librustc_lint/builtin.rs | 2 | ||||
| -rw-r--r-- | src/librustc_resolve/macros.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/config.rs | 15 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 50 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 14 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/test.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax_ext/lib.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax_ext/test.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax_ext/test_case.rs | 75 | ||||
| -rw-r--r-- | src/test/ui/cfg-non-opt-expr.rs | 2 | ||||
| -rw-r--r-- | src/test/ui/cfg-non-opt-expr.stderr | 8 | ||||
| -rw-r--r-- | src/test/ui/feature-gate-custom_test_frameworks.rs | 2 | ||||
| -rw-r--r-- | src/test/ui/feature-gate-custom_test_frameworks.stderr | 4 |
15 files changed, 108 insertions, 82 deletions
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index c6344cb9210..d27b0856c15 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -828,7 +828,6 @@ where let (mut krate, features) = syntax::config::features( krate, &sess.parse_sess, - sess.opts.test, sess.edition(), ); // these need to be set "early" so that expansion sees `quote` if enabled. diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 40662bafa1b..8ebb181247c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1872,7 +1872,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems { 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( UNNAMEABLE_TEST_ITEMS, attr.span, diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 4cc8d348667..f403e09b7f7 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -479,7 +479,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { return def; } - if kind == MacroKind::Attr && path.len() == 1 { + if kind == MacroKind::Attr { if let Some(ext) = self.unshadowable_attrs.get(&path[0].name) { return Ok(ext.def()); } diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 900d830b4c0..5233267e3a9 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -21,18 +21,16 @@ use ptr::P; /// A folder that strips out items that do not belong in the current configuration. pub struct StripUnconfigured<'a> { - pub should_test: bool, pub sess: &'a ParseSess, pub features: Option<&'a 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) { let features; { let mut strip_unconfigured = StripUnconfigured { - should_test, sess, features: None, }; @@ -118,11 +116,6 @@ impl<'a> StripUnconfigured<'a> { // Determine if a node with the given attributes should be included in this configuration. pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { 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) { return true; } 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 // 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"; 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 { attr.check_name("cfg") } - -pub fn is_test(att: &ast::Attribute) -> bool { - att.check_name("test_case") -} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 956e086b01b..3bb19121ee3 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -450,14 +450,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let (fragment_with_placeholders, invocations) = { let mut collector = InvocationCollector { cfg: StripUnconfigured { - should_test: self.cx.ecfg.should_test, sess: self.cx.parse_sess, features: self.cx.ecfg.features, }, cx: self.cx, invocations: Vec::new(), monotonic: self.monotonic, - tests_nameable: true, }; (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 { let mut cfg = StripUnconfigured { - should_test: self.cx.ecfg.should_test, sess: self.cx.parse_sess, features: self.cx.ecfg.features, }; @@ -1047,11 +1044,6 @@ struct InvocationCollector<'a, 'b: 'a> { cfg: StripUnconfigured<'a>, invocations: Vec<Invocation>, 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> { @@ -1069,20 +1061,6 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { 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 { 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>> { 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() { let item = Annotatable::Item(item); 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, .. }) => { 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; @@ -1359,32 +1337,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let orig_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.directory_ownership = orig_directory_ownership; result } - // Ensure that test items can be exported by the harness generator. - // #[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), + _ => noop_fold_item(item, self), } } @@ -1609,6 +1568,7 @@ impl<'feat> ExpansionConfig<'feat> { feature_tests! { fn enable_quotes = quote, fn enable_asm = asm, + fn enable_custom_test_frameworks = custom_test_frameworks, fn enable_global_asm = global_asm, fn enable_log_syntax = log_syntax, fn enable_concat_idents = concat_idents, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c94c7874a05..e3ea3563d85 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -777,10 +777,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG ("no_link", Normal, Ungated), ("derive", 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), ("no_implicit_prelude", 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 \ tests and will never be stable", 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 ("nll", Whitelisted, Gated(Stability::Unstable, @@ -1164,7 +1165,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG ("type_length_limit", CrateLevel, Ungated), ("test_runner", CrateLevel, Gated(Stability::Unstable, "custom_test_frameworks", - "Custom Test Frameworks is an unstable feature", + EXPLAIN_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 = "`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 = "`log_syntax!` is not stable enough for use and is subject to change"; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3862877c3d9..5f80af77f49 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -6272,7 +6272,6 @@ impl<'a> Parser<'a> { let (in_cfg, outer_attrs) = { let mut strip_unconfigured = ::config::StripUnconfigured { sess: self.sess, - should_test: false, // irrelevant features: None, // don't perform gated feature checking }; let outer_attrs = strip_unconfigured.process_cfg_attrs(outer_attrs.to_owned()); diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 91c63227d30..ab67736c389 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -433,7 +433,7 @@ fn visible_path(cx: &TestCtxt, path: &[Ident]) -> Vec<Ident>{ } 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> { diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index a9990cdeabf..e16f3b1ccb3 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -54,6 +54,7 @@ mod global_asm; mod log_syntax; mod trace_macros; mod test; +mod test_case; pub mod proc_macro_registrar; @@ -145,6 +146,7 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver, assert: assert::expand_assert, } + register(Symbol::intern("test_case"), MultiModifier(Box::new(test_case::expand))); // format_args uses `unstable` things internally. register(Symbol::intern("format_args"), diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs index d9d0f3d0a32..be3485cfa7c 100644 --- a/src/libsyntax_ext/test.rs +++ b/src/libsyntax_ext/test.rs @@ -135,8 +135,14 @@ pub fn expand_test_or_bench( }; let mut test_const = cx.item(sp, item.ident.gensym(), - // #[test_case] - vec![cx.attribute(attr_sp, cx.meta_word(attr_sp, Symbol::intern("test_case")))], + vec![ + // #[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 = ast::ItemKind::Const(cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), // test::TestDescAndFn { diff --git a/src/libsyntax_ext/test_case.rs b/src/libsyntax_ext/test_case.rs new file mode 100644 index 00000000000..0128db7dd78 --- /dev/null +++ b/src/libsyntax_ext/test_case.rs @@ -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)] +} diff --git a/src/test/ui/cfg-non-opt-expr.rs b/src/test/ui/cfg-non-opt-expr.rs index bd0a5c66b3e..55eca7f45a5 100644 --- a/src/test/ui/cfg-non-opt-expr.rs +++ b/src/test/ui/cfg-non-opt-expr.rs @@ -18,6 +18,4 @@ fn main() { //~^ ERROR removing an expression is not supported in this position let _ = [1, 2, 3][#[cfg(unset)] 1]; //~^ ERROR removing an expression is not supported in this position - let _ = #[test_case] (); - //~^ ERROR removing an expression is not supported in this position } diff --git a/src/test/ui/cfg-non-opt-expr.stderr b/src/test/ui/cfg-non-opt-expr.stderr index 8c5d8900f8b..1892cee113e 100644 --- a/src/test/ui/cfg-non-opt-expr.stderr +++ b/src/test/ui/cfg-non-opt-expr.stderr @@ -16,11 +16,5 @@ error: removing an expression is not supported in this position LL | let _ = [1, 2, 3][#[cfg(unset)] 1]; | ^^^^^^^^^^^^^ -error: removing an expression is not supported in this position - --> $DIR/cfg-non-opt-expr.rs:21:13 - | -LL | let _ = #[test_case] (); - | ^^^^^^^^^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/feature-gate-custom_test_frameworks.rs b/src/test/ui/feature-gate-custom_test_frameworks.rs index e8d1524996f..7c9f7dd0402 100644 --- a/src/test/ui/feature-gate-custom_test_frameworks.rs +++ b/src/test/ui/feature-gate-custom_test_frameworks.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // 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() {} diff --git a/src/test/ui/feature-gate-custom_test_frameworks.stderr b/src/test/ui/feature-gate-custom_test_frameworks.stderr index cd04f32697b..bfcbab50067 100644 --- a/src/test/ui/feature-gate-custom_test_frameworks.stderr +++ b/src/test/ui/feature-gate-custom_test_frameworks.stderr @@ -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 | -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 |
