]> git.lizzy.rs Git - rust.git/blob - src/expr.rs
0a2fab9cd27943f4b18c970e8d0d22b708db18bd
[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, false)
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 || block.stmts.len() > 1 ||
516                           block_contains_comment(block, context.codemap) ||
517                           prefix.contains('\n');
518
519         if ret_str.is_empty() && !needs_block {
520             // lock.stmts.len() == 1
521             if let Some(ref expr) = stmt_expr(&block.stmts[0]) {
522                 if let Some(rw) = rewrite_closure_expr(expr, &prefix, context, body_shape) {
523                     return Some(rw);
524                 }
525             }
526         }
527
528         if !needs_block {
529             // We need braces, but we might still prefer a one-liner.
530             let stmt = &block.stmts[0];
531             // 4 = braces and spaces.
532             if let Some(body_shape) = body_shape.sub_width(4) {
533                 // Checks if rewrite succeeded and fits on a single line.
534                 if let Some(rewrite) = and_one_line(stmt.rewrite(context, body_shape)) {
535                     return Some(format!("{} {{ {} }}", prefix, rewrite));
536                 }
537             }
538         }
539
540         // Either we require a block, or tried without and failed.
541         return rewrite_closure_block(&block, prefix, context, body_shape);
542     }
543
544     if let Some(rw) = rewrite_closure_expr(body, &prefix, context, body_shape) {
545         return Some(rw);
546     }
547
548     // The closure originally had a non-block expression, but we can't fit on
549     // one line, so we'll insert a block.
550     let block = ast::Block {
551         stmts: vec![ast::Stmt {
552                         id: ast::NodeId::new(0),
553                         node: ast::StmtKind::Expr(ptr::P(body.clone())),
554                         span: body.span,
555                     }],
556         id: ast::NodeId::new(0),
557         rules: ast::BlockCheckMode::Default,
558         span: body.span,
559     };
560     return rewrite_closure_block(&block, prefix, context, body_shape);
561
562     fn rewrite_closure_expr(expr: &ast::Expr,
563                             prefix: &str,
564                             context: &RewriteContext,
565                             shape: Shape)
566                             -> Option<String> {
567         let mut rewrite = expr.rewrite(context, shape);
568         if classify::expr_requires_semi_to_be_stmt(left_most_sub_expr(expr)) {
569             rewrite = and_one_line(rewrite);
570         }
571         rewrite.map(|rw| format!("{} {}", prefix, rw))
572     }
573
574     fn rewrite_closure_block(block: &ast::Block,
575                              prefix: String,
576                              context: &RewriteContext,
577                              shape: Shape)
578                              -> Option<String> {
579         // Start with visual indent, then fall back to block indent if the
580         // closure is large.
581         if let Some(block_str) = block.rewrite(&context, shape) {
582             let block_threshold = context.config.closure_block_indent_threshold;
583             if block_threshold < 0 || block_str.matches('\n').count() <= block_threshold as usize {
584                 if let Some(block_str) = block_str.rewrite(context, shape) {
585                     return Some(format!("{} {}", prefix, block_str));
586                 }
587             }
588         }
589
590         // The body of the closure is big enough to be block indented, that
591         // means we must re-format.
592         let block_shape = shape.block().with_max_width(context.config);
593         let block_str = try_opt!(block.rewrite(&context, block_shape));
594         Some(format!("{} {}",
595                      prefix,
596                      try_opt!(block_str.rewrite(context, block_shape))))
597     }
598 }
599
600 fn and_one_line(x: Option<String>) -> Option<String> {
601     x.and_then(|x| if x.contains('\n') { None } else { Some(x) })
602 }
603
604 fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
605     debug!("nop_block_collapse {:?} {}", block_str, budget);
606     block_str.map(|block_str| if block_str.starts_with('{') && budget >= 2 &&
607                                  (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() ==
608                                   block_str.len() - 2) {
609                       "{}".to_owned()
610                   } else {
611                       block_str.to_owned()
612                   })
613 }
614
615 impl Rewrite for ast::Block {
616     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
617         // shape.width is used only for the single line case: either the empty block `{}`,
618         // or an unsafe expression `unsafe { e }`.
619
620         if self.stmts.is_empty() && !block_contains_comment(self, context.codemap) &&
621            shape.width >= 2 {
622             return Some("{}".to_owned());
623         }
624
625         // If a block contains only a single-line comment, then leave it on one line.
626         let user_str = context.snippet(self.span);
627         let user_str = user_str.trim();
628         if user_str.starts_with('{') && user_str.ends_with('}') {
629             let comment_str = user_str[1..user_str.len() - 1].trim();
630             if self.stmts.is_empty() && !comment_str.contains('\n') &&
631                !comment_str.starts_with("//") &&
632                comment_str.len() + 4 <= shape.width {
633                 return Some(format!("{{ {} }}", comment_str));
634             }
635         }
636
637         let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
638         visitor.block_indent = shape.indent;
639
640         let prefix = match self.rules {
641             ast::BlockCheckMode::Unsafe(..) => {
642                 let snippet = context.snippet(self.span);
643                 let open_pos = try_opt!(snippet.find_uncommented("{"));
644                 visitor.last_pos = self.span.lo + BytePos(open_pos as u32);
645
646                 // Extract comment between unsafe and block start.
647                 let trimmed = &snippet[6..open_pos].trim();
648
649                 let prefix = if !trimmed.is_empty() {
650                     // 9 = "unsafe  {".len(), 7 = "unsafe ".len()
651                     let budget = try_opt!(shape.width.checked_sub(9));
652                     format!("unsafe {} ",
653                             try_opt!(rewrite_comment(trimmed,
654                                                      true,
655                                                      Shape::legacy(budget, shape.indent + 7),
656                                                      context.config)))
657                 } else {
658                     "unsafe ".to_owned()
659                 };
660
661                 if is_simple_block(self, context.codemap) && prefix.len() < shape.width {
662                     let expr_str =
663                         self.stmts[0].rewrite(context,
664                                               Shape::legacy(shape.width - prefix.len(),
665                                                             shape.indent));
666                     let expr_str = try_opt!(expr_str);
667                     let result = format!("{}{{ {} }}", prefix, expr_str);
668                     if result.len() <= shape.width && !result.contains('\n') {
669                         return Some(result);
670                     }
671                 }
672
673                 prefix
674             }
675             ast::BlockCheckMode::Default => {
676                 visitor.last_pos = self.span.lo;
677
678                 String::new()
679             }
680         };
681
682         visitor.visit_block(self);
683         if visitor.failed && shape.indent.alignment != 0 {
684             self.rewrite(context,
685                          Shape::indented(shape.indent.block_only(), context.config))
686         } else {
687             Some(format!("{}{}", prefix, visitor.buffer))
688         }
689     }
690 }
691
692 impl Rewrite for ast::Stmt {
693     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
694         let result = match self.node {
695             ast::StmtKind::Local(ref local) => local.rewrite(context, shape),
696             ast::StmtKind::Expr(ref ex) |
697             ast::StmtKind::Semi(ref ex) => {
698                 let suffix = if semicolon_for_stmt(self) { ";" } else { "" };
699
700                 format_expr(ex,
701                             match self.node {
702                                 ast::StmtKind::Expr(_) => ExprType::SubExpression,
703                                 ast::StmtKind::Semi(_) => ExprType::Statement,
704                                 _ => unreachable!(),
705                             },
706                             context,
707                             try_opt!(shape.sub_width(suffix.len())))
708                         .map(|s| s + suffix)
709             }
710             ast::StmtKind::Mac(..) |
711             ast::StmtKind::Item(..) => None,
712         };
713         result.and_then(|res| recover_comment_removed(res, self.span, context, shape))
714     }
715 }
716
717 // Abstraction over control flow expressions
718 #[derive(Debug)]
719 struct ControlFlow<'a> {
720     cond: Option<&'a ast::Expr>,
721     block: &'a ast::Block,
722     else_block: Option<&'a ast::Expr>,
723     label: Option<ast::SpannedIdent>,
724     pat: Option<&'a ast::Pat>,
725     keyword: &'a str,
726     matcher: &'a str,
727     connector: &'a str,
728     allow_single_line: bool,
729     // True if this is an `if` expression in an `else if` :-( hacky
730     nested_if: bool,
731     span: Span,
732 }
733
734 impl<'a> ControlFlow<'a> {
735     fn new_if(cond: &'a ast::Expr,
736               pat: Option<&'a ast::Pat>,
737               block: &'a ast::Block,
738               else_block: Option<&'a ast::Expr>,
739               allow_single_line: bool,
740               nested_if: bool,
741               span: Span)
742               -> ControlFlow<'a> {
743         ControlFlow {
744             cond: Some(cond),
745             block: block,
746             else_block: else_block,
747             label: None,
748             pat: pat,
749             keyword: "if",
750             matcher: match pat {
751                 Some(..) => "let",
752                 None => "",
753             },
754             connector: " =",
755             allow_single_line: allow_single_line,
756             nested_if: nested_if,
757             span: span,
758         }
759     }
760
761     fn new_loop(block: &'a ast::Block,
762                 label: Option<ast::SpannedIdent>,
763                 span: Span)
764                 -> ControlFlow<'a> {
765         ControlFlow {
766             cond: None,
767             block: block,
768             else_block: None,
769             label: label,
770             pat: None,
771             keyword: "loop",
772             matcher: "",
773             connector: "",
774             allow_single_line: false,
775             nested_if: false,
776             span: span,
777         }
778     }
779
780     fn new_while(pat: Option<&'a ast::Pat>,
781                  cond: &'a ast::Expr,
782                  block: &'a ast::Block,
783                  label: Option<ast::SpannedIdent>,
784                  span: Span)
785                  -> ControlFlow<'a> {
786         ControlFlow {
787             cond: Some(cond),
788             block: block,
789             else_block: None,
790             label: label,
791             pat: pat,
792             keyword: "while",
793             matcher: match pat {
794                 Some(..) => "let",
795                 None => "",
796             },
797             connector: " =",
798             allow_single_line: false,
799             nested_if: false,
800             span: span,
801         }
802     }
803
804     fn new_for(pat: &'a ast::Pat,
805                cond: &'a ast::Expr,
806                block: &'a ast::Block,
807                label: Option<ast::SpannedIdent>,
808                span: Span)
809                -> ControlFlow<'a> {
810         ControlFlow {
811             cond: Some(cond),
812             block: block,
813             else_block: None,
814             label: label,
815             pat: Some(pat),
816             keyword: "for",
817             matcher: "",
818             connector: " in",
819             allow_single_line: false,
820             nested_if: false,
821             span: span,
822         }
823     }
824
825     fn rewrite_single_line(&self,
826                            pat_expr_str: &str,
827                            context: &RewriteContext,
828                            width: usize)
829                            -> Option<String> {
830         assert!(self.allow_single_line);
831         let else_block = try_opt!(self.else_block);
832         let fixed_cost = self.keyword.len() + "  {  } else {  }".len();
833
834         if let ast::ExprKind::Block(ref else_node) = else_block.node {
835             if !is_simple_block(self.block, context.codemap) ||
836                !is_simple_block(else_node, context.codemap) ||
837                pat_expr_str.contains('\n') {
838                 return None;
839             }
840
841             let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
842             let expr = &self.block.stmts[0];
843             let if_str = try_opt!(expr.rewrite(context, Shape::legacy(new_width, Indent::empty())));
844
845             let new_width = try_opt!(new_width.checked_sub(if_str.len()));
846             let else_expr = &else_node.stmts[0];
847             let else_str = try_opt!(else_expr.rewrite(context,
848                                                       Shape::legacy(new_width, Indent::empty())));
849
850             if if_str.contains('\n') || else_str.contains('\n') {
851                 return None;
852             }
853
854             let result = format!("{} {} {{ {} }} else {{ {} }}",
855                                  self.keyword,
856                                  pat_expr_str,
857                                  if_str,
858                                  else_str);
859
860             if result.len() <= width {
861                 return Some(result);
862             }
863         }
864
865         None
866     }
867 }
868
869 impl<'a> Rewrite for ControlFlow<'a> {
870     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
871         debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
872         let constr_shape = if self.nested_if {
873             // We are part of an if-elseif-else chain. Our constraints are tightened.
874             // 7 = "} else " .len()
875             try_opt!(shape.shrink_left(7))
876         } else {
877             shape
878         };
879
880         let label_string = rewrite_label(self.label);
881         // 1 = space after keyword.
882         let add_offset = self.keyword.len() + label_string.len() + 1;
883
884         let pat_expr_string = match self.cond {
885             Some(cond) => {
886                 let mut cond_shape = match context.config.control_style {
887                     Style::Default => try_opt!(constr_shape.shrink_left(add_offset)),
888                     Style::Rfc => constr_shape,
889                 };
890                 if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine {
891                     // 2 = " {".len()
892                     cond_shape = try_opt!(cond_shape.sub_width(2));
893                 }
894
895                 try_opt!(rewrite_pat_expr(context,
896                                           self.pat,
897                                           cond,
898                                           self.matcher,
899                                           self.connector,
900                                           cond_shape))
901             }
902             None => String::new(),
903         };
904
905         let force_newline_brace = context.config.control_style == Style::Rfc &&
906                                   pat_expr_string.contains('\n');
907
908         // Try to format if-else on single line.
909         if self.allow_single_line && context.config.single_line_if_else_max_width > 0 {
910             let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width);
911
912             if trial.is_some() &&
913                trial.as_ref().unwrap().len() <= context.config.single_line_if_else_max_width {
914                 return trial;
915             }
916         }
917
918         let used_width = if pat_expr_string.contains('\n') {
919             last_line_width(&pat_expr_string)
920         } else {
921             // 2 = spaces after keyword and condition.
922             label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
923         };
924
925         let block_width = shape.width.checked_sub(used_width).unwrap_or(0);
926         // This is used only for the empty block case: `{}`. So, we use 1 if we know
927         // we should avoid the single line case.
928         let block_width = if self.else_block.is_some() || self.nested_if {
929             min(1, block_width)
930         } else {
931             block_width
932         };
933
934         let block_shape = Shape {
935             width: block_width,
936             ..shape
937         };
938         let block_str = try_opt!(self.block.rewrite(context, block_shape));
939
940         let cond_span = if let Some(cond) = self.cond {
941             cond.span
942         } else {
943             mk_sp(self.block.span.lo, self.block.span.lo)
944         };
945
946         // for event in event
947         let between_kwd_cond =
948             mk_sp(context.codemap.span_after(self.span, self.keyword.trim()),
949                   self.pat
950                       .map_or(cond_span.lo, |p| if self.matcher.is_empty() {
951                 p.span.lo
952             } else {
953                 context.codemap.span_before(self.span, self.matcher.trim())
954             }));
955
956         let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
957
958         let after_cond_comment =
959             extract_comment(mk_sp(cond_span.hi, self.block.span.lo), context, shape);
960
961         let alt_block_sep = String::from("\n") +
962                             &shape.indent.block_only().to_string(context.config);
963         let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
964             ""
965         } else if context.config.control_brace_style == ControlBraceStyle::AlwaysNextLine ||
966                   force_newline_brace {
967             alt_block_sep.as_str()
968         } else {
969             " "
970         };
971
972         let mut result = format!("{}{}{}{}{}{}",
973                                  label_string,
974                                  self.keyword,
975                                  between_kwd_cond_comment
976                                      .as_ref()
977                                      .map_or(if pat_expr_string.is_empty() ||
978                                                 pat_expr_string.starts_with('\n') {
979                                                  ""
980                                              } else {
981                                                  " "
982                                              },
983                                              |s| &**s),
984                                  pat_expr_string,
985                                  after_cond_comment.as_ref().map_or(block_sep, |s| &**s),
986                                  block_str);
987
988         if let Some(else_block) = self.else_block {
989             // Since this is an else block, we should not indent for the assignment preceding
990             // the original if, so set shape.offset to shape.indent.alignment.
991             let shape = Shape {
992                 offset: shape.indent.alignment,
993                 ..shape
994             };
995             let mut last_in_chain = false;
996             let rewrite = match else_block.node {
997                 // If the else expression is another if-else expression, prevent it
998                 // from being formatted on a single line.
999                 // Note how we're passing the original shape, as the
1000                 // cost of "else" should not cascade.
1001                 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref next_else_block) => {
1002                     ControlFlow::new_if(cond,
1003                                         Some(pat),
1004                                         if_block,
1005                                         next_else_block.as_ref().map(|e| &**e),
1006                                         false,
1007                                         true,
1008                                         mk_sp(else_block.span.lo, self.span.hi))
1009                             .rewrite(context, shape)
1010                 }
1011                 ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
1012                     ControlFlow::new_if(cond,
1013                                         None,
1014                                         if_block,
1015                                         next_else_block.as_ref().map(|e| &**e),
1016                                         false,
1017                                         true,
1018                                         mk_sp(else_block.span.lo, self.span.hi))
1019                             .rewrite(context, shape)
1020                 }
1021                 _ => {
1022                     last_in_chain = true;
1023                     // When rewriting a block, the width is only used for single line
1024                     // blocks, passing 1 lets us avoid that.
1025                     let else_shape = Shape {
1026                         width: min(1, shape.width),
1027                         ..shape
1028                     };
1029                     else_block.rewrite(context, else_shape)
1030                 }
1031             };
1032
1033             let between_kwd_else_block =
1034                 mk_sp(self.block.span.hi,
1035                       context
1036                           .codemap
1037                           .span_before(mk_sp(self.block.span.hi, else_block.span.lo), "else"));
1038             let between_kwd_else_block_comment =
1039                 extract_comment(between_kwd_else_block, context, shape);
1040
1041             let after_else =
1042                 mk_sp(context
1043                           .codemap
1044                           .span_after(mk_sp(self.block.span.hi, else_block.span.lo), "else"),
1045                       else_block.span.lo);
1046             let after_else_comment = extract_comment(after_else, context, shape);
1047
1048             let between_sep = match context.config.control_brace_style {
1049                 ControlBraceStyle::AlwaysNextLine |
1050                 ControlBraceStyle::ClosingNextLine => &*alt_block_sep,
1051                 ControlBraceStyle::AlwaysSameLine => " ",
1052             };
1053             let after_sep = match context.config.control_brace_style {
1054                 ControlBraceStyle::AlwaysNextLine if last_in_chain => &*alt_block_sep,
1055                 _ => " ",
1056             };
1057             try_opt!(write!(&mut result,
1058                             "{}else{}",
1059                             between_kwd_else_block_comment
1060                                 .as_ref()
1061                                 .map_or(between_sep, |s| &**s),
1062                             after_else_comment.as_ref().map_or(after_sep, |s| &**s))
1063                              .ok());
1064             result.push_str(&try_opt!(rewrite));
1065         }
1066
1067         Some(result)
1068     }
1069 }
1070
1071 fn rewrite_label(label: Option<ast::SpannedIdent>) -> String {
1072     match label {
1073         Some(ident) => format!("{}: ", ident.node),
1074         None => "".to_owned(),
1075     }
1076 }
1077
1078 fn extract_comment(span: Span, context: &RewriteContext, shape: Shape) -> Option<String> {
1079     let comment_str = context.snippet(span);
1080     if contains_comment(&comment_str) {
1081         let comment = try_opt!(rewrite_comment(comment_str.trim(), false, shape, context.config));
1082         Some(format!("\n{indent}{}\n{indent}",
1083                      comment,
1084                      indent = shape.indent.to_string(context.config)))
1085     } else {
1086         None
1087     }
1088 }
1089
1090 fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool {
1091     let snippet = codemap.span_to_snippet(block.span).unwrap();
1092     contains_comment(&snippet)
1093 }
1094
1095 // Checks that a block contains no statements, an expression and no comments.
1096 // FIXME: incorrectly returns false when comment is contained completely within
1097 // the expression.
1098 pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1099     (block.stmts.len() == 1 && stmt_is_expr(&block.stmts[0]) &&
1100      !block_contains_comment(block, codemap))
1101 }
1102
1103 /// Checks whether a block contains at most one statement or expression, and no comments.
1104 pub fn is_simple_block_stmt(block: &ast::Block, codemap: &CodeMap) -> bool {
1105     block.stmts.len() <= 1 && !block_contains_comment(block, codemap)
1106 }
1107
1108 /// Checks whether a block contains no statements, expressions, or comments.
1109 pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1110     block.stmts.is_empty() && !block_contains_comment(block, codemap)
1111 }
1112
1113 pub fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
1114     match stmt.node {
1115         ast::StmtKind::Expr(..) => true,
1116         _ => false,
1117     }
1118 }
1119
1120 fn is_unsafe_block(block: &ast::Block) -> bool {
1121     if let ast::BlockCheckMode::Unsafe(..) = block.rules {
1122         true
1123     } else {
1124         false
1125     }
1126 }
1127
1128 // inter-match-arm-comment-rules:
1129 //  - all comments following a match arm before the start of the next arm
1130 //    are about the second arm
1131 fn rewrite_match_arm_comment(context: &RewriteContext,
1132                              missed_str: &str,
1133                              shape: Shape,
1134                              arm_indent_str: &str)
1135                              -> Option<String> {
1136     // The leading "," is not part of the arm-comment
1137     let missed_str = match missed_str.find_uncommented(",") {
1138         Some(n) => &missed_str[n + 1..],
1139         None => &missed_str[..],
1140     };
1141
1142     let mut result = String::new();
1143     // any text not preceeded by a newline is pushed unmodified to the block
1144     let first_brk = missed_str.find(|c: char| c == '\n').unwrap_or(0);
1145     result.push_str(&missed_str[..first_brk]);
1146     let missed_str = &missed_str[first_brk..]; // If missed_str had one newline, it starts with it
1147
1148     let first = missed_str
1149         .find(|c: char| !c.is_whitespace())
1150         .unwrap_or(missed_str.len());
1151     if missed_str[..first].chars().filter(|c| c == &'\n').count() >= 2 {
1152         // Excessive vertical whitespace before comment should be preserved
1153         // FIXME handle vertical whitespace better
1154         result.push('\n');
1155     }
1156     let missed_str = missed_str[first..].trim();
1157     if !missed_str.is_empty() {
1158         let comment = try_opt!(rewrite_comment(&missed_str, false, shape, context.config));
1159         result.push('\n');
1160         result.push_str(arm_indent_str);
1161         result.push_str(&comment);
1162     }
1163
1164     Some(result)
1165 }
1166
1167 fn rewrite_match(context: &RewriteContext,
1168                  cond: &ast::Expr,
1169                  arms: &[ast::Arm],
1170                  shape: Shape,
1171                  span: Span)
1172                  -> Option<String> {
1173     if arms.is_empty() {
1174         return None;
1175     }
1176
1177     // `match `cond` {`
1178     let cond_shape = try_opt!(shape.shrink_left(6));
1179     let cond_shape = try_opt!(cond_shape.sub_width(2));
1180     let cond_str = try_opt!(cond.rewrite(context, cond_shape));
1181     let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
1182     let block_sep = match context.config.control_brace_style {
1183         ControlBraceStyle::AlwaysSameLine => " ",
1184         _ => alt_block_sep.as_str(),
1185     };
1186     let mut result = format!("match {}{}{{", cond_str, block_sep);
1187
1188     let arm_shape = if context.config.indent_match_arms {
1189         shape.block_indent(context.config.tab_spaces)
1190     } else {
1191         shape.block_indent(0)
1192     };
1193
1194     let arm_indent_str = arm_shape.indent.to_string(context.config);
1195
1196     let open_brace_pos = context
1197         .codemap
1198         .span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{");
1199
1200     for (i, arm) in arms.iter().enumerate() {
1201         // Make sure we get the stuff between arms.
1202         let missed_str = if i == 0 {
1203             context.snippet(mk_sp(open_brace_pos, arm_start_pos(arm)))
1204         } else {
1205             context.snippet(mk_sp(arm_end_pos(&arms[i - 1]), arm_start_pos(arm)))
1206         };
1207         let comment =
1208             try_opt!(rewrite_match_arm_comment(context, &missed_str, arm_shape, &arm_indent_str));
1209         result.push_str(&comment);
1210         result.push('\n');
1211         result.push_str(&arm_indent_str);
1212
1213         let arm_str = arm.rewrite(&context, arm_shape.with_max_width(context.config));
1214         if let Some(ref arm_str) = arm_str {
1215             result.push_str(arm_str);
1216         } else {
1217             // We couldn't format the arm, just reproduce the source.
1218             let snippet = context.snippet(mk_sp(arm_start_pos(arm), arm_end_pos(arm)));
1219             result.push_str(&snippet);
1220             result.push_str(arm_comma(context.config, &arm.body));
1221         }
1222     }
1223     // BytePos(1) = closing match brace.
1224     let last_span = mk_sp(arm_end_pos(&arms[arms.len() - 1]), span.hi - BytePos(1));
1225     let last_comment = context.snippet(last_span);
1226     let comment =
1227         try_opt!(rewrite_match_arm_comment(context, &last_comment, arm_shape, &arm_indent_str));
1228     result.push_str(&comment);
1229     result.push('\n');
1230     result.push_str(&shape.indent.to_string(context.config));
1231     result.push('}');
1232     Some(result)
1233 }
1234
1235 fn arm_start_pos(arm: &ast::Arm) -> BytePos {
1236     let &ast::Arm {
1237         ref attrs,
1238         ref pats,
1239         ..
1240     } = arm;
1241     if !attrs.is_empty() {
1242         return attrs[0].span.lo;
1243     }
1244
1245     pats[0].span.lo
1246 }
1247
1248 fn arm_end_pos(arm: &ast::Arm) -> BytePos {
1249     arm.body.span.hi
1250 }
1251
1252 fn arm_comma(config: &Config, body: &ast::Expr) -> &'static str {
1253     if config.match_block_trailing_comma {
1254         ","
1255     } else if let ast::ExprKind::Block(ref block) = body.node {
1256         if let ast::BlockCheckMode::Default = block.rules {
1257             ""
1258         } else {
1259             ","
1260         }
1261     } else {
1262         ","
1263     }
1264 }
1265
1266 // Match arms.
1267 impl Rewrite for ast::Arm {
1268     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
1269         debug!("Arm::rewrite {:?} {:?}", self, shape);
1270         let &ast::Arm {
1271             ref attrs,
1272             ref pats,
1273             ref guard,
1274             ref body,
1275         } = self;
1276
1277         // FIXME this is all a bit grotty, would be nice to abstract out the
1278         // treatment of attributes.
1279         let attr_str = if !attrs.is_empty() {
1280             // We only use this visitor for the attributes, should we use it for
1281             // more?
1282             let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
1283             attr_visitor.block_indent = shape.indent.block_only();
1284             attr_visitor.last_pos = attrs[0].span.lo;
1285             if attr_visitor.visit_attrs(attrs) {
1286                 // Attributes included a skip instruction.
1287                 return None;
1288             }
1289             attr_visitor.format_missing(pats[0].span.lo);
1290             attr_visitor.buffer.to_string()
1291         } else {
1292             String::new()
1293         };
1294
1295         // Patterns
1296         // 5 = ` => {`
1297         let pat_shape = try_opt!(shape.sub_width(5));
1298
1299         let pat_strs = try_opt!(pats.iter()
1300                                     .map(|p| p.rewrite(context, pat_shape))
1301                                     .collect::<Option<Vec<_>>>());
1302
1303         let all_simple = pat_strs.iter().all(|p| pat_is_simple(p));
1304         let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
1305         let fmt = ListFormatting {
1306             tactic: if all_simple {
1307                 DefinitiveListTactic::Mixed
1308             } else {
1309                 DefinitiveListTactic::Vertical
1310             },
1311             separator: " |",
1312             trailing_separator: SeparatorTactic::Never,
1313             shape: pat_shape,
1314             ends_with_newline: false,
1315             config: context.config,
1316         };
1317         let pats_str = try_opt!(write_list(items, &fmt));
1318
1319         let guard_shape = if pats_str.contains('\n') {
1320             shape.with_max_width(context.config)
1321         } else {
1322             shape
1323         };
1324
1325         let guard_str = try_opt!(rewrite_guard(context,
1326                                                guard,
1327                                                guard_shape,
1328                                                trimmed_last_line_width(&pats_str)));
1329
1330         let pats_str = format!("{}{}", pats_str, guard_str);
1331
1332         let body = match body.node {
1333             ast::ExprKind::Block(ref block) if !is_unsafe_block(block) &&
1334                                                is_simple_block(block, context.codemap) &&
1335                                                context.config.wrap_match_arms => {
1336                 if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node {
1337                     expr
1338                 } else {
1339                     &**body
1340                 }
1341             }
1342             _ => &**body,
1343         };
1344
1345         let comma = arm_comma(&context.config, body);
1346         let alt_block_sep = String::from("\n") +
1347                             &shape.indent.block_only().to_string(context.config);
1348
1349         let pat_width = extra_offset(&pats_str, shape);
1350         // Let's try and get the arm body on the same line as the condition.
1351         // 4 = ` => `.len()
1352         if shape.width > pat_width + comma.len() + 4 {
1353             let arm_shape = shape
1354                 .offset_left(pat_width + 4)
1355                 .unwrap()
1356                 .sub_width(comma.len())
1357                 .unwrap();
1358             let rewrite = nop_block_collapse(body.rewrite(context, arm_shape), arm_shape.width);
1359             let is_block = if let ast::ExprKind::Block(..) = body.node {
1360                 true
1361             } else {
1362                 false
1363             };
1364
1365             match rewrite {
1366                 Some(ref body_str) if (!body_str.contains('\n') &&
1367                                        body_str.len() <= arm_shape.width) ||
1368                                       !context.config.wrap_match_arms ||
1369                                       is_block => {
1370                     let block_sep = match context.config.control_brace_style {
1371                         ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep.as_str(),
1372                         _ => " ",
1373                     };
1374
1375                     return Some(format!("{}{} =>{}{}{}",
1376                                         attr_str.trim_left(),
1377                                         pats_str,
1378                                         block_sep,
1379                                         body_str,
1380                                         comma));
1381                 }
1382                 _ => {}
1383             }
1384         }
1385
1386         // FIXME: we're doing a second rewrite of the expr; This may not be
1387         // necessary.
1388         let body_shape = try_opt!(shape.sub_width(context.config.tab_spaces))
1389             .block_indent(context.config.tab_spaces);
1390         let next_line_body = try_opt!(nop_block_collapse(body.rewrite(context, body_shape),
1391                                                          body_shape.width));
1392         let indent_str = shape
1393             .indent
1394             .block_indent(context.config)
1395             .to_string(context.config);
1396         let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
1397             if context.config.match_block_trailing_comma {
1398                 ("{", "},")
1399             } else {
1400                 ("{", "}")
1401             }
1402         } else {
1403             ("", ",")
1404         };
1405
1406
1407         let block_sep = match context.config.control_brace_style {
1408             ControlBraceStyle::AlwaysNextLine => alt_block_sep + body_prefix + "\n",
1409             _ if body_prefix.is_empty() => "\n".to_owned(),
1410             _ => " ".to_owned() + body_prefix + "\n",
1411         };
1412
1413         if context.config.wrap_match_arms {
1414             Some(format!("{}{} =>{}{}{}\n{}{}",
1415                          attr_str.trim_left(),
1416                          pats_str,
1417                          block_sep,
1418                          indent_str,
1419                          next_line_body,
1420                          shape.indent.to_string(context.config),
1421                          body_suffix))
1422         } else {
1423             Some(format!("{}{} =>{}{}{}{}",
1424                          attr_str.trim_left(),
1425                          pats_str,
1426                          block_sep,
1427                          indent_str,
1428                          next_line_body,
1429                          body_suffix))
1430         }
1431     }
1432 }
1433
1434 // A pattern is simple if it is very short or it is short-ish and just a path.
1435 // E.g. `Foo::Bar` is simple, but `Foo(..)` is not.
1436 fn pat_is_simple(pat_str: &str) -> bool {
1437     pat_str.len() <= 16 ||
1438     (pat_str.len() <= 24 && pat_str.chars().all(|c| c.is_alphabetic() || c == ':'))
1439 }
1440
1441 // The `if ...` guard on a match arm.
1442 fn rewrite_guard(context: &RewriteContext,
1443                  guard: &Option<ptr::P<ast::Expr>>,
1444                  shape: Shape,
1445                  // The amount of space used up on this line for the pattern in
1446                  // the arm (excludes offset).
1447                  pattern_width: usize)
1448                  -> Option<String> {
1449     if let Some(ref guard) = *guard {
1450         // First try to fit the guard string on the same line as the pattern.
1451         // 4 = ` if `, 5 = ` => {`
1452         let overhead = pattern_width + 4 + 5;
1453         if overhead < shape.width {
1454             let cond_shape = shape
1455                 .shrink_left(pattern_width + 4)
1456                 .unwrap()
1457                 .sub_width(5)
1458                 .unwrap();
1459             let cond_str = guard.rewrite(context, cond_shape);
1460             if let Some(cond_str) = cond_str {
1461                 return Some(format!(" if {}", cond_str));
1462             }
1463         }
1464
1465         // Not enough space to put the guard after the pattern, try a newline.
1466         let overhead = shape.indent.block_indent(context.config).width() + 4 + 5;
1467         if overhead < shape.width {
1468             let cond_str = guard.rewrite(context,
1469                                          Shape::legacy(shape.width - overhead,
1470                                                        // 3 == `if `
1471                                                        shape.indent.block_indent(context.config) +
1472                                                        3));
1473             if let Some(cond_str) = cond_str {
1474                 return Some(format!("\n{}if {}",
1475                                     shape
1476                                         .indent
1477                                         .block_indent(context.config)
1478                                         .to_string(context.config),
1479                                     cond_str));
1480             }
1481         }
1482
1483         None
1484     } else {
1485         Some(String::new())
1486     }
1487 }
1488
1489 fn rewrite_pat_expr(context: &RewriteContext,
1490                     pat: Option<&ast::Pat>,
1491                     expr: &ast::Expr,
1492                     matcher: &str,
1493                     // Connecting piece between pattern and expression,
1494                     // *without* trailing space.
1495                     connector: &str,
1496                     shape: Shape)
1497                     -> Option<String> {
1498     debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, pat, expr);
1499     let mut pat_string = String::new();
1500     let mut result = match pat {
1501         Some(pat) => {
1502             let matcher = if matcher.is_empty() {
1503                 matcher.to_owned()
1504             } else {
1505                 format!("{} ", matcher)
1506             };
1507             let pat_shape = try_opt!(try_opt!(shape.shrink_left(matcher.len()))
1508                                          .sub_width(connector.len()));
1509             pat_string = try_opt!(pat.rewrite(context, pat_shape));
1510             format!("{}{}{}", matcher, pat_string, connector)
1511         }
1512         None => String::new(),
1513     };
1514
1515     // Consider only the last line of the pat string.
1516     let extra_offset = extra_offset(&result, shape);
1517
1518     // The expression may (partially) fit on the current line.
1519     if shape.width > extra_offset + 1 {
1520         let spacer = if pat.is_some() { " " } else { "" };
1521
1522         let expr_shape = try_opt!(shape.offset_left(extra_offset + spacer.len()));
1523         let expr_rewrite = expr.rewrite(context, expr_shape);
1524
1525         if let Some(expr_string) = expr_rewrite {
1526             if pat.is_none() || pat_is_simple(&pat_string) || !expr_string.contains('\n') {
1527                 result.push_str(spacer);
1528                 result.push_str(&expr_string);
1529                 return Some(result);
1530             }
1531         }
1532     }
1533
1534     let nested_indent = shape.indent.block_only().block_indent(context.config);
1535
1536     // The expression won't fit on the current line, jump to next.
1537     result.push('\n');
1538     result.push_str(&nested_indent.to_string(context.config));
1539
1540     let expr_rewrite = expr.rewrite(&context, Shape::indented(nested_indent, context.config));
1541     result.push_str(&try_opt!(expr_rewrite));
1542
1543     Some(result)
1544 }
1545
1546 fn rewrite_string_lit(context: &RewriteContext, span: Span, shape: Shape) -> Option<String> {
1547     let string_lit = context.snippet(span);
1548
1549     if !context.config.format_strings && !context.config.force_format_strings {
1550         return Some(string_lit);
1551     }
1552
1553     if !context.config.force_format_strings &&
1554        !string_requires_rewrite(context, span, &string_lit, shape) {
1555         return Some(string_lit);
1556     }
1557
1558     let fmt = StringFormat {
1559         opener: "\"",
1560         closer: "\"",
1561         line_start: " ",
1562         line_end: "\\",
1563         shape: shape,
1564         trim_end: false,
1565         config: context.config,
1566     };
1567
1568     // Remove the quote characters.
1569     let str_lit = &string_lit[1..string_lit.len() - 1];
1570
1571     rewrite_string(str_lit, &fmt)
1572 }
1573
1574 fn string_requires_rewrite(context: &RewriteContext,
1575                            span: Span,
1576                            string: &str,
1577                            shape: Shape)
1578                            -> bool {
1579     if context.codemap.lookup_char_pos(span.lo).col.0 != shape.indent.width() {
1580         return true;
1581     }
1582
1583     for (i, line) in string.lines().enumerate() {
1584         if i == 0 {
1585             if line.len() > shape.width {
1586                 return true;
1587             }
1588         } else {
1589             if line.len() > shape.width + shape.indent.width() {
1590                 return true;
1591             }
1592         }
1593     }
1594
1595     false
1596 }
1597
1598 pub fn rewrite_call<R>(context: &RewriteContext,
1599                        callee: &R,
1600                        args: &[ptr::P<ast::Expr>],
1601                        span: Span,
1602                        shape: Shape,
1603                        force_no_trailing_comma: bool)
1604                        -> Option<String>
1605     where R: Rewrite
1606 {
1607     let closure = |callee_max_width| {
1608         rewrite_call_inner(context,
1609                            callee,
1610                            callee_max_width,
1611                            args,
1612                            span,
1613                            shape,
1614                            force_no_trailing_comma)
1615     };
1616
1617     // 2 is for parens
1618     let max_width = try_opt!(shape.width.checked_sub(2));
1619     binary_search(1, max_width, closure)
1620 }
1621
1622 fn rewrite_call_inner<R>(context: &RewriteContext,
1623                          callee: &R,
1624                          max_callee_width: usize,
1625                          args: &[ptr::P<ast::Expr>],
1626                          span: Span,
1627                          shape: Shape,
1628                          force_no_trailing_comma: bool)
1629                          -> Result<String, Ordering>
1630     where R: Rewrite
1631 {
1632     // FIXME using byte lens instead of char lens (and probably all over the
1633     // place too)
1634     let callee_shape = Shape {
1635         width: max_callee_width,
1636         ..shape
1637     };
1638     let callee_str = callee
1639         .rewrite(context, callee_shape)
1640         .ok_or(Ordering::Greater)?;
1641
1642     // 4 = `(  )`, 2 = `()`
1643     let paren_overhead = if context.config.spaces_within_parens {
1644         4
1645     } else {
1646         2
1647     };
1648     let used_width = extra_offset(&callee_str, shape);
1649     let one_line_width = shape
1650         .width
1651         .checked_sub(used_width + paren_overhead)
1652         .ok_or(Ordering::Greater)?;
1653
1654     let nested_shape = match context.config.fn_call_style {
1655             IndentStyle::Block => shape.block().block_left(context.config.tab_spaces),
1656             // 1 = (
1657             IndentStyle::Visual => {
1658                 shape
1659                     .visual_indent(used_width + 1)
1660                     .sub_width(used_width + paren_overhead)
1661             }
1662         }
1663         .ok_or(Ordering::Greater)?;
1664
1665     let span_lo = context.codemap.span_after(span, "(");
1666     let span = mk_sp(span_lo, span.hi);
1667
1668     let list_str = rewrite_call_args(context,
1669                                      args,
1670                                      span,
1671                                      nested_shape,
1672                                      one_line_width,
1673                                      force_no_trailing_comma)
1674             .ok_or(Ordering::Less)?;
1675
1676     let result = if context.config.fn_call_style == IndentStyle::Visual ||
1677                     (!list_str.contains('\n') && list_str.chars().last().unwrap_or(' ') != ',') {
1678         if context.config.spaces_within_parens && list_str.len() > 0 {
1679             format!("{}( {} )", callee_str, list_str)
1680         } else {
1681             format!("{}({})", callee_str, list_str)
1682         }
1683     } else {
1684         format!("{}(\n{}{}\n{})",
1685                 callee_str,
1686                 nested_shape.indent.to_string(context.config),
1687                 list_str,
1688                 shape.block().indent.to_string(context.config))
1689     };
1690
1691     Ok(result)
1692 }
1693
1694 fn rewrite_call_args(context: &RewriteContext,
1695                      args: &[ptr::P<ast::Expr>],
1696                      span: Span,
1697                      shape: Shape,
1698                      one_line_width: usize,
1699                      force_no_trailing_comma: bool)
1700                      -> Option<String> {
1701     let arg_count = args.len();
1702
1703     let items = itemize_list(context.codemap,
1704                              args.iter(),
1705                              ")",
1706                              |item| item.span.lo,
1707                              |item| item.span.hi,
1708                              |item| item.rewrite(context, shape),
1709                              span.lo,
1710                              span.hi);
1711     let mut item_vec: Vec<_> = items.collect();
1712
1713     // Try letting the last argument overflow to the next line with block
1714     // indentation. If its first line fits on one line with the other arguments,
1715     // we format the function arguments horizontally.
1716     let overflow_last = match args.last().map(|x| &x.node) {
1717         Some(&ast::ExprKind::Closure(..)) |
1718         Some(&ast::ExprKind::Block(..)) |
1719         Some(&ast::ExprKind::Match(..)) if arg_count > 1 => true,
1720         _ => false,
1721     };
1722
1723     let mut orig_last = None;
1724     let mut placeholder = None;
1725
1726     // Replace the last item with its first line to see if it fits with
1727     // first arguments.
1728     if overflow_last {
1729         let nested_shape = Shape {
1730             indent: shape.indent.block_only(),
1731             ..shape
1732         };
1733         let rewrite = args.last().unwrap().rewrite(context, nested_shape);
1734
1735         if let Some(rewrite) = rewrite {
1736             let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
1737             placeholder = Some(rewrite);
1738
1739             swap(&mut item_vec[arg_count - 1].item, &mut orig_last);
1740             item_vec[arg_count - 1].item = rewrite_first_line;
1741         }
1742     }
1743
1744     let one_line_shape = Shape {
1745         width: one_line_width,
1746         ..shape
1747     };
1748
1749     let tactic =
1750         definitive_tactic(&item_vec,
1751                           ListTactic::LimitedHorizontalVertical(context.config.fn_call_width),
1752                           one_line_width);
1753
1754     // Replace the stub with the full overflowing last argument if the rewrite
1755     // succeeded and its first line fits with the other arguments.
1756     match (overflow_last, tactic, placeholder) {
1757         (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
1758             item_vec[arg_count - 1].item = placeholder;
1759         }
1760         (true, _, _) => {
1761             item_vec[arg_count - 1].item = orig_last;
1762         }
1763         (false, _, _) => {}
1764     }
1765
1766     let mut fmt = ListFormatting {
1767         tactic: tactic,
1768         separator: ",",
1769         trailing_separator: if force_no_trailing_comma ||
1770                                context.config.fn_call_style == IndentStyle::Visual ||
1771                                arg_count <= 1 {
1772             SeparatorTactic::Never
1773         } else {
1774             context.config.trailing_comma
1775         },
1776         shape: one_line_shape,
1777         ends_with_newline: false,
1778         config: context.config,
1779     };
1780
1781     match write_list(&item_vec, &fmt) {
1782         // If arguments do not fit in a single line and do not contain newline,
1783         // try to put it on the next line. Try this only when we are in block mode
1784         // and not rewriting macro.
1785         Some(ref s) if context.config.fn_call_style == IndentStyle::Block &&
1786                        !force_no_trailing_comma &&
1787                        (!s.contains('\n') &&
1788                         (s.len() > one_line_width || s.len() > context.config.fn_call_width)) => {
1789             fmt.trailing_separator = SeparatorTactic::Vertical;
1790             write_list(&item_vec, &fmt)
1791         }
1792         rewrite @ _ => rewrite,
1793     }
1794 }
1795
1796 fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> Option<String> {
1797     debug!("rewrite_paren, shape: {:?}", shape);
1798     // 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
1799     // paren on the same line as the subexpr.
1800     let sub_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
1801     let subexpr_str = subexpr.rewrite(context, sub_shape);
1802     debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
1803
1804     subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 {
1805                         format!("( {} )", s)
1806                     } else {
1807                         format!("({})", s)
1808                     })
1809 }
1810
1811 fn rewrite_index(expr: &ast::Expr,
1812                  index: &ast::Expr,
1813                  context: &RewriteContext,
1814                  shape: Shape)
1815                  -> Option<String> {
1816     let expr_str = try_opt!(expr.rewrite(context, shape));
1817
1818     let (lbr, rbr) = if context.config.spaces_within_square_brackets {
1819         ("[ ", " ]")
1820     } else {
1821         ("[", "]")
1822     };
1823
1824     let budget = shape
1825         .width
1826         .checked_sub(expr_str.len() + lbr.len() + rbr.len())
1827         .unwrap_or(0);
1828     let index_str = index.rewrite(context, Shape::legacy(budget, shape.indent));
1829     if let Some(index_str) = index_str {
1830         return Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr));
1831     }
1832
1833     let indent = shape.indent.block_indent(&context.config);
1834     let indent = indent.to_string(&context.config);
1835     // FIXME this is not right, since we don't take into account that shape.width
1836     // might be reduced from max_width by something on the right.
1837     let budget = try_opt!(context
1838                               .config
1839                               .max_width
1840                               .checked_sub(indent.len() + lbr.len() + rbr.len()));
1841     let index_str = try_opt!(index.rewrite(context, Shape::legacy(budget, shape.indent)));
1842     Some(format!("{}\n{}{}{}{}", expr_str, indent, lbr, index_str, rbr))
1843 }
1844
1845 fn rewrite_struct_lit<'a>(context: &RewriteContext,
1846                           path: &ast::Path,
1847                           fields: &'a [ast::Field],
1848                           base: Option<&'a ast::Expr>,
1849                           span: Span,
1850                           shape: Shape)
1851                           -> Option<String> {
1852     debug!("rewrite_struct_lit: shape {:?}", shape);
1853
1854     enum StructLitField<'a> {
1855         Regular(&'a ast::Field),
1856         Base(&'a ast::Expr),
1857     }
1858
1859     // 2 = " {".len()
1860     let path_shape = try_opt!(shape.sub_width(2));
1861     let path_str = try_opt!(rewrite_path(context, PathContext::Expr, None, path, path_shape));
1862
1863     if fields.len() == 0 && base.is_none() {
1864         return Some(format!("{} {{}}", path_str));
1865     }
1866
1867     let field_iter = fields
1868         .into_iter()
1869         .map(StructLitField::Regular)
1870         .chain(base.into_iter().map(StructLitField::Base));
1871
1872     // Foo { a: Foo } - indent is +3, width is -5.
1873     let (h_shape, v_shape) = try_opt!(struct_lit_shape(shape, context, path_str.len() + 3, 2));
1874
1875     let span_lo = |item: &StructLitField| match *item {
1876         StructLitField::Regular(field) => field.span.lo,
1877         StructLitField::Base(expr) => {
1878             let last_field_hi = fields.last().map_or(span.lo, |field| field.span.hi);
1879             let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo));
1880             let pos = snippet.find_uncommented("..").unwrap();
1881             last_field_hi + BytePos(pos as u32)
1882         }
1883     };
1884     let span_hi = |item: &StructLitField| match *item {
1885         StructLitField::Regular(field) => field.span.hi,
1886         StructLitField::Base(expr) => expr.span.hi,
1887     };
1888     let rewrite = |item: &StructLitField| match *item {
1889         StructLitField::Regular(field) => {
1890             // The 1 taken from the v_budget is for the comma.
1891             rewrite_field(context, field, try_opt!(v_shape.sub_width(1)))
1892         }
1893         StructLitField::Base(expr) => {
1894             // 2 = ..
1895             expr.rewrite(context, try_opt!(v_shape.shrink_left(2)))
1896                 .map(|s| format!("..{}", s))
1897         }
1898     };
1899
1900     let items = itemize_list(context.codemap,
1901                              field_iter,
1902                              "}",
1903                              span_lo,
1904                              span_hi,
1905                              rewrite,
1906                              context.codemap.span_after(span, "{"),
1907                              span.hi);
1908     let item_vec = items.collect::<Vec<_>>();
1909
1910     let tactic = struct_lit_tactic(h_shape, context, &item_vec);
1911     let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
1912     let fmt = struct_lit_formatting(nested_shape, tactic, context, base.is_some());
1913
1914     let fields_str = try_opt!(write_list(&item_vec, &fmt));
1915     let fields_str = if context.config.struct_lit_style == IndentStyle::Block &&
1916                         (fields_str.contains('\n') ||
1917                          context.config.struct_lit_multiline_style == MultilineStyle::ForceMulti ||
1918                          fields_str.len() > h_shape.map(|s| s.width).unwrap_or(0)) {
1919         format!("\n{}{}\n{}",
1920                 v_shape.indent.to_string(context.config),
1921                 fields_str,
1922                 shape.indent.to_string(context.config))
1923     } else {
1924         // One liner or visual indent.
1925         format!(" {} ", fields_str)
1926     };
1927
1928     Some(format!("{} {{{}}}", path_str, fields_str))
1929
1930     // FIXME if context.config.struct_lit_style == Visual, but we run out
1931     // of space, we should fall back to BlockIndent.
1932 }
1933
1934 pub fn type_annotation_separator(config: &Config) -> &str {
1935     colon_spaces(config.space_before_type_annotation,
1936                  config.space_after_type_annotation_colon)
1937 }
1938
1939 fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> Option<String> {
1940     let name = &field.ident.node.to_string();
1941     if field.is_shorthand {
1942         Some(name.to_string())
1943     } else {
1944         let separator = type_annotation_separator(context.config);
1945         let overhead = name.len() + separator.len();
1946         let mut expr_shape = try_opt!(shape.sub_width(overhead));
1947         expr_shape.offset += overhead;
1948         let expr = field.expr.rewrite(context, expr_shape);
1949
1950         let mut attrs_str = try_opt!((*field.attrs).rewrite(context, shape));
1951         if !attrs_str.is_empty() {
1952             attrs_str.push_str(&format!("\n{}", shape.indent.to_string(context.config)));
1953         };
1954
1955         match expr {
1956             Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)),
1957             None => {
1958                 let expr_offset = shape.indent.block_indent(context.config);
1959                 let expr = field
1960                     .expr
1961                     .rewrite(context, Shape::indented(expr_offset, context.config));
1962                 expr.map(|s| {
1963                              format!("{}{}:\n{}{}",
1964                                      attrs_str,
1965                                      name,
1966                                      expr_offset.to_string(&context.config),
1967                                      s)
1968                          })
1969             }
1970         }
1971     }
1972 }
1973
1974 pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
1975                             mut items: I,
1976                             span: Span,
1977                             shape: Shape)
1978                             -> Option<String>
1979     where I: ExactSizeIterator,
1980           <I as Iterator>::Item: Deref,
1981           <I::Item as Deref>::Target: Rewrite + Spanned + 'a
1982 {
1983     debug!("rewrite_tuple {:?}", shape);
1984     // In case of length 1, need a trailing comma
1985     if items.len() == 1 {
1986         // 3 = "(" + ",)"
1987         let nested_shape = try_opt!(shape.sub_width(3)).visual_indent(1);
1988         return items
1989                    .next()
1990                    .unwrap()
1991                    .rewrite(context, nested_shape)
1992                    .map(|s| if context.config.spaces_within_parens {
1993                             format!("( {}, )", s)
1994                         } else {
1995                             format!("({},)", s)
1996                         });
1997     }
1998
1999     let list_lo = context.codemap.span_after(span, "(");
2000     let nested_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
2001     let items = itemize_list(context.codemap,
2002                              items,
2003                              ")",
2004                              |item| item.span().lo,
2005                              |item| item.span().hi,
2006                              |item| item.rewrite(context, nested_shape),
2007                              list_lo,
2008                              span.hi - BytePos(1));
2009     let list_str = try_opt!(format_item_list(items, nested_shape, context.config));
2010
2011     if context.config.spaces_within_parens && list_str.len() > 0 {
2012         Some(format!("( {} )", list_str))
2013     } else {
2014         Some(format!("({})", list_str))
2015     }
2016 }
2017
2018 pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
2019                                         prefix: &str,
2020                                         rewrite: &R,
2021                                         mut shape: Shape,
2022                                         span: Span)
2023                                         -> Option<String> {
2024     // Heuristic: if unary is `&` and `rewrite` contains `{`,
2025     // it is likely that block indent is preferred to visual indent.
2026     if prefix == "&" {
2027         let snippet = String::from(context.snippet(span).trim_left_matches('&'));
2028         let first_line = try_opt!(snippet.lines().nth(0));
2029         if first_line.contains("{") {
2030             shape = try_opt!(shape.sub_width(prefix.len())).block_indent(0);
2031         } else {
2032             shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0);
2033         }
2034     } else {
2035         shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0);
2036     }
2037     rewrite
2038         .rewrite(context, shape)
2039         .map(|r| format!("{}{}", prefix, r))
2040 }
2041
2042 // FIXME: this is probably not correct for multi-line Rewrites. we should
2043 // subtract suffix.len() from the last line budget, not the first!
2044 pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
2045                                         suffix: &str,
2046                                         rewrite: &R,
2047                                         shape: Shape)
2048                                         -> Option<String> {
2049     rewrite
2050         .rewrite(context, try_opt!(shape.sub_width(suffix.len())))
2051         .map(|mut r| {
2052                  r.push_str(suffix);
2053                  r
2054              })
2055 }
2056
2057 fn rewrite_unary_op(context: &RewriteContext,
2058                     op: &ast::UnOp,
2059                     expr: &ast::Expr,
2060                     shape: Shape)
2061                     -> Option<String> {
2062     // For some reason, an UnOp is not spanned like BinOp!
2063     let operator_str = match *op {
2064         ast::UnOp::Deref => "*",
2065         ast::UnOp::Not => "!",
2066         ast::UnOp::Neg => "-",
2067     };
2068     rewrite_unary_prefix(context, operator_str, expr, shape, expr.span)
2069 }
2070
2071 fn rewrite_assignment(context: &RewriteContext,
2072                       lhs: &ast::Expr,
2073                       rhs: &ast::Expr,
2074                       op: Option<&ast::BinOp>,
2075                       shape: Shape)
2076                       -> Option<String> {
2077     let operator_str = match op {
2078         Some(op) => context.snippet(op.span),
2079         None => "=".to_owned(),
2080     };
2081
2082     // 1 = space between lhs and operator.
2083     let lhs_shape = try_opt!(shape.sub_width(operator_str.len() + 1));
2084     let lhs_str = format!("{} {}",
2085                           try_opt!(lhs.rewrite(context, lhs_shape)),
2086                           operator_str);
2087
2088     rewrite_assign_rhs(context, lhs_str, rhs, shape)
2089 }
2090
2091 // The left hand side must contain everything up to, and including, the
2092 // assignment operator.
2093 pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
2094                                            lhs: S,
2095                                            ex: &ast::Expr,
2096                                            shape: Shape)
2097                                            -> Option<String> {
2098     let mut result = lhs.into();
2099     let last_line_width = last_line_width(&result) -
2100                           if result.contains('\n') {
2101                               shape.indent.width()
2102                           } else {
2103                               0
2104                           };
2105     // 1 = space between operator and rhs.
2106     let max_width = try_opt!(shape.width.checked_sub(last_line_width + 1));
2107     let rhs = ex.rewrite(context,
2108                          Shape::offset(max_width,
2109                                        shape.indent,
2110                                        shape.indent.alignment + last_line_width + 1));
2111
2112     fn count_line_breaks(src: &str) -> usize {
2113         src.chars().filter(|&x| x == '\n').count()
2114     }
2115
2116     match rhs {
2117         Some(ref new_str) if count_line_breaks(new_str) < 2 => {
2118             result.push(' ');
2119             result.push_str(new_str);
2120         }
2121         _ => {
2122             // Expression did not fit on the same line as the identifier or is
2123             // at least three lines big. Try splitting the line and see
2124             // if that works better.
2125             let new_offset = shape.indent.block_indent(context.config);
2126             let max_width = try_opt!((shape.width + shape.indent.width())
2127                                          .checked_sub(new_offset.width()));
2128             let new_shape = Shape::legacy(max_width, new_offset);
2129             let new_rhs = ex.rewrite(context, new_shape);
2130
2131             // FIXME: DRY!
2132             match (rhs, new_rhs) {
2133                 (Some(ref orig_rhs), Some(ref replacement_rhs))
2134                     if count_line_breaks(orig_rhs) > count_line_breaks(replacement_rhs) + 1 ||
2135                        (orig_rhs.rewrite(context, shape).is_none() &&
2136                         replacement_rhs.rewrite(context, new_shape).is_some()) => {
2137                     result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
2138                     result.push_str(replacement_rhs);
2139                 }
2140                 (None, Some(ref final_rhs)) => {
2141                     result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
2142                     result.push_str(final_rhs);
2143                 }
2144                 (None, None) => return None,
2145                 (Some(ref orig_rhs), _) => {
2146                     result.push(' ');
2147                     result.push_str(orig_rhs);
2148                 }
2149             }
2150         }
2151     }
2152
2153     Some(result)
2154 }
2155
2156 fn rewrite_expr_addrof(context: &RewriteContext,
2157                        mutability: ast::Mutability,
2158                        expr: &ast::Expr,
2159                        shape: Shape)
2160                        -> Option<String> {
2161     let operator_str = match mutability {
2162         ast::Mutability::Immutable => "&",
2163         ast::Mutability::Mutable => "&mut ",
2164     };
2165     rewrite_unary_prefix(context, operator_str, expr, shape, expr.span)
2166 }