1 use rustc_ast
::ast
::{self, BindingMode, Pat, PatField, 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::config
::Version
;
8 use crate::expr
::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}
;
10 definitive_tactic
, itemize_list
, shape_for_tactic
, struct_lit_formatting
, struct_lit_shape
,
11 struct_lit_tactic
, write_list
, ListFormatting
, ListItem
, Separator
,
13 use crate::macros
::{rewrite_macro, MacroPosition}
;
15 use crate::pairs
::{rewrite_pair, PairParts}
;
16 use crate::rewrite
::{Rewrite, RewriteContext}
;
17 use crate::shape
::Shape
;
18 use crate::source_map
::SpanUtils
;
19 use crate::spanned
::Spanned
;
20 use crate::types
::{rewrite_path, PathContext}
;
21 use crate::utils
::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}
;
23 /// Returns `true` if the given pattern is "short".
24 /// A short pattern is defined by the following grammar:
28 /// - `&[single-line, ntp]`
32 /// - unary tuple constructor `([small, ntp])`
34 pub(crate) fn is_short_pattern(pat
: &ast
::Pat
, pat_str
: &str) -> bool
{
35 // We also require that the pattern is reasonably 'small' with its literal width.
36 pat_str
.len() <= 20 && !pat_str
.contains('
\n'
) && is_short_pattern_inner(pat
)
39 fn is_short_pattern_inner(pat
: &ast
::Pat
) -> bool
{
41 ast
::PatKind
::Rest
| ast
::PatKind
::Wild
| ast
::PatKind
::Lit(_
) => true,
42 ast
::PatKind
::Ident(_
, _
, ref pat
) => pat
.is_none(),
43 ast
::PatKind
::Struct(..)
44 | ast
::PatKind
::MacCall(..)
45 | ast
::PatKind
::Slice(..)
46 | ast
::PatKind
::Path(..)
47 | ast
::PatKind
::Range(..) => false,
48 ast
::PatKind
::Tuple(ref subpats
) => subpats
.len() <= 1,
49 ast
::PatKind
::TupleStruct(_
, ref path
, ref subpats
) => {
50 path
.segments
.len() <= 1 && subpats
.len() <= 1
52 ast
::PatKind
::Box(ref p
) | ast
::PatKind
::Ref(ref p
, _
) | ast
::PatKind
::Paren(ref p
) => {
53 is_short_pattern_inner(&*p
)
55 PatKind
::Or(ref pats
) => pats
.iter().all(|p
| is_short_pattern_inner(p
)),
59 struct RangeOperand
<'a
>(&'a Option
<ptr
::P
<ast
::Expr
>>);
61 impl<'a
> Rewrite
for RangeOperand
<'a
> {
62 fn rewrite(&self, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
64 None
=> Some("".to_owned()),
65 Some(ref exp
) => exp
.rewrite(context
, shape
),
70 impl Rewrite
for Pat
{
71 fn rewrite(&self, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
73 PatKind
::Or(ref pats
) => {
76 .map(|p
| p
.rewrite(context
, shape
))
77 .collect
::<Option
<Vec
<_
>>>()?
;
79 let use_mixed_layout
= pats
82 .all(|(pat
, pat_str
)| is_short_pattern(pat
, pat_str
));
83 let items
: Vec
<_
> = pat_strs
.into_iter().map(ListItem
::from_str
).collect();
84 let tactic
= if use_mixed_layout
{
85 DefinitiveListTactic
::Mixed
89 ListTactic
::HorizontalVertical
,
90 Separator
::VerticalBar
,
94 let fmt
= ListFormatting
::new(shape
, context
.config
)
97 .separator_place(context
.config
.binop_separator())
98 .ends_with_newline(false);
99 write_list(&items
, &fmt
)
101 PatKind
::Box(ref pat
) => rewrite_unary_prefix(context
, "box ", &**pat
, shape
),
102 PatKind
::Ident(binding_mode
, ident
, ref sub_pat
) => {
103 let (prefix
, mutability
) = match binding_mode
{
104 BindingMode
::ByRef(mutability
) => ("ref", mutability
),
105 BindingMode
::ByValue(mutability
) => ("", mutability
),
107 let mut_infix
= format_mutability(mutability
).trim();
108 let id_str
= rewrite_ident(context
, ident
);
109 let sub_pat
= match *sub_pat
{
114 .checked_sub(prefix
.len() + mut_infix
.len() + id_str
.len() + 2)?
;
115 let lo
= context
.snippet_provider
.span_after(self.span
, "@");
116 combine_strs_with_missing_comments(
119 &p
.rewrite(context
, Shape
::legacy(width
, shape
.indent
))?
,
120 mk_sp(lo
, p
.span
.lo()),
125 None
=> "".to_owned(),
128 // combine prefix and mut
129 let (first_lo
, first
) = if !prefix
.is_empty() && !mut_infix
.is_empty() {
130 let hi
= context
.snippet_provider
.span_before(self.span
, "mut");
131 let lo
= context
.snippet_provider
.span_after(self.span
, "ref");
133 context
.snippet_provider
.span_after(self.span
, "mut"),
134 combine_strs_with_missing_comments(
143 } else if !prefix
.is_empty() {
145 context
.snippet_provider
.span_after(self.span
, "ref"),
148 } else if !mut_infix
.is_empty() {
150 context
.snippet_provider
.span_after(self.span
, "mut"),
151 mut_infix
.to_owned(),
154 (self.span
.lo(), "".to_owned())
157 let next
= if !sub_pat
.is_empty() {
158 let hi
= context
.snippet_provider
.span_before(self.span
, "@");
159 combine_strs_with_missing_comments(
163 mk_sp(ident
.span
.hi(), hi
),
171 combine_strs_with_missing_comments(
175 mk_sp(first_lo
, ident
.span
.lo()),
181 if 1 <= shape
.width
{
188 if 1 <= shape
.width
{
189 Some("..".to_owned())
194 PatKind
::Range(ref lhs
, ref rhs
, ref end_kind
) => {
195 let infix
= match end_kind
.node
{
196 RangeEnd
::Included(RangeSyntax
::DotDotDot
) => "...",
197 RangeEnd
::Included(RangeSyntax
::DotDotEq
) => "..=",
198 RangeEnd
::Excluded
=> "..",
200 let infix
= if context
.config
.spaces_around_ranges() {
201 let lhs_spacing
= match lhs
{
205 let rhs_spacing
= match rhs
{
209 format
!("{}{}{}", lhs_spacing
, infix
, rhs_spacing
)
216 PairParts
::infix(&infix
),
219 SeparatorPlace
::Front
,
222 PatKind
::Ref(ref pat
, mutability
) => {
223 let prefix
= format
!("&{}", format_mutability(mutability
));
224 rewrite_unary_prefix(context
, &prefix
, &**pat
, shape
)
226 PatKind
::Tuple(ref items
) => rewrite_tuple_pat(items
, None
, self.span
, context
, shape
),
227 PatKind
::Path(ref q_self
, ref path
) => {
228 rewrite_path(context
, PathContext
::Expr
, q_self
.as_ref(), path
, shape
)
230 PatKind
::TupleStruct(ref q_self
, ref path
, ref pat_vec
) => {
232 rewrite_path(context
, PathContext
::Expr
, q_self
.as_ref(), path
, shape
)?
;
233 rewrite_tuple_pat(pat_vec
, Some(path_str
), self.span
, context
, shape
)
235 PatKind
::Lit(ref expr
) => expr
.rewrite(context
, shape
),
236 PatKind
::Slice(ref slice_pat
) if context
.config
.version() == Version
::One
=> {
237 let rw
: Vec
<String
> = slice_pat
240 if let Some(rw
) = p
.rewrite(context
, shape
) {
243 context
.snippet(p
.span
).to_string()
247 Some(format
!("[{}]", rw
.join(", ")))
249 PatKind
::Slice(ref slice_pat
) => overflow
::rewrite_with_square_brackets(
258 PatKind
::Struct(ref qself
, ref path
, ref fields
, ellipsis
) => {
259 rewrite_struct_pat(qself
, path
, fields
, ellipsis
, self.span
, context
, shape
)
261 PatKind
::MacCall(ref mac
) => {
262 rewrite_macro(mac
, None
, context
, shape
, MacroPosition
::Pat
)
264 PatKind
::Paren(ref pat
) => pat
265 .rewrite(context
, shape
.offset_left(1)?
.sub_width(1)?
)
266 .map(|inner_pat
| format
!("({})", inner_pat
)),
271 fn rewrite_struct_pat(
272 qself
: &Option
<ast
::QSelf
>,
274 fields
: &[ast
::PatField
],
277 context
: &RewriteContext
<'_
>,
279 ) -> Option
<String
> {
281 let path_shape
= shape
.sub_width(2)?
;
282 let path_str
= rewrite_path(context
, PathContext
::Expr
, qself
.as_ref(), path
, path_shape
)?
;
284 if fields
.is_empty() && !ellipsis
{
285 return Some(format
!("{} {{}}", path_str
));
288 let (ellipsis_str
, terminator
) = if ellipsis { (", ..", "..") }
else { ("", "}
") };
290 // 3 = ` { `, 2 = ` }`.
291 let (h_shape, v_shape) =
292 struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?;
294 let items = itemize_list(
295 context.snippet_provider,
300 if f.attrs.is_empty() {
303 f.attrs.first().unwrap().span.lo()
307 |f| f.rewrite(context, v_shape),
308 context.snippet_provider.span_after(span, "{"),
312 let item_vec = items.collect::<Vec<_>>();
314 let tactic = struct_lit_tactic(h_shape, context, &item_vec);
315 let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
316 let fmt = struct_lit_formatting(nested_shape, tactic, context, false);
318 let mut fields_str = write_list(&item_vec, &fmt)?;
319 let one_line_width = h_shape.map_or(0, |shape| shape.width);
322 if fields_str.contains('\n') || fields_str.len() > one_line_width {
323 // Add a missing trailing comma.
324 if context.config.trailing_comma() == SeparatorTactic::Never {
325 fields_str.push(',');
327 fields_str.push('\n');
328 fields_str.push_str(&nested_shape.indent.to_string(context.config));
330 if !fields_str.is_empty() {
331 // there are preceding struct fields being matched on
332 if tactic == DefinitiveListTactic::Vertical {
333 // if the tactic is Vertical, write_list already added a trailing ,
334 fields_str.push(' ');
336 fields_str.push_str(", ");
340 fields_str.push_str("..");
343 // ast::Pat doesn't have attrs so use &[]
344 let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
345 Some(format!("{} {{{}
}}", path_str, fields_str))
348 impl Rewrite for PatField {
349 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
350 let hi_pos = if let Some(last) = self.attrs.last() {
356 let attrs_str = if self.attrs.is_empty() {
359 self.attrs.rewrite(context, shape)?
362 let pat_str = self.pat.rewrite(context, shape)?;
363 if self.is_shorthand {
364 combine_strs_with_missing_comments(
368 mk_sp(hi_pos, self.pat.span.lo()),
373 let nested_shape = shape.block_indent(context.config.tab_spaces());
374 let id_str = rewrite_ident(context, self.ident);
375 let one_line_width = id_str.len() + 2 + pat_str.len();
376 let pat_and_id_str = if one_line_width <= shape.width {
377 format!("{}
: {}
", id_str, pat_str)
382 nested_shape.indent.to_string(context.config),
383 self.pat.rewrite(context, nested_shape)?
386 combine_strs_with_missing_comments(
390 mk_sp(hi_pos, self.pat.span.lo()),
399 pub(crate) enum TuplePatField<'a> {
400 Pat(&'a ptr::P<ast::Pat>),
404 impl<'a> Rewrite for TuplePatField<'a> {
405 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
407 TuplePatField::Pat(p) => p.rewrite(context, shape),
408 TuplePatField::Dotdot(_) => Some("..".to_string()),
413 impl<'a> Spanned for TuplePatField<'a> {
414 fn span(&self) -> Span {
416 TuplePatField::Pat(p) => p.span(),
417 TuplePatField::Dotdot(span) => span,
422 impl<'a> TuplePatField<'a> {
423 fn is_dotdot(&self) -> bool {
425 TuplePatField::Pat(pat) => matches!(pat.kind, ast::PatKind::Rest),
426 TuplePatField::Dotdot(_) => true,
431 pub(crate) fn can_be_overflowed_pat(
432 context: &RewriteContext<'_>,
433 pat: &TuplePatField<'_>,
437 TuplePatField::Pat(pat) => match pat.kind {
438 ast::PatKind::Path(..)
439 | ast::PatKind::Tuple(..)
440 | ast::PatKind::Struct(..)
441 | ast::PatKind::TupleStruct(..) => context.use_block_indent() && len == 1,
442 ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => {
443 can_be_overflowed_pat(context, &TuplePatField::Pat(p), len)
445 ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len),
448 TuplePatField::Dotdot(..) => false,
452 fn rewrite_tuple_pat(
453 pats: &[ptr::P<ast::Pat>],
454 path_str: Option<String>,
456 context: &RewriteContext<'_>,
458 ) -> Option<String> {
460 return Some(format!("{}
()", path_str.unwrap_or_default()));
462 let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect();
464 let wildcard_suffix_len = count_wildcard_suffix_len(context, &pat_vec, span, shape);
465 let (pat_vec, span) = if context.config.condense_wildcard_suffixes() && wildcard_suffix_len >= 2
467 let new_item_count = 1 + pat_vec.len() - wildcard_suffix_len;
468 let sp = pat_vec[new_item_count - 1].span();
469 let snippet = context.snippet(sp);
470 let lo = sp.lo() + BytePos(snippet.find_uncommented("_
").unwrap() as u32);
471 pat_vec[new_item_count - 1] = TuplePatField::Dotdot(mk_sp_lo_plus_one(lo));
473 &pat_vec[..new_item_count],
474 mk_sp(span.lo(), lo + BytePos(1)),
480 let is_last_pat_dotdot = pat_vec.last().map_or(false, |p| p.is_dotdot());
481 let add_comma = path_str.is_none() && pat_vec.len() == 1 && !is_last_pat_dotdot;
482 let path_str = path_str.unwrap_or_default();
484 overflow::rewrite_with_parens(
490 context.config.max_width(),
492 Some(SeparatorTactic::Always)
499 fn count_wildcard_suffix_len(
500 context: &RewriteContext<'_>,
501 patterns: &[TuplePatField<'_>],
505 let mut suffix_len = 0;
507 let items: Vec<_> = itemize_list(
508 context.snippet_provider,
512 |item| item.span().lo(),
513 |item| item.span().hi(),
514 |item| item.rewrite(context, shape),
515 context.snippet_provider.span_after(span, "("),
516 span.hi() - BytePos(1),
524 .take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_
"))
528 if item.has_comment() {