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