1 use syntax::ast::{self, BindingMode, FieldPat, Pat, PatKind, RangeEnd, RangeSyntax};
3 use syntax::source_map::{self, BytePos, Span};
5 use crate::comment::FindUncommented;
6 use crate::config::lists::*;
7 use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field};
9 itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, struct_lit_tactic,
12 use crate::macros::{rewrite_macro, MacroPosition};
14 use crate::pairs::{rewrite_pair, PairParts};
15 use crate::rewrite::{Rewrite, RewriteContext};
16 use crate::shape::Shape;
17 use crate::source_map::SpanUtils;
18 use crate::spanned::Spanned;
19 use crate::types::{rewrite_path, PathContext};
20 use crate::utils::{format_mutability, mk_sp, rewrite_ident};
22 /// Returns `true` if the given pattern is "short".
23 /// A short pattern is defined by the following grammar:
27 /// - `&[single-line, ntp]`
31 /// - unary tuple constructor `([small, ntp])`
33 pub fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool {
34 // We also require that the pattern is reasonably 'small' with its literal width.
35 pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(pat)
38 fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
40 ast::PatKind::Wild | ast::PatKind::Lit(_) => true,
41 ast::PatKind::Ident(_, _, ref pat) => pat.is_none(),
42 ast::PatKind::Struct(..)
43 | ast::PatKind::Mac(..)
44 | ast::PatKind::Slice(..)
45 | ast::PatKind::Path(..)
46 | ast::PatKind::Range(..) => false,
47 ast::PatKind::Tuple(ref subpats, _) => subpats.len() <= 1,
48 ast::PatKind::TupleStruct(ref path, ref subpats, _) => {
49 path.segments.len() <= 1 && subpats.len() <= 1
51 ast::PatKind::Box(ref p) | ast::PatKind::Ref(ref p, _) | ast::PatKind::Paren(ref p) => {
52 is_short_pattern_inner(&*p)
57 impl Rewrite for Pat {
58 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
60 PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
61 PatKind::Ident(binding_mode, ident, ref sub_pat) => {
62 let (prefix, mutability) = match binding_mode {
63 BindingMode::ByRef(mutability) => ("ref ", mutability),
64 BindingMode::ByValue(mutability) => ("", mutability),
66 let mut_infix = format_mutability(mutability);
67 let id_str = rewrite_ident(context, ident);
68 let sub_pat = match *sub_pat {
73 .checked_sub(prefix.len() + mut_infix.len() + id_str.len() + 3)?;
76 p.rewrite(context, Shape::legacy(width, shape.indent))?
79 None => "".to_owned(),
82 Some(format!("{}{}{}{}", prefix, mut_infix, id_str, sub_pat))
91 PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
92 let infix = match end_kind.node {
93 RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
94 RangeEnd::Included(RangeSyntax::DotDotEq) => "..=",
95 RangeEnd::Excluded => "..",
97 let infix = if context.config.spaces_around_ranges() {
98 format!(" {} ", infix)
105 PairParts::infix(&infix),
108 SeparatorPlace::Front,
111 PatKind::Ref(ref pat, mutability) => {
112 let prefix = format!("&{}", format_mutability(mutability));
113 rewrite_unary_prefix(context, &prefix, &**pat, shape)
115 PatKind::Tuple(ref items, dotdot_pos) => {
116 rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, shape)
118 PatKind::Path(ref q_self, ref path) => {
119 rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape)
121 PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
122 let path_str = rewrite_path(context, PathContext::Expr, None, path, shape)?;
132 PatKind::Lit(ref expr) => expr.rewrite(context, shape),
133 PatKind::Slice(ref prefix, ref slice_pat, ref suffix) => {
134 // Rewrite all the sub-patterns.
135 let prefix = prefix.iter().map(|p| p.rewrite(context, shape));
136 let slice_pat = slice_pat
138 .and_then(|p| p.rewrite(context, shape))
139 .map(|rw| Some(format!("{}..", if rw == "_" { "" } else { &rw })));
140 let suffix = suffix.iter().map(|p| p.rewrite(context, shape));
142 // Munge them together.
143 let pats: Option<Vec<String>> =
144 prefix.chain(slice_pat.into_iter()).chain(suffix).collect();
146 // Check that all the rewrites succeeded, and if not return `None`.
149 // Unwrap all the sub-strings and join them with commas.
150 Some(format!("[{}]", pats.join(", ")))
152 PatKind::Struct(ref path, ref fields, ellipsis) => {
153 rewrite_struct_pat(path, fields, ellipsis, self.span, context, shape)
155 PatKind::Mac(ref mac) => rewrite_macro(mac, None, context, shape, MacroPosition::Pat),
156 PatKind::Paren(ref pat) => pat
157 .rewrite(context, shape.offset_left(1)?.sub_width(1)?)
158 .map(|inner_pat| format!("({})", inner_pat)),
163 fn rewrite_struct_pat(
165 fields: &[source_map::Spanned<ast::FieldPat>],
168 context: &RewriteContext<'_>,
170 ) -> Option<String> {
172 let path_shape = shape.sub_width(2)?;
173 let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?;
175 if fields.is_empty() && !ellipsis {
176 return Some(format!("{} {{}}", path_str));
179 let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
181 // 3 = ` { `, 2 = ` }`.
182 let (h_shape, v_shape) =
183 struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?;
185 let items = itemize_list(
186 context.snippet_provider,
192 |f| f.node.rewrite(context, v_shape),
193 context.snippet_provider.span_after(span, "{"),
197 let item_vec = items.collect::<Vec<_>>();
199 let tactic = struct_lit_tactic(h_shape, context, &item_vec);
200 let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
201 let fmt = struct_lit_formatting(nested_shape, tactic, context, false);
203 let mut fields_str = write_list(&item_vec, &fmt)?;
204 let one_line_width = h_shape.map_or(0, |shape| shape.width);
207 if fields_str.contains('\n') || fields_str.len() > one_line_width {
208 // Add a missing trailing comma.
209 if context.config.trailing_comma() == SeparatorTactic::Never {
210 fields_str.push_str(",");
212 fields_str.push_str("\n");
213 fields_str.push_str(&nested_shape.indent.to_string(context.config));
214 fields_str.push_str("..");
216 if !fields_str.is_empty() {
217 // there are preceding struct fields being matched on
218 if tactic == DefinitiveListTactic::Vertical {
219 // if the tactic is Vertical, write_list already added a trailing ,
220 fields_str.push_str(" ");
222 fields_str.push_str(", ");
225 fields_str.push_str("..");
229 let fields_str = wrap_struct_field(context, &fields_str, shape, v_shape, one_line_width);
230 Some(format!("{} {{{}}}", path_str, fields_str))
233 impl Rewrite for FieldPat {
234 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
235 let pat = self.pat.rewrite(context, shape);
236 if self.is_shorthand {
240 let id_str = rewrite_ident(context, self.ident);
241 let one_line_width = id_str.len() + 2 + pat_str.len();
242 if one_line_width <= shape.width {
243 Some(format!("{}: {}", id_str, pat_str))
245 let nested_shape = shape.block_indent(context.config.tab_spaces());
246 let pat_str = self.pat.rewrite(context, nested_shape)?;
250 nested_shape.indent.to_string(context.config),
259 pub enum TuplePatField<'a> {
260 Pat(&'a ptr::P<ast::Pat>),
264 impl<'a> Rewrite for TuplePatField<'a> {
265 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
267 TuplePatField::Pat(p) => p.rewrite(context, shape),
268 TuplePatField::Dotdot(_) => Some("..".to_string()),
273 impl<'a> Spanned for TuplePatField<'a> {
274 fn span(&self) -> Span {
276 TuplePatField::Pat(p) => p.span(),
277 TuplePatField::Dotdot(span) => span,
282 pub fn can_be_overflowed_pat(
283 context: &RewriteContext<'_>,
284 pat: &TuplePatField<'_>,
288 TuplePatField::Pat(pat) => match pat.node {
289 ast::PatKind::Path(..)
290 | ast::PatKind::Tuple(..)
291 | ast::PatKind::Struct(..)
292 | ast::PatKind::TupleStruct(..) => context.use_block_indent() && len == 1,
293 ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => {
294 can_be_overflowed_pat(context, &TuplePatField::Pat(p), len)
296 ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len),
299 TuplePatField::Dotdot(..) => false,
303 fn rewrite_tuple_pat(
304 pats: &[ptr::P<ast::Pat>],
305 dotdot_pos: Option<usize>,
306 path_str: Option<String>,
308 context: &RewriteContext<'_>,
310 ) -> Option<String> {
311 let mut pat_vec: Vec<_> = pats.iter().map(|x| TuplePatField::Pat(x)).collect();
313 if let Some(pos) = dotdot_pos {
314 let prev = if pos == 0 {
317 pats[pos - 1].span().hi()
319 let next = if pos + 1 >= pats.len() {
322 pats[pos + 1].span().lo()
324 let dot_span = mk_sp(prev, next);
325 let snippet = context.snippet(dot_span);
326 let lo = dot_span.lo() + BytePos(snippet.find_uncommented("..").unwrap() as u32);
327 let dotdot = TuplePatField::Dotdot(Span::new(
331 source_map::NO_EXPANSION,
333 pat_vec.insert(pos, dotdot);
335 if pat_vec.is_empty() {
336 return Some(format!("{}()", path_str.unwrap_or_default()));
338 let wildcard_suffix_len = count_wildcard_suffix_len(context, &pat_vec, span, shape);
339 let (pat_vec, span, condensed) =
340 if context.config.condense_wildcard_suffixes() && wildcard_suffix_len >= 2 {
341 let new_item_count = 1 + pat_vec.len() - wildcard_suffix_len;
342 let sp = pat_vec[new_item_count - 1].span();
343 let snippet = context.snippet(sp);
344 let lo = sp.lo() + BytePos(snippet.find_uncommented("_").unwrap() as u32);
345 pat_vec[new_item_count - 1] = TuplePatField::Dotdot(mk_sp(lo, lo + BytePos(1)));
347 &pat_vec[..new_item_count],
348 mk_sp(span.lo(), lo + BytePos(1)),
352 (&pat_vec[..], span, false)
355 // add comma if `(x,)`
356 let add_comma = path_str.is_none() && pat_vec.len() == 1 && dotdot_pos.is_none() && !condensed;
357 let path_str = path_str.unwrap_or_default();
359 overflow::rewrite_with_parens(
365 context.config.max_width(),
366 if dotdot_pos.is_some() {
367 Some(SeparatorTactic::Never)
368 } else if add_comma {
369 Some(SeparatorTactic::Always)
376 fn count_wildcard_suffix_len(
377 context: &RewriteContext<'_>,
378 patterns: &[TuplePatField<'_>],
382 let mut suffix_len = 0;
384 let items: Vec<_> = itemize_list(
385 context.snippet_provider,
389 |item| item.span().lo(),
390 |item| item.span().hi(),
391 |item| item.rewrite(context, shape),
392 context.snippet_provider.span_after(span, "("),
393 span.hi() - BytePos(1),
398 for item in items.iter().rev().take_while(|i| match i.item {
399 Some(ref internal_string) if internal_string == "_" => true,
404 if item.has_comment() {