diff --git a/src/config.rs b/src/config.rs index 5407ad8e8b8..295c1076b5f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -269,6 +269,8 @@ create_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_empty_single_line: bool, true, "Put empty-body functions on a single line"; + 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/expr.rs b/src/expr.rs index c4b01e38b42..b494cb08fcc 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -17,7 +17,8 @@ use rewrite::{Rewrite, RewriteContext}; use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic, DefinitiveListTactic, definitive_tactic, ListItem, format_fn_args}; use string::{StringFormat, rewrite_string}; -use utils::{span_after, extra_offset, last_line_width, wrap_str, binary_search, first_line_width}; +use utils::{span_after, extra_offset, last_line_width, wrap_str, binary_search, first_line_width, + semicolon_for_stmt}; use visitor::FmtVisitor; use config::{StructLitStyle, MultilineStyle}; use comment::{FindUncommented, rewrite_comment, contains_comment}; @@ -475,6 +476,33 @@ impl Rewrite for ast::Block { } } +impl Rewrite for ast::Stmt { + fn rewrite(&self, context: &RewriteContext, _width: usize, offset: Indent) -> Option { + match self.node { + ast::Stmt_::StmtDecl(ref decl, _) => { + if let ast::Decl_::DeclLocal(ref local) = decl.node { + local.rewrite(context, context.config.max_width, offset) + } else { + None + } + } + ast::Stmt_::StmtExpr(ref ex, _) | ast::Stmt_::StmtSemi(ref ex, _) => { + let suffix = if semicolon_for_stmt(self) { + ";" + } else { + "" + }; + + ex.rewrite(context, + context.config.max_width - offset.width() - suffix.len(), + offset) + .map(|s| s + suffix) + } + ast::Stmt_::StmtMac(..) => None, + } + } +} + // Abstraction over for, while and loop expressions struct Loop<'a> { cond: Option<&'a ast::Expr>, @@ -677,15 +705,25 @@ fn single_line_if_else(context: &RewriteContext, None } -// Checks that a block contains no statements, an expression and no comments. -fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool { - if !block.stmts.is_empty() || block.expr.is_none() { - return false; - } - +fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool { let snippet = codemap.span_to_snippet(block.span).unwrap(); + contains_comment(&snippet) +} - !contains_comment(&snippet) +// Checks that a block contains no statements, an expression and no comments. +pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool { + block.stmts.is_empty() && block.expr.is_some() && !block_contains_comment(block, codemap) +} + +/// Checks whether a block contains at most one statement or expression, and no comments. +pub fn is_simple_block_stmt(block: &ast::Block, codemap: &CodeMap) -> bool { + (block.stmts.is_empty() || (block.stmts.len() == 1 && block.expr.is_none())) && + !block_contains_comment(block, codemap) +} + +/// Checks whether a block contains no statements, expressions, or comments. +pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool { + block.stmts.is_empty() && block.expr.is_none() && !block_contains_comment(block, codemap) } // inter-match-arm-comment-rules: diff --git a/src/items.rs b/src/items.rs index bc57a3e5ed6..c7cfc732fc1 100644 --- a/src/items.rs +++ b/src/items.rs @@ -12,10 +12,10 @@ 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}; use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, DefinitiveListTactic, definitive_tactic, format_item_list}; -use expr::rewrite_assign_rhs; +use expr::{is_empty_block, is_simple_block_stmt, rewrite_assign_rhs}; use comment::FindUncommented; use visitor::FmtVisitor; use rewrite::{Rewrite, RewriteContext}; @@ -177,7 +177,8 @@ impl<'a> FmtVisitor<'a> { constness: ast::Constness, abi: abi::Abi, vis: ast::Visibility, - span: Span) + span: Span, + block: &ast::Block) -> Option { let mut newline_brace = self.newline_for_brace(&generics.where_clause); @@ -212,7 +213,7 @@ impl<'a> FmtVisitor<'a> { result.push(' '); } - Some(result) + self.single_line_fn(&result, block).or_else(|| Some(result)) } pub fn rewrite_required_fn(&mut self, @@ -447,6 +448,53 @@ impl<'a> FmtVisitor<'a> { Some((result, force_new_line_for_brace)) } + fn single_line_fn(&self, fn_str: &str, block: &ast::Block) -> Option { + + if fn_str.contains('\n') { + return None; + } + + let codemap = self.get_context().codemap; + + if self.config.fn_empty_single_line && is_empty_block(block, codemap) && + self.block_indent.width() + fn_str.len() + 2 <= 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() { + stmt.rewrite(&self.get_context(), + self.config.max_width - self.block_indent.width(), + self.block_indent) + } else { + None + } + }; + + if let Some(res) = rewrite { + let width = self.block_indent.width() + fn_str.len() + res.len() + 4; + if !res.contains('\n') && width <= self.config.max_width { + return Some(format!("{}{{ {} }}", fn_str, res)); + } + } + } + + None + } + fn rewrite_args(&self, args: &[ast::Arg], explicit_self: Option<&ast::ExplicitSelf>, 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..fa51d70a853 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -38,28 +38,21 @@ 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 = stmt.rewrite(&self.get_context(), + self.config.max_width - self.block_indent.width(), + self.block_indent); + + 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 = stmt.rewrite(&self.get_context(), + self.config.max_width - self.block_indent.width(), + self.block_indent); + self.push_rewrite(stmt.span, rewrite); } ast::Stmt_::StmtMac(ref mac, _macro_style) => { @@ -101,7 +94,7 @@ impl<'a> FmtVisitor<'a> { 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(";"); } } @@ -144,7 +137,8 @@ impl<'a> FmtVisitor<'a> { constness, abi, vis, - codemap::mk_sp(s.lo, b.span.lo)) + codemap::mk_sp(s.lo, b.span.lo), + &b) } visit::FnKind::Method(ident, ref sig, vis) => { self.rewrite_fn(indent, @@ -156,7 +150,8 @@ impl<'a> FmtVisitor<'a> { sig.constness, sig.abi, vis.unwrap_or(ast::Visibility::Inherited), - codemap::mk_sp(s.lo, b.span.lo)) + codemap::mk_sp(s.lo, b.span.lo), + &b) } visit::FnKind::Closure => None, }; @@ -164,6 +159,12 @@ impl<'a> FmtVisitor<'a> { if let Some(fn_str) = rewrite { self.format_missing_with_indent(s.lo); self.buffer.push_str(&fn_str); + if let Some(c) = fn_str.chars().last() { + if c == '}' { + self.last_pos = b.span.hi; + return; + } + } } else { self.format_missing(b.span.lo); } @@ -501,31 +502,6 @@ impl<'a> FmtVisitor<'a> { } } -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..a34371f55ad --- /dev/null +++ b/tests/source/fn-single-line.rs @@ -0,0 +1,76 @@ +// 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 +} + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) { + } +} + +trait CoolerTypes { fn dummy(&self) { +} +} diff --git a/tests/target/attrib.rs b/tests/target/attrib.rs index 0789efc2b63..9317a837065 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 @@ impl Bar { } #[another_attribute] - fn f3(self) -> Dog { - } + fn f3(self) -> Dog {} /// Blah blah bing. #[attrib1] @@ -38,6 +36,5 @@ impl Bar { // 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..2d90d83edb6 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..fa607e131ea 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..9db1c8831b6 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..674ce1c89f9 --- /dev/null +++ b/tests/target/fn-single-line.rs @@ -0,0 +1,63 @@ +// 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 } + +fn mac() -> Vec { vec![] } + +trait CoolTypes { + fn dummy(&self) {} +} + +trait CoolerTypes { + fn dummy(&self) {} +} diff --git a/tests/target/fn.rs b/tests/target/fn.rs index da13a6f05dc..0ae9fd7ef1f 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 @@ impl Foo { { } - 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 @@ impl 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..7b371409fba 100644 --- a/tests/target/multiple.rs +++ b/tests/target/multiple.rs @@ -26,9 +26,7 @@ mod other; // 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 @@ impl Bar { } #[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..7db4572e777 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..ae09d8ddac0 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..120b17145e3 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..f328e4d9d04 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..f1b142b3a5c 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 {}