diff options
| author | bors <bors@rust-lang.org> | 2018-02-24 20:48:24 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-02-24 20:48:24 +0000 |
| commit | 28a1e4ffefa2620ad9f4179ea339833448874fd3 (patch) | |
| tree | 18a432b954cebc1cfdddc8f699e4be8e86bcac86 /src/libsyntax | |
| parent | 6070d3e47e5e9f15575a3bd33583358b52bc6eda (diff) | |
| parent | 182f8820c4b53f811c140478a0105b2a7b77c5c3 (diff) | |
| download | rust-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.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 70 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/test.rs | 191 |
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 ¶ms.types { - self.visit_ty(type_); - } - for type_binding in ¶ms.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 ¶ms.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]); |
