diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 74e7d328891e0..556ee9f5716ea 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -20,6 +20,7 @@ #![feature(box_syntax)] #![cfg_attr(unix, feature(libc))] +#![feature(option_replace)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(slice_sort_by_cached_key)] diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 5c1f3bfbe670b..3e74aef9e7345 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -33,6 +33,7 @@ use syntax::fold::{self, Folder}; use syntax::print::{pprust}; use syntax::print::pprust::PrintState; use syntax::ptr::P; +use syntax::util::ThinVec; use syntax::util::small_vector::SmallVector; use syntax_pos::{self, FileName}; @@ -650,18 +651,25 @@ impl UserIdentifiedItem { // [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 pub struct ReplaceBodyWithLoop<'a> { within_static_or_const: bool, + nested_blocks: Option>, sess: &'a Session, } impl<'a> ReplaceBodyWithLoop<'a> { pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> { - ReplaceBodyWithLoop { within_static_or_const: false, sess } + ReplaceBodyWithLoop { + within_static_or_const: false, + nested_blocks: None, + sess + } } fn run R>(&mut self, is_const: bool, action: F) -> R { let old_const = mem::replace(&mut self.within_static_or_const, is_const); + let old_blocks = self.nested_blocks.take(); let ret = action(self); self.within_static_or_const = old_const; + self.nested_blocks = old_blocks; ret } @@ -739,42 +747,88 @@ impl<'a> fold::Folder for ReplaceBodyWithLoop<'a> { self.run(is_const, |s| fold::noop_fold_impl_item(i, s)) } + fn fold_anon_const(&mut self, c: ast::AnonConst) -> ast::AnonConst { + self.run(true, |s| fold::noop_fold_anon_const(c, s)) + } + fn fold_block(&mut self, b: P) -> P { - fn expr_to_block(rules: ast::BlockCheckMode, + fn stmt_to_block(rules: ast::BlockCheckMode, recovered: bool, - e: Option>, - sess: &Session) -> P { - P(ast::Block { - stmts: e.map(|e| { - ast::Stmt { - id: sess.next_node_id(), - span: e.span, - node: ast::StmtKind::Expr(e), - } - }) - .into_iter() - .collect(), + s: Option, + sess: &Session) -> ast::Block { + ast::Block { + stmts: s.into_iter().collect(), rules, id: sess.next_node_id(), span: syntax_pos::DUMMY_SP, recovered, - }) + } } - if !self.within_static_or_const { - - let empty_block = expr_to_block(BlockCheckMode::Default, false, None, self.sess); - let loop_expr = P(ast::Expr { - node: ast::ExprKind::Loop(empty_block, None), - id: self.sess.next_node_id(), + fn block_to_stmt(b: ast::Block, sess: &Session) -> ast::Stmt { + let expr = P(ast::Expr { + id: sess.next_node_id(), + node: ast::ExprKind::Block(P(b), None), span: syntax_pos::DUMMY_SP, - attrs: ast::ThinVec::new(), + attrs: ThinVec::new(), }); - expr_to_block(b.rules, b.recovered, Some(loop_expr), self.sess) + ast::Stmt { + id: sess.next_node_id(), + node: ast::StmtKind::Expr(expr), + span: syntax_pos::DUMMY_SP, + } + } - } else { + let empty_block = stmt_to_block(BlockCheckMode::Default, false, None, self.sess); + let loop_expr = P(ast::Expr { + node: ast::ExprKind::Loop(P(empty_block), None), + id: self.sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + attrs: ast::ThinVec::new(), + }); + + let loop_stmt = ast::Stmt { + id: self.sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + node: ast::StmtKind::Expr(loop_expr), + }; + + if self.within_static_or_const { fold::noop_fold_block(b, self) + } else { + b.map(|b| { + let mut stmts = vec![]; + for s in b.stmts { + let old_blocks = self.nested_blocks.replace(vec![]); + + stmts.extend(self.fold_stmt(s).into_iter().filter(|s| s.is_item())); + + // we put a Some in there earlier with that replace(), so this is valid + let new_blocks = self.nested_blocks.take().unwrap(); + self.nested_blocks = old_blocks; + stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, &self.sess))); + } + + let mut new_block = ast::Block { + stmts, + ..b + }; + + if let Some(old_blocks) = self.nested_blocks.as_mut() { + //push our fresh block onto the cache and yield an empty block with `loop {}` + if !new_block.stmts.is_empty() { + old_blocks.push(new_block); + } + + stmt_to_block(b.rules, b.recovered, Some(loop_stmt), self.sess) + } else { + //push `loop {}` onto the end of our fresh block and yield that + new_block.stmts.push(loop_stmt); + + new_block + } + }) } } diff --git a/src/test/rustdoc/traits-in-bodies.rs b/src/test/rustdoc/traits-in-bodies.rs new file mode 100644 index 0000000000000..3acf4af5fd247 --- /dev/null +++ b/src/test/rustdoc/traits-in-bodies.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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//prior to fixing `everybody_loops` to preserve items, rustdoc would crash on this file, as it +//didn't see that `SomeStruct` implemented `Clone` + +//FIXME(misdreavus): whenever rustdoc shows traits impl'd inside bodies, make sure this test +//reflects that + +pub struct Bounded(T); + +pub struct SomeStruct; + +fn asdf() -> Bounded { + impl Clone for SomeStruct { + fn clone(&self) -> SomeStruct { + SomeStruct + } + } + + Bounded(SomeStruct) +}