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.
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.
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};
19 use comment::FindUncommented;
21 use syntax::ast::{self, BindingMode, Pat, PatKind, FieldPat, RangeEnd};
23 use syntax::codemap::{self, BytePos, Span};
25 impl Rewrite for Pat {
26 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
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),
34 let mut_infix = format_mutability(mutability);
35 let id_str = ident.node.to_string();
36 let sub_pat = match *sub_pat {
39 let width = try_opt!(shape.width.checked_sub(prefix.len() +
44 try_opt!(p.rewrite(context, Shape::legacy(width, shape.indent))))
46 None => "".to_owned(),
49 let result = format!("{}{}{}{}", prefix, mut_infix, id_str, sub_pat);
50 wrap_str(result, context.config.max_width, shape)
59 PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
61 RangeEnd::Included => {
62 rewrite_pair(&**lhs, &**rhs, "", "...", "", context, shape)
64 RangeEnd::Excluded => {
65 rewrite_pair(&**lhs, &**rhs, "", "..", "", context, shape)
69 PatKind::Ref(ref pat, mutability) => {
70 let prefix = format!("&{}", format_mutability(mutability));
71 rewrite_unary_prefix(context, &prefix, &**pat, shape)
73 PatKind::Tuple(ref items, dotdot_pos) => {
74 rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, shape)
76 PatKind::Path(ref q_self, ref path) => {
77 rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape)
79 PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
81 try_opt!(rewrite_path(context, PathContext::Expr, None, path, shape));
82 rewrite_tuple_pat(pat_vec,
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| {
95 try_opt!(p.rewrite(context, shape))))
97 let suffix = suffix.iter().map(|p| p.rewrite(context, shape));
99 // Munge them together.
100 let pats: Option<Vec<String>> =
101 prefix.chain(slice_pat.into_iter()).chain(suffix).collect();
103 // Check that all the rewrites succeeded, and if not return None.
104 let pats = try_opt!(pats);
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(", "))
110 format!("[{}]", pats.join(", "))
112 wrap_str(result, context.config.max_width, shape)
114 PatKind::Struct(ref path, ref fields, elipses) => {
115 let path = try_opt!(rewrite_path(context, PathContext::Expr, None, path, shape));
117 let (elipses_str, terminator) = if elipses { (", ..", "..") } else { ("", "}") };
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;
128 itemize_list(context.codemap,
133 |f| f.node.rewrite(context, Shape::legacy(budget, offset)),
134 context.codemap.span_after(self.span, "{"),
136 let mut field_string = try_opt!(format_item_list(items,
137 Shape::legacy(budget, offset),
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("..");
145 if !field_string.is_empty() {
146 field_string.push_str(", ");
148 field_string.push_str("..");
152 if field_string.is_empty() {
153 Some(format!("{} {{}}", path))
155 Some(format!("{} {{ {} }}", path, field_string))
158 // FIXME(#819) format pattern macros.
159 PatKind::Mac(..) => {
160 wrap_str(context.snippet(self.span), context.config.max_width, shape)
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 {
172 wrap_str(format!("{}: {}", self.ident.to_string(), try_opt!(pat)),
173 context.config.max_width,
180 enum TuplePatField<'a> {
181 Pat(&'a ptr::P<ast::Pat>),
185 impl<'a> Rewrite for TuplePatField<'a> {
186 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
188 TuplePatField::Pat(ref p) => p.rewrite(context, shape),
189 TuplePatField::Dotdot(_) => Some("..".to_string()),
194 impl<'a> Spanned for TuplePatField<'a> {
195 fn span(&self) -> Span {
197 TuplePatField::Pat(ref p) => p.span(),
198 TuplePatField::Dotdot(span) => span,
203 fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
204 dotdot_pos: Option<usize>,
205 path_str: Option<String>,
207 context: &RewriteContext,
210 let mut pat_vec: Vec<_> = pats.into_iter().map(|x| TuplePatField::Pat(x)).collect();
212 if let Some(pos) = dotdot_pos {
213 let prev = if pos == 0 {
216 pats[pos - 1].span().hi
218 let next = if pos + 1 >= pats.len() {
221 pats[pos + 1].span().lo
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);
230 expn_id: codemap::NO_EXPANSION,
232 let dotdot = TuplePatField::Dotdot(span);
233 pat_vec.insert(pos, dotdot);
236 if pat_vec.is_empty() {
239 // add comma if `(x,)`
240 let add_comma = path_str.is_none() && pat_vec.len() == 1 && dotdot_pos.is_none();
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 }));
246 let nested_shape = nested_shape.visual_indent(path_len + 1);
247 let mut items: Vec<_> = itemize_list(context.codemap,
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))
257 // Condense wildcard string suffix into a single ..
258 let wildcard_suffix_len = count_wildcard_suffix_len(&items);
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());
264 let da_iter = items.into_iter().take(new_item_count);
265 try_opt!(format_item_list(da_iter, nested_shape, context.config))
267 try_opt!(format_item_list(items.into_iter(), nested_shape, context.config))
272 Some(if context.config.spaces_within_parens {
273 format!("{}( {} )", path_str, list)
275 format!("{}({})", path_str, list)
279 let comma = if add_comma { "," } else { "" };
280 Some(if context.config.spaces_within_parens {
281 format!("( {}{} )", list, comma)
283 format!("({}{})", list, comma)
290 fn count_wildcard_suffix_len(items: &[ListItem]) -> usize {
291 let mut suffix_len = 0;
293 for item in items.iter().rev().take_while(|i| match i.item {
294 Some(ref internal_string) if internal_string ==
300 if item.pre_comment.is_some() || item.post_comment.is_some() {