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