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