X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmatches.rs;h=791f34cefe8ded442117fe601ed9c6820e7c1c94;hb=caefd218c9f6c43493c28c5cc4ea8828c494830e;hp=b97fbd8741776fca55f87901e1eb4e8fb2f83f38;hpb=88589f2ad891f115e44cab2f37993885c82c40d8;p=rust.git diff --git a/src/matches.rs b/src/matches.rs index b97fbd87417..791f34cefe8 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -13,20 +13,24 @@ use std::iter::repeat; use config::lists::*; -use syntax::{ast, ptr}; use syntax::codemap::{BytePos, Span}; +use syntax::{ast, ptr}; use codemap::SpanUtils; -use comment::combine_strs_with_missing_comments; +use comment::{combine_strs_with_missing_comments, rewrite_comment}; use config::{Config, ControlBraceStyle, IndentStyle}; -use expr::{format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, - rewrite_multiple_patterns, ExprType, RhsTactics, ToExpr}; +use expr::{ + format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, + rewrite_multiple_patterns, ExprType, RhsTactics, ToExpr, +}; use lists::{itemize_list, write_list, ListFormatting}; use rewrite::{Rewrite, RewriteContext}; use shape::Shape; use spanned::Spanned; -use utils::{contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, - mk_sp, ptr_vec_to_ref_vec, trimmed_last_line_width}; +use utils::{ + contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, mk_sp, + ptr_vec_to_ref_vec, trimmed_last_line_width, +}; /// A simple wrapper type against `ast::Arm`. Used inside `write_list()`. struct ArmWrapper<'a> { @@ -64,7 +68,7 @@ fn span(&self) -> Span { impl<'a> Rewrite for ArmWrapper<'a> { fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { - rewrite_match_arm(context, self.arm, shape, self.is_last, self.beginning_vert) + rewrite_match_arm(context, self.arm, shape, self.is_last) } } @@ -133,13 +137,14 @@ pub fn rewrite_match( Some(context.snippet(span).to_owned()) } } else { + let span_after_cond = mk_sp(cond.span.hi(), span.hi()); Some(format!( "match {}{}{{\n{}{}{}\n{}}}", cond_str, block_sep, inner_attrs_str, nested_indent_str, - rewrite_match_arms(context, arms, shape, span, open_brace_pos)?, + rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, shape.indent.to_string(context.config), )) } @@ -150,7 +155,7 @@ fn arm_comma(config: &Config, body: &ast::Expr, is_last: bool) -> &'static str { "" } else if config.match_block_trailing_comma() { "," - } else if let ast::ExprKind::Block(ref block) = body.node { + } else if let ast::ExprKind::Block(ref block, _) = body.node { if let ast::BlockCheckMode::Default = block.rules { "" } else { @@ -191,7 +196,7 @@ fn rewrite_match_arms( let arm_len = arms.len(); let is_last_iter = repeat(false) - .take(arm_len.checked_sub(1).unwrap_or(0)) + .take(arm_len.saturating_sub(1)) .chain(repeat(true)); let beginning_verts = collect_beginning_verts(context, arms, span); let items = itemize_list( @@ -219,6 +224,7 @@ fn rewrite_match_arms( shape: arm_shape, ends_with_newline: true, preserve_newline: true, + nested: false, config: context.config, }; @@ -230,7 +236,6 @@ fn rewrite_match_arm( arm: &ast::Arm, shape: Shape, is_last: bool, - beginning_vert: Option, ) -> Option { let (missing_span, attrs_str) = if !arm.attrs.is_empty() { if contains_skip(&arm.attrs) { @@ -250,28 +255,27 @@ fn rewrite_match_arm( } else { (mk_sp(arm.span().lo(), arm.span().lo()), String::new()) }; - let pats_str = rewrite_match_pattern( - context, - &ptr_vec_to_ref_vec(&arm.pats), - &arm.guard, - beginning_vert.is_some(), - shape, - ).and_then(|pats_str| { - combine_strs_with_missing_comments( - context, - &attrs_str, - &pats_str, - missing_span, - shape, - false, - ) - })?; + let pats_str = + rewrite_match_pattern(context, &ptr_vec_to_ref_vec(&arm.pats), &arm.guard, shape) + .and_then(|pats_str| { + combine_strs_with_missing_comments( + context, + &attrs_str, + &pats_str, + missing_span, + shape, + false, + ) + })?; + + let arrow_span = mk_sp(arm.pats.last().unwrap().span.hi(), arm.body.span.lo()); rewrite_match_body( context, &arm.body, &pats_str, shape, arm.guard.is_some(), + arrow_span, is_last, ) } @@ -280,46 +284,61 @@ fn rewrite_match_pattern( context: &RewriteContext, pats: &[&ast::Pat], guard: &Option>, - has_beginning_vert: bool, shape: Shape, ) -> Option { // Patterns // 5 = ` => {` - // 2 = `| ` - let pat_shape = shape - .sub_width(5)? - .offset_left(if has_beginning_vert { 2 } else { 0 })?; + let pat_shape = shape.sub_width(5)?; let pats_str = rewrite_multiple_patterns(context, pats, pat_shape)?; - let beginning_vert = if has_beginning_vert { "| " } else { "" }; // Guard - let guard_str = rewrite_guard(context, guard, shape, trimmed_last_line_width(&pats_str))?; + let guard_str = rewrite_guard( + context, + guard, + shape, + trimmed_last_line_width(&pats_str), + pats_str.contains("\n"), + )?; - Some(format!("{}{}{}", beginning_vert, pats_str, guard_str)) + Some(format!("{}{}", pats_str, guard_str)) +} + +fn block_can_be_flattened<'a>( + context: &RewriteContext, + expr: &'a ast::Expr, +) -> Option<&'a ast::Block> { + match expr.node { + ast::ExprKind::Block(ref block, _) + if !is_unsafe_block(block) + && is_simple_block(block, Some(&expr.attrs), context.codemap) => + { + Some(&*block) + } + _ => None, + } } // (extend, body) // @extend: true if the arm body can be put next to `=>` // @body: flattened body, if the body is block with a single expression fn flatten_arm_body<'a>(context: &'a RewriteContext, body: &'a ast::Expr) -> (bool, &'a ast::Expr) { - match body.node { - ast::ExprKind::Block(ref block) - if !is_unsafe_block(block) - && is_simple_block(block, Some(&body.attrs), context.codemap) => - { - if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node { - ( - !context.config.force_multiline_blocks() && can_extend_match_arm_body(expr), - &*expr, - ) + if let Some(ref block) = block_can_be_flattened(context, body) { + if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node { + if let ast::ExprKind::Block(..) = expr.node { + flatten_arm_body(context, expr) } else { - (false, &*body) + let can_extend_expr = + !context.config.force_multiline_blocks() && can_flatten_block_around_this(expr); + (can_extend_expr, &*expr) } + } else { + (false, &*body) } - _ => ( + } else { + ( !context.config.force_multiline_blocks() && body.can_be_overflowed(context, 1), &*body, - ), + ) } } @@ -329,10 +348,11 @@ fn rewrite_match_body( pats_str: &str, shape: Shape, has_guard: bool, + arrow_span: Span, is_last: bool, ) -> Option { let (extend, body) = flatten_arm_body(context, body); - let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block) = body.node { + let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block, _) = body.node { ( true, is_empty_block(block, Some(&body.attrs), context.codemap), @@ -353,24 +373,43 @@ fn rewrite_match_body( Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma)) }; - let forbid_same_line = has_guard && pats_str.contains('\n') && !is_empty_block; let next_line_indent = if !is_block || is_empty_block { shape.indent.block_indent(context.config) } else { shape.indent }; + + let forbid_same_line = has_guard && pats_str.contains('\n') && !is_empty_block; + + // Look for comments between `=>` and the start of the body. + let arrow_comment = { + let arrow_snippet = context.snippet(arrow_span).trim(); + let arrow_index = arrow_snippet.find("=>").unwrap(); + // 2 = `=>` + let comment_str = arrow_snippet[arrow_index + 2..].trim(); + if comment_str.is_empty() { + String::new() + } else { + rewrite_comment(comment_str, false, shape, &context.config)? + } + }; + let combine_next_line_body = |body_str: &str| { + let nested_indent_str = next_line_indent.to_string_with_newline(context.config); + if is_block { - return Some(format!( - "{} =>{}{}", - pats_str, - next_line_indent.to_string_with_newline(context.config), - body_str - )); + let mut result = pats_str.to_owned(); + result.push_str(" =>"); + if !arrow_comment.is_empty() { + result.push_str(&nested_indent_str); + result.push_str(&arrow_comment); + } + result.push_str(&nested_indent_str); + result.push_str(&body_str); + return Some(result); } let indent_str = shape.indent.to_string_with_newline(context.config); - let nested_indent_str = next_line_indent.to_string_with_newline(context.config); let (body_prefix, body_suffix) = if context.config.match_arm_blocks() { let comma = if context.config.match_block_trailing_comma() { "," @@ -385,14 +424,22 @@ fn rewrite_match_body( let block_sep = match context.config.control_brace_style() { ControlBraceStyle::AlwaysNextLine => format!("{}{}", alt_block_sep, body_prefix), _ if body_prefix.is_empty() => "".to_owned(), - _ if forbid_same_line => format!("{}{}", alt_block_sep, body_prefix), + _ if forbid_same_line || !arrow_comment.is_empty() => { + format!("{}{}", alt_block_sep, body_prefix) + } _ => format!(" {}", body_prefix), } + &nested_indent_str; - Some(format!( - "{} =>{}{}{}", - pats_str, block_sep, body_str, body_suffix - )) + let mut result = pats_str.to_owned(); + result.push_str(" =>"); + if !arrow_comment.is_empty() { + result.push_str(&indent_str); + result.push_str(&arrow_comment); + } + result.push_str(&block_sep); + result.push_str(&body_str); + result.push_str(&body_suffix); + Some(result) }; // Let's try and get the arm body on the same line as the condition. @@ -400,7 +447,9 @@ fn rewrite_match_body( let orig_body_shape = shape .offset_left(extra_offset(pats_str, shape) + 4) .and_then(|shape| shape.sub_width(comma.len())); - let orig_body = if let Some(body_shape) = orig_body_shape { + let orig_body = if forbid_same_line || !arrow_comment.is_empty() { + None + } else if let Some(body_shape) = orig_body_shape { let rewrite = nop_block_collapse( format_expr(body, ExprType::Statement, context, body_shape), body_shape.width, @@ -408,9 +457,7 @@ fn rewrite_match_body( match rewrite { Some(ref body_str) - if !forbid_same_line - && (is_block - || (!body_str.contains('\n') && body_str.len() <= body_shape.width)) => + if is_block || (!body_str.contains('\n') && body_str.len() <= body_shape.width) => { return combine_orig_body(body_str); } @@ -429,8 +476,7 @@ fn rewrite_match_body( ); match (orig_body, next_line_body) { (Some(ref orig_str), Some(ref next_line_str)) - if forbid_same_line - || prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => + if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => { combine_next_line_body(next_line_str) } @@ -454,6 +500,7 @@ fn rewrite_guard( // The amount of space used up on this line for the pattern in // the arm (excludes offset). pattern_width: usize, + multiline_pattern: bool, ) -> Option { if let Some(ref guard) = *guard { // First try to fit the guard string on the same line as the pattern. @@ -461,10 +508,12 @@ fn rewrite_guard( let cond_shape = shape .offset_left(pattern_width + 4) .and_then(|s| s.sub_width(5)); - if let Some(cond_shape) = cond_shape { - if let Some(cond_str) = guard.rewrite(context, cond_shape) { - if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { - return Some(format!(" if {}", cond_str)); + if !multiline_pattern { + if let Some(cond_shape) = cond_shape { + if let Some(cond_str) = guard.rewrite(context, cond_shape) { + if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { + return Some(format!(" if {}", cond_str)); + } } } } @@ -493,7 +542,8 @@ fn rewrite_guard( fn nop_block_collapse(block_str: Option, budget: usize) -> Option { debug!("nop_block_collapse {:?} {}", block_str, budget); block_str.map(|block_str| { - if block_str.starts_with('{') && budget >= 2 + if block_str.starts_with('{') + && budget >= 2 && (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() == block_str.len() - 2) { "{}".to_owned() @@ -503,15 +553,17 @@ fn nop_block_collapse(block_str: Option, budget: usize) -> Option bool { +fn can_flatten_block_around_this(body: &ast::Expr) -> bool { match body.node { // We do not allow `if` to stay on the same line, since we could easily mistake // `pat => if cond { ... }` and `pat if cond => { ... }`. ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => false, - ast::ExprKind::ForLoop(..) - | ast::ExprKind::Loop(..) - | ast::ExprKind::While(..) - | ast::ExprKind::WhileLet(..) + // We do not allow collapsing a block around expression with condition + // to avoid it being cluttered with match arm. + ast::ExprKind::ForLoop(..) | ast::ExprKind::While(..) | ast::ExprKind::WhileLet(..) => { + false + } + ast::ExprKind::Loop(..) | ast::ExprKind::Match(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) @@ -525,7 +577,7 @@ fn can_extend_match_arm_body(body: &ast::Expr) -> bool { | ast::ExprKind::Box(ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) - | ast::ExprKind::Cast(ref expr, _) => can_extend_match_arm_body(expr), + | ast::ExprKind::Cast(ref expr, _) => can_flatten_block_around_this(expr), _ => false, } }