about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-02-24 20:48:24 +0000
committerbors <bors@rust-lang.org>2018-02-24 20:48:24 +0000
commit28a1e4ffefa2620ad9f4179ea339833448874fd3 (patch)
tree18a432b954cebc1cfdddc8f699e4be8e86bcac86 /src/libsyntax
parent6070d3e47e5e9f15575a3bd33583358b52bc6eda (diff)
parent182f8820c4b53f811c140478a0105b2a7b77c5c3 (diff)
downloadrust-28a1e4ffefa2620ad9f4179ea339833448874fd3.tar.gz
rust-28a1e4ffefa2620ad9f4179ea339833448874fd3.zip
Auto merge of #48510 - Manishearth:rollup, r=Manishearth
Rollup of 15 pull requests

- Successful merges: #47987, #48056, #48061, #48084, #48143, #48185, #48206, #48208, #48232, #48246, #48258, #48317, #48353, #48356, #48402
- Failed merges:
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/build.rs10
-rw-r--r--src/libsyntax/feature_gate.rs70
-rw-r--r--src/libsyntax/parse/parser.rs2
-rw-r--r--src/libsyntax/test.rs191
4 files changed, 152 insertions, 121 deletions
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 7681f55bd8c..b88e064e7e5 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -319,14 +319,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                 types: Vec<P<ast::Ty>>,
                 bindings: Vec<ast::TypeBinding> )
                 -> ast::Path {
-        use syntax::parse::token;
-
         let last_identifier = idents.pop().unwrap();
         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)));
         let parameters = if !lifetimes.is_empty() || !types.is_empty() || !bindings.is_empty() {
@@ -335,7 +329,9 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             None
         };
         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.
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index f7cebed5f62..ba24d7f914b 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -432,9 +432,6 @@ declare_features! (
     // `foo.rs` as an alternative to `foo/mod.rs`
     (active, non_modrs_mods, "1.24.0", Some(44660)),
 
-    // Nested `impl Trait`
-    (active, nested_impl_trait, "1.24.0", Some(34511)),
-
     // Termination trait in main (RFC 1937)
     (active, termination_trait, "1.24.0", Some(43301)),
 
@@ -1352,73 +1349,8 @@ fn contains_novel_literal(item: &ast::MetaItem) -> bool {
     }
 }
 
