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