X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmatches.rs;h=85d9c5d2b9bbf4f3e80ec3371cc733d9dea157ea;hb=ce301d92f12658edf6fe410d39de445270d1420e;hp=d46b19be7234c12cc88e7c8776f0f1d5e276e072;hpb=1d4b988414e48f16c1aa44a79e6f4b11d929bb76;p=rust.git diff --git a/src/matches.rs b/src/matches.rs index d46b19be723..85d9c5d2b9b 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -1,53 +1,39 @@ -// 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. - //! Format match expression. use std::iter::repeat; -use config::lists::*; -use syntax::codemap::{BytePos, Span}; -use syntax::{ast, ptr}; +use rustc_ast::{ast, ptr}; +use rustc_span::{BytePos, Span}; -use codemap::SpanUtils; -use comment::combine_strs_with_missing_comments; -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 crate::comment::{combine_strs_with_missing_comments, rewrite_comment}; +use crate::config::lists::*; +use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version}; +use crate::expr::{ + format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond, + ExprType, RhsTactics, }; -use lists::{itemize_list, write_list, ListFormatting}; -use rewrite::{Rewrite, RewriteContext}; -use shape::Shape; -use spanned::Spanned; -use utils::{ +use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::spanned::Spanned; +use crate::utils::{ contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, mk_sp, - ptr_vec_to_ref_vec, trimmed_last_line_width, + semicolon_for_expr, trimmed_last_line_width, unicode_str_width, }; /// A simple wrapper type against `ast::Arm`. Used inside `write_list()`. struct ArmWrapper<'a> { - pub arm: &'a ast::Arm, - /// True if the arm is the last one in match expression. Used to decide on whether we should add - /// trailing comma to the match arm when `config.trailing_comma() == Never`. - pub is_last: bool, + arm: &'a ast::Arm, + /// `true` if the arm is the last one in match expression. Used to decide on whether we should + /// add trailing comma to the match arm when `config.trailing_comma() == Never`. + is_last: bool, /// Holds a byte position of `|` at the beginning of the arm pattern, if available. - pub beginning_vert: Option, + beginning_vert: Option, } impl<'a> ArmWrapper<'a> { - pub fn new( - arm: &'a ast::Arm, - is_last: bool, - beginning_vert: Option, - ) -> ArmWrapper<'a> { + fn new(arm: &'a ast::Arm, is_last: bool, beginning_vert: Option) -> ArmWrapper<'a> { ArmWrapper { arm, is_last, @@ -59,6 +45,7 @@ pub fn new( impl<'a> Spanned for ArmWrapper<'a> { fn span(&self) -> Span { if let Some(lo) = self.beginning_vert { + let lo = std::cmp::min(lo, self.arm.span().lo()); mk_sp(lo, self.arm.span().hi()) } else { self.arm.span() @@ -67,13 +54,19 @@ 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) + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + rewrite_match_arm( + context, + self.arm, + shape, + self.is_last, + self.beginning_vert.is_some(), + ) } } -pub fn rewrite_match( - context: &RewriteContext, +pub(crate) fn rewrite_match( + context: &RewriteContext<'_>, cond: &ast::Expr, arms: &[ast::Arm], shape: Shape, @@ -125,7 +118,7 @@ pub fn rewrite_match( .snippet_provider .span_after(mk_sp(cond.span.hi(), hi), "{") } else { - inner_attrs[inner_attrs.len() - 1].span().hi() + inner_attrs[inner_attrs.len() - 1].span.hi() }; if arms.is_empty() { @@ -155,7 +148,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.kind { if let ast::BlockCheckMode::Default = block.rules { "" } else { @@ -168,23 +161,21 @@ fn arm_comma(config: &Config, body: &ast::Expr, is_last: bool) -> &'static str { /// Collect a byte position of the beginning `|` for each arm, if available. fn collect_beginning_verts( - context: &RewriteContext, + context: &RewriteContext<'_>, arms: &[ast::Arm], - span: Span, ) -> Vec> { - let mut beginning_verts = Vec::with_capacity(arms.len()); - let mut lo = context.snippet_provider.span_after(span, "{"); - for arm in arms { - let hi = arm.pats[0].span.lo(); - let missing_span = mk_sp(lo, hi); - beginning_verts.push(context.snippet_provider.opt_span_before(missing_span, "|")); - lo = arm.span().hi(); - } - beginning_verts + arms.iter() + .map(|a| { + context + .snippet(a.pat.span) + .starts_with('|') + .then(|| a.pat.span().lo()) + }) + .collect() } fn rewrite_match_arms( - context: &RewriteContext, + context: &RewriteContext<'_>, arms: &[ast::Arm], shape: Shape, span: Span, @@ -198,7 +189,7 @@ fn rewrite_match_arms( let is_last_iter = repeat(false) .take(arm_len.saturating_sub(1)) .chain(repeat(true)); - let beginning_verts = collect_beginning_verts(context, arms, span); + let beginning_verts = collect_beginning_verts(context, arms); let items = itemize_list( context.snippet_provider, arms.iter() @@ -215,31 +206,24 @@ fn rewrite_match_arms( false, ); let arms_vec: Vec<_> = items.collect(); - let fmt = ListFormatting { - tactic: DefinitiveListTactic::Vertical, - // We will add/remove commas inside `arm.rewrite()`, and hence no separator here. - separator: "", - trailing_separator: SeparatorTactic::Never, - separator_place: SeparatorPlace::Back, - shape: arm_shape, - ends_with_newline: true, - preserve_newline: true, - nested: false, - config: context.config, - }; + // We will add/remove commas inside `arm.rewrite()`, and hence no separator here. + let fmt = ListFormatting::new(arm_shape, context.config) + .separator("") + .preserve_newline(true); write_list(&arms_vec, &fmt) } fn rewrite_match_arm( - context: &RewriteContext, + context: &RewriteContext<'_>, arm: &ast::Arm, shape: Shape, is_last: bool, + has_leading_pipe: bool, ) -> Option { let (missing_span, attrs_str) = if !arm.attrs.is_empty() { if contains_skip(&arm.attrs) { - let (_, body) = flatten_arm_body(context, &arm.body); + let (_, body) = flatten_arm_body(context, &arm.body, None); // `arm.span()` does not include trailing comma, add it manually. return Some(format!( "{}{}", @@ -247,92 +231,136 @@ fn rewrite_match_arm( arm_comma(context.config, body, is_last), )); } - let missing_span = mk_sp( - arm.attrs[arm.attrs.len() - 1].span.hi(), - arm.pats[0].span.lo(), - ); + let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo()); (missing_span, arm.attrs.rewrite(context, shape)?) } 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, shape) - .and_then(|pats_str| { - combine_strs_with_missing_comments( - context, - &attrs_str, - &pats_str, - missing_span, - shape, - false, - ) - })?; + + // Leading pipe offset + // 2 = `| ` + let (pipe_offset, pipe_str) = match context.config.match_arm_leading_pipes() { + MatchArmLeadingPipe::Never => (0, ""), + MatchArmLeadingPipe::Preserve if !has_leading_pipe => (0, ""), + MatchArmLeadingPipe::Preserve | MatchArmLeadingPipe::Always => (2, "| "), + }; + + // Patterns + // 5 = ` => {` + let pat_shape = shape.sub_width(5)?.offset_left(pipe_offset)?; + let pats_str = arm.pat.rewrite(context, pat_shape)?; + + // Guard + let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); + let new_line_guard = pats_str.contains('\n') && !block_like_pat; + let guard_str = rewrite_guard( + context, + &arm.guard, + shape, + trimmed_last_line_width(&pats_str), + new_line_guard, + )?; + + let lhs_str = combine_strs_with_missing_comments( + context, + &attrs_str, + &format!("{}{}{}", pipe_str, pats_str, guard_str), + missing_span, + shape, + false, + )?; + + let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.span().lo()); rewrite_match_body( context, &arm.body, - &pats_str, + &lhs_str, shape, - arm.guard.is_some(), + guard_str.contains('\n'), + arrow_span, is_last, ) } -fn rewrite_match_pattern( - context: &RewriteContext, - pats: &[&ast::Pat], - guard: &Option>, - shape: Shape, -) -> Option { - // Patterns - // 5 = ` => {` - let pat_shape = shape.sub_width(5)?; - let pats_str = rewrite_multiple_patterns(context, pats, pat_shape)?; - - // Guard - let guard_str = rewrite_guard(context, guard, shape, trimmed_last_line_width(&pats_str))?; +fn stmt_is_expr_mac(stmt: &ast::Stmt) -> bool { + if let ast::StmtKind::Expr(expr) = &stmt.kind { + if let ast::ExprKind::MacCall(_) = &expr.kind { + return true; + } + } + false +} - Some(format!("{}{}", pats_str, guard_str)) +fn block_can_be_flattened<'a>( + context: &RewriteContext<'_>, + expr: &'a ast::Expr, +) -> Option<&'a ast::Block> { + match expr.kind { + ast::ExprKind::Block(ref block, _) + if !is_unsafe_block(block) + && !context.inside_macro() + && is_simple_block(context, block, Some(&expr.attrs)) + && !stmt_is_expr_mac(&block.stmts[0]) => + { + 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_flatten_block_around_this(expr), - &*expr, - ) +fn flatten_arm_body<'a>( + context: &'a RewriteContext<'_>, + body: &'a ast::Expr, + opt_shape: Option, +) -> (bool, &'a ast::Expr) { + let can_extend = + |expr| !context.config.force_multiline_blocks() && can_flatten_block_around_this(expr); + + if let Some(block) = block_can_be_flattened(context, body) { + if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind { + if let ast::ExprKind::Block(..) = expr.kind { + if expr.attrs.is_empty() { + flatten_arm_body(context, expr, None) + } else { + (true, body) + } } else { - (false, &*body) + let cond_becomes_muti_line = opt_shape + .and_then(|shape| rewrite_cond(context, expr, shape)) + .map_or(false, |cond| cond.contains('\n')); + if cond_becomes_muti_line { + (false, &*body) + } else { + (can_extend(expr), &*expr) + } } + } else { + (false, &*body) } - _ => ( - !context.config.force_multiline_blocks() && body.can_be_overflowed(context, 1), - &*body, - ), + } else { + (can_extend(body), &*body) } } fn rewrite_match_body( - context: &RewriteContext, + context: &RewriteContext<'_>, body: &ptr::P, 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 { - ( - true, - is_empty_block(block, Some(&body.attrs), context.codemap), - ) + let (extend, body) = flatten_arm_body( + context, + body, + shape.offset_left(extra_offset(pats_str, shape) + 4), + ); + let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block, _) = body.kind { + (true, is_empty_block(context, block, Some(&body.attrs))) } else { (false, false) }; @@ -349,46 +377,87 @@ 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) || !body.attrs.is_empty(); + + // Look for comments between `=>` and the start of the body. + let arrow_comment = { + let arrow_snippet = context.snippet(arrow_span).trim(); + // search for the arrow starting from the end of the snippet since there may be a match + // expression within the guard + let arrow_index = arrow_snippet.rfind("=>").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); + result.push_str(comma); + 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() { - "," + let (body_prefix, body_suffix) = + if context.config.match_arm_blocks() && !context.inside_macro() { + let comma = if context.config.match_block_trailing_comma() { + "," + } else { + "" + }; + let semicolon = if context.config.version() == Version::One { + "" + } else { + if semicolon_for_expr(context, body) { + ";" + } else { + "" + } + }; + ("{", format!("{}{}}}{}", semicolon, indent_str, comma)) } else { - "" + ("", String::from(",")) }; - ("{", format!("{}}}{}", indent_str, comma)) - } else { - ("", String::from(",")) - }; 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. @@ -396,7 +465,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, @@ -404,9 +475,9 @@ 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') + && unicode_str_width(body_str) <= body_shape.width) => { return combine_orig_body(body_str); } @@ -425,8 +496,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) } @@ -444,12 +514,13 @@ fn rewrite_match_body( // The `if ...` guard on a match arm. fn rewrite_guard( - context: &RewriteContext, + context: &RewriteContext<'_>, guard: &Option>, shape: Shape, // 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. @@ -457,10 +528,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,23 +566,21 @@ fn nop_block_collapse(block_str: Option, budget: usize) -> Option= 2 && (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() == block_str.len() - 2) { - "{}".to_owned() + String::from("{}") } else { - block_str.to_owned() + block_str } }) } fn can_flatten_block_around_this(body: &ast::Expr) -> bool { - match body.node { + match body.kind { // 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::If(..) => false, // 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::ForLoop(..) | ast::ExprKind::While(..) => false, ast::ExprKind::Loop(..) | ast::ExprKind::Match(..) | ast::ExprKind::Block(..) @@ -517,13 +588,14 @@ fn can_flatten_block_around_this(body: &ast::Expr) -> bool { | ast::ExprKind::Array(..) | ast::ExprKind::Call(..) | ast::ExprKind::MethodCall(..) - | ast::ExprKind::Mac(..) + | ast::ExprKind::MacCall(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Tup(..) => true, - ast::ExprKind::AddrOf(_, ref expr) + ast::ExprKind::AddrOf(_, _, ref expr) | ast::ExprKind::Box(ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) + | ast::ExprKind::Index(ref expr, _) | ast::ExprKind::Cast(ref expr, _) => can_flatten_block_around_this(expr), _ => false, }