]> git.lizzy.rs Git - rust.git/blob - src/items.rs
Merge pull request #128 from marcusklaas/subexpr
[rust.git] / src / items.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 // Formatting top-level items - functions, structs, enums, traits, impls.
12
13 use {ReturnIndent, BraceStyle};
14 use utils::{format_visibility, make_indent, contains_skip, span_after, end_typaram};
15 use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
16 use comment::FindUncommented;
17 use visitor::FmtVisitor;
18
19 use syntax::{ast, abi};
20 use syntax::codemap::{self, Span, BytePos};
21 use syntax::print::pprust;
22 use syntax::parse::token;
23
24 impl<'a> FmtVisitor<'a> {
25     pub fn rewrite_fn(&mut self,
26                       indent: usize,
27                       ident: ast::Ident,
28                       fd: &ast::FnDecl,
29                       explicit_self: Option<&ast::ExplicitSelf>,
30                       generics: &ast::Generics,
31                       unsafety: &ast::Unsafety,
32                       constness: &ast::Constness,
33                       abi: &abi::Abi,
34                       vis: ast::Visibility,
35                       span: Span)
36                       -> String {
37         let newline_brace = self.newline_for_brace(&generics.where_clause);
38
39         let mut result = self.rewrite_fn_base(indent,
40                                               ident,
41                                               fd,
42                                               explicit_self,
43                                               generics,
44                                               unsafety,
45                                               constness,
46                                               abi,
47                                               vis,
48                                               span,
49                                               newline_brace);
50
51         // Prepare for the function body by possibly adding a newline and indent.
52         // FIXME we'll miss anything between the end of the signature and the start
53         // of the body, but we need more spans from the compiler to solve this.
54         if newline_brace {
55             result.push('\n');
56             result.push_str(&make_indent(indent));
57         } else {
58             result.push(' ');
59         }
60
61         result
62     }
63
64     pub fn rewrite_required_fn(&mut self,
65                                indent: usize,
66                                ident: ast::Ident,
67                                sig: &ast::MethodSig,
68                                span: Span)
69                                -> String {
70         // Drop semicolon or it will be interpreted as comment
71         let span = codemap::mk_sp(span.lo, span.hi - BytePos(1));
72
73         let mut result = self.rewrite_fn_base(indent,
74                                               ident,
75                                               &sig.decl,
76                                               Some(&sig.explicit_self),
77                                               &sig.generics,
78                                               &sig.unsafety,
79                                               &sig.constness,
80                                               &sig.abi,
81                                               ast::Visibility::Inherited,
82                                               span,
83                                               false);
84
85         // Re-attach semicolon
86         result.push(';');
87
88         result
89     }
90
91     fn rewrite_fn_base(&mut self,
92                        indent: usize,
93                        ident: ast::Ident,
94                        fd: &ast::FnDecl,
95                        explicit_self: Option<&ast::ExplicitSelf>,
96                        generics: &ast::Generics,
97                        unsafety: &ast::Unsafety,
98                        constness: &ast::Constness,
99                        abi: &abi::Abi,
100                        vis: ast::Visibility,
101                        span: Span,
102                        newline_brace: bool)
103                        -> String {
104         // FIXME we'll lose any comments in between parts of the function decl, but anyone
105         // who comments there probably deserves what they get.
106
107         let where_clause = &generics.where_clause;
108
109         let mut result = String::with_capacity(1024);
110         // Vis unsafety abi.
111         result.push_str(format_visibility(vis));
112
113         if let &ast::Unsafety::Unsafe = unsafety {
114             result.push_str("unsafe ");
115         }
116         if let &ast::Constness::Const = constness {
117             result.push_str("const ");
118         }
119         if *abi != abi::Rust {
120             result.push_str("extern ");
121             result.push_str(&abi.to_string());
122             result.push(' ');
123         }
124
125         // fn foo
126         result.push_str("fn ");
127         result.push_str(&token::get_ident(ident));
128
129         // Generics.
130         let generics_indent = indent + result.len();
131         result.push_str(&self.rewrite_generics(generics,
132                                                generics_indent,
133                                                codemap::mk_sp(span.lo,
134                                                               span_for_return(&fd.output).lo)));
135
136         let ret_str = self.rewrite_return(&fd.output);
137
138         // Args.
139         let (one_line_budget, multi_line_budget, mut arg_indent) =
140             self.compute_budgets_for_args(&result, indent, ret_str.len(), newline_brace);
141
142         debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
143                one_line_budget, multi_line_budget, arg_indent);
144
145         // Check if vertical layout was forced by compute_budget_for_args.
146         if one_line_budget <= 0 {
147             if self.config.fn_args_paren_newline {
148                 result.push('\n');
149                 result.push_str(&make_indent(arg_indent));
150                 arg_indent = arg_indent + 1; // extra space for `(`
151                 result.push('(');
152             } else {
153                 result.push_str("(\n");
154                 result.push_str(&make_indent(arg_indent));
155             }
156         } else {
157             result.push('(');
158         }
159
160         // A conservative estimation, to goal is to be over all parens in generics
161         let args_start = generics.ty_params
162                                  .last()
163                                  .map(|tp| end_typaram(tp))
164                                  .unwrap_or(span.lo);
165         let args_span = codemap::mk_sp(span_after(codemap::mk_sp(args_start, span.hi),
166                                                   "(",
167                                                   self.codemap),
168                                        span_for_return(&fd.output).lo);
169         result.push_str(&self.rewrite_args(&fd.inputs,
170                                            explicit_self,
171                                            one_line_budget,
172                                            multi_line_budget,
173                                            arg_indent,
174                                            args_span));
175         result.push(')');
176
177         // Return type.
178         if ret_str.len() > 0 {
179             // If we've already gone multi-line, or the return type would push
180             // over the max width, then put the return type on a new line.
181             if result.contains("\n") ||
182                result.len() + indent + ret_str.len() > self.config.max_width {
183                 let indent = match self.config.fn_return_indent {
184                     ReturnIndent::WithWhereClause => indent + 4,
185                     // TODO we might want to check that using the arg indent doesn't
186                     // blow our budget, and if it does, then fallback to the where
187                     // clause indent.
188                     _ => arg_indent,
189                 };
190
191                 result.push('\n');
192                 result.push_str(&make_indent(indent));
193             } else {
194                 result.push(' ');
195             }
196             result.push_str(&ret_str);
197
198             // Comment between return type and the end of the decl.
199             let snippet_lo = fd.output.span().hi;
200             if where_clause.predicates.len() == 0 {
201                 let snippet_hi = span.hi;
202                 let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
203                 let snippet = snippet.trim();
204                 if snippet.len() > 0 {
205                     result.push(' ');
206                     result.push_str(snippet);
207                 }
208             } else {
209                 // FIXME it would be nice to catch comments between the return type
210                 // and the where clause, but we don't have a span for the where
211                 // clause.
212             }
213         }
214
215         // Where clause.
216         result.push_str(&self.rewrite_where_clause(where_clause,
217                                                    indent,
218                                                    span.hi));
219
220         result
221     }
222
223     fn rewrite_args(&self,
224                     args: &[ast::Arg],
225                     explicit_self: Option<&ast::ExplicitSelf>,
226                     one_line_budget: usize,
227                     multi_line_budget: usize,
228                     arg_indent: usize,
229                     span: Span)
230                     -> String {
231         let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
232         // Account for sugary self.
233         let mut min_args = 1;
234         if let Some(explicit_self) = explicit_self {
235             match explicit_self.node {
236                 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
237                     let lt_str = match lt {
238                         &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
239                         &None => String::new(),
240                     };
241                     let mut_str = match m {
242                         &ast::Mutability::MutMutable => "mut ".to_owned(),
243                         &ast::Mutability::MutImmutable => String::new(),
244                     };
245                     arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
246                     min_args = 2;
247                 }
248                 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
249                     arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
250                 }
251                 ast::ExplicitSelf_::SelfValue(_) => {
252                     assert!(args.len() >= 1, "&[ast::Arg] shouldn't be empty.");
253
254                     // this hacky solution caused by absence of `Mutability` in `SelfValue`.
255                     let mut_str = {
256                         if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _)
257                                 = args[0].pat.node {
258                             match mutability {
259                                 ast::Mutability::MutMutable => "mut ",
260                                 ast::Mutability::MutImmutable => "",
261                             }
262                         } else {
263                             panic!("there is a bug or change in structure of AST, aborting.");
264                         }
265                     };
266
267                     arg_item_strs[0] = format!("{}self", mut_str);
268                     min_args = 2;
269                 }
270                 _ => {}
271             }
272         }
273
274         // Comments between args
275         let mut arg_items = Vec::new();
276         if min_args == 2 {
277             arg_items.push(ListItem::from_str(""));
278         }
279
280         // TODO if there are no args, there might still be a comment, but without
281         // spans for the comment or parens, there is no chance of getting it right.
282         // You also don't get to put a comment on self, unless it is explicit.
283         if args.len() >= min_args {
284             let comment_span_start = if min_args == 2 {
285                 span_after(span, ",", self.codemap)
286             } else {
287                 span.lo
288             };
289
290             arg_items = itemize_list(self.codemap,
291                                      arg_items,
292                                      args[min_args-1..].iter(),
293                                      ",",
294                                      ")",
295                                      |arg| arg.pat.span.lo,
296                                      |arg| arg.ty.span.hi,
297                                      |_| String::new(),
298                                      comment_span_start,
299                                      span.hi);
300         }
301
302         assert_eq!(arg_item_strs.len(), arg_items.len());
303
304         for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
305             item.item = arg;
306         }
307
308         let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
309                                    separator: ",",
310                                    trailing_separator: SeparatorTactic::Never,
311                                    indent: arg_indent,
312                                    h_width: one_line_budget,
313                                    v_width: multi_line_budget,
314                                    ends_with_newline: true, };
315
316         write_list(&arg_items, &fmt)
317     }
318
319     fn compute_budgets_for_args(&self,
320                                 result: &str,
321                                 indent: usize,
322                                 ret_str_len: usize,
323                                 newline_brace: bool)
324                                 -> (usize, usize, usize) {
325         let mut budgets = None;
326
327         // Try keeping everything on the same line
328         if !result.contains("\n") {
329             // 3 = `() `, space is before ret_string
330             let mut used_space = indent + result.len() + ret_str_len + 3;
331             if !newline_brace {
332                 used_space += 2;
333             }
334             let one_line_budget = if used_space > self.config.max_width {
335                 0
336             } else {
337                 self.config.max_width - used_space
338             };
339
340             // 2 = `()`
341             let used_space = indent + result.len() + 2;
342             let max_space = self.config.ideal_width + self.config.leeway;
343             debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
344                    used_space, max_space);
345             if used_space < max_space {
346                 budgets = Some((one_line_budget,
347                                 max_space - used_space,
348                                 indent + result.len() + 1));
349             }
350         }
351
352         // Didn't work. we must force vertical layout and put args on a newline.
353         if let None = budgets {
354             let new_indent = indent + self.config.tab_spaces;
355             let used_space = new_indent + 2; // account for `(` and `)`
356             let max_space = self.config.ideal_width + self.config.leeway;
357             if used_space > max_space {
358                 // Whoops! bankrupt.
359                 // TODO take evasive action, perhaps kill the indent or something.
360             } else {
361                 budgets = Some((0, max_space - used_space, new_indent));
362             }
363         }
364
365         budgets.unwrap()
366     }
367
368     fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
369         match self.config.fn_brace_style {
370             BraceStyle::AlwaysNextLine => true,
371             BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
372             _ => false,
373         }
374     }
375
376     pub fn visit_enum(&mut self,
377                       ident: ast::Ident,
378                       vis: ast::Visibility,
379                       enum_def: &ast::EnumDef,
380                       generics: &ast::Generics,
381                       span: Span) {
382         let header_str = self.format_header("enum ", ident, vis);
383         self.changes.push_str_span(span, &header_str);
384
385         let enum_snippet = self.snippet(span);
386         let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1);
387         let generics_str = self.format_generics(generics,
388                                                 " {",
389                                                 self.block_indent + self.config.tab_spaces,
390                                                 codemap::mk_sp(span.lo,
391                                                                body_start));
392         self.changes.push_str_span(span, &generics_str);
393
394         self.last_pos = body_start;
395         self.block_indent += self.config.tab_spaces;
396         for (i, f) in enum_def.variants.iter().enumerate() {
397             let next_span_start: BytePos = if i == enum_def.variants.len() - 1 {
398                 span.hi
399             } else {
400                 enum_def.variants[i + 1].span.lo
401             };
402
403             self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start);
404         }
405         self.block_indent -= self.config.tab_spaces;
406
407         self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32));
408         self.changes.push_str_span(span, "}");
409     }
410
411     // Variant of an enum
412     fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_start: BytePos) {
413         if self.visit_attrs(&field.node.attrs) {
414             return;
415         }
416
417         self.format_missing_with_indent(field.span.lo);
418
419         let result = match field.node.kind {
420             ast::VariantKind::TupleVariantKind(ref types) => {
421                 let vis = format_visibility(field.node.vis);
422                 self.changes.push_str_span(field.span, vis);
423                 let name = field.node.name.to_string();
424                 self.changes.push_str_span(field.span, &name);
425
426                 let mut result = String::new();
427
428                 if types.len() > 0 {
429                     let items = itemize_list(self.codemap,
430                                              Vec::new(),
431                                              types.iter(),
432                                              ",",
433                                              ")",
434                                              |arg| arg.ty.span.lo,
435                                              |arg| arg.ty.span.hi,
436                                              |arg| pprust::ty_to_string(&arg.ty),
437                                              span_after(field.span, "(", self.codemap),
438                                              next_span_start);
439
440                     result.push('(');
441
442                     let indent = self.block_indent
443                                  + vis.len()
444                                  + field.node.name.to_string().len()
445                                  + 1; // Open paren
446
447                     let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
448                     let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
449
450                     let fmt = ListFormatting {
451                         tactic: ListTactic::HorizontalVertical,
452                         separator: ",",
453                         trailing_separator: SeparatorTactic::Never,
454                         indent: indent,
455                         h_width: budget,
456                         v_width: budget,
457                         ends_with_newline: false,
458                     };
459                     result.push_str(&write_list(&items, &fmt));
460                     result.push(')');
461                 }
462
463                 if let Some(ref expr) = field.node.disr_expr {
464                     result.push_str(" = ");
465                     let expr_snippet = self.snippet(expr.span);
466                     result.push_str(&expr_snippet);
467
468                     // Make sure we do not exceed column limit
469                     // 4 = " = ,"
470                     assert!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
471                             "Enum variant exceeded column limit");
472                 }
473
474                 result
475             },
476             ast::VariantKind::StructVariantKind(ref struct_def) => {
477                 // TODO Should limit the width, as we have a trailing comma
478                 self.format_struct("",
479                                    field.node.name,
480                                    field.node.vis,
481                                    struct_def,
482                                    None,
483                                    field.span,
484                                    self.block_indent)
485             }
486         };
487         self.changes.push_str_span(field.span, &result);
488
489         if !last_field || self.config.enum_trailing_comma {
490             self.changes.push_str_span(field.span, ",");
491         }
492
493         self.last_pos = field.span.hi + BytePos(1);
494     }
495
496     fn format_struct(&self,
497                      item_name: &str,
498                      ident: ast::Ident,
499                      vis: ast::Visibility,
500                      struct_def: &ast::StructDef,
501                      generics: Option<&ast::Generics>,
502                      span: Span,
503                      offset: usize)
504                      -> String {
505         let mut result = String::with_capacity(1024);
506
507         let header_str = self.format_header(item_name, ident, vis);
508         result.push_str(&header_str);
509
510         if struct_def.fields.len() == 0 {
511             result.push(';');
512             return result;
513         }
514
515         let is_tuple = match struct_def.fields[0].node.kind {
516             ast::StructFieldKind::NamedField(..) => false,
517             ast::StructFieldKind::UnnamedField(..) => true
518         };
519
520         let (opener, terminator) = if is_tuple { ("(", ")") } else { (" {", "}") };
521
522         let generics_str = match generics {
523             Some(g) => self.format_generics(g,
524                                             opener,
525                                             offset + header_str.len(),
526                                             codemap::mk_sp(span.lo,
527                                                            struct_def.fields[0].span.lo)),
528             None => opener.to_owned()
529         };
530         result.push_str(&generics_str);
531
532         let items = itemize_list(self.codemap,
533                                  Vec::new(),
534                                  struct_def.fields.iter(),
535                                  ",",
536                                  terminator,
537                                  |field| {
538                                       // Include attributes and doc comments,
539                                       // if present
540                                       if field.node.attrs.len() > 0 {
541                                           field.node.attrs[0].span.lo
542                                       } else {
543                                           field.span.lo
544                                       }
545                                  },
546                                  |field| field.node.ty.span.hi,
547                                  |field| self.format_field(field),
548                                  span_after(span, opener.trim(), self.codemap),
549                                  span.hi);
550
551         // 2 terminators and a semicolon
552         let used_budget = offset + header_str.len() + generics_str.len() + 3;
553
554         // Conservative approximation
555         let single_line_cost = (span.hi - struct_def.fields[0].span.lo).0;
556         let break_line = !is_tuple || generics_str.contains('\n') ||
557                          single_line_cost as usize + used_budget > self.config.max_width;
558
559         if break_line {
560             let indentation = make_indent(offset + self.config.tab_spaces);
561             result.push('\n');
562             result.push_str(&indentation);
563         }
564
565         let tactic = if break_line { ListTactic::Vertical } else { ListTactic::Horizontal };
566
567         // 1 = ,
568         let budget = self.config.ideal_width - offset + self.config.tab_spaces - 1;
569         let fmt = ListFormatting { tactic: tactic,
570                                    separator: ",",
571                                    trailing_separator: self.config.struct_trailing_comma,
572                                    indent: offset + self.config.tab_spaces,
573                                    h_width: self.config.max_width,
574                                    v_width: budget,
575                                    ends_with_newline: false, };
576
577         result.push_str(&write_list(&items, &fmt));
578
579         if break_line {
580             result.push('\n');
581             result.push_str(&make_indent(offset));
582         }
583
584         result.push_str(terminator);
585
586         if is_tuple {
587             result.push(';');
588         }
589
590         result
591     }
592
593     pub fn visit_struct(&mut self,
594                         ident: ast::Ident,
595                         vis: ast::Visibility,
596                         struct_def: &ast::StructDef,
597                         generics: &ast::Generics,
598                         span: Span) {
599         let indent = self.block_indent;
600         let result = self.format_struct("struct ",
601                                         ident,
602                                         vis,
603                                         struct_def,
604                                         Some(generics),
605                                         span,
606                                         indent);
607         self.changes.push_str_span(span, &result);
608         self.last_pos = span.hi;
609     }
610
611     fn format_header(&self, item_name: &str, ident: ast::Ident, vis: ast::Visibility) -> String {
612         format!("{}{}{}", format_visibility(vis), item_name, &token::get_ident(ident))
613     }
614
615     fn format_generics(&self,
616                        generics: &ast::Generics,
617                        opener: &str,
618                        offset: usize,
619                        span: Span)
620                        -> String {
621         let mut result = self.rewrite_generics(generics, offset, span);
622
623         if generics.where_clause.predicates.len() > 0 || result.contains('\n') {
624             result.push_str(&self.rewrite_where_clause(&generics.where_clause,
625                                                        self.block_indent,
626                                                        span.hi));
627             result.push_str(&make_indent(self.block_indent));
628             result.push('\n');
629             result.push_str(opener.trim());
630         } else {
631             result.push_str(opener);
632         }
633
634         result
635     }
636
637     // Field of a struct
638     fn format_field(&self, field: &ast::StructField) -> String {
639         if contains_skip(&field.node.attrs) {
640             return self.snippet(codemap::mk_sp(field.node.attrs[0].span.lo, field.span.hi));
641         }
642
643         let name = match field.node.kind {
644             ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
645             ast::StructFieldKind::UnnamedField(_) => None,
646         };
647         let vis = match field.node.kind {
648             ast::StructFieldKind::NamedField(_, vis) |
649             ast::StructFieldKind::UnnamedField(vis) => format_visibility(vis)
650         };
651         let typ = pprust::ty_to_string(&field.node.ty);
652
653         let indent = self.block_indent + self.config.tab_spaces;
654         let mut attr_str = self.rewrite_attrs(&field.node.attrs, indent);
655         if attr_str.len() > 0 {
656             attr_str.push('\n');
657             attr_str.push_str(&make_indent(indent));
658         }
659
660         match name {
661             Some(name) => format!("{}{}{}: {}", attr_str, vis, name, typ),
662             None => format!("{}{}{}", attr_str, vis, typ)
663         }
664     }
665
666     fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) -> String {
667         // FIXME convert bounds to where clauses where they get too big or if
668         // there is a where clause at all.
669         let mut result = String::new();
670         let lifetimes: &[_] = &generics.lifetimes;
671         let tys: &[_] = &generics.ty_params;
672         if lifetimes.len() + tys.len() == 0 {
673             return result;
674         }
675
676         let budget = self.config.max_width - offset - 2;
677         // TODO might need to insert a newline if the generics are really long
678         result.push('<');
679
680         // Strings for the generics.
681         let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
682         let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
683
684         // Extract comments between generics.
685         let lt_spans = lifetimes.iter().map(|l| {
686             let hi = if l.bounds.len() == 0 {
687                 l.lifetime.span.hi
688             } else {
689                 l.bounds[l.bounds.len() - 1].span.hi
690             };
691             codemap::mk_sp(l.lifetime.span.lo, hi)
692         });
693         let ty_spans = tys.iter().map(span_for_ty_param);
694
695         let mut items = itemize_list(self.codemap,
696                                      Vec::new(),
697                                      lt_spans.chain(ty_spans),
698                                      ",",
699                                      ">",
700                                      |sp| sp.lo,
701                                      |sp| sp.hi,
702                                      |_| String::new(),
703                                      span_after(span, "<", self.codemap),
704                                      span.hi);
705
706         for (item, ty) in items.iter_mut().zip(lt_strs.chain(ty_strs)) {
707             item.item = ty;
708         }
709
710         let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
711                                    separator: ",",
712                                    trailing_separator: SeparatorTactic::Never,
713                                    indent: offset + 1,
714                                    h_width: budget,
715                                    v_width: budget,
716                                    ends_with_newline: true, };
717         result.push_str(&write_list(&items, &fmt));
718
719         result.push('>');
720
721         result
722     }
723
724     fn rewrite_where_clause(&self,
725                             where_clause: &ast::WhereClause,
726                             indent: usize,
727                             span_end: BytePos)
728                             -> String {
729         let mut result = String::new();
730         if where_clause.predicates.len() == 0 {
731             return result;
732         }
733
734         result.push('\n');
735         result.push_str(&make_indent(indent + 4));
736         result.push_str("where ");
737
738         let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
739         let items = itemize_list(self.codemap,
740                                  Vec::new(),
741                                  where_clause.predicates.iter(),
742                                  ",",
743                                  "{",
744                                  |pred| span_for_where_pred(pred).lo,
745                                  |pred| span_for_where_pred(pred).hi,
746                                  |pred| self.rewrite_pred(pred),
747                                  span_start,
748                                  span_end);
749
750         let budget = self.config.ideal_width + self.config.leeway - indent - 10;
751         let fmt = ListFormatting { tactic: ListTactic::Vertical,
752                                    separator: ",",
753                                    trailing_separator: SeparatorTactic::Never,
754                                    indent: indent + 10,
755                                    h_width: budget,
756                                    v_width: budget,
757                                    ends_with_newline: true, };
758         result.push_str(&write_list(&items, &fmt));
759
760         result
761     }
762
763     fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
764         match *ret {
765             ast::FunctionRetTy::DefaultReturn(_) => String::new(),
766             ast::FunctionRetTy::NoReturn(_) => "-> !".to_owned(),
767             ast::FunctionRetTy::Return(ref ty) => "-> ".to_owned() + &pprust::ty_to_string(ty),
768         }
769     }
770
771     // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
772     fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
773         format!("{}: {}",
774                 pprust::pat_to_string(&arg.pat),
775                 pprust::ty_to_string(&arg.ty))
776     }
777 }
778
779 fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
780     match *ret {
781         ast::FunctionRetTy::NoReturn(ref span) |
782         ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
783         ast::FunctionRetTy::Return(ref ty) => ty.span,
784     }
785 }
786
787 fn span_for_ty_param(ty: &ast::TyParam) -> Span {
788     // Note that ty.span is the span for ty.ident, not the whole item.
789     let lo = ty.span.lo;
790     if let Some(ref def) = ty.default {
791         return codemap::mk_sp(lo, def.span.hi);
792     }
793     if ty.bounds.len() == 0 {
794         return ty.span;
795     }
796     let hi = match ty.bounds[ty.bounds.len() - 1] {
797         ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
798         ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
799     };
800     codemap::mk_sp(lo, hi)
801 }
802
803 fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
804     match *pred {
805         ast::WherePredicate::BoundPredicate(ref p) => p.span,
806         ast::WherePredicate::RegionPredicate(ref p) => p.span,
807         ast::WherePredicate::EqPredicate(ref p) => p.span,
808     }
809 }