diff --git a/src/formatting/closures.rs b/src/formatting/closures.rs index 635b670985c..866723a0021 100644 --- a/src/formatting/closures.rs +++ b/src/formatting/closures.rs @@ -15,7 +15,7 @@ use crate::formatting::{ rewrite::{Rewrite, RewriteContext}, shape::Shape, source_map::SpanUtils, - utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}, + utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt, StmtsExt}, }; // This module is pretty messy because of the rules around closures and blocks: @@ -140,7 +140,7 @@ fn get_inner_expr<'a>( if !needs_block(block, prefix, context) { // block.stmts.len() == 1 except with `|| {{}}`; // https://github.com/rust-lang/rustfmt/issues/3844 - if let Some(expr) = block.stmts.first().and_then(stmt_expr) { + if let Some(expr) = block.stmts.find_non_empty().and_then(stmt_expr) { return get_inner_expr(expr, prefix, context); } } @@ -151,12 +151,18 @@ fn get_inner_expr<'a>( // Figure out if a block is necessary. fn needs_block(block: &ast::Block, prefix: &str, context: &RewriteContext<'_>) -> bool { - let has_attributes = block.stmts.first().map_or(false, |first_stmt| { + let has_attributes = block.stmts.find_non_empty().map_or(false, |first_stmt| { !get_attrs_from_stmt(first_stmt).is_empty() }); + let mut non_empty_stmts = block + .stmts + .iter() + .filter(|stmt| !matches!(stmt.kind, ast::StmtKind::Empty)); + non_empty_stmts.next(); + is_unsafe_block(block) - || block.stmts.len() > 1 + || non_empty_stmts.next().is_some() || has_attributes || block_contains_comment(context, block) || prefix.contains('\n') @@ -390,7 +396,7 @@ pub(crate) fn rewrite_last_closure( && !context.inside_macro() && is_simple_block(context, block, Some(&body.attrs)) => { - stmt_expr(&block.stmts[0]).unwrap_or(body) + stmt_expr(block.stmts.find_non_empty().unwrap()).unwrap_or(body) } _ => body, }; diff --git a/src/formatting/expr.rs b/src/formatting/expr.rs index b1cff4de39f..4b78615a4ab 100644 --- a/src/formatting/expr.rs +++ b/src/formatting/expr.rs @@ -31,7 +31,7 @@ use crate::formatting::{ utils::{ colon_spaces, contains_skip, count_newlines, first_line_ends_with, inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, semicolon_for_expr, - unicode_str_width, wrap_str, + unicode_str_width, wrap_str, StmtsExt, }, vertical::rewrite_with_alignment, visitor::FmtVisitor, @@ -557,7 +557,11 @@ fn rewrite_single_line_block( ) -> Option { if is_simple_block(context, block, attrs) { let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block.stmts[0].rewrite(context, expr_shape)?; + let expr_str = block + .stmts + .find_non_empty() + .unwrap() + .rewrite(context, expr_shape)?; let label_str = rewrite_label(label); let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); if result.len() <= shape.width && !result.contains('\n') { @@ -826,11 +830,11 @@ impl<'a> ControlFlow<'a> { } let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?; - let expr = &self.block.stmts[0]; + let expr = self.block.stmts.find_non_empty().unwrap(); let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; let new_width = new_width.checked_sub(if_str.len())?; - let else_expr = &else_node.stmts[0]; + let else_expr = else_node.stmts.find_non_empty().unwrap(); let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; if if_str.contains('\n') || else_str.contains('\n') { @@ -1182,8 +1186,14 @@ pub(crate) fn is_simple_block( block: &ast::Block, attrs: Option<&[ast::Attribute]>, ) -> bool { - block.stmts.len() == 1 - && stmt_is_expr(&block.stmts[0]) + let mut non_empty_stmts = block + .stmts + .iter() + .filter(|stmt| !matches!(stmt.kind, ast::StmtKind::Empty)); + let first = non_empty_stmts.next(); + first.is_some() + && non_empty_stmts.next().is_none() + && stmt_is_expr(first.unwrap()) && !block_contains_comment(context, block) && attrs.map_or(true, |a| a.is_empty()) } diff --git a/src/formatting/items.rs b/src/formatting/items.rs index d2e5e70fce7..2b4de53ae11 100644 --- a/src/formatting/items.rs +++ b/src/formatting/items.rs @@ -469,7 +469,7 @@ impl<'a> FmtVisitor<'a> { return None; } - let res = Stmt::from_ast_node(block.stmts.first()?, true) + let res = Stmt::from_ast_node(block.stmts.find_non_empty()?, true) .rewrite(&self.get_context(), self.shape())?; let width = self.block_indent.width() + fn_str.len() + res.len() + 5; diff --git a/src/formatting/matches.rs b/src/formatting/matches.rs index 61d6e8b4f08..2443f6e723f 100644 --- a/src/formatting/matches.rs +++ b/src/formatting/matches.rs @@ -20,6 +20,7 @@ use crate::formatting::{ utils::{ contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, mk_sp, mk_sp_lo_plus_one, semicolon_for_expr, trimmed_last_line_width, unicode_str_width, + StmtsExt, }, }; @@ -312,7 +313,7 @@ fn block_can_be_flattened<'a>( && is_simple_block(context, block, Some(&expr.attrs)) // Don't flatten a block containing a macro invocation, // since it may expand to a statement - && !stmt_is_expr_mac(&block.stmts[0]) => + && !stmt_is_expr_mac(block.stmts.find_non_empty().unwrap()) => { Some(&*block) } @@ -332,7 +333,7 @@ fn flatten_arm_body<'a>( |expr| !context.config.force_multiline_blocks() && can_flatten_block_around_this(expr); if let Some(ref block) = block_can_be_flattened(context, body) { - if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind { + if let ast::StmtKind::Expr(ref expr) = block.stmts.find_non_empty().unwrap().kind { if let ast::ExprKind::Block(..) = expr.kind { if expr.attrs.is_empty() { flatten_arm_body(context, expr, None) diff --git a/src/formatting/utils.rs b/src/formatting/utils.rs index c6b579eb4f7..af333f7d4ea 100644 --- a/src/formatting/utils.rs +++ b/src/formatting/utils.rs @@ -837,6 +837,17 @@ pub(crate) fn format_code_block( }) } +pub(crate) trait StmtsExt { + fn find_non_empty(&self) -> Option<&ast::Stmt>; +} + +impl StmtsExt for Vec { + fn find_non_empty(&self) -> Option<&ast::Stmt> { + self.iter() + .find(|stmt| !matches!(stmt.kind, ast::StmtKind::Empty)) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/tests/source/issue-4748.rs b/tests/source/issue-4748.rs new file mode 100644 index 00000000000..b2467acd1b2 --- /dev/null +++ b/tests/source/issue-4748.rs @@ -0,0 +1,43 @@ +fn foo(x: Option) -> i32 { + match y { + Some(y) => { + ; + ; + y + } + None => 0, + } +} + +fn foo(x: Option) -> i32 { + match y { + Some(y) => { + y + } + None => 0, + } +} + +fn foo(){ + + foo.find(|y| { + ; + ; + x + }); +} + +fn foo() { + foo.find(|y| { x }); +} + +fn foo(){ + try { + ; + ; + ; + ; + y + } +} + diff --git a/tests/target/issue-4748.rs b/tests/target/issue-4748.rs new file mode 100644 index 00000000000..e950810755f --- /dev/null +++ b/tests/target/issue-4748.rs @@ -0,0 +1,25 @@ +fn foo(x: Option) -> i32 { + match y { + Some(y) => y, + None => 0, + } +} + +fn foo(x: Option) -> i32 { + match y { + Some(y) => y, + None => 0, + } +} + +fn foo() { + foo.find(|y| x); +} + +fn foo() { + foo.find(|y| x); +} + +fn foo() { + try { y } +}