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