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