]> git.lizzy.rs Git - rust.git/blob - src/types.rs
Merge pull request #309 from marcusklaas/array-literals
[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         if first {
91             first = false;
92         } else {
93             buffer.push_str("::");
94         }
95
96         let extra_offset = extra_offset(&buffer, offset);
97         let remaining_width = try_opt!(width.checked_sub(extra_offset));
98         let new_offset = offset + extra_offset;
99         let segment_string = try_opt!(rewrite_segment(segment,
100                                                       &mut span_lo,
101                                                       span_hi,
102                                                       context,
103                                                       remaining_width,
104                                                       new_offset));
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
200                                  .iter()
201                                  .map(SegmentParam::LifeTime)
202                                  .chain(data.types.iter().map(|x| SegmentParam::Type(&*x)))
203                                  .chain(data.bindings.iter().map(|x| SegmentParam::Binding(&*x)))
204                                  .collect::<Vec<_>>();
205
206             let next_span_lo = param_list.last().unwrap().get_span().hi + BytePos(1);
207             let list_lo = span_after(codemap::mk_sp(*span_lo, span_hi), "<", context.codemap);
208             let separator = get_path_separator(context.codemap, *span_lo, list_lo);
209
210             // 1 for <
211             let extra_offset = 1 + separator.len();
212             // 1 for >
213             let list_width = try_opt!(width.checked_sub(extra_offset + 1));
214
215             let items = itemize_list(context.codemap,
216                                      param_list.into_iter(),
217                                      ">",
218                                      |param| param.get_span().lo,
219                                      |param| param.get_span().hi,
220                                      // FIXME(#133): write_list should call
221                                      // rewrite itself, because it has a better
222                                      // context.
223                                      |seg| {
224                                          seg.rewrite(context,
225                                                      context.config.max_width,
226                                                      offset + extra_offset)
227                                             .unwrap()
228                                      },
229                                      list_lo,
230                                      span_hi);
231
232             let fmt = ListFormatting::for_fn(list_width, offset + extra_offset);
233             let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
234
235             // Update position of last bracket.
236             *span_lo = next_span_lo;
237
238             format!("{}<{}>", separator, list_str)
239         }
240         ast::PathParameters::ParenthesizedParameters(ref data) => {
241             let output = match data.output {
242                 Some(ref ty) => format!(" -> {}", pprust::ty_to_string(&*ty)),
243                 None => String::new(),
244             };
245
246             let list_lo = span_after(data.span, "(", context.codemap);
247             let items = itemize_list(context.codemap,
248                                      data.inputs.iter(),
249                                      ")",
250                                      |ty| ty.span.lo,
251                                      |ty| ty.span.hi,
252                                      |ty| pprust::ty_to_string(ty),
253                                      list_lo,
254                                      span_hi);
255
256             // 2 for ()
257             let budget = try_opt!(width.checked_sub(output.len() + 2));
258
259             // 1 for (
260             let fmt = ListFormatting::for_fn(budget, offset + 1);
261             let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
262
263             format!("({}){}", list_str, output)
264         }
265         _ => String::new(),
266     };
267
268     Some(format!("{}{}", segment.identifier, params))
269 }
270
271 impl Rewrite for ast::WherePredicate {
272     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
273         // TODO dead spans?
274         // TODO assumes we'll always fit on one line...
275         Some(match *self {
276             ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { ref bound_lifetimes,
277                                                                            ref bounded_ty,
278                                                                            ref bounds,
279                                                                            .. }) => {
280                 if !bound_lifetimes.is_empty() {
281                     let lifetime_str = bound_lifetimes.iter()
282                                                       .map(|lt| {
283                                                           lt.rewrite(context, width, offset)
284                                                             .unwrap()
285                                                       })
286                                                       .collect::<Vec<_>>()
287                                                       .join(", ");
288                     let type_str = pprust::ty_to_string(bounded_ty);
289                     // 8 = "for<> : ".len()
290                     let used_width = lifetime_str.len() + type_str.len() + 8;
291                     let bounds_str = bounds.iter()
292                                            .map(|ty_bound| {
293                                                ty_bound.rewrite(context,
294                                                                 width - used_width,
295                                                                 offset + used_width)
296                                                        .unwrap()
297                                            })
298                                            .collect::<Vec<_>>()
299                                            .join(" + ");
300
301                     format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str)
302                 } else {
303                     let type_str = pprust::ty_to_string(bounded_ty);
304                     // 2 = ": ".len()
305                     let used_width = type_str.len() + 2;
306                     let bounds_str = bounds.iter()
307                                            .map(|ty_bound| {
308                                                ty_bound.rewrite(context,
309                                                                 width - used_width,
310                                                                 offset + used_width)
311                                                        .unwrap()
312                                            })
313                                            .collect::<Vec<_>>()
314                                            .join(" + ");
315
316                     format!("{}: {}", type_str, bounds_str)
317                 }
318             }
319             ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime,
320                                                                              ref bounds,
321                                                                              .. }) => {
322                 format!("{}: {}",
323                         pprust::lifetime_to_string(lifetime),
324                         bounds.iter().map(pprust::lifetime_to_string)
325                               .collect::<Vec<_>>().join(" + "))
326             }
327             ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref path, ref ty, .. }) => {
328                 let ty_str = pprust::ty_to_string(ty);
329                 // 3 = " = ".len()
330                 let used_width = 3 + ty_str.len();
331                 let path_str = try_opt!(path.rewrite(context,
332                                                      width - used_width,
333                                                      offset + used_width));
334                 format!("{} = {}", path_str, ty_str)
335             }
336         })
337     }
338 }
339
340 impl Rewrite for ast::LifetimeDef {
341     fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option<String> {
342         if self.bounds.is_empty() {
343             Some(pprust::lifetime_to_string(&self.lifetime))
344         } else {
345             Some(format!("{}: {}",
346                          pprust::lifetime_to_string(&self.lifetime),
347                          self.bounds.iter().map(pprust::lifetime_to_string)
348                                     .collect::<Vec<_>>().join(" + ")))
349         }
350     }
351 }
352
353 impl Rewrite for ast::TyParamBound {
354     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
355         match *self {
356             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
357                 tref.rewrite(context, width, offset)
358             }
359             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => {
360                 Some(format!("?{}", try_opt!(tref.rewrite(context, width - 1, offset + 1))))
361             }
362             ast::TyParamBound::RegionTyParamBound(ref l) => {
363                 Some(pprust::lifetime_to_string(l))
364             }
365         }
366     }
367 }
368
369 impl Rewrite for ast::TyParamBounds {
370     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
371         let strs: Vec<_> = self.iter()
372                                .map(|b| b.rewrite(context, width, offset).unwrap())
373                                .collect();
374         Some(strs.join(" + "))
375     }
376 }
377
378 // FIXME: this assumes everything will fit on one line
379 impl Rewrite for ast::TyParam {
380     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
381         let mut result = String::with_capacity(128);
382         result.push_str(&self.ident.to_string());
383         if !self.bounds.is_empty() {
384             result.push_str(": ");
385
386             let bounds = self.bounds
387                              .iter()
388                              .map(|ty_bound| ty_bound.rewrite(context, width, offset).unwrap())
389                              .collect::<Vec<_>>()
390                              .join(" + ");
391
392             result.push_str(&bounds);
393         }
394         if let Some(ref def) = self.default {
395             result.push_str(" = ");
396             result.push_str(&pprust::ty_to_string(&def));
397         }
398
399         Some(result)
400     }
401 }
402
403 // FIXME: this assumes everything will fit on one line
404 impl Rewrite for ast::PolyTraitRef {
405     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
406         if !self.bound_lifetimes.is_empty() {
407             let lifetime_str = self.bound_lifetimes
408                                    .iter()
409                                    .map(|lt| lt.rewrite(context, width, offset).unwrap())
410                                    .collect::<Vec<_>>()
411                                    .join(", ");
412             // 6 is "for<> ".len()
413             let extra_offset = lifetime_str.len() + 6;
414             let max_path_width = try_opt!(width.checked_sub(extra_offset));
415             let path_str = try_opt!(self.trait_ref.path.rewrite(context,
416                                                                 max_path_width,
417                                                                 offset + extra_offset));
418
419             Some(format!("for<{}> {}", lifetime_str, path_str))
420         } else {
421             self.trait_ref.path.rewrite(context, width, offset)
422         }
423     }
424 }
425
426 impl Rewrite for ast::Ty {
427     // FIXME doesn't always use width, offset
428     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
429         match self.node {
430             ast::TyPath(None, ref p) => {
431                 p.rewrite(context, width, offset)
432             }
433             ast::TyObjectSum(ref ty, ref bounds) => {
434                 Some(format!("{} + {}",
435                              try_opt!(ty.rewrite(context, width, offset)),
436                              try_opt!(bounds.rewrite(context, width, offset))))
437             }
438             _ => Some(pprust::ty_to_string(self)),
439         }
440     }
441 }