diff --git a/src/config.rs b/src/config.rs index 5407ad8e8b8..7d56e3e8a80 100644 --- a/src/config.rs +++ b/src/config.rs @@ -269,6 +269,7 @@ fn default() -> Config { newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings"; fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions"; item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums"; + fn_single_line: bool, false, "Put single-expression functions on a single line"; fn_return_indent: ReturnIndent, ReturnIndent::WithArgs, "Location of return type in function declaration"; fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline"; diff --git a/src/items.rs b/src/items.rs index bc57a3e5ed6..58ddea0c483 100644 --- a/src/items.rs +++ b/src/items.rs @@ -12,17 +12,17 @@ use Indent; use utils::{format_mutability, format_visibility, contains_skip, span_after, end_typaram, - wrap_str, last_line_width}; + wrap_str, last_line_width, semicolon_for_expr, semicolon_for_stmt}; use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, DefinitiveListTactic, definitive_tactic, format_item_list}; use expr::rewrite_assign_rhs; -use comment::FindUncommented; +use comment::{FindUncommented, contains_comment}; use visitor::FmtVisitor; use rewrite::{Rewrite, RewriteContext}; use config::{Config, BlockIndentStyle, Density, ReturnIndent, BraceStyle, StructLitStyle}; use syntax::{ast, abi}; -use syntax::codemap::{Span, BytePos, mk_sp}; +use syntax::codemap::{Span, BytePos, CodeMap, mk_sp}; use syntax::print::pprust; use syntax::parse::token; @@ -447,6 +447,81 @@ fn rewrite_fn_base(&mut self, Some((result, force_new_line_for_brace)) } + pub fn rewrite_single_line_fn(&self, + fn_rewrite: &Option, + block: &ast::Block) + -> Option { + + let fn_str = match *fn_rewrite { + Some(ref s) if !s.contains('\n') => s, + _ => return None, + }; + + let codemap = self.get_context().codemap; + + if is_empty_block(block, codemap) && + self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width { + return Some(format!("{}{{ }}", fn_str)); + } + + if self.config.fn_single_line && is_simple_block_stmt(block, codemap) { + let rewrite = { + if let Some(ref e) = block.expr { + let suffix = if semicolon_for_expr(e) { + ";" + } else { + "" + }; + + e.rewrite(&self.get_context(), + self.config.max_width - self.block_indent.width(), + self.block_indent) + .map(|s| s + suffix) + .or_else(|| Some(self.snippet(e.span))) + } else if let Some(ref stmt) = block.stmts.first() { + self.rewrite_stmt(stmt) + } else { + None + } + }; + + if let Some(res) = rewrite { + let width = self.block_indent.width() + fn_str.len() + res.len() + 3; + if !res.contains('\n') && width <= self.config.max_width { + return Some(format!("{}{{ {} }}", fn_str, res)); + } + } + } + + None + } + + pub fn rewrite_stmt(&self, stmt: &ast::Stmt) -> Option { + match stmt.node { + ast::Stmt_::StmtDecl(ref decl, _) => { + if let ast::Decl_::DeclLocal(ref local) = decl.node { + let context = self.get_context(); + local.rewrite(&context, self.config.max_width, self.block_indent) + } else { + None + } + } + ast::Stmt_::StmtExpr(ref ex, _) | ast::Stmt_::StmtSemi(ref ex, _) => { + let suffix = if semicolon_for_stmt(stmt) { + ";" + } else { + "" + }; + + ex.rewrite(&self.get_context(), + self.config.max_width - self.block_indent.width() - suffix.len(), + self.block_indent) + .map(|s| s + suffix) + } + ast::Stmt_::StmtMac(..) => None, + } + } + fn rewrite_args(&self, args: &[ast::Arg], explicit_self: Option<&ast::ExplicitSelf>, @@ -1317,3 +1392,23 @@ fn span_for_where_pred(pred: &ast::WherePredicate) -> Span { ast::WherePredicate::EqPredicate(ref p) => p.span, } } + +// Checks whether a block contains at most one statement or expression, and no comments. +fn is_simple_block_stmt(block: &ast::Block, codemap: &CodeMap) -> bool { + if (!block.stmts.is_empty() && block.expr.is_some()) || + (block.stmts.len() != 1 && block.expr.is_none()) { + return false; + } + + let snippet = codemap.span_to_snippet(block.span).unwrap(); + !contains_comment(&snippet) +} + +fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool { + if !block.stmts.is_empty() || block.expr.is_some() { + return false; + } + + let snippet = codemap.span_to_snippet(block.span).unwrap(); + !contains_comment(&snippet) +} diff --git a/src/utils.rs b/src/utils.rs index fea385532d0..3965fc93c57 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -102,6 +102,33 @@ pub fn end_typaram(typaram: &ast::TyParam) -> BytePos { .hi } +#[inline] +pub fn semicolon_for_expr(expr: &ast::Expr) -> bool { + match expr.node { + ast::Expr_::ExprRet(..) | + ast::Expr_::ExprAgain(..) | + ast::Expr_::ExprBreak(..) => true, + _ => false, + } +} + +#[inline] +pub fn semicolon_for_stmt(stmt: &ast::Stmt) -> bool { + match stmt.node { + ast::Stmt_::StmtSemi(ref expr, _) => { + match expr.node { + ast::Expr_::ExprWhile(..) | + ast::Expr_::ExprWhileLet(..) | + ast::Expr_::ExprLoop(..) | + ast::Expr_::ExprForLoop(..) => false, + _ => true, + } + } + ast::Stmt_::StmtExpr(..) => false, + _ => true, + } +} + #[inline] #[cfg(target_pointer_width="64")] // Based on the trick layed out at diff --git a/src/visitor.rs b/src/visitor.rs index f19ff1f7e8b..534e8b112e4 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -38,28 +38,15 @@ impl<'a> FmtVisitor<'a> { fn visit_stmt(&mut self, stmt: &ast::Stmt) { match stmt.node { ast::Stmt_::StmtDecl(ref decl, _) => { - match decl.node { - ast::Decl_::DeclLocal(ref local) => { - let rewrite = { - let context = self.get_context(); - local.rewrite(&context, self.config.max_width, self.block_indent) - }; - self.push_rewrite(stmt.span, rewrite); - } - ast::Decl_::DeclItem(ref item) => self.visit_item(item), + if let ast::Decl_::DeclItem(ref item) = decl.node { + self.visit_item(item); + } else { + let rewrite = self.rewrite_stmt(stmt); + self.push_rewrite(stmt.span, rewrite); } } - ast::Stmt_::StmtExpr(ref ex, _) | ast::Stmt_::StmtSemi(ref ex, _) => { - let suffix = if semicolon_for_stmt(stmt) { - ";" - } else { - "" - }; - let rewrite = ex.rewrite(&self.get_context(), - self.config.max_width - self.block_indent.width() - - suffix.len(), - self.block_indent) - .map(|s| s + suffix); + ast::Stmt_::StmtExpr(..) | ast::Stmt_::StmtSemi(..) => { + let rewrite = self.rewrite_stmt(stmt); self.push_rewrite(stmt.span, rewrite); } ast::Stmt_::StmtMac(ref mac, _macro_style) => { @@ -101,7 +88,7 @@ pub fn visit_block(&mut self, b: &ast::Block) { self.buffer.push_str(&rewrite); self.last_pos = e.span.hi; - if semicolon_for_expr(e) { + if utils::semicolon_for_expr(e) { self.buffer.push_str(";"); } } @@ -161,6 +148,13 @@ fn visit_fn(&mut self, visit::FnKind::Closure => None, }; + if let Some(ref single_line_fn) = self.rewrite_single_line_fn(&rewrite, &b) { + self.format_missing_with_indent(s.lo); + self.buffer.push_str(single_line_fn); + self.last_pos = b.span.hi; + return; + } + if let Some(fn_str) = rewrite { self.format_missing_with_indent(s.lo); self.buffer.push_str(&fn_str); @@ -501,31 +495,6 @@ pub fn get_context(&self) -> RewriteContext { } } -fn semicolon_for_stmt(stmt: &ast::Stmt) -> bool { - match stmt.node { - ast::Stmt_::StmtSemi(ref expr, _) => { - match expr.node { - ast::Expr_::ExprWhile(..) | - ast::Expr_::ExprWhileLet(..) | - ast::Expr_::ExprLoop(..) | - ast::Expr_::ExprForLoop(..) => false, - _ => true, - } - } - ast::Stmt_::StmtExpr(..) => false, - _ => true, - } -} - -fn semicolon_for_expr(expr: &ast::Expr) -> bool { - match expr.node { - ast::Expr_::ExprRet(..) | - ast::Expr_::ExprAgain(..) | - ast::Expr_::ExprBreak(..) => true, - _ => false, - } -} - impl<'a> Rewrite for [ast::Attribute] { fn rewrite(&self, context: &RewriteContext, _: usize, offset: Indent) -> Option { let mut result = String::new(); diff --git a/tests/source/fn-single-line.rs b/tests/source/fn-single-line.rs new file mode 100644 index 00000000000..b928de40de4 --- /dev/null +++ b/tests/source/fn-single-line.rs @@ -0,0 +1,74 @@ +// rustfmt-fn_single_line: true +// Test single-line functions. + +fn foo_expr() { + 1 +} + +fn foo_stmt() { + foo(); +} + +fn foo_decl_local() { + let z = 5; + } + +fn foo_decl_item(x: &mut i32) { + x = 3; +} + + fn empty() { + +} + +fn foo_return() -> String { + "yay" +} + +fn foo_where() -> T where T: Sync { + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space () { + 1 +} + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} diff --git a/tests/target/attrib.rs b/tests/target/attrib.rs index 0789efc2b63..8b793c9a925 100644 --- a/tests/target/attrib.rs +++ b/tests/target/attrib.rs @@ -13,8 +13,7 @@ impl Bar { /// Blah blah blooo. /// Blah blah blooo. #[an_attribute] - fn foo(&mut self) -> isize { - } + fn foo(&mut self) -> isize { } /// Blah blah bing. /// Blah blah bing. @@ -28,8 +27,7 @@ pub fn f2(self) { } #[another_attribute] - fn f3(self) -> Dog { - } + fn f3(self) -> Dog { } /// Blah blah bing. #[attrib1] @@ -38,6 +36,5 @@ fn f3(self) -> Dog { // Another comment that needs rewrite because it's tooooooooooooooooooooooooooooooo // loooooooooooong. /// Blah blah bing. - fn f4(self) -> Cat { - } + fn f4(self) -> Cat { } } diff --git a/tests/target/comment.rs b/tests/target/comment.rs index c3191381653..04053bd804a 100644 --- a/tests/target/comment.rs +++ b/tests/target/comment.rs @@ -32,8 +32,7 @@ fn test() { } /// test123 -fn doc_comment() { -} +fn doc_comment() { } fn chains() { foo.bar(|| { diff --git a/tests/target/comments-fn.rs b/tests/target/comments-fn.rs index 57b6ae63381..c96489b0817 100644 --- a/tests/target/comments-fn.rs +++ b/tests/target/comments-fn.rs @@ -16,8 +16,6 @@ fn foo(a: aaaaaaaaaaaaa, // A comment } -fn bar() { -} +fn bar() { } -fn baz() -> Baz /* Comment after return type */ { -} +fn baz() -> Baz /* Comment after return type */ { } diff --git a/tests/target/fn-simple.rs b/tests/target/fn-simple.rs index 9e635e31a3c..0f46b6d1318 100644 --- a/tests/target/fn-simple.rs +++ b/tests/target/fn-simple.rs @@ -28,15 +28,13 @@ fn generic(arg: T) -> &SomeType arg(a, b, c, d, e) } -fn foo() -> ! { -} +fn foo() -> ! { } pub fn http_fetch_async(listener: Box, script_chan: Box) { } -fn some_func>(val: T) { -} +fn some_func>(val: T) { } fn zzzzzzzzzzzzzzzzzzzz(selff: Type, mut handle: node::Handle>, diff --git a/tests/target/fn-single-line.rs b/tests/target/fn-single-line.rs new file mode 100644 index 00000000000..205ab897207 --- /dev/null +++ b/tests/target/fn-single-line.rs @@ -0,0 +1,61 @@ +// rustfmt-fn_single_line: true +// Test single-line functions. + +fn foo_expr() { 1 } + +fn foo_stmt() { foo(); } + +fn foo_decl_local() { let z = 5; } + +fn foo_decl_item(x: &mut i32) { x = 3; } + +fn empty() { } + +fn foo_return() -> String { "yay" } + +fn foo_where() -> T + where T: Sync +{ + let x = 2; +} + +fn fooblock() { + { + "inner-block" + } +} + +fn fooblock2(x: i32) { + let z = match x { + _ => 2, + }; +} + +fn comment() { + // this is a test comment + 1 +} + +fn comment2() { + // multi-line comment + let z = 2; + 1 +} + +fn only_comment() { + // Keep this here +} + +fn aaaaaaaaaaaaaaaaa_looooooooooooooooooooooong_name() { + let z = "aaaaaaawwwwwwwwwwwwwwwwwwwwwwwwwwww"; +} + +fn lots_of_space() { 1 } + +trait CoolTypes { + fn dummy(&self) { } +} + +trait CoolerTypes { + fn dummy(&self) { } +} diff --git a/tests/target/fn.rs b/tests/target/fn.rs index da13a6f05dc..97e1172470f 100644 --- a/tests/target/fn.rs +++ b/tests/target/fn.rs @@ -1,8 +1,6 @@ // Tests different fns -fn foo(a: AAAA, b: BBB, c: CCC) -> RetType { - -} +fn foo(a: AAAA, b: BBB, c: CCC) -> RetType { } fn foo(a: AAAA, b: BBB /* some, weird, inline comment */, c: CCC) -> RetType where T: Blah @@ -34,8 +32,7 @@ fn foo(a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, } -fn foo B /* paren inside generics */>() { -} +fn foo B /* paren inside generics */>() { } impl Foo { fn with_no_errors(&mut self, f: F) -> T @@ -43,11 +40,9 @@ fn with_no_errors(&mut self, f: F) -> T { } - fn foo(mut self, mut bar: u32) { - } + fn foo(mut self, mut bar: u32) { } - fn bar(self, mut bazz: u32) { - } + fn bar(self, mut bazz: u32) { } } pub fn render<'a, @@ -75,12 +70,9 @@ const fn foo() { } } -fn homura>(_: T) { +fn homura>(_: T) { } -} - -fn issue377() -> (Box, Box) { -} +fn issue377() -> (Box, Box) { } fn main() { let _ = function(move || 5); diff --git a/tests/target/multiple.rs b/tests/target/multiple.rs index a806f62705e..f39a7bcd53d 100644 --- a/tests/target/multiple.rs +++ b/tests/target/multiple.rs @@ -26,9 +26,7 @@ // sfdgfffffffffffffffffffffffffffffffffffffffffffffffffffffff // ffffffffffffffffffffffffffffffffffffffffff -fn foo(a: isize, b: u32 /* blah blah */, c: f64) { - -} +fn foo(a: isize, b: u32 /* blah blah */, c: f64) { } fn foo() -> Box where 'a: 'b, @@ -77,8 +75,7 @@ pub fn f2(self) { } #[an_attribute] - fn f3(self) -> Dog { - } + fn f3(self) -> Dog { } } /// The `nodes` and `edges` method each return instantiations of @@ -118,8 +115,7 @@ pub struct Foo<'a, Y: Baz> f: SomeType, // Comment beside a field } -fn foo(ann: &'a (PpAnn + 'a)) { -} +fn foo(ann: &'a (PpAnn + 'a)) { } fn main() { for i in 0i32..4 { diff --git a/tests/target/nestedmod/mod2c.rs b/tests/target/nestedmod/mod2c.rs index 9027adeb212..0a03d2b3994 100644 --- a/tests/target/nestedmod/mod2c.rs +++ b/tests/target/nestedmod/mod2c.rs @@ -1,4 +1,3 @@ // A standard mod -fn a() { -} +fn a() { } diff --git a/tests/target/nestedmod/mymod1/mod3a.rs b/tests/target/nestedmod/mymod1/mod3a.rs index a6399f5565d..febe1ff7b25 100644 --- a/tests/target/nestedmod/mymod1/mod3a.rs +++ b/tests/target/nestedmod/mymod1/mod3a.rs @@ -1,3 +1,2 @@ // Another mod -fn a() { -} +fn a() { } diff --git a/tests/target/nestedmod/submod2/a.rs b/tests/target/nestedmod/submod2/a.rs index 078a1d99f2c..53540b8b659 100644 --- a/tests/target/nestedmod/submod2/a.rs +++ b/tests/target/nestedmod/submod2/a.rs @@ -3,5 +3,4 @@ use c::a; -fn foo() { -} +fn foo() { } diff --git a/tests/target/no_new_line_beginning.rs b/tests/target/no_new_line_beginning.rs index f79c691f085..45590d86ba6 100644 --- a/tests/target/no_new_line_beginning.rs +++ b/tests/target/no_new_line_beginning.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() { } diff --git a/tests/target/paths.rs b/tests/target/paths.rs index 15cc35e69f1..303a4df4a9d 100644 --- a/tests/target/paths.rs +++ b/tests/target/paths.rs @@ -19,5 +19,4 @@ fn main() { let x: Foo; } -fn op(foo: Bar, key: &[u8], upd: Fn(Option<&memcache::Item>, Baz) -> Result) -> MapResult { -} +fn op(foo: Bar, key: &[u8], upd: Fn(Option<&memcache::Item>, Baz) -> Result) -> MapResult { }