]> git.lizzy.rs Git - rust.git/blob - src/types.rs
WIP
[rust.git] / src / types.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::ops::Deref;
12 use std::iter::ExactSizeIterator;
13
14 use syntax::abi;
15 use syntax::ast::{self, Mutability, FunctionRetTy};
16 use syntax::codemap::{self, Span, BytePos};
17 use syntax::print::pprust;
18 use syntax::symbol::keywords;
19
20 use {Shape, Spanned};
21 use codemap::SpanUtils;
22 use lists::{format_item_list, itemize_list, format_fn_args};
23 use rewrite::{Rewrite, RewriteContext};
24 use utils::{extra_offset, format_mutability, wrap_str};
25 use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
26 use config::TypeDensity;
27 use itertools::Itertools;
28
29 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
30 pub enum PathContext {
31     Expr,
32     Type,
33     Import,
34 }
35
36 // Does not wrap on simple segments.
37 pub fn rewrite_path(context: &RewriteContext,
38                     path_context: PathContext,
39                     qself: Option<&ast::QSelf>,
40                     path: &ast::Path,
41                     shape: Shape)
42                     -> Option<String> {
43     let skip_count = qself.map_or(0, |x| x.position);
44
45     let mut result = if path.is_global() && qself.is_none() &&
46                         path_context != PathContext::Import {
47         "::".to_owned()
48     } else {
49         String::new()
50     };
51
52     let mut span_lo = path.span.lo;
53
54     if let Some(qself) = qself {
55         result.push('<');
56         if context.config.spaces_within_angle_brackets {
57             result.push_str(" ")
58         }
59
60         let fmt_ty = try_opt!(qself.ty.rewrite(context, shape));
61         result.push_str(&fmt_ty);
62
63         if skip_count > 0 {
64             result.push_str(" as ");
65             if path.is_global() && path_context != PathContext::Import {
66                 result.push_str("::");
67             }
68
69             let extra_offset = extra_offset(&result, shape);
70             // 3 = ">::".len()
71             let shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(3));
72
73             result = try_opt!(rewrite_path_segments(PathContext::Type,
74                                                     result,
75                                                     path.segments.iter().take(skip_count),
76                                                     span_lo,
77                                                     path.span.hi,
78                                                     context,
79                                                     shape));
80         }
81
82         if context.config.spaces_within_angle_brackets {
83             result.push_str(" ")
84         }
85
86         result.push_str(">::");
87         span_lo = qself.ty.span.hi + BytePos(1);
88     }
89
90     let extra_offset = extra_offset(&result, shape);
91     let shape = try_opt!(shape.shrink_left(extra_offset));
92     rewrite_path_segments(path_context,
93                           result,
94                           path.segments.iter().skip(skip_count),
95                           span_lo,
96                           path.span.hi,
97                           context,
98                           shape)
99 }
100
101 fn rewrite_path_segments<'a, I>(path_context: PathContext,
102                                 mut buffer: String,
103                                 iter: I,
104                                 mut span_lo: BytePos,
105                                 span_hi: BytePos,
106                                 context: &RewriteContext,
107                                 shape: Shape)
108                                 -> Option<String>
109     where I: Iterator<Item = &'a ast::PathSegment>
110 {
111     let mut first = true;
112     let shape = shape.visual_indent(0);
113
114     for segment in iter {
115         // Indicates a global path, shouldn't be rendered.
116         if segment.identifier.name == keywords::CrateRoot.name() {
117             continue;
118         }
119         if first {
120             first = false;
121         } else {
122             buffer.push_str("::");
123         }
124
125         let extra_offset = extra_offset(&buffer, shape);
126         let new_shape = try_opt!(shape.shrink_left(extra_offset));
127         let segment_string = try_opt!(rewrite_segment(path_context,
128                                                       segment,
129                                                       &mut span_lo,
130                                                       span_hi,
131                                                       context,
132                                                       new_shape));
133
134         buffer.push_str(&segment_string);
135     }
136
137     Some(buffer)
138 }
139
140 #[derive(Debug)]
141 enum SegmentParam<'a> {
142     LifeTime(&'a ast::Lifetime),
143     Type(&'a ast::Ty),
144     Binding(&'a ast::TypeBinding),
145 }
146
147 impl<'a> SegmentParam<'a> {
148     fn get_span(&self) -> Span {
149         match *self {
150             SegmentParam::LifeTime(lt) => lt.span,
151             SegmentParam::Type(ty) => ty.span,
152             SegmentParam::Binding(binding) => binding.span,
153         }
154     }
155 }
156
157 impl<'a> Rewrite for SegmentParam<'a> {
158     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
159         match *self {
160             SegmentParam::LifeTime(lt) => lt.rewrite(context, shape),
161             SegmentParam::Type(ty) => ty.rewrite(context, shape),
162             SegmentParam::Binding(binding) => {
163                 let mut result = format!("{} = ", binding.ident);
164                 let budget = try_opt!(shape.width.checked_sub(result.len()));
165                 let rewrite = try_opt!(binding.ty
166                     .rewrite(context, Shape::legacy(budget, shape.indent + result.len())));
167                 result.push_str(&rewrite);
168                 Some(result)
169             }
170         }
171     }
172 }
173
174 // Formats a path segment. There are some hacks involved to correctly determine
175 // the segment's associated span since it's not part of the AST.
176 //
177 // The span_lo is assumed to be greater than the end of any previous segment's
178 // parameters and lesser or equal than the start of current segment.
179 //
180 // span_hi is assumed equal to the end of the entire path.
181 //
182 // When the segment contains a positive number of parameters, we update span_lo
183 // so that invariants described above will hold for the next segment.
184 fn rewrite_segment(path_context: PathContext,
185                    segment: &ast::PathSegment,
186                    span_lo: &mut BytePos,
187                    span_hi: BytePos,
188                    context: &RewriteContext,
189                    shape: Shape)
190                    -> Option<String> {
191     let ident_len = segment.identifier.to_string().len();
192     let shape = try_opt!(shape.shrink_left(ident_len));
193
194     let params = if let Some(ref params) = segment.parameters {
195         match **params {
196             ast::PathParameters::AngleBracketed(ref data) if !data.lifetimes.is_empty() ||
197                                                              !data.types.is_empty() ||
198                                                              !data.bindings.is_empty() => {
199                 let param_list = data.lifetimes
200                     .iter()
201                     .map(SegmentParam::LifeTime)
202                     .chain(data.types.iter().map(|x| SegmentParam::Type(&*x)))
203                     .chain(data.bindings.iter().map(|x| SegmentParam::Binding(&*x)))
204                     .collect::<Vec<_>>();
205
206                 let next_span_lo = param_list.last().unwrap().get_span().hi + BytePos(1);
207                 let list_lo = context.codemap.span_after(codemap::mk_sp(*span_lo, span_hi), "<");
208                 let separator = if path_context == PathContext::Expr {
209                     "::"
210                 } else {
211                     ""
212                 };
213
214                 // 1 for <
215                 let extra_offset = 1 + separator.len();
216                 // 1 for >
217                 // TODO bad visual indent
218                 let list_shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(1)).visual_indent(0);
219
220                 let items = itemize_list(context.codemap,
221                                          param_list.into_iter(),
222                                          ">",
223                                          |param| param.get_span().lo,
224                                          |param| param.get_span().hi,
225                                          |seg| seg.rewrite(context, list_shape),
226                                          list_lo,
227                                          span_hi);
228                 let list_str = try_opt!(format_item_list(items, list_shape, context.config));
229
230                 // Update position of last bracket.
231                 *span_lo = next_span_lo;
232
233                 if context.config.spaces_within_angle_brackets && list_str.len() > 0 {
234                     format!("{}< {} >", separator, list_str)
235                 } else {
236                     format!("{}<{}>", separator, list_str)
237                 }
238             }
239             ast::PathParameters::Parenthesized(ref data) => {
240                 let output = match data.output {
241                     Some(ref ty) => FunctionRetTy::Ty(ty.clone()),
242                     None => FunctionRetTy::Default(codemap::DUMMY_SP),
243                 };
244                 try_opt!(format_function_type(data.inputs.iter().map(|x| &**x),
245                                               &output,
246                                               false,
247                                               data.span,
248                                               context,
249                                               shape))
250             }
251             _ => String::new(),
252         }
253     } else {
254         String::new()
255     };
256
257     Some(format!("{}{}", segment.identifier, params))
258 }
259
260 fn format_function_type<'a, I>(inputs: I,
261                                output: &FunctionRetTy,
262                                variadic: bool,
263                                span: Span,
264                                context: &RewriteContext,
265                                shape: Shape)
266                                -> Option<String>
267     where I: ExactSizeIterator,
268           <I as Iterator>::Item: Deref,
269           <I::Item as Deref>::Target: Rewrite + Spanned + 'a
270 {
271     // Code for handling variadics is somewhat duplicated for items, but they
272     // are different enough to need some serious refactoring to share code.
273     enum ArgumentKind<T>
274         where T: Deref,
275               <T as Deref>::Target: Rewrite + Spanned
276     {
277         Regular(Box<T>),
278         Variadic(BytePos),
279     }
280
281     let variadic_arg = if variadic {
282         let variadic_start = context.codemap.span_before(span, "...");
283         Some(ArgumentKind::Variadic(variadic_start))
284     } else {
285         None
286     };
287
288     // 2 for ()
289     let budget = try_opt!(shape.width.checked_sub(2));
290     // 1 for (
291     let offset = shape.indent + 1;
292     let list_lo = context.codemap.span_after(span, "(");
293     let items = itemize_list(context.codemap,
294                              // FIXME Would be nice to avoid this allocation,
295                              // but I couldn't get the types to work out.
296                              inputs.map(|i| ArgumentKind::Regular(Box::new(i)))
297                                  .chain(variadic_arg),
298                              ")",
299                              |arg| match *arg {
300                                  ArgumentKind::Regular(ref ty) => ty.span().lo,
301                                  ArgumentKind::Variadic(start) => start,
302                              },
303                              |arg| match *arg {
304                                  ArgumentKind::Regular(ref ty) => ty.span().hi,
305                                  ArgumentKind::Variadic(start) => start + BytePos(3),
306                              },
307                              |arg| match *arg {
308                                  ArgumentKind::Regular(ref ty) => {
309                                      ty.rewrite(context, Shape::legacy(budget, offset))
310                                  }
311                                  ArgumentKind::Variadic(_) => Some("...".to_owned()),
312                              },
313                              list_lo,
314                              span.hi);
315
316     let list_str = try_opt!(format_fn_args(items, Shape::legacy(budget, offset), context.config));
317
318     let output = match *output {
319         FunctionRetTy::Ty(ref ty) => {
320             let budget = try_opt!(shape.width.checked_sub(4));
321             let type_str = try_opt!(ty.rewrite(context, Shape::legacy(budget, offset + 4)));
322             format!(" -> {}", type_str)
323         }
324         FunctionRetTy::Default(..) => String::new(),
325     };
326
327     let infix = if !output.is_empty() && output.len() + list_str.len() > shape.width {
328         format!("\n{}", (offset - 1).to_string(context.config))
329     } else {
330         String::new()
331     };
332
333     Some(if context.config.spaces_within_parens {
334         format!("( {} ){}{}", list_str, infix, output)
335     } else {
336         format!("({}){}{}", list_str, infix, output)
337     })
338 }
339
340 fn type_bound_colon(context: &RewriteContext) -> &'static str {
341     match (context.config.space_before_bound, context.config.space_after_bound_colon) {
342         (true, true) => " : ",
343         (true, false) => " :",
344         (false, true) => ": ",
345         (false, false) => ":",
346     }
347 }
348
349 impl Rewrite for ast::WherePredicate {
350     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
351         // TODO: dead spans?
352         let result = match *self {
353             ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { ref bound_lifetimes,
354                                                                            ref bounded_ty,
355                                                                            ref bounds,
356                                                                            .. }) => {
357                 let type_str = try_opt!(bounded_ty.rewrite(context, shape));
358
359                 let colon = type_bound_colon(context);
360
361                 if !bound_lifetimes.is_empty() {
362                     let lifetime_str: String = try_opt!(bound_lifetimes.iter()
363                                                                .map(|lt| {
364                                                                    lt.rewrite(context, shape)
365                                                                })
366                                                                .intersperse(Some(", ".to_string()))
367                                                                .collect());
368
369                     // 6 = "for<> ".len()
370                     let used_width = lifetime_str.len() + type_str.len() + colon.len() + 6;
371                     let budget = try_opt!(shape.width.checked_sub(used_width));
372                     let bounds_str: String = try_opt!(bounds.iter()
373                                                     .map(|ty_bound| {
374                                                         ty_bound.rewrite(context,
375                                                                          Shape::legacy(budget,
376                                                                          shape.indent + used_width))
377                                                     })
378                                                     .intersperse(Some(" + ".to_string()))
379                                                     .collect());
380
381                     if context.config.spaces_within_angle_brackets && lifetime_str.len() > 0 {
382                         format!("for< {} > {}{}{}", lifetime_str, type_str, colon, bounds_str)
383                     } else {
384                         format!("for<{}> {}{}{}", lifetime_str, type_str, colon, bounds_str)
385                     }
386                 } else {
387                     let used_width = type_str.len() + colon.len();
388                     let budget = try_opt!(shape.width.checked_sub(used_width));
389                     let bounds_str: String = try_opt!(bounds.iter()
390                                                     .map(|ty_bound| {
391                                                         ty_bound.rewrite(context,
392                                                                          Shape::legacy(budget,
393                                                                          shape.indent + used_width))
394                                                     })
395                                                     .intersperse(Some(" + ".to_string()))
396                                                     .collect());
397
398                     format!("{}{}{}", type_str, colon, bounds_str)
399                 }
400             }
401             ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime,
402                                                                              ref bounds,
403                                                                              .. }) => {
404                 try_opt!(rewrite_bounded_lifetime(lifetime, bounds.iter(), context, shape))
405             }
406             ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty,
407                                                                      ref rhs_ty,
408                                                                      .. }) => {
409                 let lhs_ty_str = try_opt!(lhs_ty.rewrite(context, shape));
410                 // 3 = " = ".len()
411                 let used_width = 3 + lhs_ty_str.len();
412                 let budget = try_opt!(shape.width.checked_sub(used_width));
413                 let rhs_ty_str = try_opt!(rhs_ty.rewrite(context,
414                                             Shape::legacy(budget, shape.indent + used_width)));
415                 format!("{} = {}", lhs_ty_str, rhs_ty_str)
416             }
417         };
418
419         wrap_str(result, context.config.max_width, shape)
420     }
421 }
422
423 impl Rewrite for ast::LifetimeDef {
424     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
425         rewrite_bounded_lifetime(&self.lifetime, self.bounds.iter(), context, shape)
426     }
427 }
428
429 fn rewrite_bounded_lifetime<'b, I>(lt: &ast::Lifetime,
430                                    bounds: I,
431                                    context: &RewriteContext,
432                                    shape: Shape)
433                                    -> Option<String>
434     where I: ExactSizeIterator<Item = &'b ast::Lifetime>
435 {
436     let result = try_opt!(lt.rewrite(context, shape));
437
438     if bounds.len() == 0 {
439         Some(result)
440     } else {
441         let appendix: Vec<_> = try_opt!(bounds.into_iter()
442             .map(|b| b.rewrite(context, shape))
443             .collect());
444         let colon = type_bound_colon(context);
445         let result = format!("{}{}{}", result, colon, appendix.join(" + "));
446         wrap_str(result, context.config.max_width, shape)
447     }
448 }
449
450 impl Rewrite for ast::TyParamBound {
451     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
452         match *self {
453             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
454                 tref.rewrite(context, shape)
455             }
456             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => {
457                 let budget = try_opt!(shape.width.checked_sub(1));
458                 Some(format!("?{}",
459                              try_opt!(tref.rewrite(context,
460                                                    Shape::legacy(budget, shape.indent + 1)))))
461             }
462             ast::TyParamBound::RegionTyParamBound(ref l) => l.rewrite(context, shape),
463         }
464     }
465 }
466
467 impl Rewrite for ast::Lifetime {
468     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
469         wrap_str(pprust::lifetime_to_string(self),
470                  context.config.max_width,
471                  shape)
472     }
473 }
474
475 impl Rewrite for ast::TyParamBounds {
476     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
477         let joiner = match context.config.type_punctuation_density {
478             TypeDensity::Compressed => "+",
479             TypeDensity::Wide => " + ",
480         };
481         let strs: Vec<_> = try_opt!(self.iter()
482             .map(|b| b.rewrite(context, shape))
483             .collect());
484         wrap_str(strs.join(joiner), context.config.max_width, shape)
485     }
486 }
487
488 impl Rewrite for ast::TyParam {
489     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
490         let mut result = String::with_capacity(128);
491         result.push_str(&self.ident.to_string());
492         if !self.bounds.is_empty() {
493             if context.config.space_before_bound {
494                 result.push_str(" ");
495             }
496             result.push_str(":");
497             if context.config.space_after_bound_colon {
498                 result.push_str(" ");
499             }
500
501             let bounds: String = try_opt!(self.bounds
502                 .iter()
503                 .map(|ty_bound| ty_bound.rewrite(context, shape))
504                 .intersperse(Some(" + ".to_string()))
505                 .collect());
506
507             result.push_str(&bounds);
508         }
509         if let Some(ref def) = self.default {
510
511             let eq_str = match context.config.type_punctuation_density {
512                 TypeDensity::Compressed => "=",
513                 TypeDensity::Wide => " = ",
514             };
515             result.push_str(eq_str);
516             let budget = try_opt!(shape.width.checked_sub(result.len()));
517             let rewrite =
518                 try_opt!(def.rewrite(context, Shape::legacy(budget, shape.indent + result.len())));
519             result.push_str(&rewrite);
520         }
521
522         wrap_str(result, context.config.max_width, shape)
523     }
524 }
525
526 impl Rewrite for ast::PolyTraitRef {
527     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
528         if !self.bound_lifetimes.is_empty() {
529             let lifetime_str: String = try_opt!(self.bound_lifetimes
530                 .iter()
531                 .map(|lt| lt.rewrite(context, shape))
532                 .intersperse(Some(", ".to_string()))
533                 .collect());
534
535             // 6 is "for<> ".len()
536             let extra_offset = lifetime_str.len() + 6;
537             let max_path_width = try_opt!(shape.width.checked_sub(extra_offset));
538             let path_str = try_opt!(self.trait_ref
539                 .rewrite(context,
540                          Shape::legacy(max_path_width, shape.indent + extra_offset)));
541
542             Some(if context.config.spaces_within_angle_brackets && lifetime_str.len() > 0 {
543                 format!("for< {} > {}", lifetime_str, path_str)
544             } else {
545                 format!("for<{}> {}", lifetime_str, path_str)
546             })
547         } else {
548             self.trait_ref.rewrite(context, shape)
549         }
550     }
551 }
552
553 impl Rewrite for ast::TraitRef {
554     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
555         rewrite_path(context, PathContext::Type, None, &self.path, shape)
556     }
557 }
558
559 impl Rewrite for ast::Ty {
560     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
561         match self.node {
562             ast::TyKind::TraitObject(ref bounds) => bounds.rewrite(context, shape),
563             ast::TyKind::Ptr(ref mt) => {
564                 let prefix = match mt.mutbl {
565                     Mutability::Mutable => "*mut ",
566                     Mutability::Immutable => "*const ",
567                 };
568
569                 rewrite_unary_prefix(context, prefix, &*mt.ty, shape)
570             }
571             ast::TyKind::Rptr(ref lifetime, ref mt) => {
572                 let mut_str = format_mutability(mt.mutbl);
573                 let mut_len = mut_str.len();
574                 Some(match *lifetime {
575                     Some(ref lifetime) => {
576                         let lt_budget = try_opt!(shape.width.checked_sub(2 + mut_len));
577                         let lt_str = try_opt!(lifetime.rewrite(context,
578                                                                Shape::legacy(lt_budget,
579                                                                              shape.indent + 2 +
580                                                                              mut_len)));
581                         let lt_len = lt_str.len();
582                         let budget = try_opt!(shape.width.checked_sub(2 + mut_len + lt_len));
583                         format!("&{} {}{}",
584                                 lt_str,
585                                 mut_str,
586                                 try_opt!(mt.ty
587                                     .rewrite(context,
588                                              Shape::legacy(budget,
589                                                            shape.indent + 2 + mut_len + lt_len))))
590                     }
591                     None => {
592                         let budget = try_opt!(shape.width.checked_sub(1 + mut_len));
593                         format!("&{}{}",
594                                 mut_str,
595                                 try_opt!(mt.ty.rewrite(context,
596                                                        Shape::legacy(budget,
597                                                                      shape.indent + 1 + mut_len))))
598                     }
599                 })
600             }
601             // FIXME: we drop any comments here, even though it's a silly place to put
602             // comments.
603             ast::TyKind::Paren(ref ty) => {
604                 let budget = try_opt!(shape.width.checked_sub(2));
605                 ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
606                     .map(|ty_str| if context.config.spaces_within_parens {
607                         format!("( {} )", ty_str)
608                     } else {
609                         format!("({})", ty_str)
610                     })
611             }
612             ast::TyKind::Slice(ref ty) => {
613                 let budget = if context.config.spaces_within_square_brackets {
614                     try_opt!(shape.width.checked_sub(4))
615                 } else {
616                     try_opt!(shape.width.checked_sub(2))
617                 };
618                 ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
619                     .map(|ty_str| if context.config.spaces_within_square_brackets {
620                         format!("[ {} ]", ty_str)
621                     } else {
622                         format!("[{}]", ty_str)
623                     })
624             }
625             ast::TyKind::Tup(ref items) => {
626                 rewrite_tuple(context, items.iter().map(|x| &**x), self.span, shape)
627             }
628             ast::TyKind::Path(ref q_self, ref path) => {
629                 rewrite_path(context, PathContext::Type, q_self.as_ref(), path, shape)
630             }
631             ast::TyKind::Array(ref ty, ref repeats) => {
632                 let use_spaces = context.config.spaces_within_square_brackets;
633                 let lbr = if use_spaces { "[ " } else { "[" };
634                 let rbr = if use_spaces { " ]" } else { "]" };
635                 rewrite_pair(&**ty, &**repeats, lbr, "; ", rbr, context, shape)
636             }
637             ast::TyKind::Infer => {
638                 if shape.width >= 1 {
639                     Some("_".to_owned())
640                 } else {
641                     None
642                 }
643             }
644             ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape),
645             ast::TyKind::Never => Some(String::from("!")),
646             ast::TyKind::Mac(..) => None,
647             ast::TyKind::ImplicitSelf => Some(String::from("")),
648             ast::TyKind::ImplTrait(ref it) => {
649                 it.rewrite(context, shape).map(|it_str| format!("impl {}", it_str))
650             }
651             ast::TyKind::Typeof(..) => unreachable!(),
652         }
653     }
654 }
655
656 fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
657                    span: Span,
658                    context: &RewriteContext,
659                    shape: Shape)
660                    -> Option<String> {
661     let mut result = String::with_capacity(128);
662
663     if !bare_fn.lifetimes.is_empty() {
664         result.push_str("for<");
665         // 6 = "for<> ".len(), 4 = "for<".
666         // This doesn't work out so nicely for mutliline situation with lots of
667         // rightward drift. If that is a problem, we could use the list stuff.
668         result.push_str(&try_opt!(bare_fn.lifetimes
669             .iter()
670             .map(|l| {
671                 l.rewrite(context,
672                           Shape::legacy(try_opt!(shape.width.checked_sub(6)), shape.indent + 4))
673             })
674             .intersperse(Some(", ".to_string()))
675             .collect::<Option<String>>()));
676         result.push_str("> ");
677     }
678
679     result.push_str(::utils::format_unsafety(bare_fn.unsafety));
680
681     if bare_fn.abi != abi::Abi::Rust {
682         result.push_str(&::utils::format_abi(bare_fn.abi, context.config.force_explicit_abi));
683     }
684
685     result.push_str("fn");
686
687     let budget = try_opt!(shape.width.checked_sub(result.len()));
688     let indent = shape.indent + result.len();
689
690     let rewrite = try_opt!(format_function_type(bare_fn.decl.inputs.iter(),
691                                                 &bare_fn.decl.output,
692                                                 bare_fn.decl.variadic,
693                                                 span,
694                                                 context,
695                                                 Shape::legacy(budget, indent)));
696
697     result.push_str(&rewrite);
698
699     Some(result)
700 }