about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJohn Renner <john@jrenner.net>2018-09-02 09:03:24 -0700
committerJohn Renner <john@jrenner.net>2018-09-04 22:33:23 -0700
commit0593dc7e3c9783f1c0bbbc8f017f9e914114e057 (patch)
tree851bd551452a99115a72773b871f0d0f1433fcc4 /src
parente5ed10571690b2ee4fc64319967973b2e50b517f (diff)
downloadrust-0593dc7e3c9783f1c0bbbc8f017f9e914114e057.tar.gz
rust-0593dc7e3c9783f1c0bbbc8f017f9e914114e057.zip
Move #[test_case] to a syntax extension
Diffstat (limited to 'src')
-rw-r--r--src/librustc_driver/driver.rs1
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_resolve/macros.rs2
-rw-r--r--src/libsyntax/config.rs15
-rw-r--r--src/libsyntax/ext/expand.rs50
-rw-r--r--src/libsyntax/feature_gate.rs14
-rw-r--r--src/libsyntax/parse/parser.rs1
-rw-r--r--src/libsyntax/test.rs2
-rw-r--r--src/libsyntax_ext/lib.rs2
-rw-r--r--src/libsyntax_ext/test.rs10
-rw-r--r--src/libsyntax_ext/test_case.rs75
-rw-r--r--src/test/ui/cfg-non-opt-expr.rs2
-rw-r--r--src/test/ui/cfg-non-opt-expr.stderr8
-rw-r--r--src/test/ui/feature-gate-custom_test_frameworks.rs2
-rw-r--r--src/test/ui/feature-gate-custom_test_frameworks.stderr4
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