1 use rustc_ast::ast::{self, BindingMode, FieldPat, Pat, PatKind, RangeEnd, RangeSyntax};
3 use rustc_span::{BytePos, Span};
5 use crate::comment::{combine_strs_with_missing_comments, FindUncommented};
6 use crate::config::lists::*;
7 use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field};
9 definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
10 struct_lit_tactic, write_list, ListFormatting, ListItem, Separator,
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(crate) 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::Rest | ast::PatKind::Wild | ast::PatKind::Lit(_) => true,
41 ast::PatKind::Ident(_, _, ref pat) => pat.is_none(),
42 ast::PatKind::Struct(..)
43 | ast::PatKind::MacCall(..)
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)
54 PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(p)),
58 struct RangeOperand<'a>(&'a Option<ptr::P<ast::Expr>>);
60 impl<'a> Rewrite for RangeOperand<'a> {
61 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
63 None => Some("".to_owned()),
64 Some(ref exp) => exp.rewrite(context, shape),
69 impl Rewrite for Pat {
70 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
72 PatKind::Or(ref pats) => {
75 .map(|p| p.rewrite(context, shape))
76 .collect::<Option<Vec<_>>>()?;
78 let use_mixed_layout = pats
81 .all(|(pat, pat_str)| is_short_pattern(pat, pat_str));
82 let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
83 let tactic = if use_mixed_layout {
84 DefinitiveListTactic::Mixed
88 ListTactic::HorizontalVertical,
89 Separator::VerticalBar,
93 let fmt = ListFormatting::new(shape, context.config)
96 .separator_place(context.config.binop_separator())
97 .ends_with_newline(false);
98 write_list(&items, &fmt)
100 PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
101 PatKind::Ident(binding_mode, ident, ref sub_pat) => {
102 let (prefix, mutability) = match binding_mode {
103 BindingMode::ByRef(mutability) => ("ref", mutability),
104 BindingMode::ByValue(mutability) => ("", mutability),
106 let mut_infix = format_mutability(mutability).trim();
107 let id_str = rewrite_ident(context, ident);
108 let sub_pat = match *sub_pat {
113 .checked_sub(prefix.len() + mut_infix.len() + id_str.len() + 2)?;
114 let lo = context.snippet_provider.span_after(self.span, "@");
115 combine_strs_with_missing_comments(
118 &p.rewrite(context, Shape::legacy(width, shape.indent))?,
119 mk_sp(lo, p.span.lo()),
124 None => "".to_owned(),
127 // combine prefix and mut
128 let (first_lo, first) = if !prefix.is_empty() && !mut_infix.is_empty() {
129 let hi = context.snippet_provider.span_before(self.span, "mut");
130 let lo = context.snippet_provider.span_after(self.span, "ref");
132 context.snippet_provider.span_after(self.span, "mut"),
133 combine_strs_with_missing_comments(
142 } else if !prefix.is_empty() {
144 context.snippet_provider.span_after(self.span, "ref"),
147 } else if !mut_infix.is_empty() {
149 context.snippet_provider.span_after(self.span, "mut"),
150 mut_infix.to_owned(),
153 (self.span.lo(), "".to_owned())
156 let next = if !sub_pat.is_empty() {
157 let hi = context.snippet_provider.span_before(self.span, "@");
158 combine_strs_with_missing_comments(
162 mk_sp(ident.span.hi(), hi),
170 combine_strs_with_missing_comments(
174 mk_sp(first_lo, ident.span.lo()),
180 if 1 <= shape.width {
187 if 1 <= shape.width {
188 Some("..".to_owned())
193 PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
194 let infix = match end_kind.node {
195 RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
196 RangeEnd::Included(RangeSyntax::DotDotEq) => "..=",
197 RangeEnd::Excluded => "..",
199 let infix = if context.config.spaces_around_ranges() {
200 let lhs_spacing = match lhs {
204 let rhs_spacing = match rhs {
208 format!("{}{}{}", lhs_spacing, infix, rhs_spacing)
215 PairParts::infix(&infix),
218 SeparatorPlace::Front,
221 PatKind::Ref(ref pat, mutability) => {
222 let prefix = format!("&{}", format_mutability(mutability));
223 rewrite_unary_prefix(context, &prefix, &**pat, shape)
225 PatKind::Tuple(ref items) => rewrite_tuple_pat(items, None, self.span, context, shape),
226 PatKind::Path(ref q_self, ref path) => {
227 rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape)
229 PatKind::TupleStruct(ref path, ref pat_vec) => {
230 let path_str = rewrite_path(context, PathContext::Expr, None, path, shape)?;
231 rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape)
233 PatKind::Lit(ref expr) => expr.rewrite(context, shape),
234 PatKind::Slice(ref slice_pat) => {
235 let rw: Vec<String> = slice_pat
238 if let Some(rw) = p.rewrite(context, shape) {
241 format!("{}", context.snippet(p.span))
245 Some(format!("[{}]", rw.join(", ")))
247 PatKind::Struct(ref path, ref fields, ellipsis) => {
248 rewrite_struct_pat(path, fields, ellipsis, self.span, context, shape)
250 PatKind::MacCall(ref mac) => {
251 rewrite_macro(mac, None, context, shape, MacroPosition::Pat)
253 PatKind::Paren(ref pat) => pat
254 .rewrite(context, shape.offset_left(1)?.sub_width(1)?)
255 .map(|inner_pat| format!("({})", inner_pat)),
260 fn rewrite_struct_pat(
262 fields: &[ast::FieldPat],
265 context: &RewriteContext<'_>,
267 ) -> Option<String> {
269 let path_shape = shape.sub_width(2)?;
270 let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?;
272 if fields.is_empty() && !ellipsis {
273 return Some(format!("{} {{}}", path_str));
276 let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
278 // 3 = ` { `, 2 = ` }`.
279 let (h_shape, v_shape) =
280 struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?;
282 let items = itemize_list(
283 context.snippet_provider,
288 if f.attrs.is_empty() {
291 f.attrs.first().unwrap().span.lo()
295 |f| f.rewrite(context, v_shape),
296 context.snippet_provider.span_after(span, "{"),
300 let item_vec = items.collect::<Vec<_>>();
302 let tactic = struct_lit_tactic(h_shape, context, &item_vec);
303 let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
304 let fmt = struct_lit_formatting(nested_shape, tactic, context, false);
306 let mut fields_str = write_list(&item_vec, &fmt)?;
307 let one_line_width = h_shape.map_or(0, |shape| shape.width);
310 if fields_str.contains('\n') || fields_str.len() > one_line_width {
311 // Add a missing trailing comma.
312 if context.config.trailing_comma() == SeparatorTactic::Never {
313 fields_str.push_str(",");
315 fields_str.push_str("\n");
316 fields_str.push_str(&nested_shape.indent.to_string(context.config));
317 fields_str.push_str("..");
319 if !fields_str.is_empty() {
320 // there are preceding struct fields being matched on
321 if tactic == DefinitiveListTactic::Vertical {
322 // if the tactic is Vertical, write_list already added a trailing ,
323 fields_str.push_str(" ");
325 fields_str.push_str(", ");
328 fields_str.push_str("..");
332 // ast::Pat doesn't have attrs so use &[]
333 let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
334 Some(format!("{} {{{}}}", path_str, fields_str))
337 impl Rewrite for FieldPat {
338 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
339 let hi_pos = if let Some(last) = self.attrs.last() {
345 let attrs_str = if self.attrs.is_empty() {
348 self.attrs.rewrite(context, shape)?
351 let pat_str = self.pat.rewrite(context, shape)?;
352 if self.is_shorthand {
353 combine_strs_with_missing_comments(
357 mk_sp(hi_pos, self.pat.span.lo()),
362 let nested_shape = shape.block_indent(context.config.tab_spaces());
363 let id_str = rewrite_ident(context, self.ident);
364 let one_line_width = id_str.len() + 2 + pat_str.len();
365 let pat_and_id_str = if one_line_width <= shape.width {
366 format!("{}: {}", id_str, pat_str)
371 nested_shape.indent.to_string(context.config),
372 self.pat.rewrite(context, nested_shape)?
375 combine_strs_with_missing_comments(
379 mk_sp(hi_pos, self.pat.span.lo()),
388 pub(crate) enum TuplePatField<'a> {
389 Pat(&'a ptr::P<ast::Pat>),
393 impl<'a> Rewrite for TuplePatField<'a> {
394 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
396 TuplePatField::Pat(p) => p.rewrite(context, shape),
397 TuplePatField::Dotdot(_) => Some("..".to_string()),
402 impl<'a> Spanned for TuplePatField<'a> {
403 fn span(&self) -> Span {
405 TuplePatField::Pat(p) => p.span(),
406 TuplePatField::Dotdot(span) => span,
411 impl<'a> TuplePatField<'a> {
412 fn is_dotdot(&self) -> bool {
414 TuplePatField::Pat(pat) => match pat.kind {
415 ast::PatKind::Rest => true,
418 TuplePatField::Dotdot(_) => true,
423 pub(crate) fn can_be_overflowed_pat(
424 context: &RewriteContext<'_>,
425 pat: &TuplePatField<'_>,
429 TuplePatField::Pat(pat) => match pat.kind {
430 ast::PatKind::Path(..)
431 | ast::PatKind::Tuple(..)
432 | ast::PatKind::Struct(..)
433 | ast::PatKind::TupleStruct(..) => context.use_block_indent() && len == 1,
434 ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => {
435 can_be_overflowed_pat(context, &TuplePatField::Pat(p), len)
437 ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len),
440 TuplePatField::Dotdot(..) => false,
444 fn rewrite_tuple_pat(
445 pats: &[ptr::P<ast::Pat>],
446 path_str: Option<String>,
448 context: &RewriteContext<'_>,
450 ) -> Option<String> {
451 let mut pat_vec: Vec<_> = pats.iter().map(|x| TuplePatField::Pat(x)).collect();
453 if pat_vec.is_empty() {
454 return Some(format!("{}()", path_str.unwrap_or_default()));
456 let wildcard_suffix_len = count_wildcard_suffix_len(context, &pat_vec, span, shape);
457 let (pat_vec, span) = if context.config.condense_wildcard_suffixes() && wildcard_suffix_len >= 2
459 let new_item_count = 1 + pat_vec.len() - wildcard_suffix_len;
460 let sp = pat_vec[new_item_count - 1].span();
461 let snippet = context.snippet(sp);
462 let lo = sp.lo() + BytePos(snippet.find_uncommented("_").unwrap() as u32);
463 pat_vec[new_item_count - 1] = TuplePatField::Dotdot(mk_sp(lo, lo + BytePos(1)));
465 &pat_vec[..new_item_count],
466 mk_sp(span.lo(), lo + BytePos(1)),
472 let is_last_pat_dotdot = pat_vec.last().map_or(false, |p| p.is_dotdot());
473 let add_comma = path_str.is_none() && pat_vec.len() == 1 && !is_last_pat_dotdot;
474 let path_str = path_str.unwrap_or_default();
476 overflow::rewrite_with_parens(
482 context.config.max_width(),
484 Some(SeparatorTactic::Always)
491 fn count_wildcard_suffix_len(
492 context: &RewriteContext<'_>,
493 patterns: &[TuplePatField<'_>],
497 let mut suffix_len = 0;
499 let items: Vec<_> = itemize_list(
500 context.snippet_provider,
504 |item| item.span().lo(),
505 |item| item.span().hi(),
506 |item| item.rewrite(context, shape),
507 context.snippet_provider.span_after(span, "("),
508 span.hi() - BytePos(1),
513 for item in items.iter().rev().take_while(|i| match i.item {
514 Some(ref internal_string) if internal_string == "_" => true,
519 if item.has_comment() {