1use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd, RangeSyntax};
2use rustc_span::{BytePos, Span};
3
4use crate::comment::{FindUncommented, combine_strs_with_missing_comments};
5use crate::config::StyleEdition;
6use crate::config::lists::*;
7use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field};
8use crate::lists::{
9 ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic,
10 struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list,
11};
12use crate::macros::{MacroPosition, rewrite_macro};
13use crate::overflow;
14use crate::pairs::{PairParts, rewrite_pair};
15use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
16use crate::shape::Shape;
17use crate::source_map::SpanUtils;
18use crate::spanned::Spanned;
19use crate::types::{PathContext, rewrite_path};
20use crate::utils::{
21 format_mutability, format_pinnedness_and_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident,
22};
23
24pub(crate) fn is_short_pattern(
36 context: &RewriteContext<'_>,
37 pat: &ast::Pat,
38 pat_str: &str,
39) -> bool {
40 pat_str.len() <= 20 && !pat_str.contains('\n') && is_short_pattern_inner(context, pat)
42}
43
44fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool {
45 match &pat.kind {
46 ast::PatKind::Missing => unreachable!(),
47 ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Err(_) => {
48 true
49 }
50 ast::PatKind::Expr(expr) => match &expr.kind {
51 ast::ExprKind::Lit(_) => true,
52 ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind {
53 ast::ExprKind::Lit(_) => true,
54 _ => unreachable!(),
55 },
56 ast::ExprKind::ConstBlock(_) | ast::ExprKind::Path(..) => {
57 context.config.style_edition() <= StyleEdition::Edition2024
58 }
59 _ => unreachable!(),
60 },
61 ast::PatKind::Ident(_, _, ref pat) => pat.is_none(),
62 ast::PatKind::Struct(..)
63 | ast::PatKind::MacCall(..)
64 | ast::PatKind::Slice(..)
65 | ast::PatKind::Path(..)
66 | ast::PatKind::Range(..)
67 | ast::PatKind::Guard(..) => false,
68 ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1,
69 ast::PatKind::TupleStruct(_, ref path, ref subpats) => {
70 path.segments.len() <= 1 && subpats.len() <= 1
71 }
72 ast::PatKind::Box(ref p)
73 | PatKind::Deref(ref p)
74 | ast::PatKind::Ref(ref p, _, _)
75 | ast::PatKind::Paren(ref p) => is_short_pattern_inner(context, &*p),
76 PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(context, p)),
77 }
78}
79
80pub(crate) struct RangeOperand<'a, T> {
81 pub operand: &'a Option<Box<T>>,
82 pub span: Span,
83}
84
85impl<'a, T: Rewrite> Rewrite for RangeOperand<'a, T> {
86 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
87 self.rewrite_result(context, shape).ok()
88 }
89
90 fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
91 match &self.operand {
92 None => Ok("".to_owned()),
93 Some(ref exp) => exp.rewrite_result(context, shape),
94 }
95 }
96}
97
98impl Rewrite for Pat {
99 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
100 self.rewrite_result(context, shape).ok()
101 }
102
103 fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
104 match self.kind {
105 PatKind::Missing => unreachable!(),
106 PatKind::Or(ref pats) => {
107 let pat_strs = pats
108 .iter()
109 .map(|p| p.rewrite_result(context, shape))
110 .collect::<Result<Vec<_>, RewriteError>>()?;
111
112 let use_mixed_layout = pats
113 .iter()
114 .zip(pat_strs.iter())
115 .all(|(pat, pat_str)| is_short_pattern(context, pat, pat_str));
116 let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
117 let tactic = if use_mixed_layout {
118 DefinitiveListTactic::Mixed
119 } else {
120 definitive_tactic(
121 &items,
122 ListTactic::HorizontalVertical,
123 Separator::VerticalBar,
124 shape.width,
125 )
126 };
127 let fmt = ListFormatting::new(shape, context.config)
128 .tactic(tactic)
129 .separator(" |")
130 .separator_place(context.config.binop_separator())
131 .ends_with_newline(false);
132 write_list(&items, &fmt)
133 }
134 PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
135 PatKind::Ident(BindingMode(by_ref, mutability), ident, ref sub_pat) => {
136 let mut_prefix = format_mutability(mutability).trim();
137
138 let (ref_kw, pin_infix, mut_infix) = match by_ref {
139 ByRef::Yes(pinnedness, rmutbl) => {
140 let (pin_infix, mut_infix) =
141 format_pinnedness_and_mutability(pinnedness, rmutbl);
142 ("ref", pin_infix.trim(), mut_infix.trim())
143 }
144 ByRef::No => ("", "", ""),
145 };
146 let id_str = rewrite_ident(context, ident);
147 let sub_pat = match *sub_pat {
148 Some(ref p) => {
149 let width = shape
151 .width
152 .checked_sub(
153 mut_prefix.len()
154 + ref_kw.len()
155 + pin_infix.len()
156 + mut_infix.len()
157 + id_str.len()
158 + 2,
159 )
160 .max_width_error(shape.width, p.span())?;
161 let lo = context.snippet_provider.span_after(self.span, "@");
162 combine_strs_with_missing_comments(
163 context,
164 "@",
165 &p.rewrite_result(context, Shape::legacy(width, shape.indent))?,
166 mk_sp(lo, p.span.lo()),
167 shape,
168 true,
169 )?
170 }
171 None => "".to_owned(),
172 };
173
174 let (first_lo, first) = match (mut_prefix.is_empty(), ref_kw.is_empty()) {
176 (false, false) => {
177 let lo = context.snippet_provider.span_after(self.span, "mut");
178 let hi = context.snippet_provider.span_before(self.span, "ref");
179 (
180 context.snippet_provider.span_after(self.span, "ref"),
181 combine_strs_with_missing_comments(
182 context,
183 mut_prefix,
184 ref_kw,
185 mk_sp(lo, hi),
186 shape,
187 true,
188 )?,
189 )
190 }
191 (false, true) => (
192 context.snippet_provider.span_after(self.span, "mut"),
193 mut_prefix.to_owned(),
194 ),
195 (true, false) => (
196 context.snippet_provider.span_after(self.span, "ref"),
197 ref_kw.to_owned(),
198 ),
199 (true, true) => (self.span.lo(), "".to_owned()),
200 };
201
202 let (second_lo, second) = match (first.is_empty(), pin_infix.is_empty()) {
204 (false, false) => {
205 let lo = context.snippet_provider.span_after(self.span, "ref");
206 let hi = context.snippet_provider.span_before(self.span, "pin");
207 (
208 context.snippet_provider.span_after(self.span, "pin"),
209 combine_strs_with_missing_comments(
210 context,
211 &first,
212 pin_infix,
213 mk_sp(lo, hi),
214 shape,
215 true,
216 )?,
217 )
218 }
219 (false, true) => (first_lo, first),
220 (true, false) => unreachable!("pin_infix necessarily follows a ref"),
221 (true, true) => (self.span.lo(), "".to_owned()),
222 };
223
224 let (third_lo, third) = match (second.is_empty(), mut_infix.is_empty()) {
226 (false, false) => {
227 let lo = context.snippet_provider.span_after(
228 self.span,
229 if pin_infix.is_empty() { "ref" } else { "pin" },
230 );
231 let end_span = mk_sp(second_lo, self.span.hi());
232 let hi = context.snippet_provider.span_before(end_span, mut_infix);
233 (
234 context.snippet_provider.span_after(end_span, mut_infix),
235 combine_strs_with_missing_comments(
236 context,
237 &second,
238 mut_infix,
239 mk_sp(lo, hi),
240 shape,
241 true,
242 )?,
243 )
244 }
245 (false, true) => (second_lo, second),
246 (true, false) => unreachable!("mut_infix necessarily follows a pin or ref"),
247 (true, true) => (self.span.lo(), "".to_owned()),
248 };
249
250 let next = if !sub_pat.is_empty() {
251 let hi = context.snippet_provider.span_before(self.span, "@");
252 combine_strs_with_missing_comments(
253 context,
254 id_str,
255 &sub_pat,
256 mk_sp(ident.span.hi(), hi),
257 shape,
258 true,
259 )?
260 } else {
261 id_str.to_owned()
262 };
263
264 combine_strs_with_missing_comments(
265 context,
266 &third,
267 &next,
268 mk_sp(third_lo, ident.span.lo()),
269 shape,
270 true,
271 )
272 }
273 PatKind::Wild => {
274 if 1 <= shape.width {
275 Ok("_".to_owned())
276 } else {
277 Err(RewriteError::ExceedsMaxWidth {
278 configured_width: 1,
279 span: self.span,
280 })
281 }
282 }
283 PatKind::Rest => {
284 if 1 <= shape.width {
285 Ok("..".to_owned())
286 } else {
287 Err(RewriteError::ExceedsMaxWidth {
288 configured_width: 1,
289 span: self.span,
290 })
291 }
292 }
293 PatKind::Never => Err(RewriteError::Unknown),
294 PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
295 rewrite_range_pat(context, shape, lhs, rhs, end_kind, self.span)
296 }
297 PatKind::Ref(ref pat, pinnedness, mutability) => {
298 let (pin_prefix, mut_prefix) =
299 format_pinnedness_and_mutability(pinnedness, mutability);
300 let prefix = format!("&{}{}", pin_prefix, mut_prefix);
301 rewrite_unary_prefix(context, &prefix, &**pat, shape)
302 }
303 PatKind::Tuple(ref items) => rewrite_tuple_pat(items, None, self.span, context, shape),
304 PatKind::Path(ref q_self, ref path) => {
305 rewrite_path(context, PathContext::Expr, q_self, path, shape)
306 }
307 PatKind::TupleStruct(ref q_self, ref path, ref pat_vec) => {
308 let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?;
309 rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape)
310 }
311 PatKind::Expr(ref expr) => expr.rewrite_result(context, shape),
312 PatKind::Slice(ref slice_pat)
313 if context.config.style_edition() <= StyleEdition::Edition2021 =>
314 {
315 let rw: Vec<String> = slice_pat
316 .iter()
317 .map(|p| {
318 if let Ok(rw) = p.rewrite_result(context, shape) {
319 rw
320 } else {
321 context.snippet(p.span).to_string()
322 }
323 })
324 .collect();
325 Ok(format!("[{}]", rw.join(", ")))
326 }
327 PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets(
328 context,
329 "",
330 slice_pat.iter(),
331 shape,
332 self.span,
333 None,
334 None,
335 ),
336 PatKind::Struct(ref qself, ref path, ref fields, rest) => rewrite_struct_pat(
337 qself,
338 path,
339 fields,
340 matches!(rest, ast::PatFieldsRest::Rest(_)),
341 self.span,
342 context,
343 shape,
344 ),
345 PatKind::MacCall(ref mac) => rewrite_macro(mac, context, shape, MacroPosition::Pat),
346 PatKind::Paren(ref pat) => pat
347 .rewrite_result(
348 context,
349 shape
350 .offset_left(1)
351 .and_then(|s| s.sub_width(1))
352 .max_width_error(shape.width, self.span)?,
353 )
354 .map(|inner_pat| format!("({})", inner_pat)),
355 PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()),
356 PatKind::Deref(_) => Err(RewriteError::Unknown),
357 PatKind::Err(_) => Err(RewriteError::Unknown),
358 }
359 }
360}
361
362pub fn rewrite_range_pat<T: Rewrite>(
363 context: &RewriteContext<'_>,
364 shape: Shape,
365 lhs: &Option<Box<T>>,
366 rhs: &Option<Box<T>>,
367 end_kind: &rustc_span::source_map::Spanned<RangeEnd>,
368 span: Span,
369) -> RewriteResult {
370 let infix = match end_kind.node {
371 RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
372 RangeEnd::Included(RangeSyntax::DotDotEq) => "..=",
373 RangeEnd::Excluded => "..",
374 };
375 let infix = if context.config.spaces_around_ranges() {
376 let lhs_spacing = match lhs {
377 None => "",
378 Some(_) => " ",
379 };
380 let rhs_spacing = match rhs {
381 None => "",
382 Some(_) => " ",
383 };
384 format!("{lhs_spacing}{infix}{rhs_spacing}")
385 } else {
386 infix.to_owned()
387 };
388 let lspan = span.with_hi(end_kind.span.lo());
389 let rspan = span.with_lo(end_kind.span.hi());
390 rewrite_pair(
391 &RangeOperand {
392 operand: lhs,
393 span: lspan,
394 },
395 &RangeOperand {
396 operand: rhs,
397 span: rspan,
398 },
399 PairParts::infix(&infix),
400 context,
401 shape,
402 SeparatorPlace::Front,
403 )
404}
405
406fn rewrite_struct_pat(
407 qself: &Option<Box<ast::QSelf>>,
408 path: &ast::Path,
409 fields: &[ast::PatField],
410 ellipsis: bool,
411 span: Span,
412 context: &RewriteContext<'_>,
413 shape: Shape,
414) -> RewriteResult {
415 let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?;
417 let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
418
419 if fields.is_empty() && !ellipsis {
420 return Ok(format!("{path_str} {{}}"));
421 }
422
423 let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
424
425 let (h_shape, v_shape) =
427 struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)
428 .max_width_error(shape.width, span)?;
429
430 let items = itemize_list(
431 context.snippet_provider,
432 fields.iter(),
433 terminator,
434 ",",
435 |f| {
436 if f.attrs.is_empty() {
437 f.span.lo()
438 } else {
439 f.attrs.first().unwrap().span.lo()
440 }
441 },
442 |f| f.span.hi(),
443 |f| f.rewrite_result(context, v_shape),
444 context.snippet_provider.span_after(span, "{"),
445 span.hi(),
446 false,
447 );
448 let item_vec = items.collect::<Vec<_>>();
449
450 let tactic = struct_lit_tactic(h_shape, context, &item_vec);
451 let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
452 let fmt = struct_lit_formatting(nested_shape, tactic, context, false);
453
454 let mut fields_str = write_list(&item_vec, &fmt)?;
455 let one_line_width = h_shape.map_or(0, |shape| shape.width);
456
457 let has_trailing_comma = fmt.needs_trailing_separator();
458
459 if ellipsis {
460 if fields_str.contains('\n') || fields_str.len() > one_line_width {
461 if !has_trailing_comma {
463 fields_str.push(',');
464 }
465 fields_str.push('\n');
466 fields_str.push_str(&nested_shape.indent.to_string(context.config));
467 } else {
468 if !fields_str.is_empty() {
469 if has_trailing_comma {
471 fields_str.push(' ');
472 } else {
473 fields_str.push_str(", ");
474 }
475 }
476 }
477 fields_str.push_str("..");
478 }
479
480 let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
482 Ok(format!("{path_str} {{{fields_str}}}"))
483}
484
485impl Rewrite for PatField {
486 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
487 self.rewrite_result(context, shape).ok()
488 }
489
490 fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
491 let hi_pos = if let Some(last) = self.attrs.last() {
492 last.span.hi()
493 } else {
494 self.pat.span.lo()
495 };
496
497 let attrs_str = if self.attrs.is_empty() {
498 String::from("")
499 } else {
500 self.attrs.rewrite_result(context, shape)?
501 };
502
503 let pat_str = self.pat.rewrite_result(context, shape)?;
504 if self.is_shorthand {
505 combine_strs_with_missing_comments(
506 context,
507 &attrs_str,
508 &pat_str,
509 mk_sp(hi_pos, self.pat.span.lo()),
510 shape,
511 false,
512 )
513 } else {
514 let nested_shape = shape.block_indent(context.config.tab_spaces());
515 let id_str = rewrite_ident(context, self.ident);
516 let one_line_width = id_str.len() + 2 + pat_str.len();
517 let pat_and_id_str = if one_line_width <= shape.width {
518 format!("{id_str}: {pat_str}")
519 } else {
520 format!(
521 "{}:\n{}{}",
522 id_str,
523 nested_shape.indent.to_string(context.config),
524 self.pat.rewrite_result(context, nested_shape)?
525 )
526 };
527 combine_strs_with_missing_comments(
528 context,
529 &attrs_str,
530 &pat_and_id_str,
531 mk_sp(hi_pos, self.pat.span.lo()),
532 nested_shape,
533 false,
534 )
535 }
536 }
537}
538
539#[derive(Debug)]
540pub(crate) enum TuplePatField<'a> {
541 Pat(&'a ast::Pat),
542 Dotdot(Span),
543}
544
545impl<'a> Rewrite for TuplePatField<'a> {
546 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
547 self.rewrite_result(context, shape).ok()
548 }
549
550 fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
551 match *self {
552 TuplePatField::Pat(p) => p.rewrite_result(context, shape),
553 TuplePatField::Dotdot(_) => Ok("..".to_string()),
554 }
555 }
556}
557
558impl<'a> Spanned for TuplePatField<'a> {
559 fn span(&self) -> Span {
560 match *self {
561 TuplePatField::Pat(p) => p.span(),
562 TuplePatField::Dotdot(span) => span,
563 }
564 }
565}
566
567impl<'a> TuplePatField<'a> {
568 fn is_dotdot(&self) -> bool {
569 match self {
570 TuplePatField::Pat(pat) => matches!(pat.kind, ast::PatKind::Rest),
571 TuplePatField::Dotdot(_) => true,
572 }
573 }
574}
575
576pub(crate) fn can_be_overflowed_pat(
577 context: &RewriteContext<'_>,
578 pat: &TuplePatField<'_>,
579 len: usize,
580) -> bool {
581 match *pat {
582 TuplePatField::Pat(pat) => match pat.kind {
583 ast::PatKind::Path(..)
584 | ast::PatKind::Tuple(..)
585 | ast::PatKind::Struct(..)
586 | ast::PatKind::TupleStruct(..) => context.use_block_indent() && len == 1,
587 ast::PatKind::Ref(ref p, _, _) | ast::PatKind::Box(ref p) => {
588 can_be_overflowed_pat(context, &TuplePatField::Pat(p), len)
589 }
590 ast::PatKind::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
591 _ => false,
592 },
593 TuplePatField::Dotdot(..) => false,
594 }
595}
596
597fn rewrite_tuple_pat(
598 pats: &[ast::Pat],
599 path_str: Option<String>,
600 span: Span,
601 context: &RewriteContext<'_>,
602 shape: Shape,
603) -> RewriteResult {
604 if pats.is_empty() {
605 return Ok(format!("{}()", path_str.unwrap_or_default()));
606 }
607 let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect();
608
609 let wildcard_suffix_len = count_wildcard_suffix_len(context, &pat_vec, span, shape);
610 let (pat_vec, span) = if context.config.condense_wildcard_suffixes() && wildcard_suffix_len >= 2
611 {
612 let new_item_count = 1 + pat_vec.len() - wildcard_suffix_len;
613 let sp = pat_vec[new_item_count - 1].span();
614 let snippet = context.snippet(sp);
615 let lo = sp.lo() + BytePos(snippet.find_uncommented("_").unwrap() as u32);
616 pat_vec[new_item_count - 1] = TuplePatField::Dotdot(mk_sp_lo_plus_one(lo));
617 (
618 &pat_vec[..new_item_count],
619 mk_sp(span.lo(), lo + BytePos(1)),
620 )
621 } else {
622 (&pat_vec[..], span)
623 };
624
625 let is_last_pat_dotdot = pat_vec.last().map_or(false, |p| p.is_dotdot());
626 let add_comma = path_str.is_none() && pat_vec.len() == 1 && !is_last_pat_dotdot;
627 let path_str = path_str.unwrap_or_default();
628
629 overflow::rewrite_with_parens(
630 context,
631 &path_str,
632 pat_vec.iter(),
633 shape,
634 span,
635 context.config.max_width(),
636 if add_comma {
637 Some(SeparatorTactic::Always)
638 } else {
639 None
640 },
641 )
642}
643
644fn count_wildcard_suffix_len(
645 context: &RewriteContext<'_>,
646 patterns: &[TuplePatField<'_>],
647 span: Span,
648 shape: Shape,
649) -> usize {
650 let mut suffix_len = 0;
651
652 let items: Vec<_> = itemize_list(
653 context.snippet_provider,
654 patterns.iter(),
655 ")",
656 ",",
657 |item| item.span().lo(),
658 |item| item.span().hi(),
659 |item| item.rewrite_result(context, shape),
660 context.snippet_provider.span_after(span, "("),
661 span.hi() - BytePos(1),
662 false,
663 )
664 .collect();
665
666 for item in items
667 .iter()
668 .rev()
669 .take_while(|i| matches!(i.item, Ok(ref internal_string) if internal_string == "_"))
670 {
671 suffix_len += 1;
672
673 if item.has_comment() {
674 break;
675 }
676 }
677
678 suffix_len
679}