Skip to main content

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