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