]> git.lizzy.rs Git - rust.git/blob - src/types.rs
0c0d9e54424ae74596adc5ff05b0578db1f1f6e4
[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;
12 use syntax::print::pprust;
13 use syntax::codemap::{self, Span, BytePos, CodeMap};
14
15 use lists::{itemize_list, write_list, ListFormatting};
16 use rewrite::{Rewrite, RewriteContext};
17 use utils::{extra_offset, span_after};
18
19 impl Rewrite for ast::Path {
20     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
21         rewrite_path(context, None, self, width, offset)
22     }
23 }
24
25 // Does not wrap on simple segments.
26 pub fn rewrite_path(context: &RewriteContext,
27                     qself: Option<&ast::QSelf>,
28                     path: &ast::Path,
29                     width: usize,
30                     offset: usize)
31                     -> Option<String> {
32     let skip_count = qself.map(|x| x.position).unwrap_or(0);
33
34     let mut result = if path.global {
35         "::".to_owned()
36     } else {
37         String::new()
38     };
39
40     let mut span_lo = path.span.lo;
41
42     if let Some(ref qself) = qself {
43         result.push('<');
44         result.push_str(&pprust::ty_to_string(&qself.ty));
45         result.push_str(" as ");
46
47         let extra_offset = extra_offset(&result, offset);
48         // 3 = ">::".len()
49         let budget = try_opt!(width.checked_sub(extra_offset)) - 3;
50
51         result = try_opt!(rewrite_path_segments(result,
52                                                 path.segments.iter().take(skip_count),
53                                                 span_lo,
54                                                 path.span.hi,
55                                                 context,
56                                                 budget,
57                                                 offset + extra_offset));
58
59         result.push_str(">::");
60         span_lo = qself.ty.span.hi + BytePos(1);
61     }
62
63     let extra_offset = extra_offset(&result, offset);
64     let budget = try_opt!(width.checked_sub(extra_offset));
65     rewrite_path_segments(result,
66                           path.segments.iter().skip(skip_count),
67                           span_lo,
68                           path.span.hi,
69                           context,
70                           budget,
71                           offset + extra_offset)
72 }
73
74 fn rewrite_path_segments<'a, I>(mut buffer: String,
75                                 iter: I,
76                                 mut span_lo: BytePos,
77                                 span_hi: BytePos,
78                                 context: &RewriteContext,
79                                 width: usize,
80                                 offset: usize)
81                                 -> Option<String>
82     where I: Iterator<Item = &'a ast::PathSegment>
83 {
84     let mut first = true;
85
86     for segment in iter {
87         let extra_offset = extra_offset(&buffer, offset);
88         let remaining_width = try_opt!(width.checked_sub(extra_offset));
89         let new_offset = offset + extra_offset;
90         let segment_string = try_opt!(rewrite_segment(segment,
91                                                       &mut span_lo,
92                                                       span_hi,
93                                                       context,
94                                                       remaining_width,
95                                                       new_offset));
96
97         if first {
98             first = false;
99         } else {
100             buffer.push_str("::");
101         }
102
103         buffer.push_str(&segment_string);
104     }
105
106     Some(buffer)
107 }
108
109 #[derive(Debug)]
110 enum SegmentParam<'a> {
111     LifeTime(&'a ast::Lifetime),
112     Type(&'a ast::Ty),
113     Binding(&'a ast::TypeBinding),
114 }
115
116 impl<'a> SegmentParam<'a> {
117     fn get_span(&self) -> Span {
118         match *self {
119             SegmentParam::LifeTime(ref lt) => lt.span,
120             SegmentParam::Type(ref ty) => ty.span,
121             SegmentParam::Binding(ref binding) => binding.span,
122         }
123     }
124 }
125
126 impl<'a> Rewrite for SegmentParam<'a> {
127     // FIXME doesn't always use width, offset
128     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
129         Some(match *self {
130                 SegmentParam::LifeTime(ref lt) => {
131                     pprust::lifetime_to_string(lt)
132                 }
133                 SegmentParam::Type(ref ty) => {
134                     try_opt!(ty.rewrite(context, width, offset))
135                 }
136                 SegmentParam::Binding(ref binding) => {
137                     format!("{} = {}", binding.ident, try_opt!(binding.ty.rewrite(context, width, offset)))
138                 }
139             })
140     }
141 }
142
143 // This is a dirty hack to determine if we're in an expression or not. Generic
144 // parameters are passed differently in expressions and items. We'd declare
145 // a struct with Foo<A, B>, but call its functions with Foo::<A, B>::f().
146 // We'd really rather not do this, but there doesn't seem to be an alternative
147 // at this point.
148 // FIXME: fails with spans containing comments with the characters < or :
149 // FIXME #184 skip due to continue.
150 #[rustfmt_skip]
151 fn get_path_separator(codemap: &CodeMap,
152                       path_start: BytePos,
153                       segment_start: BytePos)
154                       -> &'static str {
155     let span = codemap::mk_sp(path_start, segment_start);
156     let snippet = codemap.span_to_snippet(span).unwrap();
157
158     for c in snippet.chars().rev() {
159         if c == ':' {
160             return "::";
161         } else if c.is_whitespace() || c == '<' {
162             continue;
163         } else {
164             return "";
165         }
166     }
167
168     unreachable!();
169 }
170
171 // Formats a path segment. There are some hacks involved to correctly determine
172 // the segment's associated span since it's not part of the AST.
173 //
174 // The span_lo is assumed to be greater than the end of any previous segment's
175 // parameters and lesser or equal than the start of current segment.
176 //
177 // span_hi is assumed equal to the end of the entire path.
178 //
179 // When the segment contains a positive number of parameters, we update span_lo
180 // so that invariants described above will hold for the next segment.
181 fn rewrite_segment(segment: &ast::PathSegment,
182                    span_lo: &mut BytePos,
183                    span_hi: BytePos,
184                    context: &RewriteContext,
185                    width: usize,
186                    offset: usize)
187                    -> Option<String> {
188     let ident_len = segment.identifier.to_string().len();
189     let width = try_opt!(width.checked_sub(ident_len));
190     let offset = offset + ident_len;
191
192     let params = match segment.parameters {
193         ast::PathParameters::AngleBracketedParameters(ref data) if !data.lifetimes.is_empty() ||
194                                                                    !data.types.is_empty() ||
195                                                                    !data.bindings.is_empty() => {
196             let param_list = data.lifetimes.iter()
197                                            .map(SegmentParam::LifeTime)
198                                            .chain(data.types.iter()
199                                                       .map(|x| SegmentParam::Type(&*x)))
200                                            .chain(data.bindings.iter()
201                                                       .map(|x| SegmentParam::Binding(&*x)))
202                                            .collect::<Vec<_>>();
203
204             let next_span_lo = param_list.last().unwrap().get_span().hi + BytePos(1);
205             let list_lo = span_after(codemap::mk_sp(*span_lo, span_hi), "<", context.codemap);
206             let separator = get_path_separator(context.codemap, *span_lo, list_lo);
207
208             // 1 for <
209             let extra_offset = 1 + separator.len();
210             // 1 for >
211             let list_width = try_opt!(width.checked_sub(extra_offset + 1));
212
213             let items = itemize_list(context.codemap,
214                                      param_list.into_iter(),
215                                      ">",
216                                      |param| param.get_span().lo,
217                                      |param| param.get_span().hi,
218                                      |seg| {
219                                          seg.rewrite(context, list_width, offset + extra_offset).unwrap()
220                                      },
221                                      list_lo,
222                                      span_hi);
223
224             let fmt = ListFormatting::for_fn(list_width, offset + extra_offset);
225
226             // update pos
227             *span_lo = next_span_lo;
228
229             format!("{}<{}>", separator, write_list(&items.collect::<Vec<_>>(), &fmt))
230         }
231         ast::PathParameters::ParenthesizedParameters(ref data) => {
232             let output = match data.output {
233                 Some(ref ty) => format!(" -> {}", pprust::ty_to_string(&*ty)),
234                 None => String::new(),
235             };
236
237             let list_lo = span_after(data.span, "(", context.codemap);
238             let items = itemize_list(context.codemap,
239                                      data.inputs.iter(),
240                                      ")",
241                                      |ty| ty.span.lo,
242                                      |ty| ty.span.hi,
243                                      |ty| pprust::ty_to_string(ty),
244                                      list_lo,
245                                      span_hi);
246
247             // 2 for ()
248             let budget = try_opt!(width.checked_sub(output.len() + 2));
249
250             // 1 for (
251             let fmt = ListFormatting::for_fn(budget, offset + 1);
252
253             // update pos
254             *span_lo = data.span.hi + BytePos(1);
255
256             format!("({}){}", write_list(&items.collect::<Vec<_>>(), &fmt), output)
257         }
258         _ => String::new(),
259     };
260
261     Some(format!("{}{}", segment.identifier, params))
262 }
263
264 impl Rewrite for ast::WherePredicate {
265     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
266         // TODO dead spans?
267         // TODO assumes we'll always fit on one line...
268         Some(match *self {
269                 ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bound_lifetimes,
270                                                                           ref bounded_ty,
271                                                                           ref bounds,
272                                                                           ..}) => {
273                     if !bound_lifetimes.is_empty() {
274                         let lifetime_str = bound_lifetimes.iter().map(|lt| {
275                                            lt.rewrite(context, width, offset).unwrap()
276                                        }).collect::<Vec<_>>().join(", ");
277                         let type_str = pprust::ty_to_string(bounded_ty);
278                     // 8 = "for<> : ".len()
279                         let used_width = lifetime_str.len() + type_str.len() + 8;
280                         let bounds_str = bounds.iter().map(|ty_bound| {
281                                          ty_bound.rewrite(context,
282                                                           width - used_width,
283                                                           offset + used_width)
284                                                  .unwrap()
285                                      }).collect::<Vec<_>>().join(" + ");
286
287                         format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str)
288                     } else {
289                         let type_str = pprust::ty_to_string(bounded_ty);
290                     // 2 = ": ".len()
291                         let used_width = type_str.len() + 2;
292                         let bounds_str = bounds.iter().map(|ty_bound| {
293                                          ty_bound.rewrite(context,
294                                                           width - used_width,
295                                                           offset + used_width)
296                                                  .unwrap()
297                                      }).collect::<Vec<_>>().join(" + ");
298
299                         format!("{}: {}", type_str, bounds_str)
300                     }
301                 }
302                 ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime,
303                                                                             ref bounds,
304                                                                             ..}) => {
305                     format!("{}: {}",
306                         pprust::lifetime_to_string(lifetime),
307                         bounds.iter().map(pprust::lifetime_to_string)
308                               .collect::<Vec<_>>().join(" + "))
309                 }
310                 ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => {
311                     let ty_str = pprust::ty_to_string(ty);
312                 // 3 = " = ".len()
313                     let used_width = 3 + ty_str.len();
314                     let path_str = try_opt!(path.rewrite(context,
315                                                      width - used_width,
316                                                      offset + used_width));
317                     format!("{} = {}", path_str, ty_str)
318                 }
319             })
320     }
321 }
322
323 impl Rewrite for ast::LifetimeDef {
324     fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option<String> {
325         if self.bounds.is_empty() {
326             Some(pprust::lifetime_to_string(&self.lifetime))
327         } else {
328             Some(format!("{}: {}",
329                          pprust::lifetime_to_string(&self.lifetime),
330                          self.bounds.iter().map(pprust::lifetime_to_string)
331                                     .collect::<Vec<_>>().join(" + ")))
332         }
333     }
334 }
335
336 impl Rewrite for ast::TyParamBound {
337     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
338         match *self {
339             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
340                 tref.rewrite(context, width, offset)
341             }
342             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => {
343                 Some(format!("?{}", try_opt!(tref.rewrite(context, width - 1, offset + 1))))
344             }
345             ast::TyParamBound::RegionTyParamBound(ref l) => {
346                 Some(pprust::lifetime_to_string(l))
347             }
348         }
349     }
350 }
351
352 impl Rewrite for ast::TyParamBounds {
353     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
354         let strs: Vec<_> = self.iter().map(|b| b.rewrite(context, width, offset).unwrap()).collect();
355         Some(strs.join(" + "))
356     }
357 }
358
359 // FIXME: this assumes everything will fit on one line
360 impl Rewrite for ast::TyParam {
361     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
362         let mut result = String::with_capacity(128);
363         result.push_str(&self.ident.to_string());
364         if !self.bounds.is_empty() {
365             result.push_str(": ");
366
367             let bounds = self.bounds.iter().map(|ty_bound| {
368                 ty_bound.rewrite(context, width, offset).unwrap()
369             }).collect::<Vec<_>>().join(" + ");
370
371             result.push_str(&bounds);
372         }
373         if let Some(ref def) = self.default {
374             result.push_str(" = ");
375             result.push_str(&pprust::ty_to_string(&def));
376         }
377
378         Some(result)
379     }
380 }
381
382 // FIXME: this assumes everything will fit on one line
383 impl Rewrite for ast::PolyTraitRef {
384     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
385         if !self.bound_lifetimes.is_empty() {
386             let lifetime_str = self.bound_lifetimes.iter().map(|lt| {
387                 lt.rewrite(context, width, offset).unwrap()
388             }).collect::<Vec<_>>().join(", ");
389             // 6 is "for<> ".len()
390             let extra_offset = lifetime_str.len() + 6;
391             let max_path_width = try_opt!(width.checked_sub(extra_offset));
392             let path_str = try_opt!(self.trait_ref.path.rewrite(context,
393                                                                 max_path_width,
394                                                                 offset + extra_offset));
395
396             Some(format!("for<{}> {}", lifetime_str, path_str))
397         } else {
398             self.trait_ref.path.rewrite(context, width, offset)
399         }
400     }
401 }
402
403 impl Rewrite for ast::Ty {
404     // FIXME doesn't always use width, offset
405     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
406         match self.node {
407             ast::TyPath(None, ref p) => {
408                 p.rewrite(context, width, offset)
409             }
410             ast::TyObjectSum(ref ty, ref bounds) => {
411                 Some(format!("{} + {}",
412                              try_opt!(ty.rewrite(context, width, offset)),
413                              try_opt!(bounds.rewrite(context, width, offset))))
414             }
415             _ => Some(pprust::ty_to_string(self)),
416         }
417     }
418 }