-// Bans nested `impl Trait`, e.g. `impl Into<impl Debug>`.
-// Nested `impl Trait` _is_ allowed in associated type position,
-// e.g `impl Iterator<Item=impl Debug>`
-struct NestedImplTraitVisitor<'a> {
-    context: &'a Context<'a>,
-    is_in_impl_trait: bool,
-}
-
-impl<'a> NestedImplTraitVisitor<'a> {
-    fn with_impl_trait<F>(&mut self, is_in_impl_trait: bool, f: F)
-        where F: FnOnce(&mut NestedImplTraitVisitor<'a>)
-    {
-        let old_is_in_impl_trait = self.is_in_impl_trait;
-        self.is_in_impl_trait = is_in_impl_trait;
-        f(self);
-        self.is_in_impl_trait = old_is_in_impl_trait;
-    }
-}
-
-
-impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> {
-    fn visit_ty(&mut self, t: &'a ast::Ty) {
-        if let ast::TyKind::ImplTrait(_) = t.node {
-            if self.is_in_impl_trait {
-                gate_feature_post!(&self, nested_impl_trait, t.span,
-                    "nested `impl Trait` is experimental"
-                );
-            }
-            self.with_impl_trait(true, |this| visit::walk_ty(this, t));
-        } else {
-            visit::walk_ty(self, t);
-        }
-    }
-    fn visit_path_parameters(&mut self, _: Span, path_parameters: &'a ast::PathParameters) {
-        match *path_parameters {
-            ast::PathParameters::AngleBracketed(ref params) => {
-                for type_ in &params.types {
-                    self.visit_ty(type_);
-                }
-                for type_binding in &params.bindings {
-                    // Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
-                    // are allowed to contain nested `impl Trait`.
-                    self.with_impl_trait(false, |this| visit::walk_ty(this, &type_binding.ty));
-                }
-            }
-            ast::PathParameters::Parenthesized(ref params) => {
-                for type_ in &params.inputs {
-                    self.visit_ty(type_);
-                }
-                if let Some(ref type_) = params.output {
-                    // `-> Foo` syntax is essentially an associated type binding,
-                    // so it is also allowed to contain nested `impl Trait`.
-                    self.with_impl_trait(false, |this| visit::walk_ty(this, type_));
-                }
-            }
-        }
-    }
-}
-
 impl<'a> PostExpansionVisitor<'a> {
-    fn whole_crate_feature_gates(&mut self, krate: &ast::Crate) {
-        visit::walk_crate(
-            &mut NestedImplTraitVisitor {
-                context: self.context,
-                is_in_impl_trait: false,
-            }, krate);
-
+    fn whole_crate_feature_gates(&mut self, _krate: &ast::Crate) {
         for &(ident, span) in &*self.context.parse_sess.non_modrs_mods.borrow() {
             if !span.allows_unstable() {
                 let cx = &self.context;
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 1a33de84429..efc191f24ac 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -6482,6 +6482,8 @@ impl<'a> Parser<'a> {
             && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace)) {
             // UNSAFE FUNCTION ITEM
             self.bump(); // `unsafe`
+            // `{` is also expected after `unsafe`, in case of error, include it in the diagnostic
+            self.check(&token::OpenDelim(token::Brace));
             let abi = if self.eat_keyword(keywords::Extern) {
                 self.parse_opt_abi()?.unwrap_or(Abi::C)
             } else {
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 7b119c576db..e732ac3a635 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -32,6 +32,7 @@ use ext::build::AstBuilder;
 use ext::expand::ExpansionConfig;
 use ext::hygiene::{Mark, SyntaxContext};
 use fold::Folder;
+use feature_gate::Features;
 use util::move_map::MoveMap;
 use fold;
 use parse::{token, ParseSess};
@@ -63,6 +64,7 @@ struct TestCtxt<'a> {
     reexport_test_harness_main: Option<Symbol>,
     is_libtest: bool,
     ctxt: SyntaxContext,
+    features: &'a Features,
 
     // top-level re-export submodule, filled out after folding is finished
     toplevel_reexport: Option<Ident>,
@@ -74,7 +76,8 @@ pub fn modify_for_testing(sess: &ParseSess,
                           resolver: &mut Resolver,
                           should_test: bool,
                           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
     // creates a `use some_name = __test::main;`. This needs to be
     // 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");
 
     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 {
         krate
     }
@@ -265,16 +269,20 @@ fn generate_test_harness(sess: &ParseSess,
                          resolver: &mut Resolver,
                          reexport_test_harness_main: Option<Symbol>,
                          krate: ast::Crate,
-                         sd: &errors::Handler) -> ast::Crate {
+                         sd: &errors::Handler,
+                         features: &Features) -> ast::Crate {
     // Remove the entry points
     let mut cleaner = EntryPointCleaner { depth: 0 };
     let krate = cleaner.fold_crate(krate);
 
     let mark = Mark::fresh(Mark::root());
 
+    let mut econfig = ExpansionConfig::default("test".to_string());
+    econfig.features = Some(features);
+
     let cx = TestCtxt {
         span_diagnostic: sd,
-        ext_cx: ExtCtxt::new(sess, ExpansionConfig::default("test".to_string()), resolver),
+        ext_cx: ExtCtxt::new(sess, econfig, resolver),
         path: Vec::new(),
         testfns: Vec::new(),
         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),
         toplevel_reexport: None,
         ctxt: SyntaxContext::empty().apply_mark(mark),
+        features,
     };
 
     mark.set_expn_info(ExpnInfo {
@@ -318,71 +327,105 @@ enum HasTestSignature {
 fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
     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 {
-          ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
-            let no_output = match decl.output {
-                ast::FunctionRetTy::Default(..) => true,
-                ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
-                _ => false
-            };
-            if decl.inputs.is_empty()
-                   && no_output
-                   && !generics.is_parameterized() {
-                Yes
-            } else {
-                No
+            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 {
+                        ast::FunctionRetTy::Default(..) => true,
+                        ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
+                        _ => false
+                    };
+
+                    no_output && !generics.is_parameterized()
+                };
+
+                if decl.inputs.is_empty() && output_matches {
+                    Yes
+                } else {
+                    No
+                }
             }
-          }
-          _ => NotEvenAFunction,
+            _ => NotEvenAFunction,
         }
     }
 
-    if has_test_attr {
+    let has_test_signature = if has_test_attr {
         let diag = cx.span_diagnostic;
-        match has_test_signature(i) {
-            Yes => {},
-            No => diag.span_err(i.span, "functions used as tests must have signature fn() -> ()"),
-            NotEvenAFunction => diag.span_err(i.span,
-                                              "only functions may be used as tests"),
+        match has_test_signature(cx, i) {
+            Yes => true,
+            No => {
+                if cx.features.termination_trait {
+                    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 {
     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 {
             ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
                 let input_cnt = decl.inputs.len();
-                let no_output = match decl.output {
-                    ast::FunctionRetTy::Default(..) => true,
-                    ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
-                    _ => false
+
+                // 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 {
+                        ast::FunctionRetTy::Default(..) => true,
+                        ast::FunctionRetTy::Ty(ref t) if t.node == ast::TyKind::Tup(vec![]) => true,
+                        _ => false
+                    };
+                    let tparm_cnt = generics.params.iter()
+                        .filter(|param| param.is_type_param())
+                        .count();
+
+                    no_output && tparm_cnt == 0
                 };
-                let tparm_cnt = generics.params.iter()
-                    .filter(|param| param.is_type_param())
-                    .count();
 
                 // NB: inadequate check, but we're running
                 // well before resolve, can't get too deep.
-                input_cnt == 1
-                    && no_output && tparm_cnt == 0
+                input_cnt == 1 && output_matches
             }
           _ => 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;
-        diag.span_err(i.span, "functions used as benches must have signature \
-                      `fn(&mut Bencher) -> ()`");
+
+        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 \
+                                   `fn(&mut Bencher) -> ()`");
+        }
     }
 
-    has_bench_attr && has_test_signature(i)
+    has_bench_attr && has_bench_signature
 }
 
 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("allow_fail", allow_fail_expr)]);
 
-
-    let mut visible_path = match cx.toplevel_reexport {
-        Some(id) => vec![id],
+    let mut visible_path = vec![];
+    if cx.features.extern_absolute_paths {
+        visible_path.push(keywords::Crate.ident());
+    }
+    match cx.toplevel_reexport {
+        Some(id) => visible_path.push(id),
         None => {
             let diag = cx.span_diagnostic;
             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);
 
-    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" };
+
     // self::test::$variant_name($fn_expr)
     let testfn_expr = ecx.expr_call(span, ecx.expr_path(test_path(variant_name)), vec![fn_expr]);