X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fexpr.rs;h=72cf618dca546fc7c0213013d2340ec4dd989161;hb=caefd218c9f6c43493c28c5cc4ea8828c494830e;hp=6e740f43f8282ba913a9df07faa1e3b3e00907a9;hpb=f5ebcd922e2ef64ea6df17fa86e921304c88ca3c;p=rust.git diff --git a/src/expr.rs b/src/expr.rs index 6e740f43f82..72cf618dca5 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -10,32 +10,39 @@ use std::borrow::Cow; use std::cmp::min; -use std::iter::repeat; use config::lists::*; -use syntax::{ast, ptr}; use syntax::codemap::{BytePos, CodeMap, Span}; +use syntax::parse::token::DelimToken; +use syntax::{ast, ptr}; use chains::rewrite_chain; use closures; use codemap::{LineRangeUtils, SpanUtils}; -use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed, - rewrite_comment, rewrite_missing_comment, CharClasses, FindUncommented}; +use comment::{ + combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, + rewrite_missing_comment, CharClasses, FindUncommented, +}; use config::{Config, ControlBraceStyle, IndentStyle}; -use lists::{definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, - struct_lit_shape, struct_lit_tactic, write_list, ListFormatting, ListItem, Separator}; +use lists::{ + definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, + struct_lit_tactic, write_list, ListFormatting, ListItem, Separator, +}; use macros::{rewrite_macro, MacroArg, MacroPosition}; +use matches::rewrite_match; use overflow; -use patterns::{can_be_overflowed_pat, TuplePatField}; +use pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; +use patterns::{can_be_overflowed_pat, is_short_pattern, TuplePatField}; use rewrite::{Rewrite, RewriteContext}; use shape::{Indent, Shape}; use spanned::Spanned; use string::{rewrite_string, StringFormat}; use types::{can_be_overflowed_type, rewrite_path, PathContext}; -use utils::{colon_spaces, contains_skip, count_newlines, extra_offset, first_line_width, - inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, - paren_overhead, ptr_vec_to_ref_vec, semicolon_for_stmt, trimmed_last_line_width, - wrap_str}; +use utils::{ + colon_spaces, contains_skip, count_newlines, first_line_ends_with, first_line_width, + inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, + ptr_vec_to_ref_vec, semicolon_for_stmt, wrap_str, +}; use vertical::rewrite_with_alignment; use visitor::FmtVisitor; @@ -65,14 +72,13 @@ pub fn format_expr( let expr_rw = match expr.node { ast::ExprKind::Array(ref expr_vec) => rewrite_array( + "", &ptr_vec_to_ref_vec(expr_vec), - mk_sp( - context.snippet_provider.span_after(expr.span, "["), - expr.span.hi(), - ), + expr.span, context, shape, - false, + choose_separator_tactic(context, expr.span), + None, ), ast::ExprKind::Lit(ref l) => rewrite_literal(context, l, shape), ast::ExprKind::Call(ref callee, ref args) => { @@ -81,16 +87,18 @@ pub fn format_expr( rewrite_call(context, &callee_str, args, inner_span, shape) } ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span), - ast::ExprKind::Binary(ref op, ref lhs, ref rhs) => { + ast::ExprKind::Binary(op, ref lhs, ref rhs) => { // FIXME: format comments between operands and operator - rewrite_pair( - &**lhs, - &**rhs, - PairParts::new("", &format!(" {} ", context.snippet(op.span)), ""), - context, - shape, - context.config.binop_separator(), - ) + rewrite_all_pairs(expr, shape, context).or_else(|| { + rewrite_pair( + &**lhs, + &**rhs, + PairParts::infix(&format!(" {} ", context.snippet(op.span))), + context, + shape, + context.config.binop_separator(), + ) + }) } ast::ExprKind::Unary(ref op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape), ast::ExprKind::Struct(ref path, ref fields, ref base) => rewrite_struct_lit( @@ -111,20 +119,33 @@ pub fn format_expr( | ast::ExprKind::While(..) | ast::ExprKind::WhileLet(..) => to_control_flow(expr, expr_type) .and_then(|control_flow| control_flow.rewrite(context, shape)), - ast::ExprKind::Block(ref block) => { + ast::ExprKind::Block(ref block, opt_label) => { match expr_type { ExprType::Statement => { if is_unsafe_block(block) { - block.rewrite(context, shape) - } else if let rw @ Some(_) = rewrite_empty_block(context, block, shape) { + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) + } else if let rw @ Some(_) = + rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape) + { // Rewrite block without trying to put it in a single line. rw } else { let prefix = block_prefix(context, block, shape)?; - rewrite_block_with_visitor(context, &prefix, block, shape, true) + + rewrite_block_with_visitor( + context, + &prefix, + block, + Some(&expr.attrs), + opt_label, + shape, + true, + ) } } - ExprType::SubExpression => block.rewrite(context, shape), + ExprType::SubExpression => { + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) + } } } ast::ExprKind::Match(ref cond, ref arms) => { @@ -163,21 +184,14 @@ pub fn format_expr( } else { Some("yield".to_string()) }, - ast::ExprKind::Closure(capture, movability, ref fn_decl, ref body, _) => { + ast::ExprKind::Closure(capture, asyncness, movability, ref fn_decl, ref body, _) => { closures::rewrite_closure( - capture, - movability, - fn_decl, - body, - expr.span, - context, - shape, + capture, asyncness, movability, fn_decl, body, expr.span, context, shape, ) } - ast::ExprKind::Try(..) - | ast::ExprKind::Field(..) - | ast::ExprKind::TupField(..) - | ast::ExprKind::MethodCall(..) => rewrite_chain(expr, context, shape), + ast::ExprKind::Try(..) | ast::ExprKind::Field(..) | ast::ExprKind::MethodCall(..) => { + rewrite_chain(expr, context, shape) + } ast::ExprKind::Mac(ref mac) => { rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { wrap_str( @@ -198,7 +212,7 @@ pub fn format_expr( ast::ExprKind::Cast(ref expr, ref ty) => rewrite_pair( &**expr, &**ty, - PairParts::new("", " as ", ""), + PairParts::infix(" as "), context, shape, SeparatorPlace::Front, @@ -206,7 +220,7 @@ pub fn format_expr( ast::ExprKind::Type(ref expr, ref ty) => rewrite_pair( &**expr, &**ty, - PairParts::new("", ": ", ""), + PairParts::infix(": "), context, shape, SeparatorPlace::Back, @@ -214,21 +228,14 @@ pub fn format_expr( ast::ExprKind::Index(ref expr, ref index) => { rewrite_index(&**expr, &**index, context, shape) } - ast::ExprKind::Repeat(ref expr, ref repeats) => { - let (lbr, rbr) = if context.config.spaces_within_parens_and_brackets() { - ("[ ", " ]") - } else { - ("[", "]") - }; - rewrite_pair( - &**expr, - &**repeats, - PairParts::new(lbr, "; ", rbr), - context, - shape, - SeparatorPlace::Back, - ) - } + ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair( + &**expr, + &*repeats.value, + PairParts::new("[", "; ", "]"), + context, + shape, + SeparatorPlace::Back, + ), ast::ExprKind::Range(ref lhs, ref rhs, limits) => { let delim = match limits { ast::RangeLimits::HalfOpen => "..", @@ -247,19 +254,42 @@ fn needs_space_before_range(context: &RewriteContext, lhs: &ast::Expr) -> bool { } } + fn needs_space_after_range(rhs: &ast::Expr) -> bool { + match rhs.node { + // Don't format `.. ..` into `....`, which is invalid. + // + // This check is unnecessary for `lhs`, because a range + // starting from another range needs parentheses as `(x ..) ..` + // (`x .. ..` is a range from `x` to `..`). + ast::ExprKind::Range(None, _, _) => true, + _ => false, + } + } + + let default_sp_delim = |lhs: Option<&ast::Expr>, rhs: Option<&ast::Expr>| { + let space_if = |b: bool| if b { " " } else { "" }; + + format!( + "{}{}{}", + lhs.map(|lhs| space_if(needs_space_before_range(context, lhs))) + .unwrap_or(""), + delim, + rhs.map(|rhs| space_if(needs_space_after_range(rhs))) + .unwrap_or(""), + ) + }; + match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) { (Some(lhs), Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { format!(" {} ", delim) - } else if needs_space_before_range(context, lhs) { - format!(" {}", delim) } else { - delim.to_owned() + default_sp_delim(Some(lhs), Some(rhs)) }; rewrite_pair( &*lhs, &*rhs, - PairParts::new("", &sp_delim, ""), + PairParts::infix(&sp_delim), context, shape, context.config.binop_separator(), @@ -269,7 +299,7 @@ fn needs_space_before_range(context: &RewriteContext, lhs: &ast::Expr) -> bool { let sp_delim = if context.config.spaces_around_ranges() { format!("{} ", delim) } else { - delim.to_owned() + default_sp_delim(None, Some(rhs)) }; rewrite_unary_prefix(context, &sp_delim, &*rhs, shape) } @@ -277,7 +307,7 @@ fn needs_space_before_range(context: &RewriteContext, lhs: &ast::Expr) -> bool { let sp_delim = if context.config.spaces_around_ranges() { format!(" {}", delim) } else { - delim.to_owned() + default_sp_delim(Some(lhs), None) }; rewrite_unary_suffix(context, &sp_delim, &*lhs, shape) } @@ -286,19 +316,64 @@ fn needs_space_before_range(context: &RewriteContext, lhs: &ast::Expr) -> bool { } // We do not format these expressions yet, but they should still // satisfy our width restrictions. - ast::ExprKind::InPlace(..) | ast::ExprKind::InlineAsm(..) => { - Some(context.snippet(expr.span).to_owned()) - } + ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), ast::ExprKind::Catch(ref block) => { - if let rw @ Some(_) = rewrite_single_line_block(context, "do catch ", block, shape) { + if let rw @ Some(_) = rewrite_single_line_block( + context, + "do catch ", + block, + Some(&expr.attrs), + None, + shape, + ) { rw } else { // 9 = `do catch ` - let budget = shape.width.checked_sub(9).unwrap_or(0); + let budget = shape.width.saturating_sub(9); Some(format!( "{}{}", "do catch ", - block.rewrite(context, Shape::legacy(budget, shape.indent))? + rewrite_block( + block, + Some(&expr.attrs), + None, + context, + Shape::legacy(budget, shape.indent) + )? + )) + } + } + // FIXME(#2743) + ast::ExprKind::ObsoleteInPlace(..) => unimplemented!(), + ast::ExprKind::Async(capture_by, _node_id, ref block) => { + let mover = if capture_by == ast::CaptureBy::Value { + "move " + } else { + "" + }; + if let rw @ Some(_) = rewrite_single_line_block( + context, + format!("{}{}", "async ", mover).as_str(), + block, + Some(&expr.attrs), + None, + shape, + ) { + rw + } else { + // 6 = `async ` + let budget = shape.width.saturating_sub(6); + Some(format!( + "{}{}{}", + "async ", + mover, + rewrite_block( + block, + Some(&expr.attrs), + None, + context, + Shape::legacy(budget, shape.indent) + )? )) } } @@ -317,258 +392,42 @@ fn needs_space_before_range(context: &RewriteContext, lhs: &ast::Expr) -> bool { }) } -#[derive(new, Clone, Copy)] -pub struct PairParts<'a> { - prefix: &'a str, - infix: &'a str, - suffix: &'a str, -} - -pub fn rewrite_pair( - lhs: &LHS, - rhs: &RHS, - pp: PairParts, - context: &RewriteContext, - shape: Shape, - separator_place: SeparatorPlace, -) -> Option -where - LHS: Rewrite, - RHS: Rewrite, -{ - let lhs_overhead = match separator_place { - SeparatorPlace::Back => shape.used_width() + pp.prefix.len() + pp.infix.trim_right().len(), - SeparatorPlace::Front => shape.used_width(), - }; - let lhs_shape = Shape { - width: context.budget(lhs_overhead), - ..shape - }; - let lhs_result = lhs.rewrite(context, lhs_shape) - .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?; - - // Try to the both lhs and rhs on the same line. - let rhs_orig_result = shape - .offset_left(last_line_width(&lhs_result) + pp.infix.len()) - .and_then(|s| s.sub_width(pp.suffix.len())) - .and_then(|rhs_shape| rhs.rewrite(context, rhs_shape)); - if let Some(ref rhs_result) = rhs_orig_result { - // If the rhs looks like block expression, we allow it to stay on the same line - // with the lhs even if it is multi-lined. - let allow_same_line = rhs_result - .lines() - .next() - .map(|first_line| first_line.ends_with('{')) - .unwrap_or(false); - if !rhs_result.contains('\n') || allow_same_line { - let one_line_width = last_line_width(&lhs_result) + pp.infix.len() - + first_line_width(rhs_result) + pp.suffix.len(); - if one_line_width <= shape.width { - return Some(format!( - "{}{}{}{}", - lhs_result, pp.infix, rhs_result, pp.suffix - )); - } - } - } - - // We have to use multiple lines. - // Re-evaluate the rhs because we have more space now: - let mut rhs_shape = match context.config.indent_style() { - IndentStyle::Visual => shape - .sub_width(pp.suffix.len() + pp.prefix.len())? - .visual_indent(pp.prefix.len()), - IndentStyle::Block => { - // Try to calculate the initial constraint on the right hand side. - let rhs_overhead = shape.rhs_overhead(context.config); - Shape::indented(shape.indent.block_indent(context.config), context.config) - .sub_width(rhs_overhead)? - } - }; - let infix = match separator_place { - SeparatorPlace::Back => pp.infix.trim_right(), - SeparatorPlace::Front => pp.infix.trim_left(), - }; - if separator_place == SeparatorPlace::Front { - rhs_shape = rhs_shape.offset_left(infix.len())?; - } - let rhs_result = rhs.rewrite(context, rhs_shape)?; - let indent_str = rhs_shape.indent.to_string_with_newline(context.config); - let infix_with_sep = match separator_place { - SeparatorPlace::Back => format!("{}{}", infix, indent_str), - SeparatorPlace::Front => format!("{}{}", indent_str, infix), - }; - Some(format!( - "{}{}{}{}", - lhs_result, infix_with_sep, rhs_result, pp.suffix - )) -} - pub fn rewrite_array( + name: &str, exprs: &[&T], span: Span, context: &RewriteContext, shape: Shape, - trailing_comma: bool, + force_separator_tactic: Option, + delim_token: Option, ) -> Option { - let bracket_size = if context.config.spaces_within_parens_and_brackets() { - 2 // "[ " - } else { - 1 // "[" - }; - - let nested_shape = match context.config.indent_style() { - IndentStyle::Block => shape - .block() - .block_indent(context.config.tab_spaces()) - .with_max_width(context.config) - .sub_width(1)?, - IndentStyle::Visual => shape - .visual_indent(bracket_size) - .sub_width(bracket_size * 2)?, - }; - - let items = itemize_list( - context.snippet_provider, - exprs.iter(), - "]", - ",", - |item| item.span().lo(), - |item| item.span().hi(), - |item| item.rewrite(context, nested_shape), - span.lo(), - span.hi(), - false, - ).collect::>(); - - if items.is_empty() { - if context.config.spaces_within_parens_and_brackets() { - return Some("[ ]".to_string()); - } else { - return Some("[]".to_string()); - } - } - - let tactic = array_tactic(context, shape, nested_shape, exprs, &items, bracket_size); - let ends_with_newline = tactic.ends_with_newline(context.config.indent_style()); - - let fmt = ListFormatting { - tactic, - separator: ",", - trailing_separator: if trailing_comma { - SeparatorTactic::Always - } else if context.inside_macro && !exprs.is_empty() { - let ends_with_bracket = context.snippet(span).ends_with(']'); - let bracket_offset = if ends_with_bracket { 1 } else { 0 }; - let snippet = context.snippet(mk_sp(span.lo(), span.hi() - BytePos(bracket_offset))); - let last_char_index = snippet.rfind(|c: char| !c.is_whitespace())?; - if &snippet[last_char_index..last_char_index + 1] == "," { - SeparatorTactic::Always - } else { - SeparatorTactic::Never - } - } else if context.config.indent_style() == IndentStyle::Visual { - SeparatorTactic::Never - } else { - SeparatorTactic::Vertical - }, - separator_place: SeparatorPlace::Back, - shape: nested_shape, - ends_with_newline, - preserve_newline: false, - config: context.config, - }; - let list_str = write_list(&items, &fmt)?; - - let result = if context.config.indent_style() == IndentStyle::Visual - || tactic == DefinitiveListTactic::Horizontal - { - if context.config.spaces_within_parens_and_brackets() && !list_str.is_empty() { - format!("[ {} ]", list_str) - } else { - format!("[{}]", list_str) - } - } else { - format!( - "[{}{}{}]", - nested_shape.indent.to_string_with_newline(context.config), - list_str, - shape.block().indent.to_string_with_newline(context.config) - ) - }; - - Some(result) -} - -fn array_tactic( - context: &RewriteContext, - shape: Shape, - nested_shape: Shape, - exprs: &[&T], - items: &[ListItem], - bracket_size: usize, -) -> DefinitiveListTactic { - let has_long_item = items - .iter() - .any(|li| li.item.as_ref().map(|s| s.len() > 10).unwrap_or(false)); - - match context.config.indent_style() { - IndentStyle::Block => { - let tactic = match shape.width.checked_sub(2 * bracket_size) { - Some(width) => { - let tactic = ListTactic::LimitedHorizontalVertical( - context.config.width_heuristics().array_width, - ); - definitive_tactic(items, tactic, Separator::Comma, width) - } - None => DefinitiveListTactic::Vertical, - }; - if tactic == DefinitiveListTactic::Vertical && !has_long_item - && is_every_expr_simple(exprs) - { - DefinitiveListTactic::Mixed - } else { - tactic - } - } - IndentStyle::Visual => { - if has_long_item || items.iter().any(ListItem::is_multiline) { - definitive_tactic( - items, - ListTactic::LimitedHorizontalVertical( - context.config.width_heuristics().array_width, - ), - Separator::Comma, - nested_shape.width, - ) - } else { - DefinitiveListTactic::Mixed - } - } - } -} - -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 - && (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() == block_str.len() - 2) - { - "{}".to_owned() - } else { - block_str.to_owned() - } - }) + overflow::rewrite_with_square_brackets( + context, + name, + exprs, + shape, + span, + force_separator_tactic, + delim_token, + ) } fn rewrite_empty_block( context: &RewriteContext, block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + prefix: &str, shape: Shape, ) -> Option { + let label_str = rewrite_label(label); + if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) { + return None; + } + if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) && shape.width >= 2 { - return Some("{}".to_owned()); + return Some(format!("{}{}{{}}", prefix, label_str)); } // If a block contains only a single-line comment, then leave it on one line. @@ -576,10 +435,12 @@ fn rewrite_empty_block( let user_str = user_str.trim(); if user_str.starts_with('{') && user_str.ends_with('}') { let comment_str = user_str[1..user_str.len() - 1].trim(); - if block.stmts.is_empty() && !comment_str.contains('\n') && !comment_str.starts_with("//") + if block.stmts.is_empty() + && !comment_str.contains('\n') + && !comment_str.starts_with("//") && comment_str.len() + 4 <= shape.width { - return Some(format!("{{ {} }}", comment_str)); + return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str)); } } @@ -618,12 +479,15 @@ fn rewrite_single_line_block( context: &RewriteContext, prefix: &str, block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, shape: Shape, ) -> Option { - if is_simple_block(block, context.codemap) { + if is_simple_block(block, attrs, context.codemap) { let expr_shape = shape.offset_left(last_line_width(prefix))?; let expr_str = block.stmts[0].rewrite(context, expr_shape)?; - let result = format!("{}{{ {} }}", prefix, expr_str); + let label_str = rewrite_label(label); + let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); if result.len() <= shape.width && !result.contains('\n') { return Some(result); } @@ -635,16 +499,18 @@ pub fn rewrite_block_with_visitor( context: &RewriteContext, prefix: &str, block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, shape: Shape, has_braces: bool, ) -> Option { - if let rw @ Some(_) = rewrite_empty_block(context, block, shape) { + if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { return rw; } let mut visitor = FmtVisitor::from_context(context); visitor.block_indent = shape.indent; - visitor.is_if_else_block = context.is_if_else_block; + visitor.is_if_else_block = context.is_if_else_block(); match block.rules { ast::BlockCheckMode::Unsafe(..) => { let snippet = context.snippet(block.span); @@ -654,31 +520,45 @@ pub fn rewrite_block_with_visitor( ast::BlockCheckMode::Default => visitor.last_pos = block.span.lo(), } - visitor.visit_block(block, None, has_braces); - Some(format!("{}{}", prefix, visitor.buffer)) + let inner_attrs = attrs.map(inner_attributes); + let label_str = rewrite_label(label); + visitor.visit_block(block, inner_attrs.as_ref().map(|a| &**a), has_braces); + Some(format!("{}{}{}", prefix, label_str, visitor.buffer)) } impl Rewrite for ast::Block { fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { - // shape.width is used only for the single line case: either the empty block `{}`, - // or an unsafe expression `unsafe { e }`. - if let rw @ Some(_) = rewrite_empty_block(context, self, shape) { - return rw; - } + rewrite_block(self, None, None, context, shape) + } +} - let prefix = block_prefix(context, self, shape)?; +fn rewrite_block( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + label: Option, + context: &RewriteContext, + shape: Shape, +) -> Option { + let prefix = block_prefix(context, block, shape)?; - let result = rewrite_block_with_visitor(context, &prefix, self, shape, true); - if let Some(ref result_str) = result { - if result_str.lines().count() <= 3 { - if let rw @ Some(_) = rewrite_single_line_block(context, &prefix, self, shape) { - return rw; - } + // shape.width is used only for the single line case: either the empty block `{}`, + // or an unsafe expression `unsafe { e }`. + if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { + return rw; + } + + let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true); + if let Some(ref result_str) = result { + if result_str.lines().count() <= 3 { + if let rw @ Some(_) = + rewrite_single_line_block(context, &prefix, block, attrs, label, shape) + { + return rw; } } - - result } + + result } impl Rewrite for ast::Stmt { @@ -888,9 +768,9 @@ fn rewrite_single_line( let else_block = self.else_block?; let fixed_cost = self.keyword.len() + " { } else { }".len(); - if let ast::ExprKind::Block(ref else_node) = else_block.node { - if !is_simple_block(self.block, context.codemap) - || !is_simple_block(else_node, context.codemap) + if let ast::ExprKind::Block(ref else_node, _) = else_block.node { + if !is_simple_block(self.block, None, context.codemap) + || !is_simple_block(else_node, None, context.codemap) || pat_expr_str.contains('\n') { return None; @@ -1003,8 +883,7 @@ fn rewrite_cond( let one_line_budget = context .config .max_width() - .checked_sub(constr_shape.used_width() + offset + brace_overhead) - .unwrap_or(0); + .saturating_sub(constr_shape.used_width() + offset + brace_overhead); let force_newline_brace = (pat_expr_string.contains('\n') || pat_expr_string.len() > one_line_budget) && !last_line_extendable(&pat_expr_string); @@ -1014,16 +893,16 @@ fn rewrite_cond( && context .config .width_heuristics() - .single_line_if_else_max_width > 0 + .single_line_if_else_max_width + > 0 { let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width); if let Some(cond_str) = trial { - if cond_str.len() - <= context - .config - .width_heuristics() - .single_line_if_else_max_width + if cond_str.len() <= context + .config + .width_heuristics() + .single_line_if_else_max_width { return Some((cond_str, 0)); } @@ -1038,7 +917,9 @@ fn rewrite_cond( // `for event in event` // Do not include label in the span. - let lo = self.label.map_or(self.span.lo(), |label| label.span.hi()); + let lo = self + .label + .map_or(self.span.lo(), |label| label.ident.span.hi()); let between_kwd_cond = mk_sp( context .snippet_provider @@ -1108,7 +989,7 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { return Some(cond_str); } - let block_width = shape.width.checked_sub(used_width).unwrap_or(0); + let block_width = shape.width.saturating_sub(used_width); // This is used only for the empty block case: `{}`. So, we use 1 if we know // we should avoid the single line case. let block_width = if self.else_block.is_some() || self.nested_if { @@ -1120,10 +1001,13 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { width: block_width, ..shape }; - let mut block_context = context.clone(); - block_context.is_if_else_block = self.else_block.is_some(); - let block_str = - rewrite_block_with_visitor(&block_context, "", self.block, block_shape, true)?; + let block_str = { + let old_val = context.is_if_else_block.replace(self.else_block.is_some()); + let result = + rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true); + context.is_if_else_block.replace(old_val); + result? + }; let mut result = format!("{}{}", cond_str, block_str); @@ -1234,22 +1118,43 @@ pub fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool { contains_comment(&snippet) } -// Checks that a block contains no statements, an expression and no comments. +// Checks that a block contains no statements, an expression and no comments or +// attributes. // FIXME: incorrectly returns false when comment is contained completely within // the expression. -pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool { - (block.stmts.len() == 1 && stmt_is_expr(&block.stmts[0]) - && !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.len() <= 1 && !block_contains_comment(block, codemap) +pub fn is_simple_block( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + codemap: &CodeMap, +) -> bool { + (block.stmts.len() == 1 + && stmt_is_expr(&block.stmts[0]) + && !block_contains_comment(block, codemap) + && attrs.map_or(true, |a| a.is_empty())) +} + +/// Checks whether a block contains at most one statement or expression, and no +/// comments or attributes. +pub fn is_simple_block_stmt( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + codemap: &CodeMap, +) -> bool { + block.stmts.len() <= 1 + && !block_contains_comment(block, codemap) + && attrs.map_or(true, |a| a.is_empty()) } -/// 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_contains_comment(block, codemap) +/// Checks whether a block contains no statements, expressions, comments, or +/// inner attributes. +pub fn is_empty_block( + block: &ast::Block, + attrs: Option<&[ast::Attribute]>, + codemap: &CodeMap, +) -> bool { + block.stmts.is_empty() + && !block_contains_comment(block, codemap) + && attrs.map_or(true, |a| inner_attributes(a).is_empty()) } pub fn stmt_is_expr(stmt: &ast::Stmt) -> bool { @@ -1267,507 +1172,18 @@ pub fn is_unsafe_block(block: &ast::Block) -> bool { } } -/// 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, - /// Holds a byte position of `|` at the beginning of the arm pattern, if available. - pub beginning_vert: Option, -} - -impl<'a> ArmWrapper<'a> { - pub fn new( - arm: &'a ast::Arm, - is_last: bool, - beginning_vert: Option, - ) -> ArmWrapper<'a> { - ArmWrapper { - arm, - is_last, - beginning_vert, - } - } -} - -impl<'a> Spanned for ArmWrapper<'a> { - fn span(&self) -> Span { - if let Some(lo) = self.beginning_vert { - mk_sp(lo, self.arm.span().hi()) - } else { - self.arm.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) - } -} - -fn rewrite_match( - context: &RewriteContext, - cond: &ast::Expr, - arms: &[ast::Arm], - shape: Shape, - span: Span, - attrs: &[ast::Attribute], -) -> Option { - // Do not take the rhs overhead from the upper expressions into account - // when rewriting match condition. - let cond_shape = Shape { - width: context.budget(shape.used_width()), - ..shape - }; - // 6 = `match ` - let cond_shape = match context.config.indent_style() { - IndentStyle::Visual => cond_shape.shrink_left(6)?, - IndentStyle::Block => cond_shape.offset_left(6)?, - }; - let cond_str = cond.rewrite(context, cond_shape)?; - let alt_block_sep = &shape.indent.to_string_with_newline(context.config); - let block_sep = match context.config.control_brace_style() { - ControlBraceStyle::AlwaysNextLine => alt_block_sep, - _ if last_line_extendable(&cond_str) => " ", - // 2 = ` {` - _ if cond_str.contains('\n') || cond_str.len() + 2 > cond_shape.width => alt_block_sep, - _ => " ", - }; - - let nested_indent_str = shape - .indent - .block_indent(context.config) - .to_string(context.config); - // Inner attributes. - let inner_attrs = &inner_attributes(attrs); - let inner_attrs_str = if inner_attrs.is_empty() { - String::new() - } else { - inner_attrs - .rewrite(context, shape) - .map(|s| format!("{}{}\n", nested_indent_str, s))? - }; - - let open_brace_pos = if inner_attrs.is_empty() { - let hi = if arms.is_empty() { - span.hi() - } else { - arms[0].span().lo() - }; - context - .snippet_provider - .span_after(mk_sp(cond.span.hi(), hi), "{") - } else { - inner_attrs[inner_attrs.len() - 1].span().hi() - }; - - if arms.is_empty() { - let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); - if snippet.trim().is_empty() { - Some(format!("match {} {{}}", cond_str)) - } else { - // Empty match with comments or inner attributes? We are not going to bother, sorry ;) - Some(context.snippet(span).to_owned()) - } - } else { - 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)?, - shape.indent.to_string(context.config), - )) - } -} - -fn arm_comma(config: &Config, body: &ast::Expr, is_last: bool) -> &'static str { - if is_last && config.trailing_comma() == SeparatorTactic::Never { - "" - } else if config.match_block_trailing_comma() { - "," - } else if let ast::ExprKind::Block(ref block) = body.node { - if let ast::BlockCheckMode::Default = block.rules { - "" - } else { - "," - } - } else { - "," - } -} - -/// Collect a byte position of the beginning `|` for each arm, if available. -fn collect_beginning_verts( - 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 -} - -fn rewrite_match_arms( - context: &RewriteContext, - arms: &[ast::Arm], - shape: Shape, - span: Span, - open_brace_pos: BytePos, -) -> Option { - let arm_shape = shape - .block_indent(context.config.tab_spaces()) - .with_max_width(context.config); - - let arm_len = arms.len(); - let is_last_iter = repeat(false) - .take(arm_len.checked_sub(1).unwrap_or(0)) - .chain(repeat(true)); - let beginning_verts = collect_beginning_verts(context, arms, span); - let items = itemize_list( - context.snippet_provider, - arms.iter() - .zip(is_last_iter) - .zip(beginning_verts.into_iter()) - .map(|((arm, is_last), beginning_vert)| ArmWrapper::new(arm, is_last, beginning_vert)), - "}", - "|", - |arm| arm.span().lo(), - |arm| arm.span().hi(), - |arm| arm.rewrite(context, arm_shape), - open_brace_pos, - span.hi(), - 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, - config: context.config, - }; - - write_list(&arms_vec, &fmt) -} - -fn rewrite_match_arm( - context: &RewriteContext, - 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) { - let (_, body) = flatten_arm_body(context, &arm.body); - // `arm.span()` does not include trailing comma, add it manually. - return Some(format!( - "{}{}", - context.snippet(arm.span()), - 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(), - ); - (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, - beginning_vert.is_some(), - shape, - ).and_then(|pats_str| { - combine_strs_with_missing_comments( - context, - &attrs_str, - &pats_str, - missing_span, - shape, - false, - ) - })?; - rewrite_match_body( - context, - &arm.body, - &pats_str, - shape, - arm.guard.is_some(), - is_last, - ) -} - -/// Returns true if the given pattern is short. A short pattern is defined by the following grammer: -/// -/// [small, ntp]: -/// - single token -/// - `&[single-line, ntp]` -/// -/// [small]: -/// - `[small, ntp]` -/// - unary tuple constructor `([small, ntp])` -/// - `&[small]` -fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool { - // We also require that the pattern is reasonably 'small' with its literal width. - pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(pat) -} - -fn is_short_pattern_inner(pat: &ast::Pat) -> bool { - match pat.node { - ast::PatKind::Wild | ast::PatKind::Lit(_) => true, - ast::PatKind::Ident(_, _, ref pat) => pat.is_none(), - ast::PatKind::Struct(..) - | ast::PatKind::Mac(..) - | ast::PatKind::Slice(..) - | ast::PatKind::Path(..) - | ast::PatKind::Range(..) => false, - ast::PatKind::Tuple(ref subpats, _) => subpats.len() <= 1, - ast::PatKind::TupleStruct(ref path, ref subpats, _) => { - path.segments.len() <= 1 && subpats.len() <= 1 - } - ast::PatKind::Box(ref p) | ast::PatKind::Ref(ref p, _) | ast::PatKind::Paren(ref p) => { - is_short_pattern_inner(&*p) - } - } -} - -fn rewrite_match_pattern( +pub fn rewrite_multiple_patterns( 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 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))?; - - Some(format!("{}{}{}", beginning_vert, pats_str, guard_str)) -} - -// (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, 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, - ) - } else { - (false, &*body) - } - } - _ => ( - !context.config.force_multiline_blocks() && body.can_be_overflowed(context, 1), - &*body, - ), - } -} - -fn rewrite_match_body( - context: &RewriteContext, - body: &ptr::P, - pats_str: &str, - shape: Shape, - has_guard: bool, - 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, context.codemap)) - } else { - (false, false) - }; - - let comma = arm_comma(context.config, body, is_last); - let alt_block_sep = &shape.indent.to_string_with_newline(context.config); - - let combine_orig_body = |body_str: &str| { - let block_sep = match context.config.control_brace_style() { - ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep, - _ => " ", - }; - - 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 combine_next_line_body = |body_str: &str| { - if is_block { - return Some(format!( - "{} =>{}{}", - pats_str, - next_line_indent.to_string_with_newline(context.config), - body_str - )); - } - - 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() { - "," - } else { - "" - }; - ("{", 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), - _ => format!(" {}", body_prefix), - } + &nested_indent_str; - - Some(format!( - "{} =>{}{}{}", - pats_str, block_sep, body_str, body_suffix - )) - }; - - // Let's try and get the arm body on the same line as the condition. - // 4 = ` => `.len() - 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 rewrite = nop_block_collapse( - format_expr(body, ExprType::Statement, context, body_shape), - body_shape.width, - ); - - match rewrite { - Some(ref body_str) - if !forbid_same_line - && (is_block - || (!body_str.contains('\n') && body_str.len() <= body_shape.width)) => - { - return combine_orig_body(body_str); - } - _ => rewrite, - } - } else { - None - }; - let orig_budget = orig_body_shape.map_or(0, |shape| shape.width); - - // Try putting body on the next line and see if it looks better. - let next_line_body_shape = Shape::indented(next_line_indent, context.config); - let next_line_body = nop_block_collapse( - format_expr(body, ExprType::Statement, context, next_line_body_shape), - next_line_body_shape.width, - ); - 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) => - { - combine_next_line_body(next_line_str) - } - (Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { - combine_orig_body(orig_str) - } - (Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => { - combine_next_line_body(next_line_str) - } - (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str), - (None, None) => None, - (Some(ref orig_str), _) => combine_orig_body(orig_str), - } -} - -// The `if ...` guard on a match arm. -fn rewrite_guard( - 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, -) -> Option { - if let Some(ref guard) = *guard { - // First try to fit the guard string on the same line as the pattern. - // 4 = ` if `, 5 = ` => {` - 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)); - } - } - } - - // Not enough space to put the guard after the pattern, try a newline. - // 3 = `if `, 5 = ` => {` - let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config) - .offset_left(3) - .and_then(|s| s.sub_width(5)); - if let Some(cond_shape) = cond_shape { - if let Some(cond_str) = guard.rewrite(context, cond_shape) { - return Some(format!( - "{}if {}", - cond_shape.indent.to_string_with_newline(context.config), - cond_str - )); - } - } - - None - } else { - Some(String::new()) - } -} - -fn rewrite_multiple_patterns( - context: &RewriteContext, - pats: &[&ast::Pat], - shape: Shape, -) -> Option { - let pat_strs = pats.iter() + let pat_strs = pats + .iter() .map(|p| p.rewrite(context, shape)) .collect::>>()?; - let use_mixed_layout = pats.iter() + let use_mixed_layout = pats + .iter() .zip(pat_strs.iter()) .all(|(pat, pat_str)| is_short_pattern(pat, pat_str)); let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect(); @@ -1789,38 +1205,12 @@ fn rewrite_multiple_patterns( shape, ends_with_newline: false, preserve_newline: false, + nested: false, config: context.config, }; write_list(&items, &fmt) } -fn can_extend_match_arm_body(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(..) - | ast::ExprKind::Match(..) - | ast::ExprKind::Block(..) - | ast::ExprKind::Closure(..) - | ast::ExprKind::Array(..) - | ast::ExprKind::Call(..) - | ast::ExprKind::MethodCall(..) - | ast::ExprKind::Mac(..) - | ast::ExprKind::Struct(..) - | ast::ExprKind::Tup(..) => true, - ast::ExprKind::AddrOf(_, ref expr) - | 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), - _ => false, - } -} - pub fn rewrite_literal(context: &RewriteContext, l: &ast::Lit, shape: Shape) -> Option { match l.node { ast::LitKind::Str(_, ast::StrStyle::Cooked) => rewrite_string_lit(context, l.span, shape), @@ -1852,8 +1242,7 @@ fn rewrite_string_lit(context: &RewriteContext, span: Span, shape: Shape) -> Opt new_indent.to_string(context.config), line.trim_left() ) - }) - .collect::>() + }).collect::>() .join("\n") .trim_left(), ); @@ -1869,7 +1258,6 @@ fn rewrite_string_lit(context: &RewriteContext, span: Span, shape: Shape) -> Opt rewrite_string( str_lit, &StringFormat::new(shape.visual_indent(0), context.config), - None, ) } @@ -1921,6 +1309,18 @@ pub fn maybe_get_args_offset(callee_str: &str, args: &[&T]) -> Option ("debug_assert_ne!", 2), ]; +fn choose_separator_tactic(context: &RewriteContext, span: Span) -> Option { + if context.inside_macro() { + if span_ends_with_comma(context, span) { + Some(SeparatorTactic::Always) + } else { + Some(SeparatorTactic::Never) + } + } else { + None + } +} + pub fn rewrite_call( context: &RewriteContext, callee: &str, @@ -1935,15 +1335,7 @@ pub fn rewrite_call( shape, span, context.config.width_heuristics().fn_call_width, - if context.inside_macro { - if span_ends_with_comma(context, span) { - Some(SeparatorTactic::Always) - } else { - Some(SeparatorTactic::Never) - } - } else { - None - }, + choose_separator_tactic(context, span), ) } @@ -1956,16 +1348,16 @@ fn is_simple_expr(expr: &ast::Expr) -> bool { | ast::ExprKind::Cast(ref expr, _) | ast::ExprKind::Field(ref expr, _) | ast::ExprKind::Try(ref expr) - | ast::ExprKind::TupField(ref expr, _) | ast::ExprKind::Unary(_, ref expr) => is_simple_expr(expr), - ast::ExprKind::Index(ref lhs, ref rhs) | ast::ExprKind::Repeat(ref lhs, ref rhs) => { - is_simple_expr(lhs) && is_simple_expr(rhs) + ast::ExprKind::Index(ref lhs, ref rhs) => is_simple_expr(lhs) && is_simple_expr(rhs), + ast::ExprKind::Repeat(ref lhs, ref rhs) => { + is_simple_expr(lhs) && is_simple_expr(&*rhs.value) } _ => false, } } -fn is_every_expr_simple(lists: &[&T]) -> bool { +pub fn is_every_expr_simple(lists: &[&T]) -> bool { lists .iter() .all(|arg| arg.to_expr().map_or(false, is_simple_expr)) @@ -2022,11 +1414,14 @@ pub fn is_nested_call(expr: &ast::Expr) -> bool { pub fn span_ends_with_comma(context: &RewriteContext, span: Span) -> bool { let mut result: bool = Default::default(); let mut prev_char: char = Default::default(); + let closing_delimiters = &[')', '}', ']']; for (kind, c) in CharClasses::new(context.snippet(span).chars()) { match c { _ if kind.is_comment() || c.is_whitespace() => continue, - ')' | '}' => result = result && prev_char != c, + c if closing_delimiters.contains(&c) => { + result &= !closing_delimiters.contains(&prev_char); + } ',' => result = true, _ => result = false, } @@ -2047,6 +1442,7 @@ fn rewrite_paren( // Extract comments within parens. let mut pre_comment; let mut post_comment; + let remove_nested_parens = context.config.remove_nested_parens(); loop { // 1 = "(" or ")" let pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span.lo()); @@ -2056,7 +1452,7 @@ fn rewrite_paren( // Remove nested parens if there are no comments. if let ast::ExprKind::Paren(ref subsubexpr) = subexpr.node { - if pre_comment.is_empty() && post_comment.is_empty() { + if remove_nested_parens && pre_comment.is_empty() && post_comment.is_empty() { span = subexpr.span; subexpr = subsubexpr; continue; @@ -2066,27 +1462,15 @@ fn rewrite_paren( break; } - let total_paren_overhead = paren_overhead(context); - let paren_overhead = total_paren_overhead / 2; - let sub_shape = shape - .offset_left(paren_overhead) - .and_then(|s| s.sub_width(paren_overhead))?; - - let paren_wrapper = |s: &str| { - if context.config.spaces_within_parens_and_brackets() && !s.is_empty() { - format!("( {}{}{} )", pre_comment, s, post_comment) - } else { - format!("({}{}{})", pre_comment, s, post_comment) - } - }; + // 1 `(` + let sub_shape = shape.offset_left(1).and_then(|s| s.sub_width(1))?; let subexpr_str = subexpr.rewrite(context, sub_shape)?; debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str); - if subexpr_str.contains('\n') - || first_line_width(&subexpr_str) + total_paren_overhead <= shape.width - { - Some(paren_wrapper(&subexpr_str)) + // 2 = `()` + if subexpr_str.contains('\n') || first_line_width(&subexpr_str) + 2 <= shape.width { + Some(format!("({}{}{})", pre_comment, &subexpr_str, post_comment)) } else { None } @@ -2100,54 +1484,44 @@ fn rewrite_index( ) -> Option { let expr_str = expr.rewrite(context, shape)?; - let (lbr, rbr) = if context.config.spaces_within_parens_and_brackets() { - ("[ ", " ]") - } else { - ("[", "]") - }; - - let offset = last_line_width(&expr_str) + lbr.len(); + let offset = last_line_width(&expr_str) + 1; let rhs_overhead = shape.rhs_overhead(context.config); let index_shape = if expr_str.contains('\n') { Shape::legacy(context.config.max_width(), shape.indent) .offset_left(offset) - .and_then(|shape| shape.sub_width(rbr.len() + rhs_overhead)) + .and_then(|shape| shape.sub_width(1 + rhs_overhead)) } else { - shape.visual_indent(offset).sub_width(offset + rbr.len()) + shape.visual_indent(offset).sub_width(offset + 1) }; let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s)); // Return if index fits in a single line. match orig_index_rw { Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr)); + return Some(format!("{}[{}]", expr_str, index_str)); } _ => (), } // Try putting index on the next line and see if it fits in a single line. let indent = shape.indent.block_indent(context.config); - let index_shape = Shape::indented(indent, context.config).offset_left(lbr.len())?; - let index_shape = index_shape.sub_width(rbr.len() + rhs_overhead)?; + let index_shape = Shape::indented(indent, context.config).offset_left(1)?; + let index_shape = index_shape.sub_width(1 + rhs_overhead)?; let new_index_rw = index.rewrite(context, index_shape); match (orig_index_rw, new_index_rw) { (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!( - "{}{}{}{}{}", + "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), - lbr, new_index_str, - rbr )), (None, Some(ref new_index_str)) => Some(format!( - "{}{}{}{}{}", + "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), - lbr, new_index_str, - rbr )), - (Some(ref index_str), _) => Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr)), + (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)), _ => None, } } @@ -2247,11 +1621,7 @@ enum StructLitField<'a> { let nested_shape = shape_for_tactic(tactic, h_shape, v_shape); let ends_with_comma = span_ends_with_comma(context, span); - let force_no_trailing_comma = if context.inside_macro && !ends_with_comma { - true - } else { - false - }; + let force_no_trailing_comma = context.inside_macro() && !ends_with_comma; let fmt = struct_lit_formatting( nested_shape, @@ -2278,7 +1648,8 @@ pub fn wrap_struct_field( one_line_width: usize, ) -> String { if context.config.indent_style() == IndentStyle::Block - && (fields_str.contains('\n') || !context.config.struct_lit_single_line() + && (fields_str.contains('\n') + || !context.config.struct_lit_single_line() || fields_str.len() > one_line_width) { format!( @@ -2310,12 +1681,12 @@ pub fn rewrite_field( if !attrs_str.is_empty() { attrs_str.push_str(&shape.indent.to_string_with_newline(context.config)); }; - let name = field.ident.node.to_string(); + let name = context.snippet(field.ident.span); if field.is_shorthand { Some(attrs_str + &name) } else { let mut separator = String::from(struct_lit_field_separator(context.config)); - for _ in 0..prefix_max_width.checked_sub(name.len()).unwrap_or(0) { + for _ in 0..prefix_max_width.saturating_sub(name.len()) { separator.push(' '); } let overhead = name.len() + separator.len(); @@ -2365,13 +1736,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T>( .next() .unwrap() .rewrite(context, nested_shape) - .map(|s| { - if context.config.spaces_within_parens_and_brackets() { - format!("( {}, )", s) - } else { - format!("({},)", s) - } - }); + .map(|s| format!("({},)", s)); } let list_lo = context.snippet_provider.span_after(span, "("); @@ -2403,15 +1768,12 @@ fn rewrite_tuple_in_visual_indent_style<'a, T>( shape, ends_with_newline: false, preserve_newline: false, + nested: false, config: context.config, }; let list_str = write_list(&item_vec, &fmt)?; - if context.config.spaces_within_parens_and_brackets() && !list_str.is_empty() { - Some(format!("( {} )", list_str)) - } else { - Some(format!("({})", list_str)) - } + Some(format!("({})", list_str)) } pub fn rewrite_tuple<'a, T>( @@ -2426,18 +1788,16 @@ pub fn rewrite_tuple<'a, T>( debug!("rewrite_tuple {:?}", shape); if context.use_block_indent() { // We use the same rule as function calls for rewriting tuples. - let force_tactic = if context.inside_macro { + let force_tactic = if context.inside_macro() { if span_ends_with_comma(context, span) { Some(SeparatorTactic::Always) } else { Some(SeparatorTactic::Never) } + } else if items.len() == 1 { + Some(SeparatorTactic::Always) } else { - if items.len() == 1 { - Some(SeparatorTactic::Always) - } else { - None - } + None }; overflow::rewrite_with_parens( context, @@ -2514,6 +1874,15 @@ fn rewrite_assignment( rewrite_assign_rhs(context, lhs_str, rhs, shape) } +/// Controls where to put the rhs. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RhsTactics { + /// Use heuristics. + Default, + /// Put the rhs on the next line if it uses multiple line, without extra indentation. + ForceNextLineWithoutIndent, +} + // The left hand side must contain everything up to, and including, the // assignment operator. pub fn rewrite_assign_rhs, R: Rewrite>( @@ -2521,30 +1890,45 @@ pub fn rewrite_assign_rhs, R: Rewrite>( lhs: S, ex: &R, shape: Shape, +) -> Option { + rewrite_assign_rhs_with(context, lhs, ex, shape, RhsTactics::Default) +} + +pub fn rewrite_assign_rhs_with, R: Rewrite>( + context: &RewriteContext, + lhs: S, + ex: &R, + shape: Shape, + rhs_tactics: RhsTactics, ) -> Option { let lhs = lhs.into(); - let last_line_width = last_line_width(&lhs) - .checked_sub(if lhs.contains('\n') { - shape.indent.width() - } else { - 0 - }) - .unwrap_or(0); + let last_line_width = last_line_width(&lhs).saturating_sub(if lhs.contains('\n') { + shape.indent.width() + } else { + 0 + }); // 1 = space between operator and rhs. let orig_shape = shape.offset_left(last_line_width + 1).unwrap_or(Shape { width: 0, offset: shape.offset + last_line_width + 1, ..shape }); - let rhs = choose_rhs(context, ex, orig_shape, ex.rewrite(context, orig_shape))?; + let rhs = choose_rhs( + context, + ex, + orig_shape, + ex.rewrite(context, orig_shape), + rhs_tactics, + )?; Some(lhs + &rhs) } -pub fn choose_rhs( +fn choose_rhs( context: &RewriteContext, expr: &R, shape: Shape, orig_rhs: Option, + rhs_tactics: RhsTactics, ) -> Option { match orig_rhs { Some(ref new_str) if !new_str.contains('\n') && new_str.len() <= shape.width => { @@ -2553,11 +1937,12 @@ pub fn choose_rhs( _ => { // Expression did not fit on the same line as the identifier. // Try splitting the line and see if that works better. - let new_shape = - Shape::indented(shape.indent.block_indent(context.config), context.config) - .sub_width(shape.rhs_overhead(context.config))?; + let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?; let new_rhs = expr.rewrite(context, new_shape); - let new_indent_str = &new_shape.indent.to_string_with_newline(context.config); + let new_indent_str = &shape + .indent + .block_indent(context.config) + .to_string_with_newline(context.config); match (orig_rhs, new_rhs) { (Some(ref orig_rhs), Some(ref new_rhs)) @@ -2566,19 +1951,40 @@ pub fn choose_rhs( { Some(format!(" {}", orig_rhs)) } - (Some(ref orig_rhs), Some(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs) => { + (Some(ref orig_rhs), Some(ref new_rhs)) + if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => + { Some(format!("{}{}", new_indent_str, new_rhs)) } (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)), (None, None) => None, - (Some(ref orig_rhs), _) => Some(format!(" {}", orig_rhs)), + (Some(orig_rhs), _) => Some(format!(" {}", orig_rhs)), } } } } -fn prefer_next_line(orig_rhs: &str, next_line_rhs: &str) -> bool { - !next_line_rhs.contains('\n') || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1 +fn shape_from_rhs_tactic( + context: &RewriteContext, + shape: Shape, + rhs_tactic: RhsTactics, +) -> Option { + match rhs_tactic { + RhsTactics::ForceNextLineWithoutIndent => Some(shape.with_max_width(context.config)), + RhsTactics::Default => { + Shape::indented(shape.indent.block_indent(context.config), context.config) + .sub_width(shape.rhs_overhead(context.config)) + } + } +} + +pub fn prefer_next_line(orig_rhs: &str, next_line_rhs: &str, rhs_tactics: RhsTactics) -> bool { + rhs_tactics == RhsTactics::ForceNextLineWithoutIndent + || !next_line_rhs.contains('\n') + || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1 + || first_line_ends_with(orig_rhs, '(') && !first_line_ends_with(next_line_rhs, '(') + || first_line_ends_with(orig_rhs, '{') && !first_line_ends_with(next_line_rhs, '{') + || first_line_ends_with(orig_rhs, '[') && !first_line_ends_with(next_line_rhs, '[') } fn rewrite_expr_addrof( @@ -2666,3 +2072,15 @@ fn can_be_overflowed(&self, _: &RewriteContext, _: usize) -> bool { false } } + +pub fn is_method_call(expr: &ast::Expr) -> bool { + match expr.node { + ast::ExprKind::MethodCall(..) => true, + ast::ExprKind::AddrOf(_, ref expr) + | ast::ExprKind::Box(ref expr) + | ast::ExprKind::Cast(ref expr, _) + | ast::ExprKind::Try(ref expr) + | ast::ExprKind::Unary(_, ref expr) => is_method_call(expr), + _ => false, + } +}