diff options
59 files changed, 550 insertions, 77 deletions
diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index d8cf147e3ee..7e03288f572 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -179,7 +179,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex { match expr.node { - hir::ExprBlock(ref blk) => { + hir::ExprBlock(ref blk, _) => { let blk_exit = self.block(&blk, pred); self.add_ast_node(expr.hir_id.local_id, &[blk_exit]) } diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 1fb496cca62..59b058e9861 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -1015,7 +1015,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { expression.span, expression.id) } - ExprBlock(ref block) => visitor.visit_block(block), + ExprBlock(ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_block(block); + } ExprAssign(ref left_hand_expression, ref right_hand_expression) => { visitor.visit_expr(right_hand_expression); visitor.visit_expr(left_hand_expression) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 7a360f7ee43..0f4871954d6 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -3048,7 +3048,7 @@ impl<'a> LoweringContext<'a> { ); block.expr = Some(this.wrap_in_try_constructor( "from_ok", tail, unstable_span)); - hir::ExprBlock(P(block)) + hir::ExprBlock(P(block), None) }) } ExprKind::Match(ref expr, ref arms) => hir::ExprMatch( @@ -3100,7 +3100,11 @@ impl<'a> LoweringContext<'a> { }) }) } - ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, false)), + ExprKind::Block(ref blk, opt_label) => { + hir::ExprBlock(self.lower_block(blk, + opt_label.is_some()), + self.lower_label(opt_label)) + } ExprKind::Assign(ref el, ref er) => { hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er))) } @@ -3843,7 +3847,7 @@ impl<'a> LoweringContext<'a> { } fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr { - self.expr(b.span, hir::ExprBlock(b), attrs) + self.expr(b.span, hir::ExprBlock(b, None), attrs) } fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<hir::Expr>) -> P<hir::Expr> { diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 3f49e0bfd19..c48fb7ab7eb 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -778,9 +778,8 @@ pub struct Block { pub rules: BlockCheckMode, pub span: Span, /// If true, then there may exist `break 'a` values that aim to - /// break out of this block early. As of this writing, this is not - /// currently permitted in Rust itself, but it is generated as - /// part of `catch` statements. + /// break out of this block early. + /// Used by `'label: {}` blocks and by `catch` statements. pub targeted_by_break: bool, /// If true, don't emit return value type errors as the parser had /// to recover from a parse error so this block will not have an @@ -1381,8 +1380,8 @@ pub enum Expr_ { /// This may also be a generator literal, indicated by the final boolean, /// in that case there is an GeneratorClause. ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, Option<GeneratorMovability>), - /// A block (`{ ... }`) - ExprBlock(P<Block>), + /// A block (`'label: { ... }`) + ExprBlock(P<Block>, Option<Label>), /// An assignment (`a = foo()`) ExprAssign(P<Expr>, P<Expr>), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 3943c30127d..9cd9e0dce54 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1047,7 +1047,7 @@ impl<'a> State<'a> { self.print_else(e.as_ref().map(|e| &**e)) } // "final else" - hir::ExprBlock(ref b) => { + hir::ExprBlock(ref b, _) => { self.cbox(indent_unit - 1)?; self.ibox(0)?; self.s.word(" else ")?; @@ -1377,7 +1377,11 @@ impl<'a> State<'a> { // empty box to satisfy the close. self.ibox(0)?; } - hir::ExprBlock(ref blk) => { + hir::ExprBlock(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_name(label.name)?; + self.word_space(":")?; + } // containing cbox, will be closed by print-block at } self.cbox(indent_unit)?; // head-box, will be closed by print-block after { @@ -1893,7 +1897,11 @@ impl<'a> State<'a> { self.word_space("=>")?; match arm.body.node { - hir::ExprBlock(ref blk) => { + hir::ExprBlock(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_name(label.name)?; + self.word_space(":")?; + } // the block will close the pattern's ibox self.print_block_unclosed_indent(&blk, indent_unit)?; @@ -2299,7 +2307,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool { match e.node { hir::ExprIf(..) | hir::ExprMatch(..) | - hir::ExprBlock(_) | + hir::ExprBlock(..) | hir::ExprWhile(..) | hir::ExprLoop(..) => false, _ => true, diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 6edf1b35bdd..ed01704e6a4 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -589,7 +589,7 @@ impl_stable_hash_for!(enum hir::Expr_ { ExprLoop(body, label, loop_src), ExprMatch(matchee, arms, match_src), ExprClosure(capture_clause, decl, body_id, span, gen), - ExprBlock(blk), + ExprBlock(blk, label), ExprAssign(lhs, rhs), ExprAssignOp(op, lhs, rhs), ExprField(owner, field_name), diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 614ae17fa46..3ba230fe55a 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -499,7 +499,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { self.consume_expr(&rhs); } - hir::ExprBlock(ref blk) => { + hir::ExprBlock(ref blk, _) => { self.walk_block(&blk); } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 2b1663bed2f..b39311a7471 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1187,7 +1187,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { succ } - hir::ExprBlock(ref blk) => { + // Note that labels have been resolved, so we don't need to look + // at the label ident + hir::ExprBlock(ref blk, _) => { self.propagate_through_block(&blk, succ) } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index bfc9ff6660d..52217600759 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1247,7 +1247,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, hir::ExprCast(ref subexpr, _) => { record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id) } - hir::ExprBlock(ref block) => { + hir::ExprBlock(ref block, _) => { if let Some(ref subexpr) = block.expr { record_rvalue_scope_if_borrow_expr( visitor, &subexpr, blk_id); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 0b1e9081a72..19280372d55 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -228,7 +228,7 @@ impl UnsafeCode { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { - if let hir::ExprBlock(ref blk) = e.node { + if let hir::ExprBlock(ref blk, _) = e.node { // Don't warn about generated blocks, that'll just pollute the output. if blk.rules == hir::UnsafeBlock(hir::UserProvided) { self.report_unsafe(cx, blk.span, "usage of an `unsafe` block"); diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index fae06db3162..c9f86d43998 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -36,7 +36,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), block, move |this| { this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| { if targeted_by_break { - // This is a `break`-able block (currently only `catch { ... }`) + // This is a `break`-able block let exit_block = this.cfg.start_new_block(); let block_exit = this.in_breakable_scope( None, exit_block, destination.clone(), |this| { diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 3e432d84c69..97a4e0f3724 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -292,7 +292,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } - hir::ExprBlock(ref blk) => ExprKind::Block { body: &blk }, + hir::ExprBlock(ref blk, _) => ExprKind::Block { body: &blk }, hir::ExprAssign(ref lhs, ref rhs) => { ExprKind::Assign { diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 584887d0373..85af00956c9 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -141,7 +141,7 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> } // Check if this is an unsafe block, or an item match node { - Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..}) => { + Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block, _), ..}) => { if block_is_unsafe(&*block) { // Found an unsafe block, we can bail out here. return true; diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 7a54fc72d53..d031694d853 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -259,6 +259,44 @@ let result = loop { // ok! i += 1; }; ``` +"##, + +E0695: r##" +A `break` statement without a label appeared inside a labeled block. + +Example of erroneous code: + +```compile_fail,E0695 +# #![feature(label_break_value)] +loop { + 'a: { + break; + } +} +``` + +Make sure to always label the `break`: + +``` +# #![feature(label_break_value)] +'l: loop { + 'a: { + break 'l; + } +} +``` + +Or if you want to `break` the labeled block: + +``` +# #![feature(label_break_value)] +loop { + 'a: { + break 'a; + } + break; +} +``` "## } @@ -271,4 +309,5 @@ register_diagnostics! { E0642, // patterns aren't allowed in methods without bodies E0666, // nested `impl Trait` is illegal E0667, // `impl Trait` in projections + E0696, // `continue` pointing to a labeled block } diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs index 81299f4ba9f..2368b1aca69 100644 --- a/src/librustc_passes/loops.rs +++ b/src/librustc_passes/loops.rs @@ -13,7 +13,7 @@ use rustc::session::Session; use rustc::hir::map::Map; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use rustc::hir; +use rustc::hir::{self, Destination}; use syntax::ast; use syntax_pos::Span; @@ -39,6 +39,7 @@ enum Context { Normal, Loop(LoopKind), Closure, + LabeledBlock, } #[derive(Copy, Clone)] @@ -84,7 +85,16 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { hir::ExprClosure(.., b, _, _) => { self.with_context(Closure, |v| v.visit_nested_body(b)); } + hir::ExprBlock(ref b, Some(_label)) => { + self.with_context(LabeledBlock, |v| v.visit_block(&b)); + } hir::ExprBreak(label, ref opt_expr) => { + if self.require_label_in_labeled_block(e.span, &label, "break") { + // If we emitted an error about an unlabeled break in a labeled + // block, we don't need any further checking for this break any more + return; + } + let loop_id = match label.target_id.into() { Ok(loop_id) => loop_id, Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID, @@ -94,6 +104,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { }, Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID, }; + if loop_id != ast::DUMMY_NODE_ID { match self.hir_map.find(loop_id).unwrap() { hir::map::NodeBlock(_) => return, @@ -113,13 +124,15 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { }) }; match loop_kind { - None | Some(LoopKind::Loop(hir::LoopSource::Loop)) => (), + None | + Some(LoopKind::Loop(hir::LoopSource::Loop)) => (), Some(kind) => { struct_span_err!(self.sess, e.span, E0571, "`break` with value from a `{}` loop", kind.name()) .span_label(e.span, - "can only break with a value inside `loop`") + "can only break with a value inside \ + `loop` or breakable block") .span_suggestion(e.span, &format!("instead, use `break` on its own \ without a value inside this `{}` loop", @@ -130,13 +143,29 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { } } - self.require_loop("break", e.span); + self.require_break_cx("break", e.span); } hir::ExprAgain(label) => { - if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.target_id { - self.emit_unlabled_cf_in_while_condition(e.span, "continue"); + self.require_label_in_labeled_block(e.span, &label, "continue"); + + match label.target_id { + Ok(loop_id) => { + if let hir::map::NodeBlock(block) = self.hir_map.find(loop_id).unwrap() { + struct_span_err!(self.sess, e.span, E0696, + "`continue` pointing to a labeled block") + .span_label(e.span, + "labeled blocks cannot be `continue`'d") + .span_note(block.span, + "labeled block the continue points to") + .emit(); + } + } + Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { + self.emit_unlabled_cf_in_while_condition(e.span, "continue"); + } + _ => {} } - self.require_loop("continue", e.span) + self.require_break_cx("continue", e.span) }, _ => intravisit::walk_expr(self, e), } @@ -153,8 +182,9 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { self.cx = old_cx; } - fn require_loop(&self, name: &str, span: Span) { + fn require_break_cx(&self, name: &str, span: Span) { match self.cx { + LabeledBlock | Loop(_) => {} Closure => { struct_span_err!(self.sess, span, E0267, "`{}` inside of a closure", name) @@ -169,6 +199,22 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { } } + fn require_label_in_labeled_block(&mut self, span: Span, label: &Destination, cf_type: &str) + -> bool + { + if self.cx == LabeledBlock { + if label.label.is_none() { + struct_span_err!(self.sess, span, E0695, + "unlabeled `{}` inside of a labeled block", cf_type) + .span_label(span, + format!("`{}` statements that would diverge to or through \ + a labeled block need to bear a label", cf_type)) + .emit(); + return true; + } + } + return false; + } fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) { struct_span_err!(self.sess, span, E0590, "`break` or `continue` with no label in the condition of a `while` loop") diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index f20262c1333..2e737a016da 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -342,7 +342,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node let mut callee = &**callee; loop { callee = match callee.node { - hir::ExprBlock(ref block) => match block.expr { + hir::ExprBlock(ref block, _) => match block.expr { Some(ref tail) => &tail, None => break }, @@ -404,7 +404,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } - hir::ExprBlock(_) | + hir::ExprBlock(..) | hir::ExprIndex(..) | hir::ExprField(..) | hir::ExprArray(_) | diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 0f931d4374e..d2934bc9ee8 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3753,6 +3753,8 @@ impl<'a> Resolver<'a> { self.ribs[ValueNS].pop(); } + ExprKind::Block(ref block, label) => self.resolve_labeled_block(label, block.id, block), + // Equivalent to `visit::walk_expr` + passing some context to children. ExprKind::Field(ref subexpression, _) => { self.resolve_expr(subexpression, Some(expr)); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 877d6b289c9..39de09fe34f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3536,7 +3536,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Warn for non-block expressions with diverging children. match expr.node { - hir::ExprBlock(_) | + hir::ExprBlock(..) | hir::ExprLoop(..) | hir::ExprWhile(..) | hir::ExprIf(..) | hir::ExprMatch(..) => {} @@ -3912,7 +3912,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::ExprClosure(capture, ref decl, body_id, _, gen) => { self.check_expr_closure(expr, capture, &decl, body_id, gen, expected) } - hir::ExprBlock(ref body) => { + hir::ExprBlock(ref body, _) => { self.check_block_with_expected(&body, expected) } hir::ExprCall(ref callee, ref args) => { @@ -4326,8 +4326,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This cannot happen in - // normal Rust syntax today, but it can happen when we desugar + // can be targeted by multiple breaks. This can happen both + // with labeled blocks as well as when we desugar // a `do catch { ... }` expression. // // Example 1: diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index f8cd6103bdf..2b6635ec783 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -934,7 +934,7 @@ impl Expr { /// Whether this expression would be valid somewhere that expects a value, for example, an `if` /// condition. pub fn returns(&self) -> bool { - if let ExprKind::Block(ref block) = self.node { + if let ExprKind::Block(ref block, _) = self.node { match block.stmts.last().map(|last_stmt| &last_stmt.node) { // implicit return Some(&StmtKind::Expr(_)) => true, @@ -1121,8 +1121,8 @@ pub enum ExprKind { /// /// The final span is the span of the argument block `|...|` Closure(CaptureBy, Movability, P<FnDecl>, P<Expr>, Span), - /// A block (`{ ... }`) - Block(P<Block>), + /// A block (`'label: { ... }`) + Block(P<Block>, Option<Label>), /// A catch block (`catch { ... }`) Catch(P<Block>), diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 0b64189b2bc..a4f9ebcf418 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -668,7 +668,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.expr(span, ast::ExprKind::MethodCall(segment, args)) } fn expr_block(&self, b: P<ast::Block>) -> P<ast::Expr> { - self.expr(b.span, ast::ExprKind::Block(b)) + self.expr(b.span, ast::ExprKind::Block(b, None)) } fn field_imm(&self, span: Span, ident: Ident, e: P<ast::Expr>) -> ast::Field { ast::Field { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index bf78723e413..f1229520c77 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -466,6 +466,9 @@ declare_features! ( // inconsistent bounds in where clauses (active, trivial_bounds, "1.28.0", Some(48214), None), + + // 'a: { break 'a; } + (active, label_break_value, "1.28.0", Some(48594), None), ); declare_features! ( @@ -1696,6 +1699,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { "multiple patterns in `if let` and `while let` are unstable"); } } + ast::ExprKind::Block(_, opt_label) => { + if let Some(label) = opt_label { + gate_feature_post!(&self, label_break_value, label.ident.span, + "labels on blocks are unstable"); + } + } _ => {} } visit::walk_expr(self, e); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d67995761f6..29cc208c06b 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1256,7 +1256,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu folder.fold_expr(body), folder.new_span(span)) } - ExprKind::Block(blk) => ExprKind::Block(folder.fold_block(blk)), + ExprKind::Block(blk, opt_label) => { + ExprKind::Block(folder.fold_block(blk), + opt_label.map(|label| folder.fold_label(label))) + } ExprKind::Assign(el, er) => { ExprKind::Assign(folder.fold_expr(el), folder.fold_expr(er)) } diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index b8e02556625..531483e7de1 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -26,7 +26,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) | ast::ExprKind::Match(..) | - ast::ExprKind::Block(_) | + ast::ExprKind::Block(..) | ast::ExprKind::While(..) | ast::ExprKind::WhileLet(..) | ast::ExprKind::Loop(..) | diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3f0df6d055b..7b91c491700 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -128,7 +128,7 @@ macro_rules! maybe_whole_expr { token::NtBlock(ref block) => { $p.bump(); let span = $p.span; - let kind = ExprKind::Block((*block).clone()); + let kind = ExprKind::Block((*block).clone(), None); return Ok($p.mk_expr(span, kind, ThinVec::new())); } _ => {}, @@ -2244,7 +2244,7 @@ impl<'a> Parser<'a> { }; } token::OpenDelim(token::Brace) => { - return self.parse_block_expr(lo, BlockCheckMode::Default, attrs); + return self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs); } token::BinOp(token::Or) | token::OrOr => { return self.parse_lambda_expr(attrs); @@ -2318,7 +2318,13 @@ impl<'a> Parser<'a> { if self.eat_keyword(keywords::Loop) { return self.parse_loop_expr(Some(label), lo, attrs) } - let msg = "expected `while`, `for`, or `loop` after a label"; + if self.token == token::OpenDelim(token::Brace) { + return self.parse_block_expr(Some(label), + lo, + BlockCheckMode::Default, + attrs); + } + let msg = "expected `while`, `for`, `loop` or `{` after a label"; let mut err = self.fatal(msg); err.span_label(self.span, msg); return Err(err); @@ -2338,6 +2344,7 @@ impl<'a> Parser<'a> { } if self.eat_keyword(keywords::Unsafe) { return self.parse_block_expr( + None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs); @@ -2502,7 +2509,8 @@ impl<'a> Parser<'a> { } /// Parse a block or unsafe block - pub fn parse_block_expr(&mut self, lo: Span, blk_mode: BlockCheckMode, + pub fn parse_block_expr(&mut self, opt_label: Option<Label>, + lo: Span, blk_mode: BlockCheckMode, outer_attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { self.expect(&token::OpenDelim(token::Brace))?; @@ -2511,7 +2519,7 @@ impl<'a> Parser<'a> { attrs.extend(self.parse_inner_attributes()?); let blk = self.parse_block_tail(lo, blk_mode)?; - return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), attrs)); + return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs)); } /// parse a.b or a(13) or a[4] or just a @@ -3261,7 +3269,7 @@ impl<'a> Parser<'a> { // If an explicit return type is given, require a // block to appear (RFC 968). let body_lo = self.span; - self.parse_block_expr(body_lo, BlockCheckMode::Default, ThinVec::new())? + self.parse_block_expr(None, body_lo, BlockCheckMode::Default, ThinVec::new())? } }; @@ -3277,7 +3285,7 @@ impl<'a> Parser<'a> { return self.parse_if_expr(ThinVec::new()); } else { let blk = self.parse_block()?; - return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), ThinVec::new())); + return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), ThinVec::new())); } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8e33fa08083..a700799cde5 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1792,7 +1792,7 @@ impl<'a> State<'a> { self.print_else(e.as_ref().map(|e| &**e)) } // "final else" - ast::ExprKind::Block(ref b) => { + ast::ExprKind::Block(ref b, _) => { self.cbox(INDENT_UNIT - 1)?; self.ibox(0)?; self.s.word(" else ")?; @@ -2182,7 +2182,11 @@ impl<'a> State<'a> { // empty box to satisfy the close. self.ibox(0)?; } - ast::ExprKind::Block(ref blk) => { + ast::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident)?; + self.word_space(":")?; + } // containing cbox, will be closed by print-block at } self.cbox(INDENT_UNIT)?; // head-box, will be closed by print-block after { @@ -2695,7 +2699,12 @@ impl<'a> State<'a> { self.word_space("=>")?; match arm.body.node { - ast::ExprKind::Block(ref blk) => { + ast::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident)?; + self.word_space(":")?; + } + // the block will close the pattern's ibox self.print_block_unclosed_indent(blk, INDENT_UNIT)?; diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 40d59d3ff8b..2013e838c05 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -736,7 +736,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { expression.span, expression.id) } - ExprKind::Block(ref block) => visitor.visit_block(block), + ExprKind::Block(ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_block(block); + } ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => { visitor.visit_expr(left_hand_expression); visitor.visit_expr(right_hand_expression); diff --git a/src/test/run-pass/label_break_value.rs b/src/test/run-pass/label_break_value.rs new file mode 100644 index 00000000000..444845e24dc --- /dev/null +++ b/src/test/run-pass/label_break_value.rs @@ -0,0 +1,123 @@ +// 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. + +#![feature(label_break_value)] + +// Test control flow to follow label_break_value semantics +fn label_break(a: bool, b: bool) -> u32 { + let mut v = 0; + 'b: { + v = 1; + if a { + break 'b; + } + v = 2; + if b { + break 'b; + } + v = 3; + } + return v; +} + +// Test that values can be returned +fn break_value(a: bool, b: bool) -> u32 { + let result = 'block: { + if a { break 'block 1; } + if b { break 'block 2; } + 3 + }; + result +} + +// Test nesting of labeled blocks +// here we only check that it compiles +fn label_break_nested() { + 'b: { + println!("hi"); + if false { + break 'b; + } + 'c: { + if false { + break 'b; + } + break 'c; + } + println!("hello"); + if true { + break 'b; + } + } +} + +// Tests for mixing labeled blocks with loop constructs +// This function should be the identity function +fn label_break_mixed(v: u32) -> u32 { + let mut r = 0; + 'b: { + // Unlabeled break still works + // (only crossing boundaries is an error) + loop { + break; + } + if v == 0 { + break 'b; + } + // Labeled breaking an inner loop still works + 'c: loop { + if r == 1 { + break 'c; + } + r += 1; + } + assert_eq!(r, 1); + if v == 1 { + break 'b; + } + // Labeled breaking an outer loop still works + 'd: loop { + 'e: { + if v == r { + break 'b; + } + if r == 5 { + break 'd; + } + r += 1; + } + } + assert_eq!(r, 5); + assert!(v > r); + // Here we test return from inside a labeled block + return v; + } + r +} + +pub fn main() { + assert_eq!(label_break(true, false), 1); + assert_eq!(label_break(false, true), 2); + assert_eq!(label_break(false, false), 3); + + assert_eq!(break_value(true, false), 1); + assert_eq!(break_value(false, true), 2); + assert_eq!(break_value(false, false), 3); + + assert_eq!(label_break_mixed(0), 0); + assert_eq!(label_break_mixed(1), 1); + assert_eq!(label_break_mixed(2), 2); + assert_eq!(label_break_mixed(3), 3); + assert_eq!(label_break_mixed(4), 4); + assert_eq!(label_break_mixed(5), 5); + assert_eq!(label_break_mixed(6), 6); + + // FIXME: ensure that labeled blocks work if produced by macros and in match arms +} diff --git a/src/test/ui/compare-method/proj-outlives-region.stdout b/src/test/ui/compare-method/proj-outlives-region.stdout deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/compare-method/proj-outlives-region.stdout +++ /dev/null diff --git a/src/test/ui/compare-method/region-extra.stdout b/src/test/ui/compare-method/region-extra.stdout deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/compare-method/region-extra.stdout +++ /dev/null diff --git a/src/test/ui/compare-method/region-unrelated.stdout b/src/test/ui/compare-method/region-unrelated.stdout deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/compare-method/region-unrelated.stdout +++ /dev/null diff --git a/src/test/ui/const-eval/const_transmute.stderr b/src/test/ui/const-eval/const_transmute.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/const-eval/const_transmute.stderr +++ /dev/null diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/const-expr-addr-operator.stderr +++ /dev/null diff --git a/src/test/ui/const-pattern-not-const-evaluable.stderr b/src/test/ui/const-pattern-not-const-evaluable.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/const-pattern-not-const-evaluable.stderr +++ /dev/null diff --git a/src/test/ui/feature-gate-const-indexing.stderr b/src/test/ui/feature-gate-const-indexing.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/feature-gate-const-indexing.stderr +++ /dev/null diff --git a/src/test/ui/feature-gate-label_break_value.rs b/src/test/ui/feature-gate-label_break_value.rs new file mode 100644 index 00000000000..dcda7580bda --- /dev/null +++ b/src/test/ui/feature-gate-label_break_value.rs @@ -0,0 +1,15 @@ +// 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. + +pub fn main() { + 'a: { //~ ERROR labels on blocks are unstable + break 'a; + } +} diff --git a/src/test/ui/feature-gate-label_break_value.stderr b/src/test/ui/feature-gate-label_break_value.stderr new file mode 100644 index 00000000000..8e8f31e7871 --- /dev/null +++ b/src/test/ui/feature-gate-label_break_value.stderr @@ -0,0 +1,11 @@ +error[E0658]: labels on blocks are unstable (see issue #48594) + --> $DIR/feature-gate-label_break_value.rs:12:5 + | +LL | 'a: { //~ ERROR labels on blocks are unstable + | ^^ + | + = help: add #![feature(label_break_value)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/issue-38875/issue_38875.stderr b/src/test/ui/issue-38875/issue_38875.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/issue-38875/issue_38875.stderr +++ /dev/null diff --git a/src/test/ui/label_break_value_continue.rs b/src/test/ui/label_break_value_continue.rs new file mode 100644 index 00000000000..52e24b759d1 --- /dev/null +++ b/src/test/ui/label_break_value_continue.rs @@ -0,0 +1,36 @@ +// 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. + +#![feature(label_break_value)] + +// Simple continue pointing to an unlabeled break should yield in an error +fn continue_simple() { + 'b: { + continue; //~ ERROR unlabeled `continue` inside of a labeled block + } +} + +// Labeled continue pointing to an unlabeled break should yield in an error +fn continue_labeled() { + 'b: { + continue 'b; //~ ERROR `continue` pointing to a labeled block + } +} + +// Simple continue that would cross a labeled block should yield in an error +fn continue_crossing() { + loop { + 'b: { + continue; //~ ERROR unlabeled `continue` inside of a labeled block + } + } +} + +pub fn main() {} diff --git a/src/test/ui/label_break_value_continue.stderr b/src/test/ui/label_break_value_continue.stderr new file mode 100644 index 00000000000..24c2d1a22d0 --- /dev/null +++ b/src/test/ui/label_break_value_continue.stderr @@ -0,0 +1,30 @@ +error[E0695]: unlabeled `continue` inside of a labeled block + --> $DIR/label_break_value_continue.rs:16:9 + | +LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block + | ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label + +error[E0696]: `continue` pointing to a labeled block + --> $DIR/label_break_value_continue.rs:23:9 + | +LL | continue 'b; //~ ERROR `continue` pointing to a labeled block + | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d + | +note: labeled block the continue points to + --> $DIR/label_break_value_continue.rs:22:5 + | +LL | / 'b: { +LL | | continue 'b; //~ ERROR `continue` pointing to a labeled block +LL | | } + | |_____^ + +error[E0695]: unlabeled `continue` inside of a labeled block + --> $DIR/label_break_value_continue.rs:31:13 + | +LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block + | ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label + +error: aborting due to 3 previous errors + +Some errors occurred: E0695, E0696. +For more information about an error, try `rustc --explain E0695`. diff --git a/src/test/ui/label_break_value_illegal_uses.rs b/src/test/ui/label_break_value_illegal_uses.rs new file mode 100644 index 00000000000..18644033e4f --- /dev/null +++ b/src/test/ui/label_break_value_illegal_uses.rs @@ -0,0 +1,31 @@ +// 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. + +#![feature(label_break_value)] + +// These are forbidden occurences of label-break-value + +fn labeled_unsafe() { + unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{` +} + +fn labeled_if() { + if true 'b: {} //~ ERROR expected `{`, found `'b` +} + +fn labeled_else() { + if true {} else 'b: {} //~ ERROR expected `{`, found `'b` +} + +fn labeled_match() { + match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator +} + +pub fn main() {} diff --git a/src/test/ui/label_break_value_illegal_uses.stderr b/src/test/ui/label_break_value_illegal_uses.stderr new file mode 100644 index 00000000000..0ab1ad2c242 --- /dev/null +++ b/src/test/ui/label_break_value_illegal_uses.stderr @@ -0,0 +1,31 @@ +error: expected one of `extern`, `fn`, or `{`, found `'b` + --> $DIR/label_break_value_illegal_uses.rs:16:12 + | +LL | unsafe 'b: {} //~ ERROR expected one of `extern`, `fn`, or `{` + | ^^ expected one of `extern`, `fn`, or `{` here + +error: expected `{`, found `'b` + --> $DIR/label_break_value_illegal_uses.rs:20:13 + | +LL | if true 'b: {} //~ ERROR expected `{`, found `'b` + | -- ^^---- + | | | + | | help: try placing this code inside a block: `{ 'b: { } }` + | this `if` statement has a condition, but no block + +error: expected `{`, found `'b` + --> $DIR/label_break_value_illegal_uses.rs:24:21 + | +LL | if true {} else 'b: {} //~ ERROR expected `{`, found `'b` + | ^^---- + | | + | help: try placing this code inside a block: `{ 'b: { } }` + +error: expected one of `.`, `?`, `{`, or an operator, found `'b` + --> $DIR/label_break_value_illegal_uses.rs:28:17 + | +LL | match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator + | ^^ expected one of `.`, `?`, `{`, or an operator here + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/label_break_value_unlabeled_break.rs b/src/test/ui/label_break_value_unlabeled_break.rs new file mode 100644 index 00000000000..38918da291c --- /dev/null +++ b/src/test/ui/label_break_value_unlabeled_break.rs @@ -0,0 +1,29 @@ +// 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. + +#![feature(label_break_value)] + +// Simple unlabeled break should yield in an error +fn unlabeled_break_simple() { + 'b: { + break; //~ ERROR unlabeled `break` inside of a labeled block + } +} + +// Unlabeled break that would cross a labeled block should yield in an error +fn unlabeled_break_crossing() { + loop { + 'b: { + break; //~ ERROR unlabeled `break` inside of a labeled block + } + } +} + +pub fn main() {} diff --git a/src/test/ui/label_break_value_unlabeled_break.stderr b/src/test/ui/label_break_value_unlabeled_break.stderr new file mode 100644 index 00000000000..8a25975a7bd --- /dev/null +++ b/src/test/ui/label_break_value_unlabeled_break.stderr @@ -0,0 +1,15 @@ +error[E0695]: unlabeled `break` inside of a labeled block + --> $DIR/label_break_value_unlabeled_break.rs:16:9 + | +LL | break; //~ ERROR unlabeled `break` inside of a labeled block + | ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label + +error[E0695]: unlabeled `break` inside of a labeled block + --> $DIR/label_break_value_unlabeled_break.rs:24:13 + | +LL | break; //~ ERROR unlabeled `break` inside of a labeled block + | ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0695`. diff --git a/src/test/ui/loop-break-value-no-repeat.stderr b/src/test/ui/loop-break-value-no-repeat.stderr index 4421f557e42..68a2bab1674 100644 --- a/src/test/ui/loop-break-value-no-repeat.stderr +++ b/src/test/ui/loop-break-value-no-repeat.stderr @@ -2,7 +2,7 @@ error[E0571]: `break` with value from a `for` loop --> $DIR/loop-break-value-no-repeat.rs:22:9 | LL | break 22 //~ ERROR `break` with value from a `for` loop - | ^^^^^^^^ can only break with a value inside `loop` + | ^^^^^^^^ can only break with a value inside `loop` or breakable block help: instead, use `break` on its own without a value inside this `for` loop | LL | break //~ ERROR `break` with value from a `for` loop diff --git a/src/test/ui/nll/drop-may-dangle.stderr b/src/test/ui/nll/drop-may-dangle.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/nll/drop-may-dangle.stderr +++ /dev/null diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr +++ /dev/null diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr +++ /dev/null diff --git a/src/test/ui/print_type_sizes/anonymous.stderr b/src/test/ui/print_type_sizes/anonymous.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/print_type_sizes/anonymous.stderr +++ /dev/null diff --git a/src/test/ui/print_type_sizes/anonymous.stdout b/src/test/ui/print_type_sizes/anonymous.stdout deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/print_type_sizes/anonymous.stdout +++ /dev/null diff --git a/src/test/ui/print_type_sizes/multiple_types.stderr b/src/test/ui/print_type_sizes/multiple_types.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/print_type_sizes/multiple_types.stderr +++ /dev/null diff --git a/src/test/ui/print_type_sizes/packed.stderr b/src/test/ui/print_type_sizes/packed.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/print_type_sizes/packed.stderr +++ /dev/null diff --git a/src/test/ui/print_type_sizes/repr-align.stderr b/src/test/ui/print_type_sizes/repr-align.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/print_type_sizes/repr-align.stderr +++ /dev/null diff --git a/src/test/ui/reachable/expr_andand.stderr b/src/test/ui/reachable/expr_andand.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/reachable/expr_andand.stderr +++ /dev/null diff --git a/src/test/ui/reachable/expr_oror.stderr b/src/test/ui/reachable/expr_oror.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/reachable/expr_oror.stderr +++ /dev/null diff --git a/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr b/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr +++ /dev/null diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout b/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout +++ /dev/null diff --git a/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr b/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr +++ /dev/null diff --git a/src/test/ui/union/union-const-eval.stderr b/src/test/ui/union/union-const-eval.stderr deleted file mode 100644 index e69de29bb2d..00000000000 --- a/src/test/ui/union/union-const-eval.stderr +++ /dev/null diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 351005ff4b8..c1574407807 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -10,32 +10,49 @@ //! Tidy check to ensure that there are no stray `.stderr` files in UI test directories. +use std::fs; use std::path::Path; pub fn check(path: &Path, bad: &mut bool) { - super::walk_many(&[&path.join("test/ui"), &path.join("test/ui-fulldeps")], - &mut |_| false, - &mut |file_path| { - if let Some(ext) = file_path.extension() { - if ext == "stderr" || ext == "stdout" { - // Test output filenames have the format: - // $testname.stderr - // $testname.$mode.stderr - // $testname.$revision.stderr - // $testname.$revision.$mode.stderr - // - // For now, just make sure that there is a corresponding - // $testname.rs file. - let testname = file_path.file_name().unwrap() - .to_str().unwrap() - .splitn(2, '.').next().unwrap(); - if !file_path.with_file_name(testname) - .with_extension("rs") - .exists() { - println!("Stray file with UI testing output: {:?}", file_path); - *bad = true; + super::walk_many( + &[&path.join("test/ui"), &path.join("test/ui-fulldeps")], + &mut |_| false, + &mut |file_path| { + if let Some(ext) = file_path.extension() { + if ext == "stderr" || ext == "stdout" { + // Test output filenames have the format: + // $testname.stderr + // $testname.$mode.stderr + // $testname.$revision.stderr + // $testname.$revision.$mode.stderr + // + // For now, just make sure that there is a corresponding + // $testname.rs file. + let testname = file_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .splitn(2, '.') + .next() + .unwrap(); + if !file_path + .with_file_name(testname) + .with_extension("rs") + .exists() + { + println!("Stray file with UI testing output: {:?}", file_path); + *bad = true; + } + + if let Ok(metadata) = fs::metadata(file_path) { + if metadata.len() == 0 { + println!("Empty file with UI testing output: {:?}", file_path); + *bad = true; + } + } } } - } - }); + }, + ); } |
