]> git.lizzy.rs Git - rust.git/blob - src/patterns.rs
Remove BlockIndentStyle::Inherit
[rust.git] / src / patterns.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 Shape;
12 use codemap::SpanUtils;
13 use rewrite::{Rewrite, RewriteContext};
14 use utils::{wrap_str, format_mutability};
15 use lists::{format_item_list, itemize_list, ListItem};
16 use expr::{rewrite_unary_prefix, rewrite_pair};
17 use types::{rewrite_path, PathContext};
18 use super::Spanned;
19 use comment::FindUncommented;
20
21 use syntax::ast::{self, BindingMode, Pat, PatKind, FieldPat, RangeEnd};
22 use syntax::ptr;
23 use syntax::codemap::{self, BytePos, Span};
24
25 impl Rewrite for Pat {
26     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
27         match self.node {
28             PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
29             PatKind::Ident(binding_mode, ident, ref sub_pat) => {
30                 let (prefix, mutability) = match binding_mode {
31                     BindingMode::ByRef(mutability) => ("ref ", mutability),
32                     BindingMode::ByValue(mutability) => ("", mutability),
33                 };
34                 let mut_infix = format_mutability(mutability);
35                 let id_str = ident.node.to_string();
36                 let sub_pat = match *sub_pat {
37                     Some(ref p) => {
38                         // 3 - ` @ `.
39                         let width = try_opt!(shape.width.checked_sub(prefix.len() +
40                                                                      mut_infix.len() +
41                                                                      id_str.len() +
42                                                                      3));
43                         format!(" @ {}",
44                                 try_opt!(p.rewrite(context, Shape::legacy(width, shape.indent))))
45                     }
46                     None => "".to_owned(),
47                 };
48
49                 let result = format!("{}{}{}{}", prefix, mut_infix, id_str, sub_pat);
50                 wrap_str(result, context.config.max_width, shape)
51             }
52             PatKind::Wild => {
53                 if 1 <= shape.width {
54                     Some("_".to_owned())
55                 } else {
56                     None
57                 }
58             }
59             PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
60                 match *end_kind {
61                     RangeEnd::Included => {
62                         rewrite_pair(&**lhs, &**rhs, "", "...", "", context, shape)
63                     }
64                     RangeEnd::Excluded => {
65                         rewrite_pair(&**lhs, &**rhs, "", "..", "", context, shape)
66                     }
67                 }
68             }
69             PatKind::Ref(ref pat, mutability) => {
70                 let prefix = format!("&{}", format_mutability(mutability));
71                 rewrite_unary_prefix(context, &prefix, &**pat, shape)
72             }
73             PatKind::Tuple(ref items, dotdot_pos) => {
74                 rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, shape)
75             }
76             PatKind::Path(ref q_self, ref path) => {
77                 rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape)
78             }
79             PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
80                 let path_str =
81                     try_opt!(rewrite_path(context, PathContext::Expr, None, path, shape));
82                 rewrite_tuple_pat(pat_vec,
83                                   dotdot_pos,
84                                   Some(path_str),
85                                   self.span,
86                                   context,
87                                   shape)
88             }
89             PatKind::Lit(ref expr) => expr.rewrite(context, shape),
90             PatKind::Slice(ref prefix, ref slice_pat, ref suffix) => {
91                 // Rewrite all the sub-patterns.
92                 let prefix = prefix.iter().map(|p| p.rewrite(context, shape));
93                 let slice_pat = slice_pat.as_ref().map(|p| {
94                                                            Some(format!("{}..",
95                                                             try_opt!(p.rewrite(context, shape))))
96                                                        });
97                 let suffix = suffix.iter().map(|p| p.rewrite(context, shape));
98
99                 // Munge them together.
100                 let pats: Option<Vec<String>> =
101                     prefix.chain(slice_pat.into_iter()).chain(suffix).collect();
102
103                 // Check that all the rewrites succeeded, and if not return None.
104                 let pats = try_opt!(pats);
105
106                 // Unwrap all the sub-strings and join them with commas.
107                 let result = if context.config.spaces_within_square_brackets {
108                     format!("[ {} ]", pats.join(", "))
109                 } else {
110                     format!("[{}]", pats.join(", "))
111                 };
112                 wrap_str(result, context.config.max_width, shape)
113             }
114             PatKind::Struct(ref path, ref fields, elipses) => {
115                 let path = try_opt!(rewrite_path(context, PathContext::Expr, None, path, shape));
116
117                 let (elipses_str, terminator) = if elipses { (", ..", "..") } else { ("", "}") };
118
119                 // 5 = `{` plus space before and after plus `}` plus space before.
120                 let budget = try_opt!(shape.width.checked_sub(path.len() + 5 + elipses_str.len()));
121                 // FIXME Using visual indenting, should use block or visual to match
122                 // struct lit preference (however, in practice I think it is rare
123                 // for struct patterns to be multi-line).
124                 // 3 = `{` plus space before and after.
125                 let offset = shape.indent + path.len() + 3;
126
127                 let items =
128                     itemize_list(context.codemap,
129                                  fields.iter(),
130                                  terminator,
131                                  |f| f.span.lo,
132                                  |f| f.span.hi,
133                                  |f| f.node.rewrite(context, Shape::legacy(budget, offset)),
134                                  context.codemap.span_after(self.span, "{"),
135                                  self.span.hi);
136                 let mut field_string = try_opt!(format_item_list(items,
137                                                                  Shape::legacy(budget, offset),
138                                                                  context.config));
139                 if elipses {
140                     if field_string.contains('\n') {
141                         field_string.push_str(",\n");
142                         field_string.push_str(&offset.to_string(context.config));
143                         field_string.push_str("..");
144                     } else {
145                         if !field_string.is_empty() {
146                             field_string.push_str(", ");
147                         }
148                         field_string.push_str("..");
149                     }
150                 }
151
152                 if field_string.is_empty() {
153                     Some(format!("{} {{}}", path))
154                 } else {
155                     Some(format!("{} {{ {} }}", path, field_string))
156                 }
157             }
158             // FIXME(#819) format pattern macros.
159             PatKind::Mac(..) => {
160                 wrap_str(context.snippet(self.span), context.config.max_width, shape)
161             }
162         }
163     }
164 }
165
166 impl Rewrite for FieldPat {
167     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
168         let pat = self.pat.rewrite(context, shape);
169         if self.is_shorthand {
170             pat
171         } else {
172             wrap_str(format!("{}: {}", self.ident.to_string(), try_opt!(pat)),
173                      context.config.max_width,
174                      shape)
175         }
176     }
177 }
178
179
180 enum TuplePatField<'a> {
181     Pat(&'a ptr::P<ast::Pat>),
182     Dotdot(Span),
183 }
184
185 impl<'a> Rewrite for TuplePatField<'a> {
186     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
187         match *self {
188             TuplePatField::Pat(ref p) => p.rewrite(context, shape),
189             TuplePatField::Dotdot(_) => Some("..".to_string()),
190         }
191     }
192 }
193
194 impl<'a> Spanned for TuplePatField<'a> {
195     fn span(&self) -> Span {
196         match *self {
197             TuplePatField::Pat(ref p) => p.span(),
198             TuplePatField::Dotdot(span) => span,
199         }
200     }
201 }
202
203 fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
204                      dotdot_pos: Option<usize>,
205                      path_str: Option<String>,
206                      span: Span,
207                      context: &RewriteContext,
208                      shape: Shape)
209                      -> Option<String> {
210     let mut pat_vec: Vec<_> = pats.into_iter().map(|x| TuplePatField::Pat(x)).collect();
211
212     if let Some(pos) = dotdot_pos {
213         let prev = if pos == 0 {
214             span.lo
215         } else {
216             pats[pos - 1].span().hi
217         };
218         let next = if pos + 1 >= pats.len() {
219             span.hi
220         } else {
221             pats[pos + 1].span().lo
222         };
223         let dot_span = codemap::mk_sp(prev, next);
224         let snippet = context.snippet(dot_span);
225         let lo = dot_span.lo + BytePos(snippet.find_uncommented("..").unwrap() as u32);
226         let span = Span {
227             lo: lo,
228             // 2 == "..".len()
229             hi: lo + BytePos(2),
230             expn_id: codemap::NO_EXPANSION,
231         };
232         let dotdot = TuplePatField::Dotdot(span);
233         pat_vec.insert(pos, dotdot);
234     }
235
236     if pat_vec.is_empty() {
237         path_str
238     } else {
239         // add comma if `(x,)`
240         let add_comma = path_str.is_none() && pat_vec.len() == 1 && dotdot_pos.is_none();
241
242         let path_len = path_str.as_ref().map(|p| p.len()).unwrap_or(0);
243         // 2 = "()".len(), 3 = "(,)".len()
244         let nested_shape = try_opt!(shape.sub_width(path_len + if add_comma { 3 } else { 2 }));
245         // 1 = "(".len()
246         let nested_shape = nested_shape.visual_indent(path_len + 1);
247         let mut items: Vec<_> = itemize_list(context.codemap,
248                                              pat_vec.iter(),
249                                              if add_comma { ",)" } else { ")" },
250                                              |item| item.span().lo,
251                                              |item| item.span().hi,
252                                              |item| item.rewrite(context, nested_shape),
253                                              context.codemap.span_after(span, "("),
254                                              span.hi - BytePos(1))
255                 .collect();
256
257         // Condense wildcard string suffix into a single ..
258         let wildcard_suffix_len = count_wildcard_suffix_len(&items);
259
260         let list = if context.config.condense_wildcard_suffices && wildcard_suffix_len >= 2 {
261             let new_item_count = 1 + pats.len() - wildcard_suffix_len;
262             items[new_item_count - 1].item = Some("..".to_owned());
263
264             let da_iter = items.into_iter().take(new_item_count);
265             try_opt!(format_item_list(da_iter, nested_shape, context.config))
266         } else {
267             try_opt!(format_item_list(items.into_iter(), nested_shape, context.config))
268         };
269
270         match path_str {
271             Some(path_str) => {
272                 Some(if context.config.spaces_within_parens {
273                          format!("{}( {} )", path_str, list)
274                      } else {
275                          format!("{}({})", path_str, list)
276                      })
277             }
278             None => {
279                 let comma = if add_comma { "," } else { "" };
280                 Some(if context.config.spaces_within_parens {
281                          format!("( {}{} )", list, comma)
282                      } else {
283                          format!("({}{})", list, comma)
284                      })
285             }
286         }
287     }
288 }
289
290 fn count_wildcard_suffix_len(items: &[ListItem]) -> usize {
291     let mut suffix_len = 0;
292
293     for item in items.iter().rev().take_while(|i| match i.item {
294                                                   Some(ref internal_string) if internal_string ==
295                                                                                "_" => true,
296                                                   _ => false,
297                                               }) {
298         suffix_len += 1;
299
300         if item.pre_comment.is_some() || item.post_comment.is_some() {
301             break;
302         }
303     }
304
305     suffix_len
306 }