]> git.lizzy.rs Git - rust.git/blob - src/types.rs
Format bare 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             try_opt!(format_function_type(data.inputs.iter().map(|x| &**x),
219                                           data.output.as_ref().map(|x| &**x),
220                                           data.span,
221                                           context,
222                                           width,
223                                           offset))
224         }
225         _ => String::new(),
226     };
227
228     Some(format!("{}{}", segment.identifier, params))
229 }
230
231 fn format_function_type<'a, I>(inputs: I,
232                                output: Option<&ast::Ty>,
233                                span: Span,
234                                context: &RewriteContext,
235                                width: usize,
236                                offset: Indent)
237                                -> Option<String>
238     where I: Iterator<Item = &'a ast::Ty>
239 {
240     // 2 for ()
241     let budget = try_opt!(width.checked_sub(2));
242     // 1 for (
243     let offset = offset + 1;
244     let list_lo = span_after(span, "(", context.codemap);
245     let items = itemize_list(context.codemap,
246                              inputs,
247                              ")",
248                              |ty| ty.span.lo,
249                              |ty| ty.span.hi,
250                              |ty| ty.rewrite(context, budget, offset),
251                              list_lo,
252                              span.hi);
253
254     let list_str = try_opt!(format_fn_args(items, budget, offset, context.config));
255
256     let output = match output {
257         Some(ref ty) => {
258             let budget = try_opt!(width.checked_sub(4));
259             let type_str = try_opt!(ty.rewrite(context, budget, offset + 4));
260             format!(" -> {}", type_str)
261         }
262         None => String::new(),
263     };
264
265     let infix = if output.len() + list_str.len() > width {
266         format!("\n{}", (offset - 1).to_string(context.config))
267     } else {
268         String::new()
269     };
270
271     Some(format!("({}){}{}", list_str, infix, output))
272 }
273
274 impl Rewrite for ast::WherePredicate {
275     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
276         // TODO: dead spans?
277         let result = match *self {
278             ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { ref bound_lifetimes,
279                                                                            ref bounded_ty,
280                                                                            ref bounds,
281                                                                            .. }) => {
282                 let type_str = try_opt!(bounded_ty.rewrite(context, width, offset));
283
284                 if !bound_lifetimes.is_empty() {
285                     let lifetime_str = try_opt!(bound_lifetimes.iter()
286                                                                .map(|lt| {
287                                                                    lt.rewrite(context,
288                                                                               width,
289                                                                               offset)
290                                                                })
291                                                                .collect::<Option<Vec<_>>>())
292                                            .join(", ");
293                     // 8 = "for<> : ".len()
294                     let used_width = lifetime_str.len() + type_str.len() + 8;
295                     let budget = try_opt!(width.checked_sub(used_width));
296                     let bounds_str = try_opt!(bounds.iter()
297                                                     .map(|ty_bound| {
298                                                         ty_bound.rewrite(context,
299                                                                          budget,
300                                                                          offset + used_width)
301                                                     })
302                                                     .collect::<Option<Vec<_>>>())
303                                          .join(" + ");
304
305                     format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str)
306                 } else {
307                     // 2 = ": ".len()
308                     let used_width = type_str.len() + 2;
309                     let budget = try_opt!(width.checked_sub(used_width));
310                     let bounds_str = try_opt!(bounds.iter()
311                                                     .map(|ty_bound| {
312                                                         ty_bound.rewrite(context,
313                                                                          budget,
314                                                                          offset + used_width)
315                                                     })
316                                                     .collect::<Option<Vec<_>>>())
317                                          .join(" + ");
318
319                     format!("{}: {}", type_str, bounds_str)
320                 }
321             }
322             ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime,
323                                                                              ref bounds,
324                                                                              .. }) => {
325                 format!("{}: {}",
326                         pprust::lifetime_to_string(lifetime),
327                         bounds.iter()
328                               .map(pprust::lifetime_to_string)
329                               .collect::<Vec<_>>()
330                               .join(" + "))
331             }
332             ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref path, ref ty, .. }) => {
333                 let ty_str = try_opt!(ty.rewrite(context, width, offset));
334                 // 3 = " = ".len()
335                 let used_width = 3 + ty_str.len();
336                 let budget = try_opt!(width.checked_sub(used_width));
337                 let path_str = try_opt!(rewrite_path(context,
338                                                      false,
339                                                      None,
340                                                      path,
341                                                      budget,
342                                                      offset + used_width));
343                 format!("{} = {}", path_str, ty_str)
344             }
345         };
346
347         wrap_str(result, context.config.max_width, width, offset)
348     }
349 }
350
351 impl Rewrite for ast::LifetimeDef {
352     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
353         let result = if self.bounds.is_empty() {
354             pprust::lifetime_to_string(&self.lifetime)
355         } else {
356             format!("{}: {}",
357                     pprust::lifetime_to_string(&self.lifetime),
358                     self.bounds
359                         .iter()
360                         .map(pprust::lifetime_to_string)
361                         .collect::<Vec<_>>()
362                         .join(" + "))
363         };
364
365         wrap_str(result, context.config.max_width, width, offset)
366     }
367 }
368
369 impl Rewrite for ast::TyParamBound {
370     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
371         match *self {
372             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
373                 tref.rewrite(context, width, offset)
374             }
375             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => {
376                 let budget = try_opt!(width.checked_sub(1));
377                 Some(format!("?{}", try_opt!(tref.rewrite(context, budget, offset + 1))))
378             }
379             ast::TyParamBound::RegionTyParamBound(ref l) => {
380                 wrap_str(pprust::lifetime_to_string(l),
381                          context.config.max_width,
382                          width,
383                          offset)
384             }
385         }
386     }
387 }
388
389 impl Rewrite for ast::TyParamBounds {
390     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
391         let strs: Vec<_> = try_opt!(self.iter()
392                                         .map(|b| b.rewrite(context, width, offset))
393                                         .collect());
394         wrap_str(strs.join(" + "), context.config.max_width, width, offset)
395     }
396 }
397
398 impl Rewrite for ast::TyParam {
399     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
400         let mut result = String::with_capacity(128);
401         result.push_str(&self.ident.to_string());
402         if !self.bounds.is_empty() {
403             result.push_str(": ");
404
405             let bounds = try_opt!(self.bounds
406                                       .iter()
407                                       .map(|ty_bound| ty_bound.rewrite(context, width, offset))
408                                       .collect::<Option<Vec<_>>>())
409                              .join(" + ");
410
411             result.push_str(&bounds);
412         }
413         if let Some(ref def) = self.default {
414             result.push_str(" = ");
415             let budget = try_opt!(width.checked_sub(result.len()));
416             let rewrite = try_opt!(def.rewrite(context, budget, offset + result.len()));
417             result.push_str(&rewrite);
418         }
419
420         wrap_str(result, context.config.max_width, width, offset)
421     }
422 }
423
424 impl Rewrite for ast::PolyTraitRef {
425     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
426         if !self.bound_lifetimes.is_empty() {
427             let lifetime_str = try_opt!(self.bound_lifetimes
428                                             .iter()
429                                             .map(|lt| lt.rewrite(context, width, offset))
430                                             .collect::<Option<Vec<_>>>())
431                                    .join(", ");
432             // 6 is "for<> ".len()
433             let extra_offset = lifetime_str.len() + 6;
434             let max_path_width = try_opt!(width.checked_sub(extra_offset));
435             let path_str = try_opt!(rewrite_path(context,
436                                                  false,
437                                                  None,
438                                                  &self.trait_ref.path,
439                                                  max_path_width,
440                                                  offset + extra_offset));
441
442             Some(format!("for<{}> {}", lifetime_str, path_str))
443         } else {
444             rewrite_path(context, false, None, &self.trait_ref.path, width, offset)
445         }
446     }
447 }
448
449 impl Rewrite for ast::Ty {
450     fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
451         match self.node {
452             ast::TyObjectSum(ref ty, ref bounds) => {
453                 let ty_str = try_opt!(ty.rewrite(context, width, offset));
454                 let overhead = ty_str.len() + 3;
455                 Some(format!("{} + {}",
456                              ty_str,
457                              try_opt!(bounds.rewrite(context,
458                                                      try_opt!(width.checked_sub(overhead)),
459                                                      offset + overhead))))
460             }
461             ast::TyPtr(ref mt) => {
462                 let prefix = match mt.mutbl {
463                     Mutability::MutMutable => "*mut ",
464                     Mutability::MutImmutable => "*const ",
465                 };
466
467                 rewrite_unary_prefix(context, prefix, &*mt.ty, width, offset)
468             }
469             ast::TyRptr(ref lifetime, ref mt) => {
470                 let mut_str = format_mutability(mt.mutbl);
471                 let mut_len = mut_str.len();
472                 Some(match *lifetime {
473                     Some(ref lifetime) => {
474                         let lt_str = pprust::lifetime_to_string(lifetime);
475                         let lt_len = lt_str.len();
476                         let budget = try_opt!(width.checked_sub(2 + mut_len + lt_len));
477                         format!("&{} {}{}",
478                                 lt_str,
479                                 mut_str,
480                                 try_opt!(mt.ty.rewrite(context,
481                                                        budget,
482                                                        offset + 2 + mut_len + lt_len)))
483                     }
484                     None => {
485                         let budget = try_opt!(width.checked_sub(1 + mut_len));
486                         format!("&{}{}",
487                                 mut_str,
488                                 try_opt!(mt.ty.rewrite(context, budget, offset + 1 + mut_len)))
489                     }
490                 })
491             }
492             // FIXME: we drop any comments here, even though it's a silly place to put
493             // comments.
494             ast::TyParen(ref ty) => {
495                 let budget = try_opt!(width.checked_sub(2));
496                 ty.rewrite(context, budget, offset + 1).map(|ty_str| format!("({})", ty_str))
497             }
498             ast::TyVec(ref ty) => {
499                 let budget = try_opt!(width.checked_sub(2));
500                 ty.rewrite(context, budget, offset + 1).map(|ty_str| format!("[{}]", ty_str))
501             }
502             ast::TyTup(ref items) => rewrite_tuple(context, items, self.span, width, offset),
503             ast::TyPolyTraitRef(ref trait_ref) => trait_ref.rewrite(context, width, offset),
504             ast::TyPath(ref q_self, ref path) => {
505                 rewrite_path(context, false, q_self.as_ref(), path, width, offset)
506             }
507             ast::TyFixedLengthVec(ref ty, ref repeats) => {
508                 rewrite_pair(&**ty, &**repeats, "[", "; ", "]", context, width, offset)
509             }
510             ast::TyInfer => {
511                 if width >= 1 {
512                     Some("_".to_owned())
513                 } else {
514                     None
515                 }
516             }
517             ast::TyBareFn(ref bare_fn) => {
518                 rewrite_bare_fn(bare_fn, self.span, context, width, offset)
519             }
520             ast::TyMac(..) | ast::TyTypeof(..) => unreachable!(),
521         }
522     }
523 }
524
525 fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
526                    span: Span,
527                    context: &RewriteContext,
528                    width: usize,
529                    offset: Indent)
530                    -> Option<String> {
531     let mut result = String::with_capacity(128);
532
533     result.push_str(&::utils::format_unsafety(bare_fn.unsafety));
534
535     if bare_fn.abi != abi::Rust {
536         result.push_str(&::utils::format_abi(bare_fn.abi));
537     }
538
539     result.push_str("fn");
540
541     let output = match bare_fn.decl.output {
542         FunctionRetTy::Return(ref ty) => Some(&**ty),
543         FunctionRetTy::NoReturn(..) => None,
544         FunctionRetTy::DefaultReturn(..) => unreachable!(),
545     };
546
547     let budget = try_opt!(width.checked_sub(result.len()));
548     let indent = offset + result.len();
549
550     let rewrite = try_opt!(format_function_type(bare_fn.decl.inputs.iter().map(|x| &*(x.ty)),
551                                                 output,
552                                                 span,
553                                                 context,
554                                                 budget,
555                                                 indent));
556
557     result.push_str(&rewrite);
558
559     Some(result)
560 }