rustfmt_nightly/
expr.rs

1use std::borrow::Cow;
2use std::cmp::min;
3
4use itertools::Itertools;
5use rustc_ast::token::{Delimiter, Lit, LitKind};
6use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token};
7use rustc_span::{BytePos, Span};
8use tracing::debug;
9
10use crate::chains::rewrite_chain;
11use crate::closures;
12use crate::comment::{
13    CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment,
14    recover_comment_removed, rewrite_comment, rewrite_missing_comment,
15};
16use crate::config::lists::*;
17use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition};
18use crate::lists::{
19    ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic,
20    struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list,
21};
22use crate::macros::{MacroPosition, rewrite_macro};
23use crate::matches::rewrite_match;
24use crate::overflow::{self, IntoOverflowableItem, OverflowableItem};
25use crate::pairs::{PairParts, rewrite_all_pairs, rewrite_pair};
26use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
27use crate::shape::{Indent, Shape};
28use crate::source_map::{LineRangeUtils, SpanUtils};
29use crate::spanned::Spanned;
30use crate::stmt;
31use crate::string::{StringFormat, rewrite_string};
32use crate::types::{PathContext, rewrite_path};
33use crate::utils::{
34    colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with,
35    inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes,
36    semicolon_for_expr, unicode_str_width, wrap_str,
37};
38use crate::vertical::rewrite_with_alignment;
39use crate::visitor::FmtVisitor;
40
41impl Rewrite for ast::Expr {
42    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
43        self.rewrite_result(context, shape).ok()
44    }
45
46    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
47        format_expr(self, ExprType::SubExpression, context, shape)
48    }
49}
50
51#[derive(Copy, Clone, PartialEq)]
52pub(crate) enum ExprType {
53    Statement,
54    SubExpression,
55}
56
57pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool {
58    matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.'))
59}
60
61pub(crate) fn format_expr(
62    expr: &ast::Expr,
63    expr_type: ExprType,
64    context: &RewriteContext<'_>,
65    shape: Shape,
66) -> RewriteResult {
67    skip_out_of_file_lines_range_err!(context, expr.span);
68
69    if contains_skip(&*expr.attrs) {
70        return Ok(context.snippet(expr.span()).to_owned());
71    }
72    let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) {
73        shape.sub_width(1).max_width_error(shape.width, expr.span)?
74    } else {
75        shape
76    };
77
78    let expr_rw = match expr.kind {
79        ast::ExprKind::Array(ref expr_vec) => rewrite_array(
80            "",
81            expr_vec.iter(),
82            expr.span,
83            context,
84            shape,
85            choose_separator_tactic(context, expr.span),
86            None,
87        ),
88        ast::ExprKind::Lit(token_lit) => {
89            if let Ok(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) {
90                Ok(expr_rw)
91            } else {
92                if let LitKind::StrRaw(_) = token_lit.kind {
93                    Ok(context.snippet(expr.span).trim().into())
94                } else {
95                    Err(RewriteError::Unknown)
96                }
97            }
98        }
99        ast::ExprKind::Call(ref callee, ref args) => {
100            let inner_span = mk_sp(callee.span.hi(), expr.span.hi());
101            let callee_str = callee.rewrite_result(context, shape)?;
102            rewrite_call(context, &callee_str, args, inner_span, shape)
103        }
104        ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span),
105        ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
106            // FIXME: format comments between operands and operator
107            rewrite_all_pairs(expr, shape, context).or_else(|_| {
108                rewrite_pair(
109                    &**lhs,
110                    &**rhs,
111                    PairParts::infix(&format!(" {} ", context.snippet(op.span))),
112                    context,
113                    shape,
114                    context.config.binop_separator(),
115                )
116            })
117        }
118        ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
119        ast::ExprKind::Struct(ref struct_expr) => {
120            let ast::StructExpr {
121                qself,
122                fields,
123                path,
124                rest,
125            } = &**struct_expr;
126            rewrite_struct_lit(
127                context,
128                path,
129                qself,
130                fields,
131                rest,
132                &expr.attrs,
133                expr.span,
134                shape,
135            )
136        }
137        ast::ExprKind::Tup(ref items) => {
138            rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1)
139        }
140        ast::ExprKind::Let(ref pat, ref expr, _span, _) => rewrite_let(context, shape, pat, expr),
141        ast::ExprKind::If(..)
142        | ast::ExprKind::ForLoop { .. }
143        | ast::ExprKind::Loop(..)
144        | ast::ExprKind::While(..) => to_control_flow(expr, expr_type)
145            .unknown_error()
146            .and_then(|control_flow| control_flow.rewrite_result(context, shape)),
147        ast::ExprKind::ConstBlock(ref anon_const) => {
148            let rewrite = match anon_const.value.kind {
149                ast::ExprKind::Block(ref block, opt_label) => {
150                    // Inner attributes are associated with the `ast::ExprKind::ConstBlock` node,
151                    // not the `ast::Block` node we're about to rewrite. To prevent dropping inner
152                    // attributes call `rewrite_block` directly.
153                    // See https://github.com/rust-lang/rustfmt/issues/6158
154                    rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)?
155                }
156                _ => anon_const.rewrite_result(context, shape)?,
157            };
158            Ok(format!("const {}", rewrite))
159        }
160        ast::ExprKind::Block(ref block, opt_label) => {
161            match expr_type {
162                ExprType::Statement => {
163                    if is_unsafe_block(block) {
164                        rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)
165                    } else if let Some(rw) =
166                        rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape)
167                    {
168                        // Rewrite block without trying to put it in a single line.
169                        Ok(rw)
170                    } else {
171                        let prefix = block_prefix(context, block, shape)?;
172
173                        rewrite_block_with_visitor(
174                            context,
175                            &prefix,
176                            block,
177                            Some(&expr.attrs),
178                            opt_label,
179                            shape,
180                            true,
181                        )
182                    }
183                }
184                ExprType::SubExpression => {
185                    rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)
186                }
187            }
188        }
189        ast::ExprKind::Match(ref cond, ref arms, kind) => {
190            rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind)
191        }
192        ast::ExprKind::Path(ref qself, ref path) => {
193            rewrite_path(context, PathContext::Expr, qself, path, shape)
194        }
195        ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
196            rewrite_assignment(context, lhs, rhs, None, shape)
197        }
198        ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
199            rewrite_assignment(context, lhs, rhs, Some(op), shape)
200        }
201        ast::ExprKind::Continue(ref opt_label) => {
202            let id_str = match *opt_label {
203                Some(label) => format!(" {}", label.ident),
204                None => String::new(),
205            };
206            Ok(format!("continue{id_str}"))
207        }
208        ast::ExprKind::Break(ref opt_label, ref opt_expr) => {
209            let id_str = match *opt_label {
210                Some(label) => format!(" {}", label.ident),
211                None => String::new(),
212            };
213
214            if let Some(ref expr) = *opt_expr {
215                rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape)
216            } else {
217                Ok(format!("break{id_str}"))
218            }
219        }
220        ast::ExprKind::Yield(ref opt_expr) => {
221            if let Some(ref expr) = *opt_expr {
222                rewrite_unary_prefix(context, "yield ", &**expr, shape)
223            } else {
224                Ok("yield".to_string())
225            }
226        }
227        ast::ExprKind::Closure(ref cl) => closures::rewrite_closure(
228            &cl.binder,
229            cl.constness,
230            cl.capture_clause,
231            &cl.coroutine_kind,
232            cl.movability,
233            &cl.fn_decl,
234            &cl.body,
235            expr.span,
236            context,
237            shape,
238        ),
239        ast::ExprKind::Try(..)
240        | ast::ExprKind::Field(..)
241        | ast::ExprKind::MethodCall(..)
242        | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape),
243        ast::ExprKind::MacCall(ref mac) => {
244            rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| {
245                wrap_str(
246                    context.snippet(expr.span).to_owned(),
247                    context.config.max_width(),
248                    shape,
249                )
250                .max_width_error(shape.width, expr.span)
251            })
252        }
253        ast::ExprKind::Ret(None) => Ok("return".to_owned()),
254        ast::ExprKind::Ret(Some(ref expr)) => {
255            rewrite_unary_prefix(context, "return ", &**expr, shape)
256        }
257        ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape),
258        ast::ExprKind::Yeet(None) => Ok("do yeet".to_owned()),
259        ast::ExprKind::Yeet(Some(ref expr)) => {
260            rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
261        }
262        ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => {
263            rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape)
264        }
265        ast::ExprKind::Cast(ref expr, ref ty) => rewrite_pair(
266            &**expr,
267            &**ty,
268            PairParts::infix(" as "),
269            context,
270            shape,
271            SeparatorPlace::Front,
272        ),
273        ast::ExprKind::Index(ref expr, ref index, _) => {
274            rewrite_index(&**expr, &**index, context, shape)
275        }
276        ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair(
277            &**expr,
278            &*repeats.value,
279            PairParts::new("[", "; ", "]"),
280            context,
281            shape,
282            SeparatorPlace::Back,
283        ),
284        ast::ExprKind::Range(ref lhs, ref rhs, limits) => {
285            let delim = match limits {
286                ast::RangeLimits::HalfOpen => "..",
287                ast::RangeLimits::Closed => "..=",
288            };
289
290            fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool {
291                match lhs.kind {
292                    ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit),
293                    ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr),
294                    ast::ExprKind::Binary(_, _, ref rhs_expr) => {
295                        needs_space_before_range(context, rhs_expr)
296                    }
297                    _ => false,
298                }
299            }
300
301            fn needs_space_after_range(rhs: &ast::Expr) -> bool {
302                // Don't format `.. ..` into `....`, which is invalid.
303                //
304                // This check is unnecessary for `lhs`, because a range
305                // starting from another range needs parentheses as `(x ..) ..`
306                // (`x .. ..` is a range from `x` to `..`).
307                matches!(rhs.kind, ast::ExprKind::Range(None, _, _))
308            }
309
310            let default_sp_delim = |lhs: Option<&ast::Expr>, rhs: Option<&ast::Expr>| {
311                let space_if = |b: bool| if b { " " } else { "" };
312
313                format!(
314                    "{}{}{}",
315                    lhs.map_or("", |lhs| space_if(needs_space_before_range(context, lhs))),
316                    delim,
317                    rhs.map_or("", |rhs| space_if(needs_space_after_range(rhs))),
318                )
319            };
320
321            match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
322                (Some(lhs), Some(rhs)) => {
323                    let sp_delim = if context.config.spaces_around_ranges() {
324                        format!(" {delim} ")
325                    } else {
326                        default_sp_delim(Some(lhs), Some(rhs))
327                    };
328                    rewrite_pair(
329                        &*lhs,
330                        &*rhs,
331                        PairParts::infix(&sp_delim),
332                        context,
333                        shape,
334                        context.config.binop_separator(),
335                    )
336                }
337                (None, Some(rhs)) => {
338                    let sp_delim = if context.config.spaces_around_ranges() {
339                        format!("{delim} ")
340                    } else {
341                        default_sp_delim(None, Some(rhs))
342                    };
343                    rewrite_unary_prefix(context, &sp_delim, &*rhs, shape)
344                }
345                (Some(lhs), None) => {
346                    let sp_delim = if context.config.spaces_around_ranges() {
347                        format!(" {delim}")
348                    } else {
349                        default_sp_delim(Some(lhs), None)
350                    };
351                    rewrite_unary_suffix(context, &sp_delim, &*lhs, shape)
352                }
353                (None, None) => Ok(delim.to_owned()),
354            }
355        }
356        // We do not format these expressions yet, but they should still
357        // satisfy our width restrictions.
358        // Style Guide RFC for InlineAsm variant pending
359        // https://github.com/rust-dev-tools/fmt-rfcs/issues/152
360        ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()),
361        ast::ExprKind::TryBlock(ref block) => {
362            if let rw @ Ok(_) =
363                rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape)
364            {
365                rw
366            } else {
367                // 9 = `try `
368                let budget = shape.width.saturating_sub(9);
369                Ok(format!(
370                    "{}{}",
371                    "try ",
372                    rewrite_block(
373                        block,
374                        Some(&expr.attrs),
375                        None,
376                        context,
377                        Shape::legacy(budget, shape.indent)
378                    )?
379                ))
380            }
381        }
382        ast::ExprKind::Gen(capture_by, ref block, ref kind, _) => {
383            let mover = if matches!(capture_by, ast::CaptureBy::Value { .. }) {
384                "move "
385            } else {
386                ""
387            };
388            if let rw @ Ok(_) = rewrite_single_line_block(
389                context,
390                format!("{kind} {mover}").as_str(),
391                block,
392                Some(&expr.attrs),
393                None,
394                shape,
395            ) {
396                rw
397            } else {
398                // 6 = `async `
399                let budget = shape.width.saturating_sub(6);
400                Ok(format!(
401                    "{kind} {mover}{}",
402                    rewrite_block(
403                        block,
404                        Some(&expr.attrs),
405                        None,
406                        context,
407                        Shape::legacy(budget, shape.indent)
408                    )?
409                ))
410            }
411        }
412        ast::ExprKind::Underscore => Ok("_".to_owned()),
413        ast::ExprKind::FormatArgs(..)
414        | ast::ExprKind::Type(..)
415        | ast::ExprKind::IncludedBytes(..)
416        | ast::ExprKind::OffsetOf(..)
417        | ast::ExprKind::UnsafeBinderCast(..) => {
418            // These don't normally occur in the AST because macros aren't expanded. However,
419            // rustfmt tries to parse macro arguments when formatting macros, so it's not totally
420            // impossible for rustfmt to come across one of these nodes when formatting a file.
421            // Also, rustfmt might get passed the output from `-Zunpretty=expanded`.
422            Err(RewriteError::Unknown)
423        }
424        ast::ExprKind::Err(_) | ast::ExprKind::Dummy => Err(RewriteError::Unknown),
425    };
426
427    expr_rw
428        .map(|expr_str| recover_comment_removed(expr_str, expr.span, context))
429        .and_then(|expr_str| {
430            let attrs = outer_attributes(&expr.attrs);
431            let attrs_str = attrs.rewrite_result(context, shape)?;
432            let span = mk_sp(
433                attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()),
434                expr.span.lo(),
435            );
436            combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false)
437        })
438}
439
440pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>(
441    name: &'a str,
442    exprs: impl Iterator<Item = &'a T>,
443    span: Span,
444    context: &'a RewriteContext<'_>,
445    shape: Shape,
446    force_separator_tactic: Option<SeparatorTactic>,
447    delim_token: Option<Delimiter>,
448) -> RewriteResult {
449    overflow::rewrite_with_square_brackets(
450        context,
451        name,
452        exprs,
453        shape,
454        span,
455        force_separator_tactic,
456        delim_token,
457    )
458}
459
460fn rewrite_empty_block(
461    context: &RewriteContext<'_>,
462    block: &ast::Block,
463    attrs: Option<&[ast::Attribute]>,
464    label: Option<ast::Label>,
465    prefix: &str,
466    shape: Shape,
467) -> Option<String> {
468    if block_has_statements(block) {
469        return None;
470    }
471
472    let label_str = rewrite_label(context, label);
473    if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) {
474        return None;
475    }
476
477    if !block_contains_comment(context, block) && shape.width >= 2 {
478        return Some(format!("{prefix}{label_str}{{}}"));
479    }
480
481    // If a block contains only a single-line comment, then leave it on one line.
482    let user_str = context.snippet(block.span);
483    let user_str = user_str.trim();
484    if user_str.starts_with('{') && user_str.ends_with('}') {
485        let comment_str = user_str[1..user_str.len() - 1].trim();
486        if block.stmts.is_empty()
487            && !comment_str.contains('\n')
488            && !comment_str.starts_with("//")
489            && comment_str.len() + 4 <= shape.width
490        {
491            return Some(format!("{prefix}{label_str}{{ {comment_str} }}"));
492        }
493    }
494
495    None
496}
497
498fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> RewriteResult {
499    Ok(match block.rules {
500        ast::BlockCheckMode::Unsafe(..) => {
501            let snippet = context.snippet(block.span);
502            let open_pos = snippet.find_uncommented("{").unknown_error()?;
503            // Extract comment between unsafe and block start.
504            let trimmed = &snippet[6..open_pos].trim();
505
506            if !trimmed.is_empty() {
507                // 9 = "unsafe  {".len(), 7 = "unsafe ".len()
508                let budget = shape
509                    .width
510                    .checked_sub(9)
511                    .max_width_error(shape.width, block.span)?;
512                format!(
513                    "unsafe {} ",
514                    rewrite_comment(
515                        trimmed,
516                        true,
517                        Shape::legacy(budget, shape.indent + 7),
518                        context.config,
519                    )?
520                )
521            } else {
522                "unsafe ".to_owned()
523            }
524        }
525        ast::BlockCheckMode::Default => String::new(),
526    })
527}
528
529fn rewrite_single_line_block(
530    context: &RewriteContext<'_>,
531    prefix: &str,
532    block: &ast::Block,
533    attrs: Option<&[ast::Attribute]>,
534    label: Option<ast::Label>,
535    shape: Shape,
536) -> RewriteResult {
537    if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) {
538        let expr_shape = shape
539            .offset_left(last_line_width(prefix))
540            .max_width_error(shape.width, block_expr.span())?;
541        let expr_str = block_expr.rewrite_result(context, expr_shape)?;
542        let label_str = rewrite_label(context, label);
543        let result = format!("{prefix}{label_str}{{ {expr_str} }}");
544        if result.len() <= shape.width && !result.contains('\n') {
545            return Ok(result);
546        }
547    }
548    Err(RewriteError::Unknown)
549}
550
551pub(crate) fn rewrite_block_with_visitor(
552    context: &RewriteContext<'_>,
553    prefix: &str,
554    block: &ast::Block,
555    attrs: Option<&[ast::Attribute]>,
556    label: Option<ast::Label>,
557    shape: Shape,
558    has_braces: bool,
559) -> RewriteResult {
560    if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, prefix, shape) {
561        return Ok(rw_str);
562    }
563
564    let mut visitor = FmtVisitor::from_context(context);
565    visitor.block_indent = shape.indent;
566    visitor.is_if_else_block = context.is_if_else_block();
567    match (block.rules, label) {
568        (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => {
569            let snippet = context.snippet(block.span);
570            let open_pos = snippet.find_uncommented("{").unknown_error()?;
571            visitor.last_pos = block.span.lo() + BytePos(open_pos as u32)
572        }
573        (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(),
574    }
575
576    let inner_attrs = attrs.map(inner_attributes);
577    let label_str = rewrite_label(context, label);
578    visitor.visit_block(block, inner_attrs.as_deref(), has_braces);
579    let visitor_context = visitor.get_context();
580    context
581        .skipped_range
582        .borrow_mut()
583        .append(&mut visitor_context.skipped_range.borrow_mut());
584    Ok(format!("{}{}{}", prefix, label_str, visitor.buffer))
585}
586
587impl Rewrite for ast::Block {
588    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
589        self.rewrite_result(context, shape).ok()
590    }
591
592    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
593        rewrite_block(self, None, None, context, shape)
594    }
595}
596
597fn rewrite_block(
598    block: &ast::Block,
599    attrs: Option<&[ast::Attribute]>,
600    label: Option<ast::Label>,
601    context: &RewriteContext<'_>,
602    shape: Shape,
603) -> RewriteResult {
604    rewrite_block_inner(block, attrs, label, true, context, shape)
605}
606
607fn rewrite_block_inner(
608    block: &ast::Block,
609    attrs: Option<&[ast::Attribute]>,
610    label: Option<ast::Label>,
611    allow_single_line: bool,
612    context: &RewriteContext<'_>,
613    shape: Shape,
614) -> RewriteResult {
615    let prefix = block_prefix(context, block, shape)?;
616
617    // shape.width is used only for the single line case: either the empty block `{}`,
618    // or an unsafe expression `unsafe { e }`.
619    if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) {
620        return Ok(rw_str);
621    }
622
623    let result_str =
624        rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true)?;
625    if allow_single_line && result_str.lines().count() <= 3 {
626        if let rw @ Ok(_) = rewrite_single_line_block(context, &prefix, block, attrs, label, shape)
627        {
628            return rw;
629        }
630    }
631    Ok(result_str)
632}
633
634/// Rewrite the divergent block of a `let-else` statement.
635pub(crate) fn rewrite_let_else_block(
636    block: &ast::Block,
637    allow_single_line: bool,
638    context: &RewriteContext<'_>,
639    shape: Shape,
640) -> RewriteResult {
641    rewrite_block_inner(block, None, None, allow_single_line, context, shape)
642}
643
644// Rewrite condition if the given expression has one.
645pub(crate) fn rewrite_cond(
646    context: &RewriteContext<'_>,
647    expr: &ast::Expr,
648    shape: Shape,
649) -> Option<String> {
650    match expr.kind {
651        ast::ExprKind::Match(ref cond, _, MatchKind::Prefix) => {
652            // `match `cond` {`
653            let cond_shape = match context.config.indent_style() {
654                IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?,
655                IndentStyle::Block => shape.offset_left(8)?,
656            };
657            cond.rewrite(context, cond_shape)
658        }
659        _ => to_control_flow(expr, ExprType::SubExpression).and_then(|control_flow| {
660            let alt_block_sep =
661                String::from("\n") + &shape.indent.block_only().to_string(context.config);
662            control_flow
663                .rewrite_cond(context, shape, &alt_block_sep)
664                .ok()
665                .map(|rw| rw.0)
666        }),
667    }
668}
669
670// Abstraction over control flow expressions
671#[derive(Debug)]
672struct ControlFlow<'a> {
673    cond: Option<&'a ast::Expr>,
674    block: &'a ast::Block,
675    else_block: Option<&'a ast::Expr>,
676    label: Option<ast::Label>,
677    pat: Option<&'a ast::Pat>,
678    keyword: &'a str,
679    matcher: &'a str,
680    connector: &'a str,
681    allow_single_line: bool,
682    // HACK: `true` if this is an `if` expression in an `else if`.
683    nested_if: bool,
684    span: Span,
685}
686
687fn extract_pats_and_cond(expr: &ast::Expr) -> (Option<&ast::Pat>, &ast::Expr) {
688    match expr.kind {
689        ast::ExprKind::Let(ref pat, ref cond, _, _) => (Some(pat), cond),
690        _ => (None, expr),
691    }
692}
693
694// FIXME: Refactor this.
695fn to_control_flow(expr: &ast::Expr, expr_type: ExprType) -> Option<ControlFlow<'_>> {
696    match expr.kind {
697        ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
698            let (pat, cond) = extract_pats_and_cond(cond);
699            Some(ControlFlow::new_if(
700                cond,
701                pat,
702                if_block,
703                else_block.as_ref().map(|e| &**e),
704                expr_type == ExprType::SubExpression,
705                false,
706                expr.span,
707            ))
708        }
709        ast::ExprKind::ForLoop {
710            ref pat,
711            ref iter,
712            ref body,
713            label,
714            kind,
715        } => Some(ControlFlow::new_for(
716            pat, iter, body, label, expr.span, kind,
717        )),
718        ast::ExprKind::Loop(ref block, label, _) => {
719            Some(ControlFlow::new_loop(block, label, expr.span))
720        }
721        ast::ExprKind::While(ref cond, ref block, label) => {
722            let (pat, cond) = extract_pats_and_cond(cond);
723            Some(ControlFlow::new_while(pat, cond, block, label, expr.span))
724        }
725        _ => None,
726    }
727}
728
729fn choose_matcher(pat: Option<&ast::Pat>) -> &'static str {
730    pat.map_or("", |_| "let")
731}
732
733impl<'a> ControlFlow<'a> {
734    fn new_if(
735        cond: &'a ast::Expr,
736        pat: Option<&'a ast::Pat>,
737        block: &'a ast::Block,
738        else_block: Option<&'a ast::Expr>,
739        allow_single_line: bool,
740        nested_if: bool,
741        span: Span,
742    ) -> ControlFlow<'a> {
743        let matcher = choose_matcher(pat);
744        ControlFlow {
745            cond: Some(cond),
746            block,
747            else_block,
748            label: None,
749            pat,
750            keyword: "if",
751            matcher,
752            connector: " =",
753            allow_single_line,
754            nested_if,
755            span,
756        }
757    }
758
759    fn new_loop(block: &'a ast::Block, label: Option<ast::Label>, span: Span) -> ControlFlow<'a> {
760        ControlFlow {
761            cond: None,
762            block,
763            else_block: None,
764            label,
765            pat: None,
766            keyword: "loop",
767            matcher: "",
768            connector: "",
769            allow_single_line: false,
770            nested_if: false,
771            span,
772        }
773    }
774
775    fn new_while(
776        pat: Option<&'a ast::Pat>,
777        cond: &'a ast::Expr,
778        block: &'a ast::Block,
779        label: Option<ast::Label>,
780        span: Span,
781    ) -> ControlFlow<'a> {
782        let matcher = choose_matcher(pat);
783        ControlFlow {
784            cond: Some(cond),
785            block,
786            else_block: None,
787            label,
788            pat,
789            keyword: "while",
790            matcher,
791            connector: " =",
792            allow_single_line: false,
793            nested_if: false,
794            span,
795        }
796    }
797
798    fn new_for(
799        pat: &'a ast::Pat,
800        cond: &'a ast::Expr,
801        block: &'a ast::Block,
802        label: Option<ast::Label>,
803        span: Span,
804        kind: ForLoopKind,
805    ) -> ControlFlow<'a> {
806        ControlFlow {
807            cond: Some(cond),
808            block,
809            else_block: None,
810            label,
811            pat: Some(pat),
812            keyword: match kind {
813                ForLoopKind::For => "for",
814                ForLoopKind::ForAwait => "for await",
815            },
816            matcher: "",
817            connector: " in",
818            allow_single_line: false,
819            nested_if: false,
820            span,
821        }
822    }
823
824    fn rewrite_single_line(
825        &self,
826        pat_expr_str: &str,
827        context: &RewriteContext<'_>,
828        width: usize,
829    ) -> Option<String> {
830        assert!(self.allow_single_line);
831        let else_block = self.else_block?;
832        let fixed_cost = self.keyword.len() + "  {  } else {  }".len();
833
834        if let ast::ExprKind::Block(ref else_node, _) = else_block.kind {
835            let (if_expr, else_expr) = match (
836                stmt::Stmt::from_simple_block(context, self.block, None),
837                stmt::Stmt::from_simple_block(context, else_node, None),
838                pat_expr_str.contains('\n'),
839            ) {
840                (Some(if_expr), Some(else_expr), false) => (if_expr, else_expr),
841                _ => return None,
842            };
843
844            let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?;
845            let if_str = if_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
846
847            let new_width = new_width.checked_sub(if_str.len())?;
848            let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?;
849
850            if if_str.contains('\n') || else_str.contains('\n') {
851                return None;
852            }
853
854            let result = format!(
855                "{} {} {{ {} }} else {{ {} }}",
856                self.keyword, pat_expr_str, if_str, else_str
857            );
858
859            if result.len() <= width {
860                return Some(result);
861            }
862        }
863
864        None
865    }
866}
867
868/// Returns `true` if the last line of pat_str has leading whitespace and it is wider than the
869/// shape's indent.
870fn last_line_offsetted(start_column: usize, pat_str: &str) -> bool {
871    let mut leading_whitespaces = 0;
872    for c in pat_str.chars().rev() {
873        match c {
874            '\n' => break,
875            _ if c.is_whitespace() => leading_whitespaces += 1,
876            _ => leading_whitespaces = 0,
877        }
878    }
879    leading_whitespaces > start_column
880}
881
882impl<'a> ControlFlow<'a> {
883    fn rewrite_pat_expr(
884        &self,
885        context: &RewriteContext<'_>,
886        expr: &ast::Expr,
887        shape: Shape,
888        offset: usize,
889    ) -> RewriteResult {
890        debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr);
891
892        let cond_shape = shape
893            .offset_left(offset)
894            .max_width_error(shape.width, expr.span)?;
895        if let Some(pat) = self.pat {
896            let matcher = if self.matcher.is_empty() {
897                self.matcher.to_owned()
898            } else {
899                format!("{} ", self.matcher)
900            };
901            let pat_shape = cond_shape
902                .offset_left(matcher.len())
903                .and_then(|s| s.sub_width(self.connector.len()))
904                .max_width_error(cond_shape.width, pat.span)?;
905            let pat_string = pat.rewrite_result(context, pat_shape)?;
906            let comments_lo = context
907                .snippet_provider
908                .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim());
909            let comments_span = mk_sp(comments_lo, expr.span.lo());
910            return rewrite_assign_rhs_with_comments(
911                context,
912                &format!("{}{}{}", matcher, pat_string, self.connector),
913                expr,
914                cond_shape,
915                &RhsAssignKind::Expr(&expr.kind, expr.span),
916                RhsTactics::Default,
917                comments_span,
918                true,
919            );
920        }
921
922        let expr_rw = expr.rewrite_result(context, cond_shape);
923        // The expression may (partially) fit on the current line.
924        // We do not allow splitting between `if` and condition.
925        if self.keyword == "if" || expr_rw.is_ok() {
926            return expr_rw;
927        }
928
929        // The expression won't fit on the current line, jump to next.
930        let nested_shape = shape
931            .block_indent(context.config.tab_spaces())
932            .with_max_width(context.config);
933        let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config);
934        expr.rewrite_result(context, nested_shape)
935            .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw))
936    }
937
938    fn rewrite_cond(
939        &self,
940        context: &RewriteContext<'_>,
941        shape: Shape,
942        alt_block_sep: &str,
943    ) -> Result<(String, usize), RewriteError> {
944        // Do not take the rhs overhead from the upper expressions into account
945        // when rewriting pattern.
946        let new_width = context.budget(shape.used_width());
947        let fresh_shape = Shape {
948            width: new_width,
949            ..shape
950        };
951        let constr_shape = if self.nested_if {
952            // We are part of an if-elseif-else chain. Our constraints are tightened.
953            // 7 = "} else " .len()
954            fresh_shape
955                .offset_left(7)
956                .max_width_error(fresh_shape.width, self.span)?
957        } else {
958            fresh_shape
959        };
960
961        let label_string = rewrite_label(context, self.label);
962        // 1 = space after keyword.
963        let offset = self.keyword.len() + label_string.len() + 1;
964
965        let pat_expr_string = match self.cond {
966            Some(cond) => self.rewrite_pat_expr(context, cond, constr_shape, offset)?,
967            None => String::new(),
968        };
969
970        let brace_overhead =
971            if context.config.control_brace_style() != ControlBraceStyle::AlwaysNextLine {
972                // 2 = ` {`
973                2
974            } else {
975                0
976            };
977        let one_line_budget = context
978            .config
979            .max_width()
980            .saturating_sub(constr_shape.used_width() + offset + brace_overhead);
981        let force_newline_brace = (pat_expr_string.contains('\n')
982            || pat_expr_string.len() > one_line_budget)
983            && (!last_line_extendable(&pat_expr_string)
984                || last_line_offsetted(shape.used_width(), &pat_expr_string));
985
986        // Try to format if-else on single line.
987        if self.allow_single_line && context.config.single_line_if_else_max_width() > 0 {
988            let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width);
989
990            if let Some(cond_str) = trial {
991                if cond_str.len() <= context.config.single_line_if_else_max_width() {
992                    return Ok((cond_str, 0));
993                }
994            }
995        }
996
997        let cond_span = if let Some(cond) = self.cond {
998            cond.span
999        } else {
1000            mk_sp(self.block.span.lo(), self.block.span.lo())
1001        };
1002
1003        // `for event in event`
1004        // Do not include label in the span.
1005        let lo = self
1006            .label
1007            .map_or(self.span.lo(), |label| label.ident.span.hi());
1008        let between_kwd_cond = mk_sp(
1009            context
1010                .snippet_provider
1011                .span_after(mk_sp(lo, self.span.hi()), self.keyword.trim()),
1012            if self.pat.is_none() {
1013                cond_span.lo()
1014            } else if self.matcher.is_empty() {
1015                self.pat.unwrap().span.lo()
1016            } else {
1017                context
1018                    .snippet_provider
1019                    .span_before(self.span, self.matcher.trim())
1020            },
1021        );
1022
1023        let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
1024
1025        let after_cond_comment =
1026            extract_comment(mk_sp(cond_span.hi(), self.block.span.lo()), context, shape);
1027
1028        let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
1029            ""
1030        } else if context.config.control_brace_style() == ControlBraceStyle::AlwaysNextLine
1031            || force_newline_brace
1032        {
1033            alt_block_sep
1034        } else {
1035            " "
1036        };
1037
1038        let used_width = if pat_expr_string.contains('\n') {
1039            last_line_width(&pat_expr_string)
1040        } else {
1041            // 2 = spaces after keyword and condition.
1042            label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
1043        };
1044
1045        Ok((
1046            format!(
1047                "{}{}{}{}{}",
1048                label_string,
1049                self.keyword,
1050                between_kwd_cond_comment.as_ref().map_or(
1051                    if pat_expr_string.is_empty() || pat_expr_string.starts_with('\n') {
1052                        ""
1053                    } else {
1054                        " "
1055                    },
1056                    |s| &**s,
1057                ),
1058                pat_expr_string,
1059                after_cond_comment.as_ref().map_or(block_sep, |s| &**s)
1060            ),
1061            used_width,
1062        ))
1063    }
1064}
1065
1066/// Rewrite the `else` keyword with surrounding comments.
1067///
1068/// force_newline_else: whether or not to rewrite the `else` keyword on a newline.
1069/// is_last: true if this is an `else` and `false` if this is an `else if` block.
1070/// context: rewrite context
1071/// span: Span between the end of the last expression and the start of the else block,
1072///       which contains the `else` keyword
1073/// shape: Shape
1074pub(crate) fn rewrite_else_kw_with_comments(
1075    force_newline_else: bool,
1076    is_last: bool,
1077    context: &RewriteContext<'_>,
1078    span: Span,
1079    shape: Shape,
1080) -> String {
1081    let else_kw_lo = context.snippet_provider.span_before(span, "else");
1082    let before_else_kw = mk_sp(span.lo(), else_kw_lo);
1083    let before_else_kw_comment = extract_comment(before_else_kw, context, shape);
1084
1085    let else_kw_hi = context.snippet_provider.span_after(span, "else");
1086    let after_else_kw = mk_sp(else_kw_hi, span.hi());
1087    let after_else_kw_comment = extract_comment(after_else_kw, context, shape);
1088
1089    let newline_sep = &shape.indent.to_string_with_newline(context.config);
1090    let before_sep = match context.config.control_brace_style() {
1091        _ if force_newline_else => newline_sep.as_ref(),
1092        ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => {
1093            newline_sep.as_ref()
1094        }
1095        ControlBraceStyle::AlwaysSameLine => " ",
1096    };
1097    let after_sep = match context.config.control_brace_style() {
1098        ControlBraceStyle::AlwaysNextLine if is_last => newline_sep.as_ref(),
1099        _ => " ",
1100    };
1101
1102    format!(
1103        "{}else{}",
1104        before_else_kw_comment.as_ref().map_or(before_sep, |s| &**s),
1105        after_else_kw_comment.as_ref().map_or(after_sep, |s| &**s),
1106    )
1107}
1108
1109impl<'a> Rewrite for ControlFlow<'a> {
1110    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1111        self.rewrite_result(context, shape).ok()
1112    }
1113
1114    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
1115        debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
1116
1117        let alt_block_sep = &shape.indent.to_string_with_newline(context.config);
1118        let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?;
1119        // If `used_width` is 0, it indicates that whole control flow is written in a single line.
1120        if used_width == 0 {
1121            return Ok(cond_str);
1122        }
1123
1124        let block_width = shape.width.saturating_sub(used_width);
1125        // This is used only for the empty block case: `{}`. So, we use 1 if we know
1126        // we should avoid the single line case.
1127        let block_width = if self.else_block.is_some() || self.nested_if {
1128            min(1, block_width)
1129        } else {
1130            block_width
1131        };
1132        let block_shape = Shape {
1133            width: block_width,
1134            ..shape
1135        };
1136        let block_str = {
1137            let old_val = context.is_if_else_block.replace(self.else_block.is_some());
1138            let result =
1139                rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true);
1140            context.is_if_else_block.replace(old_val);
1141            result?
1142        };
1143
1144        let mut result = format!("{cond_str}{block_str}");
1145
1146        if let Some(else_block) = self.else_block {
1147            let shape = Shape::indented(shape.indent, context.config);
1148            let mut last_in_chain = false;
1149            let rewrite = match else_block.kind {
1150                // If the else expression is another if-else expression, prevent it
1151                // from being formatted on a single line.
1152                // Note how we're passing the original shape, as the
1153                // cost of "else" should not cascade.
1154                ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
1155                    let (pats, cond) = extract_pats_and_cond(cond);
1156                    ControlFlow::new_if(
1157                        cond,
1158                        pats,
1159                        if_block,
1160                        next_else_block.as_ref().map(|e| &**e),
1161                        false,
1162                        true,
1163                        mk_sp(else_block.span.lo(), self.span.hi()),
1164                    )
1165                    .rewrite_result(context, shape)
1166                }
1167                _ => {
1168                    last_in_chain = true;
1169                    // When rewriting a block, the width is only used for single line
1170                    // blocks, passing 1 lets us avoid that.
1171                    let else_shape = Shape {
1172                        width: min(1, shape.width),
1173                        ..shape
1174                    };
1175                    format_expr(else_block, ExprType::Statement, context, else_shape)
1176                }
1177            };
1178
1179            let else_kw = rewrite_else_kw_with_comments(
1180                false,
1181                last_in_chain,
1182                context,
1183                self.block.span.between(else_block.span),
1184                shape,
1185            );
1186            result.push_str(&else_kw);
1187            result.push_str(&rewrite?);
1188        }
1189
1190        Ok(result)
1191    }
1192}
1193
1194fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option<ast::Label>) -> Cow<'static, str> {
1195    match opt_label {
1196        Some(label) => Cow::from(format!("{}: ", context.snippet(label.ident.span))),
1197        None => Cow::from(""),
1198    }
1199}
1200
1201fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1202    match rewrite_missing_comment(span, shape, context) {
1203        Ok(ref comment) if !comment.is_empty() => Some(format!(
1204            "{indent}{comment}{indent}",
1205            indent = shape.indent.to_string_with_newline(context.config)
1206        )),
1207        _ => None,
1208    }
1209}
1210
1211pub(crate) fn block_contains_comment(context: &RewriteContext<'_>, block: &ast::Block) -> bool {
1212    contains_comment(context.snippet(block.span))
1213}
1214
1215// Checks that a block contains no statements, an expression and no comments or
1216// attributes.
1217// FIXME: incorrectly returns false when comment is contained completely within
1218// the expression.
1219pub(crate) fn is_simple_block(
1220    context: &RewriteContext<'_>,
1221    block: &ast::Block,
1222    attrs: Option<&[ast::Attribute]>,
1223) -> bool {
1224    block.stmts.len() == 1
1225        && stmt_is_expr(&block.stmts[0])
1226        && !block_contains_comment(context, block)
1227        && attrs.map_or(true, |a| a.is_empty())
1228}
1229
1230/// Checks whether a block contains at most one statement or expression, and no
1231/// comments or attributes.
1232pub(crate) fn is_simple_block_stmt(
1233    context: &RewriteContext<'_>,
1234    block: &ast::Block,
1235    attrs: Option<&[ast::Attribute]>,
1236) -> bool {
1237    block.stmts.len() <= 1
1238        && !block_contains_comment(context, block)
1239        && attrs.map_or(true, |a| a.is_empty())
1240}
1241
1242fn block_has_statements(block: &ast::Block) -> bool {
1243    block
1244        .stmts
1245        .iter()
1246        .any(|stmt| !matches!(stmt.kind, ast::StmtKind::Empty))
1247}
1248
1249/// Checks whether a block contains no statements, expressions, comments, or
1250/// inner attributes.
1251pub(crate) fn is_empty_block(
1252    context: &RewriteContext<'_>,
1253    block: &ast::Block,
1254    attrs: Option<&[ast::Attribute]>,
1255) -> bool {
1256    !block_has_statements(block)
1257        && !block_contains_comment(context, block)
1258        && attrs.map_or(true, |a| inner_attributes(a).is_empty())
1259}
1260
1261pub(crate) fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
1262    matches!(stmt.kind, ast::StmtKind::Expr(..))
1263}
1264
1265pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool {
1266    matches!(block.rules, ast::BlockCheckMode::Unsafe(..))
1267}
1268
1269pub(crate) fn rewrite_literal(
1270    context: &RewriteContext<'_>,
1271    token_lit: token::Lit,
1272    span: Span,
1273    shape: Shape,
1274) -> RewriteResult {
1275    match token_lit.kind {
1276        token::LitKind::Str => rewrite_string_lit(context, span, shape),
1277        token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape),
1278        _ => wrap_str(
1279            context.snippet(span).to_owned(),
1280            context.config.max_width(),
1281            shape,
1282        )
1283        .max_width_error(shape.width, span),
1284    }
1285}
1286
1287fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> RewriteResult {
1288    let string_lit = context.snippet(span);
1289
1290    if !context.config.format_strings() {
1291        if string_lit
1292            .lines()
1293            .dropping_back(1)
1294            .all(|line| line.ends_with('\\'))
1295            && context.config.style_edition() >= StyleEdition::Edition2024
1296        {
1297            return Ok(string_lit.to_owned());
1298        } else {
1299            return wrap_str(string_lit.to_owned(), context.config.max_width(), shape)
1300                .max_width_error(shape.width, span);
1301        }
1302    }
1303
1304    // Remove the quote characters.
1305    let str_lit = &string_lit[1..string_lit.len() - 1];
1306
1307    rewrite_string(
1308        str_lit,
1309        &StringFormat::new(shape.visual_indent(0), context.config),
1310        shape.width.saturating_sub(2),
1311    )
1312    .max_width_error(shape.width, span)
1313}
1314
1315fn rewrite_int_lit(
1316    context: &RewriteContext<'_>,
1317    token_lit: token::Lit,
1318    span: Span,
1319    shape: Shape,
1320) -> RewriteResult {
1321    let symbol = token_lit.symbol.as_str();
1322
1323    if let Some(symbol_stripped) = symbol.strip_prefix("0x") {
1324        let hex_lit = match context.config.hex_literal_case() {
1325            HexLiteralCase::Preserve => None,
1326            HexLiteralCase::Upper => Some(symbol_stripped.to_ascii_uppercase()),
1327            HexLiteralCase::Lower => Some(symbol_stripped.to_ascii_lowercase()),
1328        };
1329        if let Some(hex_lit) = hex_lit {
1330            return wrap_str(
1331                format!(
1332                    "0x{}{}",
1333                    hex_lit,
1334                    token_lit.suffix.as_ref().map_or("", |s| s.as_str())
1335                ),
1336                context.config.max_width(),
1337                shape,
1338            )
1339            .max_width_error(shape.width, span);
1340        }
1341    }
1342
1343    wrap_str(
1344        context.snippet(span).to_owned(),
1345        context.config.max_width(),
1346        shape,
1347    )
1348    .max_width_error(shape.width, span)
1349}
1350
1351fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option<SeparatorTactic> {
1352    if context.inside_macro() {
1353        if span_ends_with_comma(context, span) {
1354            Some(SeparatorTactic::Always)
1355        } else {
1356            Some(SeparatorTactic::Never)
1357        }
1358    } else {
1359        None
1360    }
1361}
1362
1363pub(crate) fn rewrite_call(
1364    context: &RewriteContext<'_>,
1365    callee: &str,
1366    args: &[ptr::P<ast::Expr>],
1367    span: Span,
1368    shape: Shape,
1369) -> RewriteResult {
1370    overflow::rewrite_with_parens(
1371        context,
1372        callee,
1373        args.iter(),
1374        shape,
1375        span,
1376        context.config.fn_call_width(),
1377        choose_separator_tactic(context, span),
1378    )
1379}
1380
1381pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool {
1382    match expr.kind {
1383        ast::ExprKind::Lit(..) => true,
1384        ast::ExprKind::Path(ref qself, ref path) => qself.is_none() && path.segments.len() <= 1,
1385        ast::ExprKind::AddrOf(_, _, ref expr)
1386        | ast::ExprKind::Cast(ref expr, _)
1387        | ast::ExprKind::Field(ref expr, _)
1388        | ast::ExprKind::Try(ref expr)
1389        | ast::ExprKind::Unary(_, ref expr) => is_simple_expr(expr),
1390        ast::ExprKind::Index(ref lhs, ref rhs, _) => is_simple_expr(lhs) && is_simple_expr(rhs),
1391        ast::ExprKind::Repeat(ref lhs, ref rhs) => {
1392            is_simple_expr(lhs) && is_simple_expr(&*rhs.value)
1393        }
1394        _ => false,
1395    }
1396}
1397
1398pub(crate) fn is_every_expr_simple(lists: &[OverflowableItem<'_>]) -> bool {
1399    lists.iter().all(OverflowableItem::is_simple)
1400}
1401
1402pub(crate) fn can_be_overflowed_expr(
1403    context: &RewriteContext<'_>,
1404    expr: &ast::Expr,
1405    args_len: usize,
1406) -> bool {
1407    match expr.kind {
1408        _ if !expr.attrs.is_empty() => false,
1409        ast::ExprKind::Match(..) => {
1410            (context.use_block_indent() && args_len == 1)
1411                || (context.config.indent_style() == IndentStyle::Visual && args_len > 1)
1412                || context.config.overflow_delimited_expr()
1413        }
1414        ast::ExprKind::If(..)
1415        | ast::ExprKind::ForLoop { .. }
1416        | ast::ExprKind::Loop(..)
1417        | ast::ExprKind::While(..) => {
1418            context.config.combine_control_expr() && context.use_block_indent() && args_len == 1
1419        }
1420
1421        // Handle always block-like expressions
1422        ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
1423
1424        // Handle `[]` and `{}`-like expressions
1425        ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => {
1426            context.config.overflow_delimited_expr()
1427                || (context.use_block_indent() && args_len == 1)
1428        }
1429        ast::ExprKind::MacCall(ref mac) => {
1430            match (mac.args.delim, context.config.overflow_delimited_expr()) {
1431                (Delimiter::Bracket, true) | (Delimiter::Brace, true) => true,
1432                _ => context.use_block_indent() && args_len == 1,
1433            }
1434        }
1435
1436        // Handle parenthetical expressions
1437        ast::ExprKind::Call(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Tup(..) => {
1438            context.use_block_indent() && args_len == 1
1439        }
1440
1441        // Handle unary-like expressions
1442        ast::ExprKind::AddrOf(_, _, ref expr)
1443        | ast::ExprKind::Try(ref expr)
1444        | ast::ExprKind::Unary(_, ref expr)
1445        | ast::ExprKind::Cast(ref expr, _) => can_be_overflowed_expr(context, expr, args_len),
1446        _ => false,
1447    }
1448}
1449
1450pub(crate) fn is_nested_call(expr: &ast::Expr) -> bool {
1451    match expr.kind {
1452        ast::ExprKind::Call(..) | ast::ExprKind::MacCall(..) => true,
1453        ast::ExprKind::AddrOf(_, _, ref expr)
1454        | ast::ExprKind::Try(ref expr)
1455        | ast::ExprKind::Unary(_, ref expr)
1456        | ast::ExprKind::Cast(ref expr, _) => is_nested_call(expr),
1457        _ => false,
1458    }
1459}
1460
1461/// Returns `true` if a function call or a method call represented by the given span ends with a
1462/// trailing comma. This function is used when rewriting macro, as adding or removing a trailing
1463/// comma from macro can potentially break the code.
1464pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> bool {
1465    let mut result: bool = Default::default();
1466    let mut prev_char: char = Default::default();
1467    let closing_delimiters = &[')', '}', ']'];
1468
1469    for (kind, c) in CharClasses::new(context.snippet(span).chars()) {
1470        match c {
1471            _ if kind.is_comment() || c.is_whitespace() => continue,
1472            c if closing_delimiters.contains(&c) => {
1473                result &= !closing_delimiters.contains(&prev_char);
1474            }
1475            ',' => result = true,
1476            _ => result = false,
1477        }
1478        prev_char = c;
1479    }
1480
1481    result
1482}
1483
1484pub(crate) fn rewrite_paren(
1485    context: &RewriteContext<'_>,
1486    mut subexpr: &ast::Expr,
1487    shape: Shape,
1488    mut span: Span,
1489) -> RewriteResult {
1490    debug!("rewrite_paren, shape: {:?}", shape);
1491
1492    // Extract comments within parens.
1493    let mut pre_span;
1494    let mut post_span;
1495    let mut pre_comment;
1496    let mut post_comment;
1497    let remove_nested_parens = context.config.remove_nested_parens();
1498    loop {
1499        // 1 = "(" or ")"
1500        pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span().lo());
1501        post_span = mk_sp(subexpr.span.hi(), span.hi() - BytePos(1));
1502        pre_comment = rewrite_missing_comment(pre_span, shape, context)?;
1503        post_comment = rewrite_missing_comment(post_span, shape, context)?;
1504
1505        // Remove nested parens if there are no comments.
1506        if let ast::ExprKind::Paren(ref subsubexpr) = subexpr.kind {
1507            if remove_nested_parens && pre_comment.is_empty() && post_comment.is_empty() {
1508                span = subexpr.span;
1509                subexpr = subsubexpr;
1510                continue;
1511            }
1512        }
1513
1514        break;
1515    }
1516
1517    // 1 = `(` and `)`
1518    let sub_shape = shape
1519        .offset_left(1)
1520        .and_then(|s| s.sub_width(1))
1521        .max_width_error(shape.width, span)?;
1522    let subexpr_str = subexpr.rewrite_result(context, sub_shape)?;
1523    let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//");
1524    if fits_single_line {
1525        Ok(format!("({pre_comment}{subexpr_str}{post_comment})"))
1526    } else {
1527        rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span)
1528    }
1529}
1530
1531fn rewrite_paren_in_multi_line(
1532    context: &RewriteContext<'_>,
1533    subexpr: &ast::Expr,
1534    shape: Shape,
1535    pre_span: Span,
1536    post_span: Span,
1537) -> RewriteResult {
1538    let nested_indent = shape.indent.block_indent(context.config);
1539    let nested_shape = Shape::indented(nested_indent, context.config);
1540    let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?;
1541    let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?;
1542    let subexpr_str = subexpr.rewrite_result(context, nested_shape)?;
1543
1544    let mut result = String::with_capacity(subexpr_str.len() * 2);
1545    result.push('(');
1546    if !pre_comment.is_empty() {
1547        result.push_str(&nested_indent.to_string_with_newline(context.config));
1548        result.push_str(&pre_comment);
1549    }
1550    result.push_str(&nested_indent.to_string_with_newline(context.config));
1551    result.push_str(&subexpr_str);
1552    if !post_comment.is_empty() {
1553        result.push_str(&nested_indent.to_string_with_newline(context.config));
1554        result.push_str(&post_comment);
1555    }
1556    result.push_str(&shape.indent.to_string_with_newline(context.config));
1557    result.push(')');
1558
1559    Ok(result)
1560}
1561
1562fn rewrite_index(
1563    expr: &ast::Expr,
1564    index: &ast::Expr,
1565    context: &RewriteContext<'_>,
1566    shape: Shape,
1567) -> RewriteResult {
1568    let expr_str = expr.rewrite_result(context, shape)?;
1569
1570    let offset = last_line_width(&expr_str) + 1;
1571    let rhs_overhead = shape.rhs_overhead(context.config);
1572    let index_shape = if expr_str.contains('\n') {
1573        Shape::legacy(context.config.max_width(), shape.indent)
1574            .offset_left(offset)
1575            .and_then(|shape| shape.sub_width(1 + rhs_overhead))
1576    } else {
1577        match context.config.indent_style() {
1578            IndentStyle::Block => shape
1579                .offset_left(offset)
1580                .and_then(|shape| shape.sub_width(1)),
1581            IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1),
1582        }
1583    }
1584    .max_width_error(shape.width, index.span());
1585    let orig_index_rw = index_shape.and_then(|s| index.rewrite_result(context, s));
1586
1587    // Return if index fits in a single line.
1588    match orig_index_rw {
1589        Ok(ref index_str) if !index_str.contains('\n') => {
1590            return Ok(format!("{expr_str}[{index_str}]"));
1591        }
1592        _ => (),
1593    }
1594
1595    // Try putting index on the next line and see if it fits in a single line.
1596    let indent = shape.indent.block_indent(context.config);
1597    let index_shape = Shape::indented(indent, context.config)
1598        .offset_left(1)
1599        .max_width_error(shape.width, index.span())?;
1600    let index_shape = index_shape
1601        .sub_width(1 + rhs_overhead)
1602        .max_width_error(index_shape.width, index.span())?;
1603    let new_index_rw = index.rewrite_result(context, index_shape);
1604    match (orig_index_rw, new_index_rw) {
1605        (_, Ok(ref new_index_str)) if !new_index_str.contains('\n') => Ok(format!(
1606            "{}{}[{}]",
1607            expr_str,
1608            indent.to_string_with_newline(context.config),
1609            new_index_str,
1610        )),
1611        (Err(_), Ok(ref new_index_str)) => Ok(format!(
1612            "{}{}[{}]",
1613            expr_str,
1614            indent.to_string_with_newline(context.config),
1615            new_index_str,
1616        )),
1617        (Ok(ref index_str), _) => Ok(format!("{expr_str}[{index_str}]")),
1618        // When both orig_index_rw and new_index_rw result in errors, we currently propagate the
1619        // error from the second attempt since it is more generous with width constraints.
1620        // This decision is somewhat arbitrary and is open to change.
1621        (Err(_), Err(new_index_rw_err)) => Err(new_index_rw_err),
1622    }
1623}
1624
1625fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool {
1626    !has_base && fields.iter().all(|field| !field.is_shorthand)
1627}
1628
1629fn rewrite_struct_lit<'a>(
1630    context: &RewriteContext<'_>,
1631    path: &ast::Path,
1632    qself: &Option<ptr::P<ast::QSelf>>,
1633    fields: &'a [ast::ExprField],
1634    struct_rest: &ast::StructRest,
1635    attrs: &[ast::Attribute],
1636    span: Span,
1637    shape: Shape,
1638) -> RewriteResult {
1639    debug!("rewrite_struct_lit: shape {:?}", shape);
1640
1641    enum StructLitField<'a> {
1642        Regular(&'a ast::ExprField),
1643        Base(&'a ast::Expr),
1644        Rest(Span),
1645    }
1646
1647    // 2 = " {".len()
1648    let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?;
1649    let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
1650
1651    let has_base_or_rest = match struct_rest {
1652        ast::StructRest::None if fields.is_empty() => return Ok(format!("{path_str} {{}}")),
1653        ast::StructRest::Rest(_) if fields.is_empty() => {
1654            return Ok(format!("{path_str} {{ .. }}"));
1655        }
1656        ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true,
1657        _ => false,
1658    };
1659
1660    // Foo { a: Foo } - indent is +3, width is -5.
1661    let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)
1662        .max_width_error(shape.width, span)?;
1663
1664    let one_line_width = h_shape.map_or(0, |shape| shape.width);
1665    let body_lo = context.snippet_provider.span_after(span, "{");
1666    let fields_str = if struct_lit_can_be_aligned(fields, has_base_or_rest)
1667        && context.config.struct_field_align_threshold() > 0
1668    {
1669        rewrite_with_alignment(
1670            fields,
1671            context,
1672            v_shape,
1673            mk_sp(body_lo, span.hi()),
1674            one_line_width,
1675        )
1676        .unknown_error()?
1677    } else {
1678        let field_iter = fields.iter().map(StructLitField::Regular).chain(
1679            match struct_rest {
1680                ast::StructRest::Base(expr) => Some(StructLitField::Base(&**expr)),
1681                ast::StructRest::Rest(span) => Some(StructLitField::Rest(*span)),
1682                ast::StructRest::None => None,
1683            }
1684            .into_iter(),
1685        );
1686
1687        let span_lo = |item: &StructLitField<'_>| match *item {
1688            StructLitField::Regular(field) => field.span().lo(),
1689            StructLitField::Base(expr) => {
1690                let last_field_hi = fields.last().map_or(span.lo(), |field| field.span.hi());
1691                let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo()));
1692                let pos = snippet.find_uncommented("..").unwrap();
1693                last_field_hi + BytePos(pos as u32)
1694            }
1695            StructLitField::Rest(span) => span.lo(),
1696        };
1697        let span_hi = |item: &StructLitField<'_>| match *item {
1698            StructLitField::Regular(field) => field.span().hi(),
1699            StructLitField::Base(expr) => expr.span.hi(),
1700            StructLitField::Rest(span) => span.hi(),
1701        };
1702        let rewrite = |item: &StructLitField<'_>| match *item {
1703            StructLitField::Regular(field) => {
1704                // The 1 taken from the v_budget is for the comma.
1705                rewrite_field(
1706                    context,
1707                    field,
1708                    v_shape.sub_width(1).max_width_error(v_shape.width, span)?,
1709                    0,
1710                )
1711            }
1712            StructLitField::Base(expr) => {
1713                // 2 = ..
1714                expr.rewrite_result(
1715                    context,
1716                    v_shape
1717                        .offset_left(2)
1718                        .max_width_error(v_shape.width, span)?,
1719                )
1720                .map(|s| format!("..{}", s))
1721            }
1722            StructLitField::Rest(_) => Ok("..".to_owned()),
1723        };
1724
1725        let items = itemize_list(
1726            context.snippet_provider,
1727            field_iter,
1728            "}",
1729            ",",
1730            span_lo,
1731            span_hi,
1732            rewrite,
1733            body_lo,
1734            span.hi(),
1735            false,
1736        );
1737        let item_vec = items.collect::<Vec<_>>();
1738
1739        let tactic = struct_lit_tactic(h_shape, context, &item_vec);
1740        let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
1741
1742        let ends_with_comma = span_ends_with_comma(context, span);
1743        let force_no_trailing_comma = context.inside_macro() && !ends_with_comma;
1744
1745        let fmt = struct_lit_formatting(
1746            nested_shape,
1747            tactic,
1748            context,
1749            force_no_trailing_comma || has_base_or_rest || !context.use_block_indent(),
1750        );
1751
1752        write_list(&item_vec, &fmt)?
1753    };
1754
1755    let fields_str =
1756        wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?;
1757    Ok(format!("{path_str} {{{fields_str}}}"))
1758
1759    // FIXME if context.config.indent_style() == Visual, but we run out
1760    // of space, we should fall back to BlockIndent.
1761}
1762
1763pub(crate) fn wrap_struct_field(
1764    context: &RewriteContext<'_>,
1765    attrs: &[ast::Attribute],
1766    fields_str: &str,
1767    shape: Shape,
1768    nested_shape: Shape,
1769    one_line_width: usize,
1770) -> RewriteResult {
1771    let should_vertical = context.config.indent_style() == IndentStyle::Block
1772        && (fields_str.contains('\n')
1773            || !context.config.struct_lit_single_line()
1774            || fields_str.len() > one_line_width);
1775
1776    let inner_attrs = &inner_attributes(attrs);
1777    if inner_attrs.is_empty() {
1778        if should_vertical {
1779            Ok(format!(
1780                "{}{}{}",
1781                nested_shape.indent.to_string_with_newline(context.config),
1782                fields_str,
1783                shape.indent.to_string_with_newline(context.config)
1784            ))
1785        } else {
1786            // One liner or visual indent.
1787            Ok(format!(" {fields_str} "))
1788        }
1789    } else {
1790        Ok(format!(
1791            "{}{}{}{}{}",
1792            nested_shape.indent.to_string_with_newline(context.config),
1793            inner_attrs.rewrite_result(context, shape)?,
1794            nested_shape.indent.to_string_with_newline(context.config),
1795            fields_str,
1796            shape.indent.to_string_with_newline(context.config)
1797        ))
1798    }
1799}
1800
1801pub(crate) fn struct_lit_field_separator(config: &Config) -> &str {
1802    colon_spaces(config)
1803}
1804
1805pub(crate) fn rewrite_field(
1806    context: &RewriteContext<'_>,
1807    field: &ast::ExprField,
1808    shape: Shape,
1809    prefix_max_width: usize,
1810) -> RewriteResult {
1811    if contains_skip(&field.attrs) {
1812        return Ok(context.snippet(field.span()).to_owned());
1813    }
1814    let mut attrs_str = field.attrs.rewrite_result(context, shape)?;
1815    if !attrs_str.is_empty() {
1816        attrs_str.push_str(&shape.indent.to_string_with_newline(context.config));
1817    };
1818    let name = context.snippet(field.ident.span);
1819    if field.is_shorthand {
1820        Ok(attrs_str + name)
1821    } else {
1822        let mut separator = String::from(struct_lit_field_separator(context.config));
1823        for _ in 0..prefix_max_width.saturating_sub(name.len()) {
1824            separator.push(' ');
1825        }
1826        let overhead = name.len() + separator.len();
1827        let expr_shape = shape
1828            .offset_left(overhead)
1829            .max_width_error(shape.width, field.span)?;
1830        let expr = field.expr.rewrite_result(context, expr_shape);
1831        let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_));
1832        match expr {
1833            Ok(ref e)
1834                if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() =>
1835            {
1836                Ok(attrs_str + name)
1837            }
1838            Ok(e) => Ok(format!("{attrs_str}{name}{separator}{e}")),
1839            Err(_) => {
1840                let expr_offset = shape.indent.block_indent(context.config);
1841                let expr = field
1842                    .expr
1843                    .rewrite_result(context, Shape::indented(expr_offset, context.config));
1844                expr.map(|s| {
1845                    format!(
1846                        "{}{}:\n{}{}",
1847                        attrs_str,
1848                        name,
1849                        expr_offset.to_string(context.config),
1850                        s
1851                    )
1852                })
1853            }
1854        }
1855    }
1856}
1857
1858fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>(
1859    context: &RewriteContext<'_>,
1860    mut items: impl Iterator<Item = &'a T>,
1861    span: Span,
1862    shape: Shape,
1863    is_singleton_tuple: bool,
1864) -> RewriteResult {
1865    // In case of length 1, need a trailing comma
1866    debug!("rewrite_tuple_in_visual_indent_style {:?}", shape);
1867    if is_singleton_tuple {
1868        // 3 = "(" + ",)"
1869        let nested_shape = shape
1870            .sub_width(3)
1871            .max_width_error(shape.width, span)?
1872            .visual_indent(1);
1873        return items
1874            .next()
1875            .unwrap()
1876            .rewrite_result(context, nested_shape)
1877            .map(|s| format!("({},)", s));
1878    }
1879
1880    let list_lo = context.snippet_provider.span_after(span, "(");
1881    let nested_shape = shape
1882        .sub_width(2)
1883        .max_width_error(shape.width, span)?
1884        .visual_indent(1);
1885    let items = itemize_list(
1886        context.snippet_provider,
1887        items,
1888        ")",
1889        ",",
1890        |item| item.span().lo(),
1891        |item| item.span().hi(),
1892        |item| item.rewrite_result(context, nested_shape),
1893        list_lo,
1894        span.hi() - BytePos(1),
1895        false,
1896    );
1897    let item_vec: Vec<_> = items.collect();
1898    let tactic = definitive_tactic(
1899        &item_vec,
1900        ListTactic::HorizontalVertical,
1901        Separator::Comma,
1902        nested_shape.width,
1903    );
1904    let fmt = ListFormatting::new(nested_shape, context.config)
1905        .tactic(tactic)
1906        .ends_with_newline(false);
1907    let list_str = write_list(&item_vec, &fmt)?;
1908
1909    Ok(format!("({list_str})"))
1910}
1911
1912fn rewrite_let(
1913    context: &RewriteContext<'_>,
1914    shape: Shape,
1915    pat: &ast::Pat,
1916    expr: &ast::Expr,
1917) -> RewriteResult {
1918    let mut result = "let ".to_owned();
1919
1920    // TODO(ytmimi) comments could appear between `let` and the `pat`
1921
1922    // 4 = "let ".len()
1923    let pat_shape = shape
1924        .offset_left(4)
1925        .max_width_error(shape.width, pat.span)?;
1926    let pat_str = pat.rewrite_result(context, pat_shape)?;
1927    result.push_str(&pat_str);
1928
1929    // TODO(ytmimi) comments could appear between `pat` and `=`
1930    result.push_str(" =");
1931
1932    let comments_lo = context
1933        .snippet_provider
1934        .span_after(expr.span.with_lo(pat.span.hi()), "=");
1935    let comments_span = mk_sp(comments_lo, expr.span.lo());
1936    rewrite_assign_rhs_with_comments(
1937        context,
1938        result,
1939        expr,
1940        shape,
1941        &RhsAssignKind::Expr(&expr.kind, expr.span),
1942        RhsTactics::Default,
1943        comments_span,
1944        true,
1945    )
1946}
1947
1948pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>(
1949    context: &'a RewriteContext<'_>,
1950    items: impl Iterator<Item = &'a T>,
1951    span: Span,
1952    shape: Shape,
1953    is_singleton_tuple: bool,
1954) -> RewriteResult {
1955    debug!("rewrite_tuple {:?}", shape);
1956    if context.use_block_indent() {
1957        // We use the same rule as function calls for rewriting tuples.
1958        let force_tactic = if context.inside_macro() {
1959            if span_ends_with_comma(context, span) {
1960                Some(SeparatorTactic::Always)
1961            } else {
1962                Some(SeparatorTactic::Never)
1963            }
1964        } else if is_singleton_tuple {
1965            Some(SeparatorTactic::Always)
1966        } else {
1967            None
1968        };
1969        overflow::rewrite_with_parens(
1970            context,
1971            "",
1972            items,
1973            shape,
1974            span,
1975            context.config.fn_call_width(),
1976            force_tactic,
1977        )
1978    } else {
1979        rewrite_tuple_in_visual_indent_style(context, items, span, shape, is_singleton_tuple)
1980    }
1981}
1982
1983pub(crate) fn rewrite_unary_prefix<R: Rewrite + Spanned>(
1984    context: &RewriteContext<'_>,
1985    prefix: &str,
1986    rewrite: &R,
1987    shape: Shape,
1988) -> RewriteResult {
1989    let shape = shape
1990        .offset_left(prefix.len())
1991        .max_width_error(shape.width, rewrite.span())?;
1992    rewrite
1993        .rewrite_result(context, shape)
1994        .map(|r| format!("{}{}", prefix, r))
1995}
1996
1997// FIXME: this is probably not correct for multi-line Rewrites. we should
1998// subtract suffix.len() from the last line budget, not the first!
1999pub(crate) fn rewrite_unary_suffix<R: Rewrite + Spanned>(
2000    context: &RewriteContext<'_>,
2001    suffix: &str,
2002    rewrite: &R,
2003    shape: Shape,
2004) -> RewriteResult {
2005    let shape = shape
2006        .sub_width(suffix.len())
2007        .max_width_error(shape.width, rewrite.span())?;
2008    rewrite.rewrite_result(context, shape).map(|mut r| {
2009        r.push_str(suffix);
2010        r
2011    })
2012}
2013
2014fn rewrite_unary_op(
2015    context: &RewriteContext<'_>,
2016    op: ast::UnOp,
2017    expr: &ast::Expr,
2018    shape: Shape,
2019) -> RewriteResult {
2020    // For some reason, an UnOp is not spanned like BinOp!
2021    rewrite_unary_prefix(context, op.as_str(), expr, shape)
2022}
2023
2024pub(crate) enum RhsAssignKind<'ast> {
2025    Expr(&'ast ast::ExprKind, #[allow(dead_code)] Span),
2026    Bounds,
2027    Ty,
2028}
2029
2030impl<'ast> RhsAssignKind<'ast> {
2031    // TODO(calebcartwright)
2032    // Preemptive addition for handling RHS with chains, not yet utilized.
2033    // It may make more sense to construct the chain first and then check
2034    // whether there are actually chain elements.
2035    #[allow(dead_code)]
2036    fn is_chain(&self) -> bool {
2037        match self {
2038            RhsAssignKind::Expr(kind, _) => {
2039                matches!(
2040                    kind,
2041                    ast::ExprKind::Try(..)
2042                        | ast::ExprKind::Field(..)
2043                        | ast::ExprKind::MethodCall(..)
2044                        | ast::ExprKind::Await(_, _)
2045                )
2046            }
2047            _ => false,
2048        }
2049    }
2050}
2051
2052fn rewrite_assignment(
2053    context: &RewriteContext<'_>,
2054    lhs: &ast::Expr,
2055    rhs: &ast::Expr,
2056    op: Option<&ast::BinOp>,
2057    shape: Shape,
2058) -> RewriteResult {
2059    let operator_str = match op {
2060        Some(op) => context.snippet(op.span),
2061        None => "=",
2062    };
2063
2064    // 1 = space between lhs and operator.
2065    let lhs_shape = shape
2066        .sub_width(operator_str.len() + 1)
2067        .max_width_error(shape.width, lhs.span())?;
2068    let lhs_str = format!(
2069        "{} {}",
2070        lhs.rewrite_result(context, lhs_shape)?,
2071        operator_str
2072    );
2073
2074    rewrite_assign_rhs(
2075        context,
2076        lhs_str,
2077        rhs,
2078        &RhsAssignKind::Expr(&rhs.kind, rhs.span),
2079        shape,
2080    )
2081}
2082
2083/// Controls where to put the rhs.
2084#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2085pub(crate) enum RhsTactics {
2086    /// Use heuristics.
2087    Default,
2088    /// Put the rhs on the next line if it uses multiple line, without extra indentation.
2089    ForceNextLineWithoutIndent,
2090    /// Allow overflowing max width if neither `Default` nor `ForceNextLineWithoutIndent`
2091    /// did not work.
2092    AllowOverflow,
2093}
2094
2095// The left hand side must contain everything up to, and including, the
2096// assignment operator.
2097pub(crate) fn rewrite_assign_rhs<S: Into<String>, R: Rewrite>(
2098    context: &RewriteContext<'_>,
2099    lhs: S,
2100    ex: &R,
2101    rhs_kind: &RhsAssignKind<'_>,
2102    shape: Shape,
2103) -> RewriteResult {
2104    rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default)
2105}
2106
2107pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>(
2108    context: &RewriteContext<'_>,
2109    lhs: &str,
2110    ex: &R,
2111    shape: Shape,
2112    rhs_kind: &RhsAssignKind<'_>,
2113    rhs_tactics: RhsTactics,
2114) -> RewriteResult {
2115    let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') {
2116        shape.indent.width()
2117    } else {
2118        0
2119    });
2120    // 1 = space between operator and rhs.
2121    let orig_shape = shape.offset_left(last_line_width + 1).unwrap_or(Shape {
2122        width: 0,
2123        offset: shape.offset + last_line_width + 1,
2124        ..shape
2125    });
2126    let has_rhs_comment = if let Some(offset) = lhs.find_last_uncommented("=") {
2127        lhs.trim_end().len() > offset + 1
2128    } else {
2129        false
2130    };
2131
2132    choose_rhs(
2133        context,
2134        ex,
2135        orig_shape,
2136        ex.rewrite_result(context, orig_shape),
2137        rhs_kind,
2138        rhs_tactics,
2139        has_rhs_comment,
2140    )
2141}
2142
2143pub(crate) fn rewrite_assign_rhs_with<S: Into<String>, R: Rewrite>(
2144    context: &RewriteContext<'_>,
2145    lhs: S,
2146    ex: &R,
2147    shape: Shape,
2148    rhs_kind: &RhsAssignKind<'_>,
2149    rhs_tactics: RhsTactics,
2150) -> RewriteResult {
2151    let lhs = lhs.into();
2152    let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?;
2153    Ok(lhs + &rhs)
2154}
2155
2156pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite + Spanned>(
2157    context: &RewriteContext<'_>,
2158    lhs: S,
2159    ex: &R,
2160    shape: Shape,
2161    rhs_kind: &RhsAssignKind<'_>,
2162    rhs_tactics: RhsTactics,
2163    between_span: Span,
2164    allow_extend: bool,
2165) -> RewriteResult {
2166    let lhs = lhs.into();
2167    let contains_comment = contains_comment(context.snippet(between_span));
2168    let shape = if contains_comment {
2169        shape
2170            .block_left(context.config.tab_spaces())
2171            .max_width_error(shape.width, between_span.with_hi(ex.span().hi()))?
2172    } else {
2173        shape
2174    };
2175    let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?;
2176    if contains_comment {
2177        let rhs = rhs.trim_start();
2178        combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend)
2179    } else {
2180        Ok(lhs + &rhs)
2181    }
2182}
2183
2184fn choose_rhs<R: Rewrite>(
2185    context: &RewriteContext<'_>,
2186    expr: &R,
2187    shape: Shape,
2188    orig_rhs: RewriteResult,
2189    _rhs_kind: &RhsAssignKind<'_>,
2190    rhs_tactics: RhsTactics,
2191    has_rhs_comment: bool,
2192) -> RewriteResult {
2193    match orig_rhs {
2194        Ok(ref new_str) if new_str.is_empty() => Ok(String::new()),
2195        Ok(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => {
2196            Ok(format!(" {new_str}"))
2197        }
2198        _ => {
2199            // Expression did not fit on the same line as the identifier.
2200            // Try splitting the line and see if that works better.
2201            let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)
2202                // TODO(ding-young) Ideally, we can replace unknown_error() with max_width_error(),
2203                // but this requires either implementing the Spanned trait for ast::GenericBounds
2204                // or grabbing the span from the call site.
2205                .unknown_error()?;
2206            let new_rhs = expr.rewrite_result(context, new_shape);
2207            let new_indent_str = &shape
2208                .indent
2209                .block_indent(context.config)
2210                .to_string_with_newline(context.config);
2211            let before_space_str = if has_rhs_comment { "" } else { " " };
2212
2213            match (orig_rhs, new_rhs) {
2214                (Ok(ref orig_rhs), Ok(ref new_rhs))
2215                    if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) =>
2216                {
2217                    Ok(format!("{before_space_str}{orig_rhs}"))
2218                }
2219                (Ok(ref orig_rhs), Ok(ref new_rhs))
2220                    if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) =>
2221                {
2222                    Ok(format!("{new_indent_str}{new_rhs}"))
2223                }
2224                (Err(_), Ok(ref new_rhs)) => Ok(format!("{new_indent_str}{new_rhs}")),
2225                (Err(_), Err(_)) if rhs_tactics == RhsTactics::AllowOverflow => {
2226                    let shape = shape.infinite_width();
2227                    expr.rewrite_result(context, shape)
2228                        .map(|s| format!("{}{}", before_space_str, s))
2229                }
2230                // When both orig_rhs and new_rhs result in errors, we currently propagate
2231                // the error from the second attempt since it is more generous with
2232                // width constraints. This decision is somewhat arbitrary and is open to change.
2233                (Err(_), Err(new_rhs_err)) => Err(new_rhs_err),
2234                (Ok(orig_rhs), _) => Ok(format!("{before_space_str}{orig_rhs}")),
2235            }
2236        }
2237    }
2238}
2239
2240fn shape_from_rhs_tactic(
2241    context: &RewriteContext<'_>,
2242    shape: Shape,
2243    rhs_tactic: RhsTactics,
2244) -> Option<Shape> {
2245    match rhs_tactic {
2246        RhsTactics::ForceNextLineWithoutIndent => shape
2247            .with_max_width(context.config)
2248            .sub_width(shape.indent.width()),
2249        RhsTactics::Default | RhsTactics::AllowOverflow => {
2250            Shape::indented(shape.indent.block_indent(context.config), context.config)
2251                .sub_width(shape.rhs_overhead(context.config))
2252        }
2253    }
2254}
2255
2256/// Returns true if formatting next_line_rhs is better on a new line when compared to the
2257/// original's line formatting.
2258///
2259/// It is considered better if:
2260/// 1. the tactic is ForceNextLineWithoutIndent
2261/// 2. next_line_rhs doesn't have newlines
2262/// 3. the original line has more newlines than next_line_rhs
2263/// 4. the original formatting of the first line ends with `(`, `{`, or `[` and next_line_rhs
2264///    doesn't
2265pub(crate) fn prefer_next_line(
2266    orig_rhs: &str,
2267    next_line_rhs: &str,
2268    rhs_tactics: RhsTactics,
2269) -> bool {
2270    rhs_tactics == RhsTactics::ForceNextLineWithoutIndent
2271        || !next_line_rhs.contains('\n')
2272        || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1
2273        || first_line_ends_with(orig_rhs, '(') && !first_line_ends_with(next_line_rhs, '(')
2274        || first_line_ends_with(orig_rhs, '{') && !first_line_ends_with(next_line_rhs, '{')
2275        || first_line_ends_with(orig_rhs, '[') && !first_line_ends_with(next_line_rhs, '[')
2276}
2277
2278fn rewrite_expr_addrof(
2279    context: &RewriteContext<'_>,
2280    borrow_kind: ast::BorrowKind,
2281    mutability: ast::Mutability,
2282    expr: &ast::Expr,
2283    shape: Shape,
2284) -> RewriteResult {
2285    let operator_str = match (mutability, borrow_kind) {
2286        (ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
2287        (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
2288        (ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ",
2289        (ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ",
2290    };
2291    rewrite_unary_prefix(context, operator_str, expr, shape)
2292}
2293
2294pub(crate) fn is_method_call(expr: &ast::Expr) -> bool {
2295    match expr.kind {
2296        ast::ExprKind::MethodCall(..) => true,
2297        ast::ExprKind::AddrOf(_, _, ref expr)
2298        | ast::ExprKind::Cast(ref expr, _)
2299        | ast::ExprKind::Try(ref expr)
2300        | ast::ExprKind::Unary(_, ref expr) => is_method_call(expr),
2301        _ => false,
2302    }
2303}
2304
2305#[cfg(test)]
2306mod test {
2307    use super::last_line_offsetted;
2308
2309    #[test]
2310    fn test_last_line_offsetted() {
2311        let lines = "one\n    two";
2312        assert_eq!(last_line_offsetted(2, lines), true);
2313        assert_eq!(last_line_offsetted(4, lines), false);
2314        assert_eq!(last_line_offsetted(6, lines), false);
2315
2316        let lines = "one    two";
2317        assert_eq!(last_line_offsetted(2, lines), false);
2318        assert_eq!(last_line_offsetted(0, lines), false);
2319
2320        let lines = "\ntwo";
2321        assert_eq!(last_line_offsetted(2, lines), false);
2322        assert_eq!(last_line_offsetted(0, lines), false);
2323
2324        let lines = "one\n    two      three";
2325        assert_eq!(last_line_offsetted(2, lines), true);
2326        let lines = "one\n two      three";
2327        assert_eq!(last_line_offsetted(2, lines), false);
2328    }
2329}