4 use itertools
::Itertools
;
5 use rustc_ast
::token
::{Delimiter, LitKind}
;
6 use rustc_ast
::{ast, ptr}
;
7 use rustc_span
::{BytePos, Span}
;
9 use crate::chains
::rewrite_chain
;
12 combine_strs_with_missing_comments
, contains_comment
, recover_comment_removed
, rewrite_comment
,
13 rewrite_missing_comment
, CharClasses
, FindUncommented
,
15 use crate::config
::lists
::*;
16 use crate::config
::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version}
;
18 definitive_tactic
, itemize_list
, shape_for_tactic
, struct_lit_formatting
, struct_lit_shape
,
19 struct_lit_tactic
, write_list
, ListFormatting
, Separator
,
21 use crate::macros
::{rewrite_macro, MacroPosition}
;
22 use crate::matches
::rewrite_match
;
23 use crate::overflow
::{self, IntoOverflowableItem, OverflowableItem}
;
24 use crate::pairs
::{rewrite_all_pairs, rewrite_pair, PairParts}
;
25 use crate::rewrite
::{Rewrite, RewriteContext}
;
26 use crate::shape
::{Indent, Shape}
;
27 use crate::source_map
::{LineRangeUtils, SpanUtils}
;
28 use crate::spanned
::Spanned
;
29 use crate::string
::{rewrite_string, StringFormat}
;
30 use crate::types
::{rewrite_path, PathContext}
;
32 colon_spaces
, contains_skip
, count_newlines
, first_line_ends_with
, inner_attributes
,
33 last_line_extendable
, last_line_width
, mk_sp
, outer_attributes
, semicolon_for_expr
,
34 unicode_str_width
, wrap_str
,
36 use crate::vertical
::rewrite_with_alignment
;
37 use crate::visitor
::FmtVisitor
;
39 impl Rewrite
for ast
::Expr
{
40 fn rewrite(&self, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
41 format_expr(self, ExprType
::SubExpression
, context
, shape
)
45 #[derive(Copy, Clone, PartialEq)]
46 pub(crate) enum ExprType
{
51 pub(crate) fn format_expr(
54 context
: &RewriteContext
<'_
>,
57 skip_out_of_file_lines_range
!(context
, expr
.span
);
59 if contains_skip(&*expr
.attrs
) {
60 return Some(context
.snippet(expr
.span()).to_owned());
62 let shape
= if expr_type
== ExprType
::Statement
&& semicolon_for_expr(context
, expr
) {
68 let expr_rw
= match expr
.kind
{
69 ast
::ExprKind
::Array(ref expr_vec
) => rewrite_array(
75 choose_separator_tactic(context
, expr
.span
),
78 ast
::ExprKind
::Lit(ref l
) => {
79 if let Some(expr_rw
) = rewrite_literal(context
, l
, shape
) {
82 if let LitKind
::StrRaw(_
) = l
.token_lit
.kind
{
83 Some(context
.snippet(l
.span
).trim().into())
89 ast
::ExprKind
::Call(ref callee
, ref args
) => {
90 let inner_span
= mk_sp(callee
.span
.hi(), expr
.span
.hi());
91 let callee_str
= callee
.rewrite(context
, shape
)?
;
92 rewrite_call(context
, &callee_str
, args
, inner_span
, shape
)
94 ast
::ExprKind
::Paren(ref subexpr
) => rewrite_paren(context
, subexpr
, shape
, expr
.span
),
95 ast
::ExprKind
::Binary(op
, ref lhs
, ref rhs
) => {
96 // FIXME: format comments between operands and operator
97 rewrite_all_pairs(expr
, shape
, context
).or_else(|| {
101 PairParts
::infix(&format
!(" {} ", context
.snippet(op
.span
))),
104 context
.config
.binop_separator(),
108 ast
::ExprKind
::Unary(op
, ref subexpr
) => rewrite_unary_op(context
, op
, subexpr
, shape
),
109 ast
::ExprKind
::Struct(ref struct_expr
) => {
110 let ast
::StructExpr
{
127 ast
::ExprKind
::Tup(ref items
) => {
128 rewrite_tuple(context
, items
.iter(), expr
.span
, shape
, items
.len() == 1)
130 ast
::ExprKind
::Let(..) => None
,
131 ast
::ExprKind
::If(..)
132 | ast
::ExprKind
::ForLoop(..)
133 | ast
::ExprKind
::Loop(..)
134 | ast
::ExprKind
::While(..) => to_control_flow(expr
, expr_type
)
135 .and_then(|control_flow
| control_flow
.rewrite(context
, shape
)),
136 ast
::ExprKind
::ConstBlock(ref anon_const
) => {
137 Some(format
!("const {}", anon_const
.rewrite(context
, shape
)?
))
139 ast
::ExprKind
::Block(ref block
, opt_label
) => {
141 ExprType
::Statement
=> {
142 if is_unsafe_block(block
) {
143 rewrite_block(block
, Some(&expr
.attrs
), opt_label
, context
, shape
)
144 } else if let rw @
Some(_
) =
145 rewrite_empty_block(context
, block
, Some(&expr
.attrs
), opt_label
, "", shape
)
147 // Rewrite block without trying to put it in a single line.
150 let prefix
= block_prefix(context
, block
, shape
)?
;
152 rewrite_block_with_visitor(
163 ExprType
::SubExpression
=> {
164 rewrite_block(block
, Some(&expr
.attrs
), opt_label
, context
, shape
)
168 ast
::ExprKind
::Match(ref cond
, ref arms
) => {
169 rewrite_match(context
, cond
, arms
, shape
, expr
.span
, &expr
.attrs
)
171 ast
::ExprKind
::Path(ref qself
, ref path
) => {
172 rewrite_path(context
, PathContext
::Expr
, qself
.as_ref(), path
, shape
)
174 ast
::ExprKind
::Assign(ref lhs
, ref rhs
, _
) => {
175 rewrite_assignment(context
, lhs
, rhs
, None
, shape
)
177 ast
::ExprKind
::AssignOp(ref op
, ref lhs
, ref rhs
) => {
178 rewrite_assignment(context
, lhs
, rhs
, Some(op
), shape
)
180 ast
::ExprKind
::Continue(ref opt_label
) => {
181 let id_str
= match *opt_label
{
182 Some(label
) => format
!(" {}", label
.ident
),
183 None
=> String
::new(),
185 Some(format
!("continue{}", id_str
))
187 ast
::ExprKind
::Break(ref opt_label
, ref opt_expr
) => {
188 let id_str
= match *opt_label
{
189 Some(label
) => format
!(" {}", label
.ident
),
190 None
=> String
::new(),
193 if let Some(ref expr
) = *opt_expr
{
194 rewrite_unary_prefix(context
, &format
!("break{} ", id_str
), &**expr
, shape
)
196 Some(format
!("break{}", id_str
))
199 ast
::ExprKind
::Yield(ref opt_expr
) => {
200 if let Some(ref expr
) = *opt_expr
{
201 rewrite_unary_prefix(context
, "yield ", &**expr
, shape
)
203 Some("yield".to_string())
206 ast
::ExprKind
::Closure(
214 ) => closures
::rewrite_closure(
215 binder
, capture
, is_async
, movability
, fn_decl
, body
, expr
.span
, context
, shape
,
217 ast
::ExprKind
::Try(..)
218 | ast
::ExprKind
::Field(..)
219 | ast
::ExprKind
::MethodCall(..)
220 | ast
::ExprKind
::Await(_
) => rewrite_chain(expr
, context
, shape
),
221 ast
::ExprKind
::MacCall(ref mac
) => {
222 rewrite_macro(mac
, None
, context
, shape
, MacroPosition
::Expression
).or_else(|| {
224 context
.snippet(expr
.span
).to_owned(),
225 context
.config
.max_width(),
230 ast
::ExprKind
::Ret(None
) => Some("return".to_owned()),
231 ast
::ExprKind
::Ret(Some(ref expr
)) => {
232 rewrite_unary_prefix(context
, "return ", &**expr
, shape
)
234 ast
::ExprKind
::Yeet(None
) => Some("do yeet".to_owned()),
235 ast
::ExprKind
::Yeet(Some(ref expr
)) => {
236 rewrite_unary_prefix(context
, "do yeet ", &**expr
, shape
)
238 ast
::ExprKind
::Box(ref expr
) => rewrite_unary_prefix(context
, "box ", &**expr
, shape
),
239 ast
::ExprKind
::AddrOf(borrow_kind
, mutability
, ref expr
) => {
240 rewrite_expr_addrof(context
, borrow_kind
, mutability
, expr
, shape
)
242 ast
::ExprKind
::Cast(ref expr
, ref ty
) => rewrite_pair(
245 PairParts
::infix(" as "),
248 SeparatorPlace
::Front
,
250 ast
::ExprKind
::Type(ref expr
, ref ty
) => rewrite_pair(
253 PairParts
::infix(": "),
256 SeparatorPlace
::Back
,
258 ast
::ExprKind
::Index(ref expr
, ref index
) => {
259 rewrite_index(&**expr
, &**index
, context
, shape
)
261 ast
::ExprKind
::Repeat(ref expr
, ref repeats
) => rewrite_pair(
264 PairParts
::new("[", "; ", "]"),
267 SeparatorPlace
::Back
,
269 ast
::ExprKind
::Range(ref lhs
, ref rhs
, limits
) => {
270 let delim
= match limits
{
271 ast
::RangeLimits
::HalfOpen
=> "..",
272 ast
::RangeLimits
::Closed
=> "..=",
275 fn needs_space_before_range(context
: &RewriteContext
<'_
>, lhs
: &ast
::Expr
) -> bool
{
277 ast
::ExprKind
::Lit(ref lit
) => match lit
.kind
{
278 ast
::LitKind
::Float(_
, ast
::LitFloatType
::Unsuffixed
) => {
279 context
.snippet(lit
.span
).ends_with('
.'
)
283 ast
::ExprKind
::Unary(_
, ref expr
) => needs_space_before_range(context
, expr
),
288 fn needs_space_after_range(rhs
: &ast
::Expr
) -> bool
{
289 // Don't format `.. ..` into `....`, which is invalid.
291 // This check is unnecessary for `lhs`, because a range
292 // starting from another range needs parentheses as `(x ..) ..`
293 // (`x .. ..` is a range from `x` to `..`).
294 matches
!(rhs
.kind
, ast
::ExprKind
::Range(None
, _
, _
))
297 let default_sp_delim
= |lhs
: Option
<&ast
::Expr
>, rhs
: Option
<&ast
::Expr
>| {
298 let space_if
= |b
: bool
| if b { " " }
else { "" }
;
302 lhs
.map_or("", |lhs
| space_if(needs_space_before_range(context
, lhs
))),
304 rhs
.map_or("", |rhs
| space_if(needs_space_after_range(rhs
))),
308 match (lhs
.as_ref().map(|x
| &**x
), rhs
.as_ref().map(|x
| &**x
)) {
309 (Some(lhs
), Some(rhs
)) => {
310 let sp_delim
= if context
.config
.spaces_around_ranges() {
311 format
!(" {} ", delim
)
313 default_sp_delim(Some(lhs
), Some(rhs
))
318 PairParts
::infix(&sp_delim
),
321 context
.config
.binop_separator(),
324 (None
, Some(rhs
)) => {
325 let sp_delim
= if context
.config
.spaces_around_ranges() {
326 format
!("{} ", delim
)
328 default_sp_delim(None
, Some(rhs
))
330 rewrite_unary_prefix(context
, &sp_delim
, &*rhs
, shape
)
332 (Some(lhs
), None
) => {
333 let sp_delim
= if context
.config
.spaces_around_ranges() {
334 format
!(" {}", delim
)
336 default_sp_delim(Some(lhs
), None
)
338 rewrite_unary_suffix(context
, &sp_delim
, &*lhs
, shape
)
340 (None
, None
) => Some(delim
.to_owned()),
343 // We do not format these expressions yet, but they should still
344 // satisfy our width restrictions.
345 // Style Guide RFC for InlineAsm variant pending
346 // https://github.com/rust-dev-tools/fmt-rfcs/issues/152
347 ast
::ExprKind
::InlineAsm(..) => Some(context
.snippet(expr
.span
).to_owned()),
348 ast
::ExprKind
::TryBlock(ref block
) => {
349 if let rw @
Some(_
) =
350 rewrite_single_line_block(context
, "try ", block
, Some(&expr
.attrs
), None
, shape
)
355 let budget
= shape
.width
.saturating_sub(9);
364 Shape
::legacy(budget
, shape
.indent
)
369 ast
::ExprKind
::Async(capture_by
, _node_id
, ref block
) => {
370 let mover
= if capture_by
== ast
::CaptureBy
::Value
{
375 if let rw @
Some(_
) = rewrite_single_line_block(
377 format
!("{}{}", "async ", mover
).as_str(),
386 let budget
= shape
.width
.saturating_sub(6);
396 Shape
::legacy(budget
, shape
.indent
)
401 ast
::ExprKind
::Underscore
=> Some("_".to_owned()),
402 ast
::ExprKind
::Err
=> None
,
406 .and_then(|expr_str
| recover_comment_removed(expr_str
, expr
.span
, context
))
407 .and_then(|expr_str
| {
408 let attrs
= outer_attributes(&expr
.attrs
);
409 let attrs_str
= attrs
.rewrite(context
, shape
)?
;
411 attrs
.last().map_or(expr
.span
.lo(), |attr
| attr
.span
.hi()),
414 combine_strs_with_missing_comments(context
, &attrs_str
, &expr_str
, span
, shape
, false)
418 pub(crate) fn rewrite_array
<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>>(
420 exprs
: impl Iterator
<Item
= &'a T
>,
422 context
: &'a RewriteContext
<'_
>,
424 force_separator_tactic
: Option
<SeparatorTactic
>,
425 delim_token
: Option
<Delimiter
>,
426 ) -> Option
<String
> {
427 overflow
::rewrite_with_square_brackets(
433 force_separator_tactic
,
438 fn rewrite_empty_block(
439 context
: &RewriteContext
<'_
>,
441 attrs
: Option
<&[ast
::Attribute
]>,
442 label
: Option
<ast
::Label
>,
445 ) -> Option
<String
> {
446 if block_has_statements(block
) {
450 let label_str
= rewrite_label(label
);
451 if attrs
.map_or(false, |a
| !inner_attributes(a
).is_empty()) {
455 if !block_contains_comment(context
, block
) && shape
.width
>= 2 {
456 return Some(format
!("{}{}{{}}", prefix
, label_str
));
459 // If a block contains only a single-line comment, then leave it on one line.
460 let user_str
= context
.snippet(block
.span
);
461 let user_str
= user_str
.trim();
462 if user_str
.starts_with('{') && user_str.ends_with('}'
) {
463 let comment_str
= user_str
[1..user_str
.len() - 1].trim();
464 if block
.stmts
.is_empty()
465 && !comment_str
.contains('
\n'
)
466 && !comment_str
.starts_with("//")
467 && comment_str
.len() + 4 <= shape
.width
469 return Some(format
!("{}{}{{ {} }}", prefix
, label_str
, comment_str
));
476 fn block_prefix(context
: &RewriteContext
<'_
>, block
: &ast
::Block
, shape
: Shape
) -> Option
<String
> {
477 Some(match block
.rules
{
478 ast
::BlockCheckMode
::Unsafe(..) => {
479 let snippet
= context
.snippet(block
.span
);
480 let open_pos
= snippet
.find_uncommented("{")?
;
481 // Extract comment between unsafe and block start.
482 let trimmed
= &snippet
[6..open_pos
].trim();
484 if !trimmed
.is_empty() {
485 // 9 = "unsafe {".len(), 7 = "unsafe ".len()
486 let budget
= shape
.width
.checked_sub(9)?
;
492 Shape
::legacy(budget
, shape
.indent
+ 7),
500 ast
::BlockCheckMode
::Default
=> String
::new(),
504 fn rewrite_single_line_block(
505 context
: &RewriteContext
<'_
>,
508 attrs
: Option
<&[ast
::Attribute
]>,
509 label
: Option
<ast
::Label
>,
511 ) -> Option
<String
> {
512 if is_simple_block(context
, block
, attrs
) {
513 let expr_shape
= shape
.offset_left(last_line_width(prefix
))?
;
514 let expr_str
= block
.stmts
[0].rewrite(context
, expr_shape
)?
;
515 let label_str
= rewrite_label(label
);
516 let result
= format
!("{}{}{{ {} }}", prefix
, label_str
, expr_str
);
517 if result
.len() <= shape
.width
&& !result
.contains('
\n'
) {
524 pub(crate) fn rewrite_block_with_visitor(
525 context
: &RewriteContext
<'_
>,
528 attrs
: Option
<&[ast
::Attribute
]>,
529 label
: Option
<ast
::Label
>,
532 ) -> Option
<String
> {
533 if let rw @
Some(_
) = rewrite_empty_block(context
, block
, attrs
, label
, prefix
, shape
) {
537 let mut visitor
= FmtVisitor
::from_context(context
);
538 visitor
.block_indent
= shape
.indent
;
539 visitor
.is_if_else_block
= context
.is_if_else_block();
540 match (block
.rules
, label
) {
541 (ast
::BlockCheckMode
::Unsafe(..), _
) | (ast
::BlockCheckMode
::Default
, Some(_
)) => {
542 let snippet
= context
.snippet(block
.span
);
543 let open_pos
= snippet
.find_uncommented("{")?
;
544 visitor
.last_pos
= block
.span
.lo() + BytePos(open_pos
as u32)
546 (ast
::BlockCheckMode
::Default
, None
) => visitor
.last_pos
= block
.span
.lo(),
549 let inner_attrs
= attrs
.map(inner_attributes
);
550 let label_str
= rewrite_label(label
);
551 visitor
.visit_block(block
, inner_attrs
.as_deref(), has_braces
);
552 let visitor_context
= visitor
.get_context();
556 .append(&mut visitor_context
.skipped_range
.borrow_mut());
557 Some(format
!("{}{}{}", prefix
, label_str
, visitor
.buffer
))
560 impl Rewrite
for ast
::Block
{
561 fn rewrite(&self, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
562 rewrite_block(self, None
, None
, context
, shape
)
568 attrs
: Option
<&[ast
::Attribute
]>,
569 label
: Option
<ast
::Label
>,
570 context
: &RewriteContext
<'_
>,
572 ) -> Option
<String
> {
573 let prefix
= block_prefix(context
, block
, shape
)?
;
575 // shape.width is used only for the single line case: either the empty block `{}`,
576 // or an unsafe expression `unsafe { e }`.
577 if let rw @
Some(_
) = rewrite_empty_block(context
, block
, attrs
, label
, &prefix
, shape
) {
581 let result
= rewrite_block_with_visitor(context
, &prefix
, block
, attrs
, label
, shape
, true);
582 if let Some(ref result_str
) = result
{
583 if result_str
.lines().count() <= 3 {
584 if let rw @
Some(_
) =
585 rewrite_single_line_block(context
, &prefix
, block
, attrs
, label
, shape
)
595 // Rewrite condition if the given expression has one.
596 pub(crate) fn rewrite_cond(
597 context
: &RewriteContext
<'_
>,
600 ) -> Option
<String
> {
602 ast
::ExprKind
::Match(ref cond
, _
) => {
604 let cond_shape
= match context
.config
.indent_style() {
605 IndentStyle
::Visual
=> shape
.shrink_left(6).and_then(|s
| s
.sub_width(2))?
,
606 IndentStyle
::Block
=> shape
.offset_left(8)?
,
608 cond
.rewrite(context
, cond_shape
)
610 _
=> to_control_flow(expr
, ExprType
::SubExpression
).and_then(|control_flow
| {
612 String
::from("\n") + &shape
.indent
.block_only().to_string(context
.config
);
614 .rewrite_cond(context
, shape
, &alt_block_sep
)
620 // Abstraction over control flow expressions
622 struct ControlFlow
<'a
> {
623 cond
: Option
<&'a ast
::Expr
>,
624 block
: &'a ast
::Block
,
625 else_block
: Option
<&'a ast
::Expr
>,
626 label
: Option
<ast
::Label
>,
627 pat
: Option
<&'a ast
::Pat
>,
631 allow_single_line
: bool
,
632 // HACK: `true` if this is an `if` expression in an `else if`.
637 fn extract_pats_and_cond(expr
: &ast
::Expr
) -> (Option
<&ast
::Pat
>, &ast
::Expr
) {
639 ast
::ExprKind
::Let(ref pat
, ref cond
, _
) => (Some(pat
), cond
),
644 // FIXME: Refactor this.
645 fn to_control_flow(expr
: &ast
::Expr
, expr_type
: ExprType
) -> Option
<ControlFlow
<'_
>> {
647 ast
::ExprKind
::If(ref cond
, ref if_block
, ref else_block
) => {
648 let (pat
, cond
) = extract_pats_and_cond(cond
);
649 Some(ControlFlow
::new_if(
653 else_block
.as_ref().map(|e
| &**e
),
654 expr_type
== ExprType
::SubExpression
,
659 ast
::ExprKind
::ForLoop(ref pat
, ref cond
, ref block
, label
) => {
660 Some(ControlFlow
::new_for(pat
, cond
, block
, label
, expr
.span
))
662 ast
::ExprKind
::Loop(ref block
, label
) => {
663 Some(ControlFlow
::new_loop(block
, label
, expr
.span
))
665 ast
::ExprKind
::While(ref cond
, ref block
, label
) => {
666 let (pat
, cond
) = extract_pats_and_cond(cond
);
667 Some(ControlFlow
::new_while(pat
, cond
, block
, label
, expr
.span
))
673 fn choose_matcher(pat
: Option
<&ast
::Pat
>) -> &'
static str {
674 pat
.map_or("", |_
| "let")
677 impl<'a
> ControlFlow
<'a
> {
680 pat
: Option
<&'a ast
::Pat
>,
681 block
: &'a ast
::Block
,
682 else_block
: Option
<&'a ast
::Expr
>,
683 allow_single_line
: bool
,
686 ) -> ControlFlow
<'a
> {
687 let matcher
= choose_matcher(pat
);
703 fn new_loop(block
: &'a ast
::Block
, label
: Option
<ast
::Label
>, span
: Span
) -> ControlFlow
<'a
> {
713 allow_single_line
: false,
720 pat
: Option
<&'a ast
::Pat
>,
722 block
: &'a ast
::Block
,
723 label
: Option
<ast
::Label
>,
725 ) -> ControlFlow
<'a
> {
726 let matcher
= choose_matcher(pat
);
736 allow_single_line
: false,
745 block
: &'a ast
::Block
,
746 label
: Option
<ast
::Label
>,
748 ) -> ControlFlow
<'a
> {
758 allow_single_line
: false,
764 fn rewrite_single_line(
767 context
: &RewriteContext
<'_
>,
769 ) -> Option
<String
> {
770 assert
!(self.allow_single_line
);
771 let else_block
= self.else_block?
;
772 let fixed_cost
= self.keyword
.len() + " { } else { }".len();
774 if let ast
::ExprKind
::Block(ref else_node
, _
) = else_block
.kind
{
775 if !is_simple_block(context
, self.block
, None
)
776 || !is_simple_block(context
, else_node
, None
)
777 || pat_expr_str
.contains('
\n'
)
782 let new_width
= width
.checked_sub(pat_expr_str
.len() + fixed_cost
)?
;
783 let expr
= &self.block
.stmts
[0];
784 let if_str
= expr
.rewrite(context
, Shape
::legacy(new_width
, Indent
::empty()))?
;
786 let new_width
= new_width
.checked_sub(if_str
.len())?
;
787 let else_expr
= &else_node
.stmts
[0];
788 let else_str
= else_expr
.rewrite(context
, Shape
::legacy(new_width
, Indent
::empty()))?
;
790 if if_str
.contains('
\n'
) || else_str
.contains('
\n'
) {
794 let result
= format
!(
795 "{} {} {{ {} }} else {{ {} }}",
796 self.keyword
, pat_expr_str
, if_str
, else_str
799 if result
.len() <= width
{
808 /// Returns `true` if the last line of pat_str has leading whitespace and it is wider than the
810 fn last_line_offsetted(start_column
: usize, pat_str
: &str) -> bool
{
811 let mut leading_whitespaces
= 0;
812 for c
in pat_str
.chars().rev() {
815 _
if c
.is_whitespace() => leading_whitespaces
+= 1,
816 _
=> leading_whitespaces
= 0,
819 leading_whitespaces
> start_column
822 impl<'a
> ControlFlow
<'a
> {
825 context
: &RewriteContext
<'_
>,
829 ) -> Option
<String
> {
830 debug
!("rewrite_pat_expr {:?} {:?} {:?}", shape
, self.pat
, expr
);
832 let cond_shape
= shape
.offset_left(offset
)?
;
833 if let Some(pat
) = self.pat
{
834 let matcher
= if self.matcher
.is_empty() {
835 self.matcher
.to_owned()
837 format
!("{} ", self.matcher
)
839 let pat_shape
= cond_shape
840 .offset_left(matcher
.len())?
841 .sub_width(self.connector
.len())?
;
842 let pat_string
= pat
.rewrite(context
, pat_shape
)?
;
843 let comments_lo
= context
845 .span_after(self.span
.with_lo(pat
.span
.hi()), self.connector
.trim());
846 let comments_span
= mk_sp(comments_lo
, expr
.span
.lo());
847 return rewrite_assign_rhs_with_comments(
849 &format
!("{}{}{}", matcher
, pat_string
, self.connector
),
852 &RhsAssignKind
::Expr(&expr
.kind
, expr
.span
),
859 let expr_rw
= expr
.rewrite(context
, cond_shape
);
860 // The expression may (partially) fit on the current line.
861 // We do not allow splitting between `if` and condition.
862 if self.keyword
== "if" || expr_rw
.is_some() {
866 // The expression won't fit on the current line, jump to next.
867 let nested_shape
= shape
868 .block_indent(context
.config
.tab_spaces())
869 .with_max_width(context
.config
);
870 let nested_indent_str
= nested_shape
.indent
.to_string_with_newline(context
.config
);
871 expr
.rewrite(context
, nested_shape
)
872 .map(|expr_rw
| format
!("{}{}", nested_indent_str
, expr_rw
))
877 context
: &RewriteContext
<'_
>,
880 ) -> Option
<(String
, usize)> {
881 // Do not take the rhs overhead from the upper expressions into account
882 // when rewriting pattern.
883 let new_width
= context
.budget(shape
.used_width());
884 let fresh_shape
= Shape
{
888 let constr_shape
= if self.nested_if
{
889 // We are part of an if-elseif-else chain. Our constraints are tightened.
890 // 7 = "} else " .len()
891 fresh_shape
.offset_left(7)?
896 let label_string
= rewrite_label(self.label
);
897 // 1 = space after keyword.
898 let offset
= self.keyword
.len() + label_string
.len() + 1;
900 let pat_expr_string
= match self.cond
{
901 Some(cond
) => self.rewrite_pat_expr(context
, cond
, constr_shape
, offset
)?
,
902 None
=> String
::new(),
906 if context
.config
.control_brace_style() != ControlBraceStyle
::AlwaysNextLine
{
912 let one_line_budget
= context
915 .saturating_sub(constr_shape
.used_width() + offset
+ brace_overhead
);
916 let force_newline_brace
= (pat_expr_string
.contains('
\n'
)
917 || pat_expr_string
.len() > one_line_budget
)
918 && (!last_line_extendable(&pat_expr_string
)
919 || last_line_offsetted(shape
.used_width(), &pat_expr_string
));
921 // Try to format if-else on single line.
922 if self.allow_single_line
&& context
.config
.single_line_if_else_max_width() > 0 {
923 let trial
= self.rewrite_single_line(&pat_expr_string
, context
, shape
.width
);
925 if let Some(cond_str
) = trial
{
926 if cond_str
.len() <= context
.config
.single_line_if_else_max_width() {
927 return Some((cond_str
, 0));
932 let cond_span
= if let Some(cond
) = self.cond
{
935 mk_sp(self.block
.span
.lo(), self.block
.span
.lo())
938 // `for event in event`
939 // Do not include label in the span.
942 .map_or(self.span
.lo(), |label
| label
.ident
.span
.hi());
943 let between_kwd_cond
= mk_sp(
946 .span_after(mk_sp(lo
, self.span
.hi()), self.keyword
.trim()),
947 if self.pat
.is_none() {
949 } else if self.matcher
.is_empty() {
950 self.pat
.unwrap().span
.lo()
954 .span_before(self.span
, self.matcher
.trim())
958 let between_kwd_cond_comment
= extract_comment(between_kwd_cond
, context
, shape
);
960 let after_cond_comment
=
961 extract_comment(mk_sp(cond_span
.hi(), self.block
.span
.lo()), context
, shape
);
963 let block_sep
= if self.cond
.is_none() && between_kwd_cond_comment
.is_some() {
965 } else if context
.config
.control_brace_style() == ControlBraceStyle
::AlwaysNextLine
966 || force_newline_brace
973 let used_width
= if pat_expr_string
.contains('
\n'
) {
974 last_line_width(&pat_expr_string
)
976 // 2 = spaces after keyword and condition.
977 label_string
.len() + self.keyword
.len() + pat_expr_string
.len() + 2
985 between_kwd_cond_comment
.as_ref().map_or(
986 if pat_expr_string
.is_empty() || pat_expr_string
.starts_with('
\n'
) {
994 after_cond_comment
.as_ref().map_or(block_sep
, |s
| &**s
)
1001 impl<'a
> Rewrite
for ControlFlow
<'a
> {
1002 fn rewrite(&self, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
1003 debug
!("ControlFlow::rewrite {:?} {:?}", self, shape
);
1005 let alt_block_sep
= &shape
.indent
.to_string_with_newline(context
.config
);
1006 let (cond_str
, used_width
) = self.rewrite_cond(context
, shape
, alt_block_sep
)?
;
1007 // If `used_width` is 0, it indicates that whole control flow is written in a single line.
1008 if used_width
== 0 {
1009 return Some(cond_str
);
1012 let block_width
= shape
.width
.saturating_sub(used_width
);
1013 // This is used only for the empty block case: `{}`. So, we use 1 if we know
1014 // we should avoid the single line case.
1015 let block_width
= if self.else_block
.is_some() || self.nested_if
{
1020 let block_shape
= Shape
{
1025 let old_val
= context
.is_if_else_block
.replace(self.else_block
.is_some());
1027 rewrite_block_with_visitor(context
, "", self.block
, None
, None
, block_shape
, true);
1028 context
.is_if_else_block
.replace(old_val
);
1032 let mut result
= format
!("{}{}", cond_str
, block_str
);
1034 if let Some(else_block
) = self.else_block
{
1035 let shape
= Shape
::indented(shape
.indent
, context
.config
);
1036 let mut last_in_chain
= false;
1037 let rewrite
= match else_block
.kind
{
1038 // If the else expression is another if-else expression, prevent it
1039 // from being formatted on a single line.
1040 // Note how we're passing the original shape, as the
1041 // cost of "else" should not cascade.
1042 ast
::ExprKind
::If(ref cond
, ref if_block
, ref next_else_block
) => {
1043 let (pats
, cond
) = extract_pats_and_cond(cond
);
1044 ControlFlow
::new_if(
1048 next_else_block
.as_ref().map(|e
| &**e
),
1051 mk_sp(else_block
.span
.lo(), self.span
.hi()),
1053 .rewrite(context
, shape
)
1056 last_in_chain
= true;
1057 // When rewriting a block, the width is only used for single line
1058 // blocks, passing 1 lets us avoid that.
1059 let else_shape
= Shape
{
1060 width
: min(1, shape
.width
),
1063 format_expr(else_block
, ExprType
::Statement
, context
, else_shape
)
1067 let between_kwd_else_block
= mk_sp(
1068 self.block
.span
.hi(),
1071 .span_before(mk_sp(self.block
.span
.hi(), else_block
.span
.lo()), "else"),
1073 let between_kwd_else_block_comment
=
1074 extract_comment(between_kwd_else_block
, context
, shape
);
1076 let after_else
= mk_sp(
1079 .span_after(mk_sp(self.block
.span
.hi(), else_block
.span
.lo()), "else"),
1080 else_block
.span
.lo(),
1082 let after_else_comment
= extract_comment(after_else
, context
, shape
);
1084 let between_sep
= match context
.config
.control_brace_style() {
1085 ControlBraceStyle
::AlwaysNextLine
| ControlBraceStyle
::ClosingNextLine
=> {
1088 ControlBraceStyle
::AlwaysSameLine
=> " ",
1090 let after_sep
= match context
.config
.control_brace_style() {
1091 ControlBraceStyle
::AlwaysNextLine
if last_in_chain
=> &*alt_block_sep
,
1095 result
.push_str(&format
!(
1097 between_kwd_else_block_comment
1099 .map_or(between_sep
, |s
| &**s
),
1100 after_else_comment
.as_ref().map_or(after_sep
, |s
| &**s
),
1102 result
.push_str(&rewrite?
);
1109 fn rewrite_label(opt_label
: Option
<ast
::Label
>) -> Cow
<'
static, str> {
1111 Some(label
) => Cow
::from(format
!("{}: ", label
.ident
)),
1112 None
=> Cow
::from(""),
1116 fn extract_comment(span
: Span
, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
1117 match rewrite_missing_comment(span
, shape
, context
) {
1118 Some(ref comment
) if !comment
.is_empty() => Some(format
!(
1119 "{indent}{}{indent}",
1121 indent
= shape
.indent
.to_string_with_newline(context
.config
)
1127 pub(crate) fn block_contains_comment(context
: &RewriteContext
<'_
>, block
: &ast
::Block
) -> bool
{
1128 contains_comment(context
.snippet(block
.span
))
1131 // Checks that a block contains no statements, an expression and no comments or
1133 // FIXME: incorrectly returns false when comment is contained completely within
1135 pub(crate) fn is_simple_block(
1136 context
: &RewriteContext
<'_
>,
1138 attrs
: Option
<&[ast
::Attribute
]>,
1140 block
.stmts
.len() == 1
1141 && stmt_is_expr(&block
.stmts
[0])
1142 && !block_contains_comment(context
, block
)
1143 && attrs
.map_or(true, |a
| a
.is_empty())
1146 /// Checks whether a block contains at most one statement or expression, and no
1147 /// comments or attributes.
1148 pub(crate) fn is_simple_block_stmt(
1149 context
: &RewriteContext
<'_
>,
1151 attrs
: Option
<&[ast
::Attribute
]>,
1153 block
.stmts
.len() <= 1
1154 && !block_contains_comment(context
, block
)
1155 && attrs
.map_or(true, |a
| a
.is_empty())
1158 fn block_has_statements(block
: &ast
::Block
) -> bool
{
1162 .any(|stmt
| !matches
!(stmt
.kind
, ast
::StmtKind
::Empty
))
1165 /// Checks whether a block contains no statements, expressions, comments, or
1166 /// inner attributes.
1167 pub(crate) fn is_empty_block(
1168 context
: &RewriteContext
<'_
>,
1170 attrs
: Option
<&[ast
::Attribute
]>,
1172 !block_has_statements(block
)
1173 && !block_contains_comment(context
, block
)
1174 && attrs
.map_or(true, |a
| inner_attributes(a
).is_empty())
1177 pub(crate) fn stmt_is_expr(stmt
: &ast
::Stmt
) -> bool
{
1178 matches
!(stmt
.kind
, ast
::StmtKind
::Expr(..))
1181 pub(crate) fn is_unsafe_block(block
: &ast
::Block
) -> bool
{
1182 matches
!(block
.rules
, ast
::BlockCheckMode
::Unsafe(..))
1185 pub(crate) fn rewrite_literal(
1186 context
: &RewriteContext
<'_
>,
1189 ) -> Option
<String
> {
1191 ast
::LitKind
::Str(_
, ast
::StrStyle
::Cooked
) => rewrite_string_lit(context
, l
.span
, shape
),
1192 ast
::LitKind
::Int(..) => rewrite_int_lit(context
, l
, shape
),
1194 context
.snippet(l
.span
).to_owned(),
1195 context
.config
.max_width(),
1201 fn rewrite_string_lit(context
: &RewriteContext
<'_
>, span
: Span
, shape
: Shape
) -> Option
<String
> {
1202 let string_lit
= context
.snippet(span
);
1204 if !context
.config
.format_strings() {
1208 .all(|line
| line
.ends_with('
\\'
))
1209 && context
.config
.version() == Version
::Two
1211 return Some(string_lit
.to_owned());
1213 return wrap_str(string_lit
.to_owned(), context
.config
.max_width(), shape
);
1217 // Remove the quote characters.
1218 let str_lit
= &string_lit
[1..string_lit
.len() - 1];
1222 &StringFormat
::new(shape
.visual_indent(0), context
.config
),
1223 shape
.width
.saturating_sub(2),
1227 fn rewrite_int_lit(context
: &RewriteContext
<'_
>, lit
: &ast
::Lit
, shape
: Shape
) -> Option
<String
> {
1228 let span
= lit
.span
;
1229 let symbol
= lit
.token_lit
.symbol
.as_str();
1231 if let Some(symbol_stripped
) = symbol
.strip_prefix("0x") {
1232 let hex_lit
= match context
.config
.hex_literal_case() {
1233 HexLiteralCase
::Preserve
=> None
,
1234 HexLiteralCase
::Upper
=> Some(symbol_stripped
.to_ascii_uppercase()),
1235 HexLiteralCase
::Lower
=> Some(symbol_stripped
.to_ascii_lowercase()),
1237 if let Some(hex_lit
) = hex_lit
{
1244 .map_or(String
::new(), |s
| s
.to_string())
1246 context
.config
.max_width(),
1253 context
.snippet(span
).to_owned(),
1254 context
.config
.max_width(),
1259 fn choose_separator_tactic(context
: &RewriteContext
<'_
>, span
: Span
) -> Option
<SeparatorTactic
> {
1260 if context
.inside_macro() {
1261 if span_ends_with_comma(context
, span
) {
1262 Some(SeparatorTactic
::Always
)
1264 Some(SeparatorTactic
::Never
)
1271 pub(crate) fn rewrite_call(
1272 context
: &RewriteContext
<'_
>,
1274 args
: &[ptr
::P
<ast
::Expr
>],
1277 ) -> Option
<String
> {
1278 overflow
::rewrite_with_parens(
1284 context
.config
.fn_call_width(),
1285 choose_separator_tactic(context
, span
),
1289 pub(crate) fn is_simple_expr(expr
: &ast
::Expr
) -> bool
{
1291 ast
::ExprKind
::Lit(..) => true,
1292 ast
::ExprKind
::Path(ref qself
, ref path
) => qself
.is_none() && path
.segments
.len() <= 1,
1293 ast
::ExprKind
::AddrOf(_
, _
, ref expr
)
1294 | ast
::ExprKind
::Box(ref expr
)
1295 | ast
::ExprKind
::Cast(ref expr
, _
)
1296 | ast
::ExprKind
::Field(ref expr
, _
)
1297 | ast
::ExprKind
::Try(ref expr
)
1298 | ast
::ExprKind
::Unary(_
, ref expr
) => is_simple_expr(expr
),
1299 ast
::ExprKind
::Index(ref lhs
, ref rhs
) => is_simple_expr(lhs
) && is_simple_expr(rhs
),
1300 ast
::ExprKind
::Repeat(ref lhs
, ref rhs
) => {
1301 is_simple_expr(lhs
) && is_simple_expr(&*rhs
.value
)
1307 pub(crate) fn is_every_expr_simple(lists
: &[OverflowableItem
<'_
>]) -> bool
{
1308 lists
.iter().all(OverflowableItem
::is_simple
)
1311 pub(crate) fn can_be_overflowed_expr(
1312 context
: &RewriteContext
<'_
>,
1317 _
if !expr
.attrs
.is_empty() => false,
1318 ast
::ExprKind
::Match(..) => {
1319 (context
.use_block_indent() && args_len
== 1)
1320 || (context
.config
.indent_style() == IndentStyle
::Visual
&& args_len
> 1)
1321 || context
.config
.overflow_delimited_expr()
1323 ast
::ExprKind
::If(..)
1324 | ast
::ExprKind
::ForLoop(..)
1325 | ast
::ExprKind
::Loop(..)
1326 | ast
::ExprKind
::While(..) => {
1327 context
.config
.combine_control_expr() && context
.use_block_indent() && args_len
== 1
1330 // Handle always block-like expressions
1331 ast
::ExprKind
::Async(..) | ast
::ExprKind
::Block(..) | ast
::ExprKind
::Closure(..) => true,
1333 // Handle `[]` and `{}`-like expressions
1334 ast
::ExprKind
::Array(..) | ast
::ExprKind
::Struct(..) => {
1335 context
.config
.overflow_delimited_expr()
1336 || (context
.use_block_indent() && args_len
== 1)
1338 ast
::ExprKind
::MacCall(ref mac
) => {
1340 rustc_ast
::ast
::MacDelimiter
::from_token(mac
.args
.delim().unwrap()),
1341 context
.config
.overflow_delimited_expr(),
1343 (Some(ast
::MacDelimiter
::Bracket
), true)
1344 | (Some(ast
::MacDelimiter
::Brace
), true) => true,
1345 _
=> context
.use_block_indent() && args_len
== 1,
1349 // Handle parenthetical expressions
1350 ast
::ExprKind
::Call(..) | ast
::ExprKind
::MethodCall(..) | ast
::ExprKind
::Tup(..) => {
1351 context
.use_block_indent() && args_len
== 1
1354 // Handle unary-like expressions
1355 ast
::ExprKind
::AddrOf(_
, _
, ref expr
)
1356 | ast
::ExprKind
::Box(ref expr
)
1357 | ast
::ExprKind
::Try(ref expr
)
1358 | ast
::ExprKind
::Unary(_
, ref expr
)
1359 | ast
::ExprKind
::Cast(ref expr
, _
) => can_be_overflowed_expr(context
, expr
, args_len
),
1364 pub(crate) fn is_nested_call(expr
: &ast
::Expr
) -> bool
{
1366 ast
::ExprKind
::Call(..) | ast
::ExprKind
::MacCall(..) => true,
1367 ast
::ExprKind
::AddrOf(_
, _
, ref expr
)
1368 | ast
::ExprKind
::Box(ref expr
)
1369 | ast
::ExprKind
::Try(ref expr
)
1370 | ast
::ExprKind
::Unary(_
, ref expr
)
1371 | ast
::ExprKind
::Cast(ref expr
, _
) => is_nested_call(expr
),
1376 /// Returns `true` if a function call or a method call represented by the given span ends with a
1377 /// trailing comma. This function is used when rewriting macro, as adding or removing a trailing
1378 /// comma from macro can potentially break the code.
1379 pub(crate) fn span_ends_with_comma(context
: &RewriteContext
<'_
>, span
: Span
) -> bool
{
1380 let mut result
: bool
= Default
::default();
1381 let mut prev_char
: char = Default
::default();
1382 let closing_delimiters
= &['
)'
, '
}'
, '
]'
];
1384 for (kind
, c
) in CharClasses
::new(context
.snippet(span
).chars()) {
1386 _
if kind
.is_comment() || c
.is_whitespace() => continue,
1387 c
if closing_delimiters
.contains(&c
) => {
1388 result
&= !closing_delimiters
.contains(&prev_char
);
1390 '
,'
=> result
= true,
1391 _
=> result
= false,
1400 context
: &RewriteContext
<'_
>,
1401 mut subexpr
: &ast
::Expr
,
1404 ) -> Option
<String
> {
1405 debug
!("rewrite_paren, shape: {:?}", shape
);
1407 // Extract comments within parens.
1410 let mut pre_comment
;
1411 let mut post_comment
;
1412 let remove_nested_parens
= context
.config
.remove_nested_parens();
1415 pre_span
= mk_sp(span
.lo() + BytePos(1), subexpr
.span
.lo());
1416 post_span
= mk_sp(subexpr
.span
.hi(), span
.hi() - BytePos(1));
1417 pre_comment
= rewrite_missing_comment(pre_span
, shape
, context
)?
;
1418 post_comment
= rewrite_missing_comment(post_span
, shape
, context
)?
;
1420 // Remove nested parens if there are no comments.
1421 if let ast
::ExprKind
::Paren(ref subsubexpr
) = subexpr
.kind
{
1422 if remove_nested_parens
&& pre_comment
.is_empty() && post_comment
.is_empty() {
1423 span
= subexpr
.span
;
1424 subexpr
= subsubexpr
;
1433 let sub_shape
= shape
.offset_left(1)?
.sub_width(1)?
;
1434 let subexpr_str
= subexpr
.rewrite(context
, sub_shape
)?
;
1435 let fits_single_line
= !pre_comment
.contains("//") && !post_comment
.contains("//");
1436 if fits_single_line
{
1437 Some(format
!("({}{}{})", pre_comment
, subexpr_str
, post_comment
))
1439 rewrite_paren_in_multi_line(context
, subexpr
, shape
, pre_span
, post_span
)
1443 fn rewrite_paren_in_multi_line(
1444 context
: &RewriteContext
<'_
>,
1445 subexpr
: &ast
::Expr
,
1449 ) -> Option
<String
> {
1450 let nested_indent
= shape
.indent
.block_indent(context
.config
);
1451 let nested_shape
= Shape
::indented(nested_indent
, context
.config
);
1452 let pre_comment
= rewrite_missing_comment(pre_span
, nested_shape
, context
)?
;
1453 let post_comment
= rewrite_missing_comment(post_span
, nested_shape
, context
)?
;
1454 let subexpr_str
= subexpr
.rewrite(context
, nested_shape
)?
;
1456 let mut result
= String
::with_capacity(subexpr_str
.len() * 2);
1458 if !pre_comment
.is_empty() {
1459 result
.push_str(&nested_indent
.to_string_with_newline(context
.config
));
1460 result
.push_str(&pre_comment
);
1462 result
.push_str(&nested_indent
.to_string_with_newline(context
.config
));
1463 result
.push_str(&subexpr_str
);
1464 if !post_comment
.is_empty() {
1465 result
.push_str(&nested_indent
.to_string_with_newline(context
.config
));
1466 result
.push_str(&post_comment
);
1468 result
.push_str(&shape
.indent
.to_string_with_newline(context
.config
));
1477 context
: &RewriteContext
<'_
>,
1479 ) -> Option
<String
> {
1480 let expr_str
= expr
.rewrite(context
, shape
)?
;
1482 let offset
= last_line_width(&expr_str
) + 1;
1483 let rhs_overhead
= shape
.rhs_overhead(context
.config
);
1484 let index_shape
= if expr_str
.contains('
\n'
) {
1485 Shape
::legacy(context
.config
.max_width(), shape
.indent
)
1486 .offset_left(offset
)
1487 .and_then(|shape
| shape
.sub_width(1 + rhs_overhead
))
1489 match context
.config
.indent_style() {
1490 IndentStyle
::Block
=> shape
1491 .offset_left(offset
)
1492 .and_then(|shape
| shape
.sub_width(1)),
1493 IndentStyle
::Visual
=> shape
.visual_indent(offset
).sub_width(offset
+ 1),
1496 let orig_index_rw
= index_shape
.and_then(|s
| index
.rewrite(context
, s
));
1498 // Return if index fits in a single line.
1499 match orig_index_rw
{
1500 Some(ref index_str
) if !index_str
.contains('
\n'
) => {
1501 return Some(format
!("{}[{}]", expr_str
, index_str
));
1506 // Try putting index on the next line and see if it fits in a single line.
1507 let indent
= shape
.indent
.block_indent(context
.config
);
1508 let index_shape
= Shape
::indented(indent
, context
.config
).offset_left(1)?
;
1509 let index_shape
= index_shape
.sub_width(1 + rhs_overhead
)?
;
1510 let new_index_rw
= index
.rewrite(context
, index_shape
);
1511 match (orig_index_rw
, new_index_rw
) {
1512 (_
, Some(ref new_index_str
)) if !new_index_str
.contains('
\n'
) => Some(format
!(
1515 indent
.to_string_with_newline(context
.config
),
1518 (None
, Some(ref new_index_str
)) => Some(format
!(
1521 indent
.to_string_with_newline(context
.config
),
1524 (Some(ref index_str
), _
) => Some(format
!("{}[{}]", expr_str
, index_str
)),
1529 fn struct_lit_can_be_aligned(fields
: &[ast
::ExprField
], has_base
: bool
) -> bool
{
1530 !has_base
&& fields
.iter().all(|field
| !field
.is_shorthand
)
1533 fn rewrite_struct_lit
<'a
>(
1534 context
: &RewriteContext
<'_
>,
1536 qself
: Option
<&ast
::QSelf
>,
1537 fields
: &'a
[ast
::ExprField
],
1538 struct_rest
: &ast
::StructRest
,
1539 attrs
: &[ast
::Attribute
],
1542 ) -> Option
<String
> {
1543 debug
!("rewrite_struct_lit: shape {:?}", shape
);
1545 enum StructLitField
<'a
> {
1546 Regular(&'a ast
::ExprField
),
1547 Base(&'a ast
::Expr
),
1552 let path_shape
= shape
.sub_width(2)?
;
1553 let path_str
= rewrite_path(context
, PathContext
::Expr
, qself
, path
, path_shape
)?
;
1555 let has_base_or_rest
= match struct_rest
{
1556 ast
::StructRest
::None
if fields
.is_empty() => return Some(format
!("{} {{}}", path_str
)),
1557 ast
::StructRest
::Rest(_
) if fields
.is_empty() => {
1558 return Some(format
!("{} {{ .. }}", path_str
));
1560 ast
::StructRest
::Rest(_
) | ast
::StructRest
::Base(_
) => true,
1564 // Foo { a: Foo } - indent is +3, width is -5.
1565 let (h_shape
, v_shape
) = struct_lit_shape(shape
, context
, path_str
.len() + 3, 2)?
;
1567 let one_line_width
= h_shape
.map_or(0, |shape
| shape
.width
);
1568 let body_lo
= context
.snippet_provider
.span_after(span
, "{");
1569 let fields_str
= if struct_lit_can_be_aligned(fields
, has_base_or_rest
)
1570 && context
.config
.struct_field_align_threshold() > 0
1572 rewrite_with_alignment(
1576 mk_sp(body_lo
, span
.hi()),
1580 let field_iter
= fields
.iter().map(StructLitField
::Regular
).chain(
1582 ast
::StructRest
::Base(expr
) => Some(StructLitField
::Base(&**expr
)),
1583 ast
::StructRest
::Rest(span
) => Some(StructLitField
::Rest(*span
)),
1584 ast
::StructRest
::None
=> None
,
1589 let span_lo
= |item
: &StructLitField
<'_
>| match *item
{
1590 StructLitField
::Regular(field
) => field
.span().lo(),
1591 StructLitField
::Base(expr
) => {
1592 let last_field_hi
= fields
.last().map_or(span
.lo(), |field
| field
.span
.hi());
1593 let snippet
= context
.snippet(mk_sp(last_field_hi
, expr
.span
.lo()));
1594 let pos
= snippet
.find_uncommented("..").unwrap();
1595 last_field_hi
+ BytePos(pos
as u32)
1597 StructLitField
::Rest(span
) => span
.lo(),
1599 let span_hi
= |item
: &StructLitField
<'_
>| match *item
{
1600 StructLitField
::Regular(field
) => field
.span().hi(),
1601 StructLitField
::Base(expr
) => expr
.span
.hi(),
1602 StructLitField
::Rest(span
) => span
.hi(),
1604 let rewrite
= |item
: &StructLitField
<'_
>| match *item
{
1605 StructLitField
::Regular(field
) => {
1606 // The 1 taken from the v_budget is for the comma.
1607 rewrite_field(context
, field
, v_shape
.sub_width(1)?
, 0)
1609 StructLitField
::Base(expr
) => {
1611 expr
.rewrite(context
, v_shape
.offset_left(2)?
)
1612 .map(|s
| format
!("..{}", s
))
1614 StructLitField
::Rest(_
) => Some("..".to_owned()),
1617 let items
= itemize_list(
1618 context
.snippet_provider
,
1629 let item_vec
= items
.collect
::<Vec
<_
>>();
1631 let tactic
= struct_lit_tactic(h_shape
, context
, &item_vec
);
1632 let nested_shape
= shape_for_tactic(tactic
, h_shape
, v_shape
);
1634 let ends_with_comma
= span_ends_with_comma(context
, span
);
1635 let force_no_trailing_comma
= context
.inside_macro() && !ends_with_comma
;
1637 let fmt
= struct_lit_formatting(
1641 force_no_trailing_comma
|| has_base_or_rest
|| !context
.use_block_indent(),
1644 write_list(&item_vec
, &fmt
)?
1648 wrap_struct_field(context
, attrs
, &fields_str
, shape
, v_shape
, one_line_width
)?
;
1649 Some(format
!("{} {{{}}}", path_str
, fields_str
))
1651 // FIXME if context.config.indent_style() == Visual, but we run out
1652 // of space, we should fall back to BlockIndent.
1655 pub(crate) fn wrap_struct_field(
1656 context
: &RewriteContext
<'_
>,
1657 attrs
: &[ast
::Attribute
],
1660 nested_shape
: Shape
,
1661 one_line_width
: usize,
1662 ) -> Option
<String
> {
1663 let should_vertical
= context
.config
.indent_style() == IndentStyle
::Block
1664 && (fields_str
.contains('
\n'
)
1665 || !context
.config
.struct_lit_single_line()
1666 || fields_str
.len() > one_line_width
);
1668 let inner_attrs
= &inner_attributes(attrs
);
1669 if inner_attrs
.is_empty() {
1670 if should_vertical
{
1673 nested_shape
.indent
.to_string_with_newline(context
.config
),
1675 shape
.indent
.to_string_with_newline(context
.config
)
1678 // One liner or visual indent.
1679 Some(format
!(" {} ", fields_str
))
1684 nested_shape
.indent
.to_string_with_newline(context
.config
),
1685 inner_attrs
.rewrite(context
, shape
)?
,
1686 nested_shape
.indent
.to_string_with_newline(context
.config
),
1688 shape
.indent
.to_string_with_newline(context
.config
)
1693 pub(crate) fn struct_lit_field_separator(config
: &Config
) -> &str {
1694 colon_spaces(config
)
1697 pub(crate) fn rewrite_field(
1698 context
: &RewriteContext
<'_
>,
1699 field
: &ast
::ExprField
,
1701 prefix_max_width
: usize,
1702 ) -> Option
<String
> {
1703 if contains_skip(&field
.attrs
) {
1704 return Some(context
.snippet(field
.span()).to_owned());
1706 let mut attrs_str
= field
.attrs
.rewrite(context
, shape
)?
;
1707 if !attrs_str
.is_empty() {
1708 attrs_str
.push_str(&shape
.indent
.to_string_with_newline(context
.config
));
1710 let name
= context
.snippet(field
.ident
.span
);
1711 if field
.is_shorthand
{
1712 Some(attrs_str
+ name
)
1714 let mut separator
= String
::from(struct_lit_field_separator(context
.config
));
1715 for _
in 0..prefix_max_width
.saturating_sub(name
.len()) {
1716 separator
.push(' '
);
1718 let overhead
= name
.len() + separator
.len();
1719 let expr_shape
= shape
.offset_left(overhead
)?
;
1720 let expr
= field
.expr
.rewrite(context
, expr_shape
);
1723 Some(ref e
) if e
.as_str() == name
&& context
.config
.use_field_init_shorthand() => {
1724 Some(attrs_str
+ name
)
1726 Some(e
) => Some(format
!("{}{}{}{}", attrs_str
, name
, separator
, e
)),
1728 let expr_offset
= shape
.indent
.block_indent(context
.config
);
1731 .rewrite(context
, Shape
::indented(expr_offset
, context
.config
));
1737 expr_offset
.to_string(context
.config
),
1746 fn rewrite_tuple_in_visual_indent_style
<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>>(
1747 context
: &RewriteContext
<'_
>,
1748 mut items
: impl Iterator
<Item
= &'a T
>,
1751 is_singleton_tuple
: bool
,
1752 ) -> Option
<String
> {
1753 // In case of length 1, need a trailing comma
1754 debug
!("rewrite_tuple_in_visual_indent_style {:?}", shape
);
1755 if is_singleton_tuple
{
1757 let nested_shape
= shape
.sub_width(3)?
.visual_indent(1);
1761 .rewrite(context
, nested_shape
)
1762 .map(|s
| format
!("({},)", s
));
1765 let list_lo
= context
.snippet_provider
.span_after(span
, "(");
1766 let nested_shape
= shape
.sub_width(2)?
.visual_indent(1);
1767 let items
= itemize_list(
1768 context
.snippet_provider
,
1772 |item
| item
.span().lo(),
1773 |item
| item
.span().hi(),
1774 |item
| item
.rewrite(context
, nested_shape
),
1776 span
.hi() - BytePos(1),
1779 let item_vec
: Vec
<_
> = items
.collect();
1780 let tactic
= definitive_tactic(
1782 ListTactic
::HorizontalVertical
,
1786 let fmt
= ListFormatting
::new(nested_shape
, context
.config
)
1788 .ends_with_newline(false);
1789 let list_str
= write_list(&item_vec
, &fmt
)?
;
1791 Some(format
!("({})", list_str
))
1794 pub(crate) fn rewrite_tuple
<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>>(
1795 context
: &'a RewriteContext
<'_
>,
1796 items
: impl Iterator
<Item
= &'a T
>,
1799 is_singleton_tuple
: bool
,
1800 ) -> Option
<String
> {
1801 debug
!("rewrite_tuple {:?}", shape
);
1802 if context
.use_block_indent() {
1803 // We use the same rule as function calls for rewriting tuples.
1804 let force_tactic
= if context
.inside_macro() {
1805 if span_ends_with_comma(context
, span
) {
1806 Some(SeparatorTactic
::Always
)
1808 Some(SeparatorTactic
::Never
)
1810 } else if is_singleton_tuple
{
1811 Some(SeparatorTactic
::Always
)
1815 overflow
::rewrite_with_parens(
1821 context
.config
.fn_call_width(),
1825 rewrite_tuple_in_visual_indent_style(context
, items
, span
, shape
, is_singleton_tuple
)
1829 pub(crate) fn rewrite_unary_prefix
<R
: Rewrite
>(
1830 context
: &RewriteContext
<'_
>,
1834 ) -> Option
<String
> {
1836 .rewrite(context
, shape
.offset_left(prefix
.len())?
)
1837 .map(|r
| format
!("{}{}", prefix
, r
))
1840 // FIXME: this is probably not correct for multi-line Rewrites. we should
1841 // subtract suffix.len() from the last line budget, not the first!
1842 pub(crate) fn rewrite_unary_suffix
<R
: Rewrite
>(
1843 context
: &RewriteContext
<'_
>,
1847 ) -> Option
<String
> {
1849 .rewrite(context
, shape
.sub_width(suffix
.len())?
)
1856 fn rewrite_unary_op(
1857 context
: &RewriteContext
<'_
>,
1861 ) -> Option
<String
> {
1862 // For some reason, an UnOp is not spanned like BinOp!
1863 rewrite_unary_prefix(context
, ast
::UnOp
::to_string(op
), expr
, shape
)
1866 pub(crate) enum RhsAssignKind
<'ast
> {
1867 Expr(&'ast ast
::ExprKind
, Span
),
1872 impl<'ast
> RhsAssignKind
<'ast
> {
1873 // TODO(calebcartwright)
1874 // Preemptive addition for handling RHS with chains, not yet utilized.
1875 // It may make more sense to construct the chain first and then check
1876 // whether there are actually chain elements.
1878 fn is_chain(&self) -> bool
{
1880 RhsAssignKind
::Expr(kind
, _
) => {
1883 ast
::ExprKind
::Try(..)
1884 | ast
::ExprKind
::Field(..)
1885 | ast
::ExprKind
::MethodCall(..)
1886 | ast
::ExprKind
::Await(_
)
1894 fn rewrite_assignment(
1895 context
: &RewriteContext
<'_
>,
1898 op
: Option
<&ast
::BinOp
>,
1900 ) -> Option
<String
> {
1901 let operator_str
= match op
{
1902 Some(op
) => context
.snippet(op
.span
),
1906 // 1 = space between lhs and operator.
1907 let lhs_shape
= shape
.sub_width(operator_str
.len() + 1)?
;
1908 let lhs_str
= format
!("{} {}", lhs
.rewrite(context
, lhs_shape
)?
, operator_str
);
1914 &RhsAssignKind
::Expr(&rhs
.kind
, rhs
.span
),
1919 /// Controls where to put the rhs.
1920 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
1921 pub(crate) enum RhsTactics
{
1924 /// Put the rhs on the next line if it uses multiple line, without extra indentation.
1925 ForceNextLineWithoutIndent
,
1926 /// Allow overflowing max width if neither `Default` nor `ForceNextLineWithoutIndent`
1931 // The left hand side must contain everything up to, and including, the
1932 // assignment operator.
1933 pub(crate) fn rewrite_assign_rhs
<S
: Into
<String
>, R
: Rewrite
>(
1934 context
: &RewriteContext
<'_
>,
1937 rhs_kind
: &RhsAssignKind
<'_
>,
1939 ) -> Option
<String
> {
1940 rewrite_assign_rhs_with(context
, lhs
, ex
, shape
, rhs_kind
, RhsTactics
::Default
)
1943 pub(crate) fn rewrite_assign_rhs_expr
<R
: Rewrite
>(
1944 context
: &RewriteContext
<'_
>,
1948 rhs_kind
: &RhsAssignKind
<'_
>,
1949 rhs_tactics
: RhsTactics
,
1950 ) -> Option
<String
> {
1951 let last_line_width
= last_line_width(lhs
).saturating_sub(if lhs
.contains('
\n'
) {
1952 shape
.indent
.width()
1956 // 1 = space between operator and rhs.
1957 let orig_shape
= shape
.offset_left(last_line_width
+ 1).unwrap_or(Shape
{
1959 offset
: shape
.offset
+ last_line_width
+ 1,
1962 let has_rhs_comment
= if let Some(offset
) = lhs
.find_last_uncommented("=") {
1963 lhs
.trim_end().len() > offset
+ 1
1972 ex
.rewrite(context
, orig_shape
),
1979 pub(crate) fn rewrite_assign_rhs_with
<S
: Into
<String
>, R
: Rewrite
>(
1980 context
: &RewriteContext
<'_
>,
1984 rhs_kind
: &RhsAssignKind
<'_
>,
1985 rhs_tactics
: RhsTactics
,
1986 ) -> Option
<String
> {
1987 let lhs
= lhs
.into();
1988 let rhs
= rewrite_assign_rhs_expr(context
, &lhs
, ex
, shape
, rhs_kind
, rhs_tactics
)?
;
1992 pub(crate) fn rewrite_assign_rhs_with_comments
<S
: Into
<String
>, R
: Rewrite
>(
1993 context
: &RewriteContext
<'_
>,
1997 rhs_kind
: &RhsAssignKind
<'_
>,
1998 rhs_tactics
: RhsTactics
,
2001 ) -> Option
<String
> {
2002 let lhs
= lhs
.into();
2003 let contains_comment
= contains_comment(context
.snippet(between_span
));
2004 let shape
= if contains_comment
{
2005 shape
.block_left(context
.config
.tab_spaces())?
2009 let rhs
= rewrite_assign_rhs_expr(context
, &lhs
, ex
, shape
, rhs_kind
, rhs_tactics
)?
;
2011 if contains_comment
{
2012 let rhs
= rhs
.trim_start();
2013 combine_strs_with_missing_comments(context
, &lhs
, rhs
, between_span
, shape
, allow_extend
)
2019 fn choose_rhs
<R
: Rewrite
>(
2020 context
: &RewriteContext
<'_
>,
2023 orig_rhs
: Option
<String
>,
2024 _rhs_kind
: &RhsAssignKind
<'_
>,
2025 rhs_tactics
: RhsTactics
,
2026 has_rhs_comment
: bool
,
2027 ) -> Option
<String
> {
2029 Some(ref new_str
) if new_str
.is_empty() => Some(String
::new()),
2031 if !new_str
.contains('
\n'
) && unicode_str_width(new_str
) <= shape
.width
=>
2033 Some(format
!(" {}", new_str
))
2036 // Expression did not fit on the same line as the identifier.
2037 // Try splitting the line and see if that works better.
2038 let new_shape
= shape_from_rhs_tactic(context
, shape
, rhs_tactics
)?
;
2039 let new_rhs
= expr
.rewrite(context
, new_shape
);
2040 let new_indent_str
= &shape
2042 .block_indent(context
.config
)
2043 .to_string_with_newline(context
.config
);
2044 let before_space_str
= if has_rhs_comment { "" }
else { " " }
;
2046 match (orig_rhs
, new_rhs
) {
2047 (Some(ref orig_rhs
), Some(ref new_rhs
))
2048 if wrap_str(new_rhs
.clone(), context
.config
.max_width(), new_shape
)
2051 Some(format
!("{}{}", before_space_str
, orig_rhs
))
2053 (Some(ref orig_rhs
), Some(ref new_rhs
))
2054 if prefer_next_line(orig_rhs
, new_rhs
, rhs_tactics
) =>
2056 Some(format
!("{}{}", new_indent_str
, new_rhs
))
2058 (None
, Some(ref new_rhs
)) => Some(format
!("{}{}", new_indent_str
, new_rhs
)),
2059 (None
, None
) if rhs_tactics
== RhsTactics
::AllowOverflow
=> {
2060 let shape
= shape
.infinite_width();
2061 expr
.rewrite(context
, shape
)
2062 .map(|s
| format
!("{}{}", before_space_str
, s
))
2064 (None
, None
) => None
,
2065 (Some(orig_rhs
), _
) => Some(format
!("{}{}", before_space_str
, orig_rhs
)),
2071 fn shape_from_rhs_tactic(
2072 context
: &RewriteContext
<'_
>,
2074 rhs_tactic
: RhsTactics
,
2075 ) -> Option
<Shape
> {
2077 RhsTactics
::ForceNextLineWithoutIndent
=> shape
2078 .with_max_width(context
.config
)
2079 .sub_width(shape
.indent
.width()),
2080 RhsTactics
::Default
| RhsTactics
::AllowOverflow
=> {
2081 Shape
::indented(shape
.indent
.block_indent(context
.config
), context
.config
)
2082 .sub_width(shape
.rhs_overhead(context
.config
))
2087 /// Returns true if formatting next_line_rhs is better on a new line when compared to the
2088 /// original's line formatting.
2090 /// It is considered better if:
2091 /// 1. the tactic is ForceNextLineWithoutIndent
2092 /// 2. next_line_rhs doesn't have newlines
2093 /// 3. the original line has more newlines than next_line_rhs
2094 /// 4. the original formatting of the first line ends with `(`, `{`, or `[` and next_line_rhs
2096 pub(crate) fn prefer_next_line(
2098 next_line_rhs
: &str,
2099 rhs_tactics
: RhsTactics
,
2101 rhs_tactics
== RhsTactics
::ForceNextLineWithoutIndent
2102 || !next_line_rhs
.contains('
\n'
)
2103 || count_newlines(orig_rhs
) > count_newlines(next_line_rhs
) + 1
2104 || first_line_ends_with(orig_rhs
, '
('
) && !first_line_ends_with(next_line_rhs
, '
('
)
2105 || first_line_ends_with(orig_rhs
, '
{'
) && !first_line_ends_with(next_line_rhs
, '
{'
)
2106 || first_line_ends_with(orig_rhs
, '
['
) && !first_line_ends_with(next_line_rhs
, '
['
)
2109 fn rewrite_expr_addrof(
2110 context
: &RewriteContext
<'_
>,
2111 borrow_kind
: ast
::BorrowKind
,
2112 mutability
: ast
::Mutability
,
2115 ) -> Option
<String
> {
2116 let operator_str
= match (mutability
, borrow_kind
) {
2117 (ast
::Mutability
::Not
, ast
::BorrowKind
::Ref
) => "&",
2118 (ast
::Mutability
::Not
, ast
::BorrowKind
::Raw
) => "&raw const ",
2119 (ast
::Mutability
::Mut
, ast
::BorrowKind
::Ref
) => "&mut ",
2120 (ast
::Mutability
::Mut
, ast
::BorrowKind
::Raw
) => "&raw mut ",
2122 rewrite_unary_prefix(context
, operator_str
, expr
, shape
)
2125 pub(crate) fn is_method_call(expr
: &ast
::Expr
) -> bool
{
2127 ast
::ExprKind
::MethodCall(..) => true,
2128 ast
::ExprKind
::AddrOf(_
, _
, ref expr
)
2129 | ast
::ExprKind
::Box(ref expr
)
2130 | ast
::ExprKind
::Cast(ref expr
, _
)
2131 | ast
::ExprKind
::Try(ref expr
)
2132 | ast
::ExprKind
::Unary(_
, ref expr
) => is_method_call(expr
),
2139 use super::last_line_offsetted
;
2142 fn test_last_line_offsetted() {
2143 let lines
= "one\n two";
2144 assert_eq
!(last_line_offsetted(2, lines
), true);
2145 assert_eq
!(last_line_offsetted(4, lines
), false);
2146 assert_eq
!(last_line_offsetted(6, lines
), false);
2148 let lines
= "one two";
2149 assert_eq
!(last_line_offsetted(2, lines
), false);
2150 assert_eq
!(last_line_offsetted(0, lines
), false);
2152 let lines
= "\ntwo";
2153 assert_eq
!(last_line_offsetted(2, lines
), false);
2154 assert_eq
!(last_line_offsetted(0, lines
), false);
2156 let lines
= "one\n two three";
2157 assert_eq
!(last_line_offsetted(2, lines
), true);
2158 let lines
= "one\n two three";
2159 assert_eq
!(last_line_offsetted(2, lines
), false);