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