]> git.lizzy.rs Git - rust.git/blob - src/expr.rs
Merge pull request #1551 from topecongiro/else-if-early-line-break
[rust.git] / src / expr.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::cmp::{Ordering, min};
12 use std::mem::swap;
13 use std::ops::Deref;
14 use std::iter::ExactSizeIterator;
15 use std::fmt::Write;
16
17 use {Indent, Shape, Spanned};
18 use codemap::SpanUtils;
19 use rewrite::{Rewrite, RewriteContext};
20 use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
21             DefinitiveListTactic, definitive_tactic, ListItem, format_item_list, struct_lit_shape,
22             struct_lit_tactic, shape_for_tactic, struct_lit_formatting};
23 use string::{StringFormat, rewrite_string};
24 use utils::{extra_offset, last_line_width, wrap_str, binary_search, first_line_width,
25             semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr, stmt_expr,
26             colon_spaces};
27 use visitor::FmtVisitor;
28 use config::{Config, IndentStyle, MultilineStyle, ControlBraceStyle, Style};
29 use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};
30 use types::{rewrite_path, PathContext};
31 use items::{span_lo_for_arg, span_hi_for_arg};
32 use chains::rewrite_chain;
33 use macros::{rewrite_macro, MacroPosition};
34
35 use syntax::{ast, ptr};
36 use syntax::codemap::{CodeMap, Span, BytePos, mk_sp};
37 use syntax::parse::classify;
38
39 impl Rewrite for ast::Expr {
40     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
41         format_expr(self, ExprType::SubExpression, context, shape)
42     }
43 }
44
45 #[derive(PartialEq)]
46 enum ExprType {
47     Statement,
48     SubExpression,
49 }
50
51 fn format_expr(expr: &ast::Expr,
52                expr_type: ExprType,
53                context: &RewriteContext,
54                shape: Shape)
55                -> Option<String> {
56     let result = match expr.node {
57         ast::ExprKind::Array(ref expr_vec) => {
58             rewrite_array(expr_vec.iter().map(|e| &**e),
59                           mk_sp(context.codemap.span_after(expr.span, "["), expr.span.hi),
60                           context,
61                           shape)
62         }
63         ast::ExprKind::Lit(ref l) => {
64             match l.node {
65                 ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
66                     rewrite_string_lit(context, l.span, shape)
67                 }
68                 _ => wrap_str(context.snippet(expr.span), context.config.max_width, shape),
69             }
70         }
71         ast::ExprKind::Call(ref callee, ref args) => {
72             let inner_span = mk_sp(callee.span.hi, expr.span.hi);
73             rewrite_call(context, &**callee, args, inner_span, shape)
74         }
75         ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape),
76         ast::ExprKind::Binary(ref op, ref lhs, ref rhs) => {
77             // FIXME: format comments between operands and operator
78             rewrite_pair(&**lhs,
79                          &**rhs,
80                          "",
81                          &format!(" {} ", context.snippet(op.span)),
82                          "",
83                          context,
84                          shape)
85         }
86         ast::ExprKind::Unary(ref op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
87         ast::ExprKind::Struct(ref path, ref fields, ref base) => {
88             rewrite_struct_lit(context,
89                                path,
90                                fields,
91                                base.as_ref().map(|e| &**e),
92                                expr.span,
93                                shape)
94         }
95         ast::ExprKind::Tup(ref items) => {
96             rewrite_tuple(context, items.iter().map(|x| &**x), expr.span, shape)
97         }
98         ast::ExprKind::While(ref cond, ref block, label) => {
99             ControlFlow::new_while(None, cond, block, label, expr.span).rewrite(context, shape)
100         }
101         ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => {
102             ControlFlow::new_while(Some(pat), cond, block, label, expr.span).rewrite(context, shape)
103         }
104         ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
105             ControlFlow::new_for(pat, cond, block, label, expr.span).rewrite(context, shape)
106         }
107         ast::ExprKind::Loop(ref block, label) => {
108             ControlFlow::new_loop(block, label, expr.span).rewrite(context, shape)
109         }
110         ast::ExprKind::Block(ref block) => block.rewrite(context, shape),
111         ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
112             ControlFlow::new_if(cond,
113                                 None,
114                                 if_block,
115                                 else_block.as_ref().map(|e| &**e),
116                                 expr_type == ExprType::SubExpression,
117                                 false,
118                                 expr.span)
119                     .rewrite(context, shape)
120         }
121         ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref else_block) => {
122             ControlFlow::new_if(cond,
123                                 Some(pat),
124                                 if_block,
125                                 else_block.as_ref().map(|e| &**e),
126                                 expr_type == ExprType::SubExpression,
127                                 false,
128                                 expr.span)
129                     .rewrite(context, shape)
130         }
131         ast::ExprKind::Match(ref cond, ref arms) => {
132             rewrite_match(context, cond, arms, shape, expr.span)
133         }
134         ast::ExprKind::Path(ref qself, ref path) => {
135             rewrite_path(context, PathContext::Expr, qself.as_ref(), path, shape)
136         }
137         ast::ExprKind::Assign(ref lhs, ref rhs) => {
138             rewrite_assignment(context, lhs, rhs, None, shape)
139         }
140         ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
141             rewrite_assignment(context, lhs, rhs, Some(op), shape)
142         }
143         ast::ExprKind::Continue(ref opt_ident) => {
144             let id_str = match *opt_ident {
145                 Some(ident) => format!(" {}", ident.node),
146                 None => String::new(),
147             };
148             wrap_str(format!("continue{}", id_str),
149                      context.config.max_width,
150                      shape)
151         }
152         ast::ExprKind::Break(ref opt_ident, ref opt_expr) => {
153             let id_str = match *opt_ident {
154                 Some(ident) => format!(" {}", ident.node),
155                 None => String::new(),
156             };
157
158             if let Some(ref expr) = *opt_expr {
159                 rewrite_unary_prefix(context,
160                                      &format!("break{} ", id_str),
161                                      &**expr,
162                                      shape,
163                                      expr.span)
164             } else {
165                 wrap_str(format!("break{}", id_str), context.config.max_width, shape)
166             }
167         }
168         ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
169             rewrite_closure(capture, fn_decl, body, expr.span, context, shape)
170         }
171         ast::ExprKind::Try(..) |
172         ast::ExprKind::Field(..) |
173         ast::ExprKind::TupField(..) |
174         ast::ExprKind::MethodCall(..) => rewrite_chain(expr, context, shape),
175         ast::ExprKind::Mac(ref mac) => {
176             // Failure to rewrite a marco should not imply failure to
177             // rewrite the expression.
178             rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
179                 .or_else(|| wrap_str(context.snippet(expr.span), context.config.max_width, shape))
180         }
181         ast::ExprKind::Ret(None) => wrap_str("return".to_owned(), context.config.max_width, shape),
182         ast::ExprKind::Ret(Some(ref expr)) => {
183             rewrite_unary_prefix(context, "return ", &**expr, shape, expr.span)
184         }
185         ast::ExprKind::Box(ref expr) => {
186             rewrite_unary_prefix(context, "box ", &**expr, shape, expr.span)
187         }
188         ast::ExprKind::AddrOf(mutability, ref expr) => {
189             rewrite_expr_addrof(context, mutability, expr, shape)
190         }
191         ast::ExprKind::Cast(ref expr, ref ty) => {
192             rewrite_pair(&**expr, &**ty, "", " as ", "", context, shape)
193         }
194         ast::ExprKind::Type(ref expr, ref ty) => {
195             rewrite_pair(&**expr, &**ty, "", ": ", "", context, shape)
196         }
197         ast::ExprKind::Index(ref expr, ref index) => {
198             rewrite_index(&**expr, &**index, context, shape)
199         }
200         ast::ExprKind::Repeat(ref expr, ref repeats) => {
201             let (lbr, rbr) = if context.config.spaces_within_square_brackets {
202                 ("[ ", " ]")
203             } else {
204                 ("[", "]")
205             };
206             rewrite_pair(&**expr, &**repeats, lbr, "; ", rbr, context, shape)
207         }
208         ast::ExprKind::Range(ref lhs, ref rhs, limits) => {
209             let delim = match limits {
210                 ast::RangeLimits::HalfOpen => "..",
211                 ast::RangeLimits::Closed => "...",
212             };
213
214             match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
215                 (Some(ref lhs), Some(ref rhs)) => {
216                     let sp_delim = if context.config.spaces_around_ranges {
217                         format!(" {} ", delim)
218                     } else {
219                         delim.into()
220                     };
221                     rewrite_pair(&**lhs, &**rhs, "", &sp_delim, "", context, shape)
222                 }
223                 (None, Some(ref rhs)) => {
224                     let sp_delim = if context.config.spaces_around_ranges {
225                         format!("{} ", delim)
226                     } else {
227                         delim.into()
228                     };
229                     rewrite_unary_prefix(context, &sp_delim, &**rhs, shape, expr.span)
230                 }
231                 (Some(ref lhs), None) => {
232                     let sp_delim = if context.config.spaces_around_ranges {
233                         format!(" {}", delim)
234                     } else {
235                         delim.into()
236                     };
237                     rewrite_unary_suffix(context, &sp_delim, &**lhs, shape)
238                 }
239                 (None, None) => wrap_str(delim.into(), context.config.max_width, shape),
240             }
241         }
242         // We do not format these expressions yet, but they should still
243         // satisfy our width restrictions.
244         ast::ExprKind::InPlace(..) |
245         ast::ExprKind::InlineAsm(..) => {
246             wrap_str(context.snippet(expr.span), context.config.max_width, shape)
247         }
248     };
249     result.and_then(|res| recover_comment_removed(res, expr.span, context, shape))
250 }
251
252 pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
253                               rhs: &RHS,
254                               prefix: &str,
255                               infix: &str,
256                               suffix: &str,
257                               context: &RewriteContext,
258                               shape: Shape)
259                               -> Option<String>
260     where LHS: Rewrite,
261           RHS: Rewrite
262 {
263     // Get "full width" rhs and see if it fits on the current line. This
264     // usually works fairly well since it tends to place operands of
265     // operations with high precendence close together.
266     // Note that this is non-conservative, but its just to see if it's even
267     // worth trying to put everything on one line.
268     let rhs_shape = try_opt!(shape.sub_width(suffix.len()));
269     let rhs_result = rhs.rewrite(context, rhs_shape);
270
271     if let Some(rhs_result) = rhs_result {
272         // This is needed in case of line break not caused by a
273         // shortage of space, but by end-of-line comments, for example.
274         if !rhs_result.contains('\n') {
275             let lhs_shape = try_opt!(shape.sub_width(prefix.len() + infix.len()));
276             let lhs_result = lhs.rewrite(context, lhs_shape);
277             if let Some(lhs_result) = lhs_result {
278                 let mut result = format!("{}{}{}", prefix, lhs_result, infix);
279
280                 let remaining_width = shape
281                     .width
282                     .checked_sub(last_line_width(&result))
283                     .unwrap_or(0);
284
285                 if rhs_result.len() <= remaining_width {
286                     result.push_str(&rhs_result);
287                     result.push_str(suffix);
288                     return Some(result);
289                 }
290
291                 // Try rewriting the rhs into the remaining space.
292                 let rhs_shape = shape.shrink_left(last_line_width(&result) + suffix.len());
293                 if let Some(rhs_shape) = rhs_shape {
294                     if let Some(rhs_result) = rhs.rewrite(context, rhs_shape) {
295                         // FIXME this should always hold.
296                         if rhs_result.len() <= remaining_width {
297                             result.push_str(&rhs_result);
298                             result.push_str(suffix);
299                             return Some(result);
300                         }
301                     }
302                 }
303             }
304         }
305     }
306
307     // We have to use multiple lines.
308
309     // Re-evaluate the rhs because we have more space now:
310     let infix = infix.trim_right();
311     let lhs_budget = try_opt!(context
312                                   .config
313                                   .max_width
314                                   .checked_sub(shape.used_width() + prefix.len() +
315                                                infix.len()));
316     let rhs_shape = match context.config.control_style {
317         Style::Default => {
318             try_opt!(shape.sub_width(suffix.len() + prefix.len())).visual_indent(prefix.len())
319         }
320         Style::Rfc => try_opt!(shape.block_left(context.config.tab_spaces)),
321     };
322
323     let rhs_result = try_opt!(rhs.rewrite(context, rhs_shape));
324     let lhs_result = try_opt!(lhs.rewrite(context,
325                                           Shape {
326                                               width: lhs_budget,
327                                               ..shape
328                                           }));
329     Some(format!("{}{}{}\n{}{}{}",
330                  prefix,
331                  lhs_result,
332                  infix,
333                  rhs_shape.indent.to_string(context.config),
334                  rhs_result,
335                  suffix))
336 }
337
338 pub fn rewrite_array<'a, I>(expr_iter: I,
339                             span: Span,
340                             context: &RewriteContext,
341                             shape: Shape)
342                             -> Option<String>
343     where I: Iterator<Item = &'a ast::Expr>
344 {
345     let bracket_size = if context.config.spaces_within_square_brackets {
346         2 // "[ "
347     } else {
348         1 // "["
349     };
350
351     let nested_shape = match context.config.array_layout {
352         IndentStyle::Block => shape.block().block_indent(context.config.tab_spaces),
353         IndentStyle::Visual => {
354             try_opt!(shape
355                          .visual_indent(bracket_size)
356                          .sub_width(bracket_size * 2))
357         }
358     };
359
360     let items = itemize_list(context.codemap,
361                              expr_iter,
362                              "]",
363                              |item| item.span.lo,
364                              |item| item.span.hi,
365                              |item| item.rewrite(context, nested_shape),
366                              span.lo,
367                              span.hi)
368             .collect::<Vec<_>>();
369
370     if items.is_empty() {
371         if context.config.spaces_within_square_brackets {
372             return Some("[ ]".to_string());
373         } else {
374             return Some("[]".to_string());
375         }
376     }
377
378     let has_long_item = try_opt!(items
379                                      .iter()
380                                      .map(|li| li.item.as_ref().map(|s| s.len() > 10))
381                                      .fold(Some(false),
382                                            |acc, x| acc.and_then(|y| x.map(|x| x || y))));
383
384     let tactic = match context.config.array_layout {
385         IndentStyle::Block => {
386             // FIXME wrong shape in one-line case
387             match shape.width.checked_sub(2 * bracket_size) {
388                 Some(width) => {
389                     let tactic = ListTactic::LimitedHorizontalVertical(context.config.array_width);
390                     definitive_tactic(&items, tactic, width)
391                 }
392                 None => DefinitiveListTactic::Vertical,
393             }
394         }
395         IndentStyle::Visual => {
396             if has_long_item || items.iter().any(ListItem::is_multiline) {
397                 definitive_tactic(&items,
398                                   ListTactic::LimitedHorizontalVertical(context.config.array_width),
399                                   nested_shape.width)
400             } else {
401                 DefinitiveListTactic::Mixed
402             }
403         }
404     };
405
406     let fmt = ListFormatting {
407         tactic: tactic,
408         separator: ",",
409         trailing_separator: SeparatorTactic::Never,
410         shape: nested_shape,
411         ends_with_newline: false,
412         config: context.config,
413     };
414     let list_str = try_opt!(write_list(&items, &fmt));
415
416     let result = if context.config.array_layout == IndentStyle::Visual ||
417                     tactic != DefinitiveListTactic::Vertical {
418         if context.config.spaces_within_square_brackets && list_str.len() > 0 {
419             format!("[ {} ]", list_str)
420         } else {
421             format!("[{}]", list_str)
422         }
423     } else {
424         format!("[\n{}{},\n{}]",
425                 nested_shape.indent.to_string(context.config),
426                 list_str,
427                 shape.block().indent.to_string(context.config))
428     };
429
430     Some(result)
431 }
432
433 // This functions is pretty messy because of the rules around closures and blocks:
434 // FIXME - the below is probably no longer true in full.
435 //   * if there is a return type, then there must be braces,
436 //   * given a closure with braces, whether that is parsed to give an inner block
437 //     or not depends on if there is a return type and if there are statements
438 //     in that block,
439 //   * if the first expression in the body ends with a block (i.e., is a
440 //     statement without needing a semi-colon), then adding or removing braces
441 //     can change whether it is treated as an expression or statement.
442 fn rewrite_closure(capture: ast::CaptureBy,
443                    fn_decl: &ast::FnDecl,
444                    body: &ast::Expr,
445                    span: Span,
446                    context: &RewriteContext,
447                    shape: Shape)
448                    -> Option<String> {
449     let mover = if capture == ast::CaptureBy::Value {
450         "move "
451     } else {
452         ""
453     };
454     // 4 = "|| {".len(), which is overconservative when the closure consists of
455     // a single expression.
456     let nested_shape = try_opt!(try_opt!(shape.shrink_left(mover.len())).sub_width(4));
457
458     // 1 = |
459     let argument_offset = nested_shape.indent + 1;
460     let arg_shape = try_opt!(nested_shape.shrink_left(1)).visual_indent(0);
461     let ret_str = try_opt!(fn_decl.output.rewrite(context, arg_shape));
462
463     let arg_items = itemize_list(context.codemap,
464                                  fn_decl.inputs.iter(),
465                                  "|",
466                                  |arg| span_lo_for_arg(arg),
467                                  |arg| span_hi_for_arg(arg),
468                                  |arg| arg.rewrite(context, arg_shape),
469                                  context.codemap.span_after(span, "|"),
470                                  body.span.lo);
471     let item_vec = arg_items.collect::<Vec<_>>();
472     // 1 = space between arguments and return type.
473     let horizontal_budget = nested_shape
474         .width
475         .checked_sub(ret_str.len() + 1)
476         .unwrap_or(0);
477     let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget);
478     let arg_shape = match tactic {
479         DefinitiveListTactic::Horizontal => try_opt!(arg_shape.sub_width(ret_str.len() + 1)),
480         _ => arg_shape,
481     };
482
483     let fmt = ListFormatting {
484         tactic: tactic,
485         separator: ",",
486         trailing_separator: SeparatorTactic::Never,
487         shape: arg_shape,
488         ends_with_newline: false,
489         config: context.config,
490     };
491     let list_str = try_opt!(write_list(&item_vec, &fmt));
492     let mut prefix = format!("{}|{}|", mover, list_str);
493
494     if !ret_str.is_empty() {
495         if prefix.contains('\n') {
496             prefix.push('\n');
497             prefix.push_str(&argument_offset.to_string(context.config));
498         } else {
499             prefix.push(' ');
500         }
501         prefix.push_str(&ret_str);
502     }
503
504     // 1 = space between `|...|` and body.
505     let extra_offset = extra_offset(&prefix, shape) + 1;
506     let body_shape = try_opt!(shape.sub_width(extra_offset)).add_offset(extra_offset);
507
508     if let ast::ExprKind::Block(ref block) = body.node {
509         // The body of the closure is an empty block.
510         if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) {
511             return Some(format!("{} {{}}", prefix));
512         }
513
514         // Figure out if the block is necessary.
515         let needs_block = block.rules != ast::BlockCheckMode::Default ||
516                           block.stmts.len() > 1 || context.inside_macro ||
517                           block_contains_comment(block, context.codemap) ||
518                           prefix.contains('\n');
519
520         if ret_str.is_empty() && !needs_block {
521             // lock.stmts.len() == 1
522             if let Some(ref expr) = stmt_expr(&block.stmts[0]) {
523                 if let Some(rw) = rewrite_closure_expr(expr, &prefix, context, body_shape) {
524                     return Some(rw);
525                 }
526             }
527         }
528
529         if !needs_block {
530             // We need braces, but we might still prefer a one-liner.
531             let stmt = &block.stmts[0];
532             // 4 = braces and spaces.
533             if let Some(body_shape) = body_shape.sub_width(4) {
534                 // Checks if rewrite succeeded and fits on a single line.
535                 if let Some(rewrite) = and_one_line(stmt.rewrite(context, body_shape)) {
536                     return Some(format!("{} {{ {} }}", prefix, rewrite));
537                 }
538             }
539         }
540
541         // Either we require a block, or tried without and failed.
542         return rewrite_closure_block(&block, prefix, context, body_shape);
543     }
544
545     if let Some(rw) = rewrite_closure_expr(body, &prefix, context, body_shape) {
546         return Some(rw);
547     }
548
549     // The closure originally had a non-block expression, but we can't fit on
550     // one line, so we'll insert a block.
551     let block = ast::Block {
552         stmts: vec![ast::Stmt {
553                         id: ast::NodeId::new(0),
554                         node: ast::StmtKind::Expr(ptr::P(body.clone())),
555                         span: body.span,
556                     }],
557         id: ast::NodeId::new(0),
558         rules: ast::BlockCheckMode::Default,
559         span: body.span,
560     };
561     return rewrite_closure_block(&block, prefix, context, body_shape);
562
563     fn rewrite_closure_expr(expr: &ast::Expr,
564                             prefix: &str,
565                             context: &RewriteContext,
566                             shape: Shape)
567                             -> Option<String> {
568         let mut rewrite = expr.rewrite(context, shape);
569         if classify::expr_requires_semi_to_be_stmt(left_most_sub_expr(expr)) {
570             rewrite = and_one_line(rewrite);
571         }
572         rewrite.map(|rw| format!("{} {}", prefix, rw))
573     }
574
575     fn rewrite_closure_block(block: &ast::Block,
576                              prefix: String,
577                              context: &RewriteContext,
578                              shape: Shape)
579                              -> Option<String> {
580         // Start with visual indent, then fall back to block indent if the
581         // closure is large.
582         if let Some(block_str) = block.rewrite(&context, shape) {
583             let block_threshold = context.config.closure_block_indent_threshold;
584             if block_threshold < 0 || block_str.matches('\n').count() <= block_threshold as usize {
585                 if let Some(block_str) = block_str.rewrite(context, shape) {
586                     return Some(format!("{} {}", prefix, block_str));
587                 }
588             }
589         }
590
591         // The body of the closure is big enough to be block indented, that
592         // means we must re-format.
593         let block_shape = shape.block().with_max_width(context.config);
594         let block_str = try_opt!(block.rewrite(&context, block_shape));
595         Some(format!("{} {}",
596                      prefix,
597                      try_opt!(block_str.rewrite(context, block_shape))))
598     }
599 }
600
601 fn and_one_line(x: Option<String>) -> Option<String> {
602     x.and_then(|x| if x.contains('\n') { None } else { Some(x) })
603 }
604
605 fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
606     debug!("nop_block_collapse {:?} {}", block_str, budget);
607     block_str.map(|block_str| if block_str.starts_with('{') && budget >= 2 &&
608                                  (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() ==
609                                   block_str.len() - 2) {
610                       "{}".to_owned()
611                   } else {
612                       block_str.to_owned()
613                   })
614 }
615
616 impl Rewrite for ast::Block {
617     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
618         // shape.width is used only for the single line case: either the empty block `{}`,
619         // or an unsafe expression `unsafe { e }`.
620
621         if self.stmts.is_empty() && !block_contains_comment(self, context.codemap) &&
622            shape.width >= 2 {
623             return Some("{}".to_owned());
624         }
625
626         // If a block contains only a single-line comment, then leave it on one line.
627         let user_str = context.snippet(self.span);
628         let user_str = user_str.trim();
629         if user_str.starts_with('{') && user_str.ends_with('}') {
630             let comment_str = user_str[1..user_str.len() - 1].trim();
631             if self.stmts.is_empty() && !comment_str.contains('\n') &&
632                !comment_str.starts_with("//") &&
633                comment_str.len() + 4 <= shape.width {
634                 return Some(format!("{{ {} }}", comment_str));
635             }
636         }
637
638         let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
639         visitor.block_indent = shape.indent;
640
641         let prefix = match self.rules {
642             ast::BlockCheckMode::Unsafe(..) => {
643                 let snippet = context.snippet(self.span);
644                 let open_pos = try_opt!(snippet.find_uncommented("{"));
645                 visitor.last_pos = self.span.lo + BytePos(open_pos as u32);
646
647                 // Extract comment between unsafe and block start.
648                 let trimmed = &snippet[6..open_pos].trim();
649
650                 let prefix = if !trimmed.is_empty() {
651                     // 9 = "unsafe  {".len(), 7 = "unsafe ".len()
652                     let budget = try_opt!(shape.width.checked_sub(9));
653                     format!("unsafe {} ",
654                             try_opt!(rewrite_comment(trimmed,
655                                                      true,
656                                                      Shape::legacy(budget, shape.indent + 7),
657                                                      context.config)))
658                 } else {
659                     "unsafe ".to_owned()
660                 };
661
662                 if is_simple_block(self, context.codemap) && prefix.len() < shape.width {
663                     let expr_str =
664                         self.stmts[0].rewrite(context,
665                                               Shape::legacy(shape.width - prefix.len(),
666                                                             shape.indent));
667                     let expr_str = try_opt!(expr_str);
668                     let result = format!("{}{{ {} }}", prefix, expr_str);
669                     if result.len() <= shape.width && !result.contains('\n') {
670                         return Some(result);
671                     }
672                 }
673
674                 prefix
675             }
676             ast::BlockCheckMode::Default => {
677                 visitor.last_pos = self.span.lo;
678
679                 String::new()
680             }
681         };
682
683         visitor.visit_block(self);
684         if visitor.failed && shape.indent.alignment != 0 {
685             self.rewrite(context,
686                          Shape::indented(shape.indent.block_only(), context.config))
687         } else {
688             Some(format!("{}{}", prefix, visitor.buffer))
689         }
690     }
691 }
692
693 impl Rewrite for ast::Stmt {
694     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
695         let result = match self.node {
696             ast::StmtKind::Local(ref local) => local.rewrite(context, shape),
697             ast::StmtKind::Expr(ref ex) |
698             ast::StmtKind::Semi(ref ex) => {
699                 let suffix = if semicolon_for_stmt(self) { ";" } else { "" };
700
701                 format_expr(ex,
702                             match self.node {
703                                 ast::StmtKind::Expr(_) => ExprType::SubExpression,
704                                 ast::StmtKind::Semi(_) => ExprType::Statement,
705                                 _ => unreachable!(),
706                             },
707                             context,
708                             try_opt!(shape.sub_width(suffix.len())))
709                         .map(|s| s + suffix)
710             }
711             ast::StmtKind::Mac(..) |
712             ast::StmtKind::Item(..) => None,
713         };
714         result.and_then(|res| recover_comment_removed(res, self.span, context, shape))
715     }
716 }
717
718 // Abstraction over control flow expressions
719 #[derive(Debug)]
720 struct ControlFlow<'a> {
721     cond: Option<&'a ast::Expr>,
722     block: &'a ast::Block,
723     else_block: Option<&'a ast::Expr>,
724     label: Option<ast::SpannedIdent>,
725     pat: Option<&'a ast::Pat>,
726     keyword: &'a str,
727     matcher: &'a str,
728     connector: &'a str,
729     allow_single_line: bool,
730     // True if this is an `if` expression in an `else if` :-( hacky
731     nested_if: bool,
732     span: Span,
733 }
734
735 impl<'a> ControlFlow<'a> {
736     fn new_if(cond: &'a ast::Expr,
737               pat: Option<&'a ast::Pat>,
738               block: &'a ast::Block,
739               else_block: Option<&'a ast::Expr>,
740               allow_single_line: bool,
741               nested_if: bool,
742               span: Span)
743               -> ControlFlow<'a> {
744         ControlFlow {
745             cond: Some(cond),
746             block: block,
747             else_block: else_block,
748             label: None,
749             pat: pat,
750             keyword: "if",
751             matcher: match pat {
752                 Some(..) => "let",
753                 None => "",
754             },
755             connector: " =",
756             allow_single_line: allow_single_line,
757             nested_if: nested_if,
758             span: span,
759         }
760     }
761
762     fn new_loop(block: &'a ast::Block,
763                 label: Option<ast::SpannedIdent>,
764                 span: Span)
765                 -> ControlFlow<'a> {
766         ControlFlow {
767             cond: None,
768             block: block,
769             else_block: None,
770             label: label,
771             pat: None,
772             keyword: "loop",
773             matcher: "",
774             connector: "",
775             allow_single_line: false,
776             nested_if: false,
777             span: span,
778         }
779     }
780
781     fn new_while(pat: Option<&'a ast::Pat>,
782                  cond: &'a ast::Expr,
783                  block: &'a ast::Block,
784                  label: Option<ast::SpannedIdent>,
785                  span: Span)
786                  -> ControlFlow<'a> {
787         ControlFlow {
788             cond: Some(cond),
789             block: block,
790             else_block: None,
791             label: label,
792             pat: pat,
793             keyword: "while",
794             matcher: match pat {
795                 Some(..) => "let",
796                 None => "",
797             },
798             connector: " =",
799             allow_single_line: false,
800             nested_if: false,
801             span: span,
802         }
803     }
804
805     fn new_for(pat: &'a ast::Pat,
806                cond: &'a ast::Expr,
807                block: &'a ast::Block,
808                label: Option<ast::SpannedIdent>,
809                span: Span)
810                -> ControlFlow<'a> {
811         ControlFlow {
812             cond: Some(cond),
813             block: block,
814             else_block: None,
815             label: label,
816             pat: Some(pat),
817             keyword: "for",
818             matcher: "",
819             connector: " in",
820             allow_single_line: false,
821             nested_if: false,
822             span: span,
823         }
824     }
825
826     fn rewrite_single_line(&self,
827                            pat_expr_str: &str,
828                            context: &RewriteContext,
829                            width: usize)
830                            -> Option<String> {
831         assert!(self.allow_single_line);
832         let else_block = try_opt!(self.else_block);
833         let fixed_cost = self.keyword.len() + "  {  } else {  }".len();
834
835         if let ast::ExprKind::Block(ref else_node) = else_block.node {
836             if !is_simple_block(self.block, context.codemap) ||
837                !is_simple_block(else_node, context.codemap) ||
838                pat_expr_str.contains('\n') {
839                 return None;
840             }
841
842             let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
843             let expr = &self.block.stmts[0];
844             let if_str = try_opt!(expr.rewrite(context, Shape::legacy(new_width, Indent::empty())));
845
846             let new_width = try_opt!(new_width.checked_sub(if_str.len()));
847             let else_expr = &else_node.stmts[0];
848             let else_str = try_opt!(else_expr.rewrite(context,
849                                                       Shape::legacy(new_width, Indent::empty())));
850
851             if if_str.contains('\n') || else_str.contains('\n') {
852                 return None;
853             }
854
855             let result = format!("{} {} {{ {} }} else {{ {} }}",
856                                  self.keyword,
857                                  pat_expr_str,
858                                  if_str,
859                                  else_str);
860
861             if result.len() <= width {
862                 return Some(result);
863             }
864         }
865
866         None
867     }
868 }
869
870 impl<'a> Rewrite for ControlFlow<'a> {
871     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
872         debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
873         let constr_shape = if self.nested_if {
874             // We are part of an if-elseif-else chain. Our constraints are tightened.
875             // 7 = "} else " .len()
876             try_opt!(shape.shrink_left(7))
877         } else {
878             shape
879         };
880
881         let label_string = rewrite_label(self.label);
882         // 1 = space after keyword.
883         let add_offset = self.keyword.len() + label_string.len() + 1;
884
885         let pat_expr_string = match self.cond {
886             Some(cond) => {
887                 let mut cond_shape = match context.config.control_style {
888                     Style::Default => try_opt!(constr_shape.shrink_left(add_offset)),
889                     Style::Rfc => constr_shape,
890                 };
891                 if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine {
892                     // 2 = " {".len()
893                     cond_shape = try_opt!(cond_shape.sub_width(2));
894                 }
895
896                 try_opt!(rewrite_pat_expr(context,
897                                           self.pat,
898                                           cond,
899                                           self.matcher,
900                                           self.connector,
901                                           cond_shape))
902             }
903             None => String::new(),
904         };
905
906         let force_newline_brace = context.config.control_style == Style::Rfc &&
907                                   pat_expr_string.contains('\n');
908
909         // Try to format if-else on single line.
910         if self.allow_single_line && context.config.single_line_if_else_max_width > 0 {
911             let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width);
912
913             if trial.is_some() &&
914                trial.as_ref().unwrap().len() <= context.config.single_line_if_else_max_width {
915                 return trial;
916             }
917         }
918
919         let used_width = if pat_expr_string.contains('\n') {
920             last_line_width(&pat_expr_string)
921         } else {
922             // 2 = spaces after keyword and condition.
923             label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
924         };
925
926         let block_width = shape.width.checked_sub(used_width).unwrap_or(0);
927         // This is used only for the empty block case: `{}`. So, we use 1 if we know
928         // we should avoid the single line case.
929         let block_width = if self.else_block.is_some() || self.nested_if {
930             min(1, block_width)
931         } else {
932             block_width
933         };
934
935         let block_shape = Shape {
936             width: block_width,
937             ..shape
938         };
939         let block_str = try_opt!(self.block.rewrite(context, block_shape));
940
941         let cond_span = if let Some(cond) = self.cond {
942             cond.span
943         } else {
944             mk_sp(self.block.span.lo, self.block.span.lo)
945         };
946
947         // for event in event
948         let between_kwd_cond =
949             mk_sp(context.codemap.span_after(self.span, self.keyword.trim()),
950                   self.pat
951                       .map_or(cond_span.lo, |p| if self.matcher.is_empty() {
952                 p.span.lo
953             } else {
954                 context.codemap.span_before(self.span, self.matcher.trim())
955             }));
956
957         let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
958
959         let after_cond_comment =
960             extract_comment(mk_sp(cond_span.hi, self.block.span.lo), context, shape);
961
962         let alt_block_sep = String::from("\n") +
963                             &shape.indent.block_only().to_string(context.config);
964         let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
965             ""
966         } else if context.config.control_brace_style == ControlBraceStyle::AlwaysNextLine ||
967                   force_newline_brace {
968             alt_block_sep.as_str()
969         } else {
970             " "
971         };
972
973         let mut result = format!("{}{}{}{}{}{}",
974                                  label_string,
975                                  self.keyword,
976                                  between_kwd_cond_comment
977                                      .as_ref()
978                                      .map_or(if pat_expr_string.is_empty() ||
979                                                 pat_expr_string.starts_with('\n') {
980                                                  ""
981                                              } else {
982                                                  " "
983                                              },
984                                              |s| &**s),
985                                  pat_expr_string,
986                                  after_cond_comment.as_ref().map_or(block_sep, |s| &**s),
987                                  block_str);
988
989         if let Some(else_block) = self.else_block {
990             let shape = Shape::indented(shape.indent, context.config);
991             let mut last_in_chain = false;
992             let rewrite = match else_block.node {
993                 // If the else expression is another if-else expression, prevent it
994                 // from being formatted on a single line.
995                 // Note how we're passing the original shape, as the
996                 // cost of "else" should not cascade.
997                 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref next_else_block) => {
998                     ControlFlow::new_if(cond,
999                                         Some(pat),
1000                                         if_block,
1001                                         next_else_block.as_ref().map(|e| &**e),
1002                                         false,
1003                                         true,
1004                                         mk_sp(else_block.span.lo, self.span.hi))
1005                             .rewrite(context, shape)
1006                 }
1007                 ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
1008                     ControlFlow::new_if(cond,
1009                                         None,
1010                                         if_block,
1011                                         next_else_block.as_ref().map(|e| &**e),
1012                                         false,
1013                                         true,
1014                                         mk_sp(else_block.span.lo, self.span.hi))
1015                             .rewrite(context, shape)
1016                 }
1017                 _ => {
1018                     last_in_chain = true;
1019                     // When rewriting a block, the width is only used for single line
1020                     // blocks, passing 1 lets us avoid that.
1021                     let else_shape = Shape {
1022                         width: min(1, shape.width),
1023                         ..shape
1024                     };
1025                     else_block.rewrite(context, else_shape)
1026                 }
1027             };
1028
1029             let between_kwd_else_block =
1030                 mk_sp(self.block.span.hi,
1031                       context
1032                           .codemap
1033                           .span_before(mk_sp(self.block.span.hi, else_block.span.lo), "else"));
1034             let between_kwd_else_block_comment =
1035                 extract_comment(between_kwd_else_block, context, shape);
1036
1037             let after_else =
1038                 mk_sp(context
1039                           .codemap
1040                           .span_after(mk_sp(self.block.span.hi, else_block.span.lo), "else"),
1041                       else_block.span.lo);
1042             let after_else_comment = extract_comment(after_else, context, shape);
1043
1044             let between_sep = match context.config.control_brace_style {
1045                 ControlBraceStyle::AlwaysNextLine |
1046                 ControlBraceStyle::ClosingNextLine => &*alt_block_sep,
1047                 ControlBraceStyle::AlwaysSameLine => " ",
1048             };
1049             let after_sep = match context.config.control_brace_style {
1050                 ControlBraceStyle::AlwaysNextLine if last_in_chain => &*alt_block_sep,
1051                 _ => " ",
1052             };
1053             try_opt!(write!(&mut result,
1054                             "{}else{}",
1055                             between_kwd_else_block_comment
1056                                 .as_ref()
1057                                 .map_or(between_sep, |s| &**s),
1058                             after_else_comment.as_ref().map_or(after_sep, |s| &**s))
1059                              .ok());
1060             result.push_str(&try_opt!(rewrite));
1061         }
1062
1063         Some(result)
1064     }
1065 }
1066
1067 fn rewrite_label(label: Option<ast::SpannedIdent>) -> String {
1068     match label {
1069         Some(ident) => format!("{}: ", ident.node),
1070         None => "".to_owned(),
1071     }
1072 }
1073
1074 fn extract_comment(span: Span, context: &RewriteContext, shape: Shape) -> Option<String> {
1075     let comment_str = context.snippet(span);
1076     if contains_comment(&comment_str) {
1077         let comment = try_opt!(rewrite_comment(comment_str.trim(), false, shape, context.config));
1078         Some(format!("\n{indent}{}\n{indent}",
1079                      comment,
1080                      indent = shape.indent.to_string(context.config)))
1081     } else {
1082         None
1083     }
1084 }
1085
1086 fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool {
1087     let snippet = codemap.span_to_snippet(block.span).unwrap();
1088     contains_comment(&snippet)
1089 }
1090
1091 // Checks that a block contains no statements, an expression and no comments.
1092 // FIXME: incorrectly returns false when comment is contained completely within
1093 // the expression.
1094 pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1095     (block.stmts.len() == 1 && stmt_is_expr(&block.stmts[0]) &&
1096      !block_contains_comment(block, codemap))
1097 }
1098
1099 /// Checks whether a block contains at most one statement or expression, and no comments.
1100 pub fn is_simple_block_stmt(block: &ast::Block, codemap: &CodeMap) -> bool {
1101     block.stmts.len() <= 1 && !block_contains_comment(block, codemap)
1102 }
1103
1104 /// Checks whether a block contains no statements, expressions, or comments.
1105 pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1106     block.stmts.is_empty() && !block_contains_comment(block, codemap)
1107 }
1108
1109 pub fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
1110     match stmt.node {
1111         ast::StmtKind::Expr(..) => true,
1112         _ => false,
1113     }
1114 }
1115
1116 fn is_unsafe_block(block: &ast::Block) -> bool {
1117     if let ast::BlockCheckMode::Unsafe(..) = block.rules {
1118         true
1119     } else {
1120         false
1121     }
1122 }
1123
1124 // inter-match-arm-comment-rules:
1125 //  - all comments following a match arm before the start of the next arm
1126 //    are about the second arm
1127 fn rewrite_match_arm_comment(context: &RewriteContext,
1128                              missed_str: &str,
1129                              shape: Shape,
1130                              arm_indent_str: &str)
1131                              -> Option<String> {
1132     // The leading "," is not part of the arm-comment
1133     let missed_str = match missed_str.find_uncommented(",") {
1134         Some(n) => &missed_str[n + 1..],
1135         None => &missed_str[..],
1136     };
1137
1138     let mut result = String::new();
1139     // any text not preceeded by a newline is pushed unmodified to the block
1140     let first_brk = missed_str.find(|c: char| c == '\n').unwrap_or(0);
1141     result.push_str(&missed_str[..first_brk]);
1142     let missed_str = &missed_str[first_brk..]; // If missed_str had one newline, it starts with it
1143
1144     let first = missed_str
1145         .find(|c: char| !c.is_whitespace())
1146         .unwrap_or(missed_str.len());
1147     if missed_str[..first].chars().filter(|c| c == &'\n').count() >= 2 {
1148         // Excessive vertical whitespace before comment should be preserved
1149         // FIXME handle vertical whitespace better
1150         result.push('\n');
1151     }
1152     let missed_str = missed_str[first..].trim();
1153     if !missed_str.is_empty() {
1154         let comment = try_opt!(rewrite_comment(&missed_str, false, shape, context.config));
1155         result.push('\n');
1156         result.push_str(arm_indent_str);
1157         result.push_str(&comment);
1158     }
1159
1160     Some(result)
1161 }
1162
1163 fn rewrite_match(context: &RewriteContext,
1164                  cond: &ast::Expr,
1165                  arms: &[ast::Arm],
1166                  shape: Shape,
1167                  span: Span)
1168                  -> Option<String> {
1169     if arms.is_empty() {
1170         return None;
1171     }
1172
1173     // `match `cond` {`
1174     let cond_shape = try_opt!(shape.shrink_left(6));
1175     let cond_shape = try_opt!(cond_shape.sub_width(2));
1176     let cond_str = try_opt!(cond.rewrite(context, cond_shape));
1177     let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
1178     let block_sep = match context.config.control_brace_style {
1179         ControlBraceStyle::AlwaysSameLine => " ",
1180         _ => alt_block_sep.as_str(),
1181     };
1182     let mut result = format!("match {}{}{{", cond_str, block_sep);
1183
1184     let arm_shape = if context.config.indent_match_arms {
1185         shape.block_indent(context.config.tab_spaces)
1186     } else {
1187         shape.block_indent(0)
1188     };
1189
1190     let arm_indent_str = arm_shape.indent.to_string(context.config);
1191
1192     let open_brace_pos = context
1193         .codemap
1194         .span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{");
1195
1196     for (i, arm) in arms.iter().enumerate() {
1197         // Make sure we get the stuff between arms.
1198         let missed_str = if i == 0 {
1199             context.snippet(mk_sp(open_brace_pos, arm_start_pos(arm)))
1200         } else {
1201             context.snippet(mk_sp(arm_end_pos(&arms[i - 1]), arm_start_pos(arm)))
1202         };
1203         let comment =
1204             try_opt!(rewrite_match_arm_comment(context, &missed_str, arm_shape, &arm_indent_str));
1205         result.push_str(&comment);
1206         result.push('\n');
1207         result.push_str(&arm_indent_str);
1208
1209         let arm_str = arm.rewrite(&context, arm_shape.with_max_width(context.config));
1210         if let Some(ref arm_str) = arm_str {
1211             result.push_str(arm_str);
1212         } else {
1213             // We couldn't format the arm, just reproduce the source.
1214             let snippet = context.snippet(mk_sp(arm_start_pos(arm), arm_end_pos(arm)));
1215             result.push_str(&snippet);
1216             result.push_str(arm_comma(context.config, &arm.body));
1217         }
1218     }
1219     // BytePos(1) = closing match brace.
1220     let last_span = mk_sp(arm_end_pos(&arms[arms.len() - 1]), span.hi - BytePos(1));
1221     let last_comment = context.snippet(last_span);
1222     let comment =
1223         try_opt!(rewrite_match_arm_comment(context, &last_comment, arm_shape, &arm_indent_str));
1224     result.push_str(&comment);
1225     result.push('\n');
1226     result.push_str(&shape.indent.to_string(context.config));
1227     result.push('}');
1228     Some(result)
1229 }
1230
1231 fn arm_start_pos(arm: &ast::Arm) -> BytePos {
1232     let &ast::Arm {
1233         ref attrs,
1234         ref pats,
1235         ..
1236     } = arm;
1237     if !attrs.is_empty() {
1238         return attrs[0].span.lo;
1239     }
1240
1241     pats[0].span.lo
1242 }
1243
1244 fn arm_end_pos(arm: &ast::Arm) -> BytePos {
1245     arm.body.span.hi
1246 }
1247
1248 fn arm_comma(config: &Config, body: &ast::Expr) -> &'static str {
1249     if config.match_block_trailing_comma {
1250         ","
1251     } else if let ast::ExprKind::Block(ref block) = body.node {
1252         if let ast::BlockCheckMode::Default = block.rules {
1253             ""
1254         } else {
1255             ","
1256         }
1257     } else {
1258         ","
1259     }
1260 }
1261
1262 // Match arms.
1263 impl Rewrite for ast::Arm {
1264     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
1265         debug!("Arm::rewrite {:?} {:?}", self, shape);
1266         let &ast::Arm {
1267             ref attrs,
1268             ref pats,
1269             ref guard,
1270             ref body,
1271         } = self;
1272
1273         // FIXME this is all a bit grotty, would be nice to abstract out the
1274         // treatment of attributes.
1275         let attr_str = if !attrs.is_empty() {
1276             // We only use this visitor for the attributes, should we use it for
1277             // more?
1278             let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
1279             attr_visitor.block_indent = shape.indent.block_only();
1280             attr_visitor.last_pos = attrs[0].span.lo;
1281             if attr_visitor.visit_attrs(attrs) {
1282                 // Attributes included a skip instruction.
1283                 return None;
1284             }
1285             attr_visitor.format_missing(pats[0].span.lo);
1286             attr_visitor.buffer.to_string()
1287         } else {
1288             String::new()
1289         };
1290
1291         // Patterns
1292         // 5 = ` => {`
1293         let pat_shape = try_opt!(shape.sub_width(5));
1294
1295         let pat_strs = try_opt!(pats.iter()
1296                                     .map(|p| p.rewrite(context, pat_shape))
1297                                     .collect::<Option<Vec<_>>>());
1298
1299         let all_simple = pat_strs.iter().all(|p| pat_is_simple(p));
1300         let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
1301         let fmt = ListFormatting {
1302             tactic: if all_simple {
1303                 DefinitiveListTactic::Mixed
1304             } else {
1305                 DefinitiveListTactic::Vertical
1306             },
1307             separator: " |",
1308             trailing_separator: SeparatorTactic::Never,
1309             shape: pat_shape,
1310             ends_with_newline: false,
1311             config: context.config,
1312         };
1313         let pats_str = try_opt!(write_list(items, &fmt));
1314
1315         let guard_shape = if pats_str.contains('\n') {
1316             shape.with_max_width(context.config)
1317         } else {
1318             shape
1319         };
1320
1321         let guard_str = try_opt!(rewrite_guard(context,
1322                                                guard,
1323                                                guard_shape,
1324                                                trimmed_last_line_width(&pats_str)));
1325
1326         let pats_str = format!("{}{}", pats_str, guard_str);
1327
1328         let body = match body.node {
1329             ast::ExprKind::Block(ref block) if !is_unsafe_block(block) &&
1330                                                is_simple_block(block, context.codemap) &&
1331                                                context.config.wrap_match_arms => {
1332                 if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node {
1333                     expr
1334                 } else {
1335                     &**body
1336                 }
1337             }
1338             _ => &**body,
1339         };
1340
1341         let comma = arm_comma(&context.config, body);
1342         let alt_block_sep = String::from("\n") +
1343                             &shape.indent.block_only().to_string(context.config);
1344
1345         let pat_width = extra_offset(&pats_str, shape);
1346         // Let's try and get the arm body on the same line as the condition.
1347         // 4 = ` => `.len()
1348         if shape.width > pat_width + comma.len() + 4 {
1349             let arm_shape = shape
1350                 .offset_left(pat_width + 4)
1351                 .unwrap()
1352                 .sub_width(comma.len())
1353                 .unwrap();
1354             let rewrite = nop_block_collapse(body.rewrite(context, arm_shape), arm_shape.width);
1355             let is_block = if let ast::ExprKind::Block(..) = body.node {
1356                 true
1357             } else {
1358                 false
1359             };
1360
1361             match rewrite {
1362                 Some(ref body_str) if (!body_str.contains('\n') &&
1363                                        body_str.len() <= arm_shape.width) ||
1364                                       !context.config.wrap_match_arms ||
1365                                       is_block => {
1366                     let block_sep = match context.config.control_brace_style {
1367                         ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep.as_str(),
1368                         _ => " ",
1369                     };
1370
1371                     return Some(format!("{}{} =>{}{}{}",
1372                                         attr_str.trim_left(),
1373                                         pats_str,
1374                                         block_sep,
1375                                         body_str,
1376                                         comma));
1377                 }
1378                 _ => {}
1379             }
1380         }
1381
1382         // FIXME: we're doing a second rewrite of the expr; This may not be
1383         // necessary.
1384         let body_shape = try_opt!(shape.sub_width(context.config.tab_spaces))
1385             .block_indent(context.config.tab_spaces);
1386         let next_line_body = try_opt!(nop_block_collapse(body.rewrite(context, body_shape),
1387                                                          body_shape.width));
1388         let indent_str = shape
1389             .indent
1390             .block_indent(context.config)
1391             .to_string(context.config);
1392         let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
1393             if context.config.match_block_trailing_comma {
1394                 ("{", "},")
1395             } else {
1396                 ("{", "}")
1397             }
1398         } else {
1399             ("", ",")
1400         };
1401
1402
1403         let block_sep = match context.config.control_brace_style {
1404             ControlBraceStyle::AlwaysNextLine => alt_block_sep + body_prefix + "\n",
1405             _ if body_prefix.is_empty() => "\n".to_owned(),
1406             _ => " ".to_owned() + body_prefix + "\n",
1407         };
1408
1409         if context.config.wrap_match_arms {
1410             Some(format!("{}{} =>{}{}{}\n{}{}",
1411                          attr_str.trim_left(),
1412                          pats_str,
1413                          block_sep,
1414                          indent_str,
1415                          next_line_body,
1416                          shape.indent.to_string(context.config),
1417                          body_suffix))
1418         } else {
1419             Some(format!("{}{} =>{}{}{}{}",
1420                          attr_str.trim_left(),
1421                          pats_str,
1422                          block_sep,
1423                          indent_str,
1424                          next_line_body,
1425                          body_suffix))
1426         }
1427     }
1428 }
1429
1430 // A pattern is simple if it is very short or it is short-ish and just a path.
1431 // E.g. `Foo::Bar` is simple, but `Foo(..)` is not.
1432 fn pat_is_simple(pat_str: &str) -> bool {
1433     pat_str.len() <= 16 ||
1434     (pat_str.len() <= 24 && pat_str.chars().all(|c| c.is_alphabetic() || c == ':'))
1435 }
1436
1437 // The `if ...` guard on a match arm.
1438 fn rewrite_guard(context: &RewriteContext,
1439                  guard: &Option<ptr::P<ast::Expr>>,
1440                  shape: Shape,
1441                  // The amount of space used up on this line for the pattern in
1442                  // the arm (excludes offset).
1443                  pattern_width: usize)
1444                  -> Option<String> {
1445     if let Some(ref guard) = *guard {
1446         // First try to fit the guard string on the same line as the pattern.
1447         // 4 = ` if `, 5 = ` => {`
1448         let overhead = pattern_width + 4 + 5;
1449         if overhead < shape.width {
1450             let cond_shape = shape
1451                 .shrink_left(pattern_width + 4)
1452                 .unwrap()
1453                 .sub_width(5)
1454                 .unwrap();
1455             let cond_str = guard.rewrite(context, cond_shape);
1456             if let Some(cond_str) = cond_str {
1457                 return Some(format!(" if {}", cond_str));
1458             }
1459         }
1460
1461         // Not enough space to put the guard after the pattern, try a newline.
1462         let overhead = shape.indent.block_indent(context.config).width() + 4 + 5;
1463         if overhead < shape.width {
1464             let cond_str = guard.rewrite(context,
1465                                          Shape::legacy(shape.width - overhead,
1466                                                        // 3 == `if `
1467                                                        shape.indent.block_indent(context.config) +
1468                                                        3));
1469             if let Some(cond_str) = cond_str {
1470                 return Some(format!("\n{}if {}",
1471                                     shape
1472                                         .indent
1473                                         .block_indent(context.config)
1474                                         .to_string(context.config),
1475                                     cond_str));
1476             }
1477         }
1478
1479         None
1480     } else {
1481         Some(String::new())
1482     }
1483 }
1484
1485 fn rewrite_pat_expr(context: &RewriteContext,
1486                     pat: Option<&ast::Pat>,
1487                     expr: &ast::Expr,
1488                     matcher: &str,
1489                     // Connecting piece between pattern and expression,
1490                     // *without* trailing space.
1491                     connector: &str,
1492                     shape: Shape)
1493                     -> Option<String> {
1494     debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, pat, expr);
1495     let mut pat_string = String::new();
1496     let mut result = match pat {
1497         Some(pat) => {
1498             let matcher = if matcher.is_empty() {
1499                 matcher.to_owned()
1500             } else {
1501                 format!("{} ", matcher)
1502             };
1503             let pat_shape = try_opt!(try_opt!(shape.shrink_left(matcher.len()))
1504                                          .sub_width(connector.len()));
1505             pat_string = try_opt!(pat.rewrite(context, pat_shape));
1506             format!("{}{}{}", matcher, pat_string, connector)
1507         }
1508         None => String::new(),
1509     };
1510
1511     // Consider only the last line of the pat string.
1512     let extra_offset = extra_offset(&result, shape);
1513
1514     // The expression may (partially) fit on the current line.
1515     if shape.width > extra_offset + 1 {
1516         let spacer = if pat.is_some() { " " } else { "" };
1517
1518         let expr_shape = try_opt!(shape.offset_left(extra_offset + spacer.len()));
1519         let expr_rewrite = expr.rewrite(context, expr_shape);
1520
1521         if let Some(expr_string) = expr_rewrite {
1522             if pat.is_none() || pat_is_simple(&pat_string) || !expr_string.contains('\n') {
1523                 result.push_str(spacer);
1524                 result.push_str(&expr_string);
1525                 return Some(result);
1526             }
1527         }
1528     }
1529
1530     let nested_indent = shape.indent.block_only().block_indent(context.config);
1531
1532     // The expression won't fit on the current line, jump to next.
1533     result.push('\n');
1534     result.push_str(&nested_indent.to_string(context.config));
1535
1536     let expr_rewrite = expr.rewrite(&context, Shape::indented(nested_indent, context.config));
1537     result.push_str(&try_opt!(expr_rewrite));
1538
1539     Some(result)
1540 }
1541
1542 fn rewrite_string_lit(context: &RewriteContext, span: Span, shape: Shape) -> Option<String> {
1543     let string_lit = context.snippet(span);
1544
1545     if !context.config.format_strings && !context.config.force_format_strings {
1546         return Some(string_lit);
1547     }
1548
1549     if !context.config.force_format_strings &&
1550        !string_requires_rewrite(context, span, &string_lit, shape) {
1551         return Some(string_lit);
1552     }
1553
1554     let fmt = StringFormat {
1555         opener: "\"",
1556         closer: "\"",
1557         line_start: " ",
1558         line_end: "\\",
1559         shape: shape,
1560         trim_end: false,
1561         config: context.config,
1562     };
1563
1564     // Remove the quote characters.
1565     let str_lit = &string_lit[1..string_lit.len() - 1];
1566
1567     rewrite_string(str_lit, &fmt)
1568 }
1569
1570 fn string_requires_rewrite(context: &RewriteContext,
1571                            span: Span,
1572                            string: &str,
1573                            shape: Shape)
1574                            -> bool {
1575     if context.codemap.lookup_char_pos(span.lo).col.0 != shape.indent.width() {
1576         return true;
1577     }
1578
1579     for (i, line) in string.lines().enumerate() {
1580         if i == 0 {
1581             if line.len() > shape.width {
1582                 return true;
1583             }
1584         } else {
1585             if line.len() > shape.width + shape.indent.width() {
1586                 return true;
1587             }
1588         }
1589     }
1590
1591     false
1592 }
1593
1594 pub fn rewrite_call<R>(context: &RewriteContext,
1595                        callee: &R,
1596                        args: &[ptr::P<ast::Expr>],
1597                        span: Span,
1598                        shape: Shape)
1599                        -> Option<String>
1600     where R: Rewrite
1601 {
1602     let closure =
1603         |callee_max_width| rewrite_call_inner(context, callee, callee_max_width, args, span, shape);
1604
1605     // 2 is for parens
1606     let max_width = try_opt!(shape.width.checked_sub(2));
1607     binary_search(1, max_width, closure)
1608 }
1609
1610 fn rewrite_call_inner<R>(context: &RewriteContext,
1611                          callee: &R,
1612                          max_callee_width: usize,
1613                          args: &[ptr::P<ast::Expr>],
1614                          span: Span,
1615                          shape: Shape)
1616                          -> Result<String, Ordering>
1617     where R: Rewrite
1618 {
1619     // FIXME using byte lens instead of char lens (and probably all over the
1620     // place too)
1621     let callee_shape = Shape {
1622         width: max_callee_width,
1623         ..shape
1624     };
1625     let callee_str = callee
1626         .rewrite(context, callee_shape)
1627         .ok_or(Ordering::Greater)?;
1628
1629     // 4 = `(  )`, 2 = `()`
1630     let paren_overhead = if context.config.spaces_within_parens {
1631         4
1632     } else {
1633         2
1634     };
1635     let used_width = extra_offset(&callee_str, shape);
1636     let one_line_width = shape
1637         .width
1638         .checked_sub(used_width + paren_overhead)
1639         .ok_or(Ordering::Greater)?;
1640
1641     let nested_shape = match context.config.fn_call_style {
1642             IndentStyle::Block => shape.block().block_left(context.config.tab_spaces),
1643             // 1 = (
1644             IndentStyle::Visual => {
1645                 shape
1646                     .visual_indent(used_width + 1)
1647                     .sub_width(used_width + paren_overhead)
1648             }
1649         }
1650         .ok_or(Ordering::Greater)?;
1651
1652     let span_lo = context.codemap.span_after(span, "(");
1653     let span = mk_sp(span_lo, span.hi);
1654
1655     let list_str = rewrite_call_args(context, args, span, nested_shape, one_line_width)
1656         .ok_or(Ordering::Less)?;
1657
1658     let result = if context.config.fn_call_style == IndentStyle::Visual ||
1659                     (!list_str.contains('\n') && list_str.chars().last().unwrap_or(' ') != ',') {
1660         if context.config.spaces_within_parens && list_str.len() > 0 {
1661             format!("{}( {} )", callee_str, list_str)
1662         } else {
1663             format!("{}({})", callee_str, list_str)
1664         }
1665     } else {
1666         format!("{}(\n{}{}\n{})",
1667                 callee_str,
1668                 nested_shape.indent.to_string(context.config),
1669                 list_str,
1670                 shape.block().indent.to_string(context.config))
1671     };
1672
1673     Ok(result)
1674 }
1675
1676 fn rewrite_call_args(context: &RewriteContext,
1677                      args: &[ptr::P<ast::Expr>],
1678                      span: Span,
1679                      shape: Shape,
1680                      one_line_width: usize)
1681                      -> Option<String> {
1682     let arg_count = args.len();
1683
1684     let items = itemize_list(context.codemap,
1685                              args.iter(),
1686                              ")",
1687                              |item| item.span.lo,
1688                              |item| item.span.hi,
1689                              |item| item.rewrite(context, shape),
1690                              span.lo,
1691                              span.hi);
1692     let mut item_vec: Vec<_> = items.collect();
1693
1694     // Try letting the last argument overflow to the next line with block
1695     // indentation. If its first line fits on one line with the other arguments,
1696     // we format the function arguments horizontally.
1697     let overflow_last = match args.last().map(|x| &x.node) {
1698         Some(&ast::ExprKind::Closure(..)) |
1699         Some(&ast::ExprKind::Block(..)) |
1700         Some(&ast::ExprKind::Match(..)) if arg_count > 1 => true,
1701         _ => false,
1702     };
1703
1704     let mut orig_last = None;
1705     let mut placeholder = None;
1706
1707     // Replace the last item with its first line to see if it fits with
1708     // first arguments.
1709     if overflow_last {
1710         let nested_shape = Shape {
1711             indent: shape.indent.block_only(),
1712             ..shape
1713         };
1714         let rewrite = args.last().unwrap().rewrite(context, nested_shape);
1715
1716         if let Some(rewrite) = rewrite {
1717             let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
1718             placeholder = Some(rewrite);
1719
1720             swap(&mut item_vec[arg_count - 1].item, &mut orig_last);
1721             item_vec[arg_count - 1].item = rewrite_first_line;
1722         }
1723     }
1724
1725     let one_line_shape = Shape {
1726         width: one_line_width,
1727         ..shape
1728     };
1729
1730     let tactic =
1731         definitive_tactic(&item_vec,
1732                           ListTactic::LimitedHorizontalVertical(context.config.fn_call_width),
1733                           one_line_width);
1734
1735     // Replace the stub with the full overflowing last argument if the rewrite
1736     // succeeded and its first line fits with the other arguments.
1737     match (overflow_last, tactic, placeholder) {
1738         (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
1739             item_vec[arg_count - 1].item = placeholder;
1740         }
1741         (true, _, _) => {
1742             item_vec[arg_count - 1].item = orig_last;
1743         }
1744         (false, _, _) => {}
1745     }
1746
1747     let mut fmt = ListFormatting {
1748         tactic: tactic,
1749         separator: ",",
1750         trailing_separator: if context.inside_macro ||
1751                                context.config.fn_call_style == IndentStyle::Visual ||
1752                                arg_count <= 1 {
1753             SeparatorTactic::Never
1754         } else {
1755             context.config.trailing_comma
1756         },
1757         shape: one_line_shape,
1758         ends_with_newline: false,
1759         config: context.config,
1760     };
1761
1762     match write_list(&item_vec, &fmt) {
1763         // If arguments do not fit in a single line and do not contain newline,
1764         // try to put it on the next line. Try this only when we are in block mode
1765         // and not rewriting macro.
1766         Some(ref s) if context.config.fn_call_style == IndentStyle::Block &&
1767                        !context.inside_macro &&
1768                        (!s.contains('\n') &&
1769                         (s.len() > one_line_width || s.len() > context.config.fn_call_width)) => {
1770             fmt.trailing_separator = SeparatorTactic::Vertical;
1771             write_list(&item_vec, &fmt)
1772         }
1773         rewrite @ _ => rewrite,
1774     }
1775 }
1776
1777 fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> Option<String> {
1778     debug!("rewrite_paren, shape: {:?}", shape);
1779     // 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
1780     // paren on the same line as the subexpr.
1781     let sub_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
1782     let subexpr_str = subexpr.rewrite(context, sub_shape);
1783     debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
1784
1785     subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 {
1786                         format!("( {} )", s)
1787                     } else {
1788                         format!("({})", s)
1789                     })
1790 }
1791
1792 fn rewrite_index(expr: &ast::Expr,
1793                  index: &ast::Expr,
1794                  context: &RewriteContext,
1795                  shape: Shape)
1796                  -> Option<String> {
1797     let expr_str = try_opt!(expr.rewrite(context, shape));
1798
1799     let (lbr, rbr) = if context.config.spaces_within_square_brackets {
1800         ("[ ", " ]")
1801     } else {
1802         ("[", "]")
1803     };
1804
1805     let budget = shape
1806         .width
1807         .checked_sub(expr_str.len() + lbr.len() + rbr.len())
1808         .unwrap_or(0);
1809     let index_str = index.rewrite(context, Shape::legacy(budget, shape.indent));
1810     if let Some(index_str) = index_str {
1811         return Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr));
1812     }
1813
1814     let indent = shape.indent.block_indent(&context.config);
1815     let indent = indent.to_string(&context.config);
1816     // FIXME this is not right, since we don't take into account that shape.width
1817     // might be reduced from max_width by something on the right.
1818     let budget = try_opt!(context
1819                               .config
1820                               .max_width
1821                               .checked_sub(indent.len() + lbr.len() + rbr.len()));
1822     let index_str = try_opt!(index.rewrite(context, Shape::legacy(budget, shape.indent)));
1823     Some(format!("{}\n{}{}{}{}", expr_str, indent, lbr, index_str, rbr))
1824 }
1825
1826 fn rewrite_struct_lit<'a>(context: &RewriteContext,
1827                           path: &ast::Path,
1828                           fields: &'a [ast::Field],
1829                           base: Option<&'a ast::Expr>,
1830                           span: Span,
1831                           shape: Shape)
1832                           -> Option<String> {
1833     debug!("rewrite_struct_lit: shape {:?}", shape);
1834
1835     enum StructLitField<'a> {
1836         Regular(&'a ast::Field),
1837         Base(&'a ast::Expr),
1838     }
1839
1840     // 2 = " {".len()
1841     let path_shape = try_opt!(shape.sub_width(2));
1842     let path_str = try_opt!(rewrite_path(context, PathContext::Expr, None, path, path_shape));
1843
1844     if fields.len() == 0 && base.is_none() {
1845         return Some(format!("{} {{}}", path_str));
1846     }
1847
1848     let field_iter = fields
1849         .into_iter()
1850         .map(StructLitField::Regular)
1851         .chain(base.into_iter().map(StructLitField::Base));
1852
1853     // Foo { a: Foo } - indent is +3, width is -5.
1854     let (h_shape, v_shape) = try_opt!(struct_lit_shape(shape, context, path_str.len() + 3, 2));
1855
1856     let span_lo = |item: &StructLitField| match *item {
1857         StructLitField::Regular(field) => field.span.lo,
1858         StructLitField::Base(expr) => {
1859             let last_field_hi = fields.last().map_or(span.lo, |field| field.span.hi);
1860             let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo));
1861             let pos = snippet.find_uncommented("..").unwrap();
1862             last_field_hi + BytePos(pos as u32)
1863         }
1864     };
1865     let span_hi = |item: &StructLitField| match *item {
1866         StructLitField::Regular(field) => field.span.hi,
1867         StructLitField::Base(expr) => expr.span.hi,
1868     };
1869     let rewrite = |item: &StructLitField| match *item {
1870         StructLitField::Regular(field) => {
1871             // The 1 taken from the v_budget is for the comma.
1872             rewrite_field(context, field, try_opt!(v_shape.sub_width(1)))
1873         }
1874         StructLitField::Base(expr) => {
1875             // 2 = ..
1876             expr.rewrite(context, try_opt!(v_shape.shrink_left(2)))
1877                 .map(|s| format!("..{}", s))
1878         }
1879     };
1880
1881     let items = itemize_list(context.codemap,
1882                              field_iter,
1883                              "}",
1884                              span_lo,
1885                              span_hi,
1886                              rewrite,
1887                              context.codemap.span_after(span, "{"),
1888                              span.hi);
1889     let item_vec = items.collect::<Vec<_>>();
1890
1891     let tactic = struct_lit_tactic(h_shape, context, &item_vec);
1892     let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
1893     let fmt = struct_lit_formatting(nested_shape, tactic, context, base.is_some());
1894
1895     let fields_str = try_opt!(write_list(&item_vec, &fmt));
1896     let fields_str = if context.config.struct_lit_style == IndentStyle::Block &&
1897                         (fields_str.contains('\n') ||
1898                          context.config.struct_lit_multiline_style == MultilineStyle::ForceMulti ||
1899                          fields_str.len() > h_shape.map(|s| s.width).unwrap_or(0)) {
1900         format!("\n{}{}\n{}",
1901                 v_shape.indent.to_string(context.config),
1902                 fields_str,
1903                 shape.indent.to_string(context.config))
1904     } else {
1905         // One liner or visual indent.
1906         format!(" {} ", fields_str)
1907     };
1908
1909     Some(format!("{} {{{}}}", path_str, fields_str))
1910
1911     // FIXME if context.config.struct_lit_style == Visual, but we run out
1912     // of space, we should fall back to BlockIndent.
1913 }
1914
1915 pub fn type_annotation_separator(config: &Config) -> &str {
1916     colon_spaces(config.space_before_type_annotation,
1917                  config.space_after_type_annotation_colon)
1918 }
1919
1920 fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> Option<String> {
1921     let name = &field.ident.node.to_string();
1922     if field.is_shorthand {
1923         Some(name.to_string())
1924     } else {
1925         let separator = type_annotation_separator(context.config);
1926         let overhead = name.len() + separator.len();
1927         let mut expr_shape = try_opt!(shape.sub_width(overhead));
1928         expr_shape.offset += overhead;
1929         let expr = field.expr.rewrite(context, expr_shape);
1930
1931         let mut attrs_str = try_opt!((*field.attrs).rewrite(context, shape));
1932         if !attrs_str.is_empty() {
1933             attrs_str.push_str(&format!("\n{}", shape.indent.to_string(context.config)));
1934         };
1935
1936         match expr {
1937             Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)),
1938             None => {
1939                 let expr_offset = shape.indent.block_indent(context.config);
1940                 let expr = field
1941                     .expr
1942                     .rewrite(context, Shape::indented(expr_offset, context.config));
1943                 expr.map(|s| {
1944                              format!("{}{}:\n{}{}",
1945                                      attrs_str,
1946                                      name,
1947                                      expr_offset.to_string(&context.config),
1948                                      s)
1949                          })
1950             }
1951         }
1952     }
1953 }
1954
1955 pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
1956                             mut items: I,
1957                             span: Span,
1958                             shape: Shape)
1959                             -> Option<String>
1960     where I: ExactSizeIterator,
1961           <I as Iterator>::Item: Deref,
1962           <I::Item as Deref>::Target: Rewrite + Spanned + 'a
1963 {
1964     debug!("rewrite_tuple {:?}", shape);
1965     // In case of length 1, need a trailing comma
1966     if items.len() == 1 {
1967         // 3 = "(" + ",)"
1968         let nested_shape = try_opt!(shape.sub_width(3)).visual_indent(1);
1969         return items
1970                    .next()
1971                    .unwrap()
1972                    .rewrite(context, nested_shape)
1973                    .map(|s| if context.config.spaces_within_parens {
1974                             format!("( {}, )", s)
1975                         } else {
1976                             format!("({},)", s)
1977                         });
1978     }
1979
1980     let list_lo = context.codemap.span_after(span, "(");
1981     let nested_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
1982     let items = itemize_list(context.codemap,
1983                              items,
1984                              ")",
1985                              |item| item.span().lo,
1986                              |item| item.span().hi,
1987                              |item| item.rewrite(context, nested_shape),
1988                              list_lo,
1989                              span.hi - BytePos(1));
1990     let list_str = try_opt!(format_item_list(items, nested_shape, context.config));
1991
1992     if context.config.spaces_within_parens && list_str.len() > 0 {
1993         Some(format!("( {} )", list_str))
1994     } else {
1995         Some(format!("({})", list_str))
1996     }
1997 }
1998
1999 pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
2000                                         prefix: &str,
2001                                         rewrite: &R,
2002                                         mut shape: Shape,
2003                                         span: Span)
2004                                         -> Option<String> {
2005     // Heuristic: if unary is `&` and `rewrite` contains `{`,
2006     // it is likely that block indent is preferred to visual indent.
2007     if prefix == "&" {
2008         let snippet = String::from(context.snippet(span).trim_left_matches('&'));
2009         let first_line = try_opt!(snippet.lines().nth(0));
2010         if first_line.contains("{") {
2011             shape = try_opt!(shape.sub_width(prefix.len())).block_indent(0);
2012         } else {
2013             shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0);
2014         }
2015     } else {
2016         shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0);
2017     }
2018     rewrite
2019         .rewrite(context, shape)
2020         .map(|r| format!("{}{}", prefix, r))
2021 }
2022
2023 // FIXME: this is probably not correct for multi-line Rewrites. we should
2024 // subtract suffix.len() from the last line budget, not the first!
2025 pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
2026                                         suffix: &str,
2027                                         rewrite: &R,
2028                                         shape: Shape)
2029                                         -> Option<String> {
2030     rewrite
2031         .rewrite(context, try_opt!(shape.sub_width(suffix.len())))
2032         .map(|mut r| {
2033                  r.push_str(suffix);
2034                  r
2035              })
2036 }
2037
2038 fn rewrite_unary_op(context: &RewriteContext,
2039                     op: &ast::UnOp,
2040                     expr: &ast::Expr,
2041                     shape: Shape)
2042                     -> Option<String> {
2043     // For some reason, an UnOp is not spanned like BinOp!
2044     let operator_str = match *op {
2045         ast::UnOp::Deref => "*",
2046         ast::UnOp::Not => "!",
2047         ast::UnOp::Neg => "-",
2048     };
2049     rewrite_unary_prefix(context, operator_str, expr, shape, expr.span)
2050 }
2051
2052 fn rewrite_assignment(context: &RewriteContext,
2053                       lhs: &ast::Expr,
2054                       rhs: &ast::Expr,
2055                       op: Option<&ast::BinOp>,
2056                       shape: Shape)
2057                       -> Option<String> {
2058     let operator_str = match op {
2059         Some(op) => context.snippet(op.span),
2060         None => "=".to_owned(),
2061     };
2062
2063     // 1 = space between lhs and operator.
2064     let lhs_shape = try_opt!(shape.sub_width(operator_str.len() + 1));
2065     let lhs_str = format!("{} {}",
2066                           try_opt!(lhs.rewrite(context, lhs_shape)),
2067                           operator_str);
2068
2069     rewrite_assign_rhs(context, lhs_str, rhs, shape)
2070 }
2071
2072 // The left hand side must contain everything up to, and including, the
2073 // assignment operator.
2074 pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
2075                                            lhs: S,
2076                                            ex: &ast::Expr,
2077                                            shape: Shape)
2078                                            -> Option<String> {
2079     let mut result = lhs.into();
2080     let last_line_width = last_line_width(&result) -
2081                           if result.contains('\n') {
2082                               shape.indent.width()
2083                           } else {
2084                               0
2085                           };
2086     // 1 = space between operator and rhs.
2087     let max_width = try_opt!(shape.width.checked_sub(last_line_width + 1));
2088     let rhs = ex.rewrite(context,
2089                          Shape::offset(max_width,
2090                                        shape.indent,
2091                                        shape.indent.alignment + last_line_width + 1));
2092
2093     fn count_line_breaks(src: &str) -> usize {
2094         src.chars().filter(|&x| x == '\n').count()
2095     }
2096
2097     match rhs {
2098         Some(ref new_str) if count_line_breaks(new_str) < 2 => {
2099             result.push(' ');
2100             result.push_str(new_str);
2101         }
2102         _ => {
2103             // Expression did not fit on the same line as the identifier or is
2104             // at least three lines big. Try splitting the line and see
2105             // if that works better.
2106             let new_offset = shape.indent.block_indent(context.config);
2107             let max_width = try_opt!((shape.width + shape.indent.width())
2108                                          .checked_sub(new_offset.width()));
2109             let new_shape = Shape::legacy(max_width, new_offset);
2110             let new_rhs = ex.rewrite(context, new_shape);
2111
2112             // FIXME: DRY!
2113             match (rhs, new_rhs) {
2114                 (Some(ref orig_rhs), Some(ref replacement_rhs))
2115                     if count_line_breaks(orig_rhs) > count_line_breaks(replacement_rhs) + 1 ||
2116                        (orig_rhs.rewrite(context, shape).is_none() &&
2117                         replacement_rhs.rewrite(context, new_shape).is_some()) => {
2118                     result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
2119                     result.push_str(replacement_rhs);
2120                 }
2121                 (None, Some(ref final_rhs)) => {
2122                     result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
2123                     result.push_str(final_rhs);
2124                 }
2125                 (None, None) => return None,
2126                 (Some(ref orig_rhs), _) => {
2127                     result.push(' ');
2128                     result.push_str(orig_rhs);
2129                 }
2130             }
2131         }
2132     }
2133
2134     Some(result)
2135 }
2136
2137 fn rewrite_expr_addrof(context: &RewriteContext,
2138                        mutability: ast::Mutability,
2139                        expr: &ast::Expr,
2140                        shape: Shape)
2141                        -> Option<String> {
2142     let operator_str = match mutability {
2143         ast::Mutability::Immutable => "&",
2144         ast::Mutability::Mutable => "&mut ",
2145     };
2146     rewrite_unary_prefix(context, operator_str, expr, shape, expr.span)
2147 }