Skip to main content

rustfmt_nightly/
chains.rs

1//! Formatting of chained expressions, i.e., expressions that are chained by
2//! dots: struct and enum field access, method calls, and try shorthand (`?`).
3//!
4//! Instead of walking these subexpressions one-by-one, as is our usual strategy
5//! for expression formatting, we collect maximal sequences of these expressions
6//! and handle them simultaneously.
7//!
8//! Whenever possible, the entire chain is put on a single line. If that fails,
9//! we put each subexpression on a separate, much like the (default) function
10//! argument function argument strategy.
11//!
12//! Depends on config options: `chain_indent` is the indent to use for
13//! blocks in the parent/root/base of the chain (and the rest of the chain's
14//! alignment).
15//! E.g., `let foo = { aaaa; bbb; ccc }.bar.baz();`, we would layout for the
16//! following values of `chain_indent`:
17//! Block:
18//!
19//! ```text
20//! let foo = {
21//!     aaaa;
22//!     bbb;
23//!     ccc
24//! }.bar
25//!     .baz();
26//! ```
27//!
28//! Visual:
29//!
30//! ```text
31//! let foo = {
32//!               aaaa;
33//!               bbb;
34//!               ccc
35//!           }
36//!           .bar
37//!           .baz();
38//! ```
39//!
40//! If the first item in the chain is a block expression, we align the dots with
41//! the braces.
42//! Block:
43//!
44//! ```text
45//! let a = foo.bar
46//!     .baz()
47//!     .qux
48//! ```
49//!
50//! Visual:
51//!
52//! ```text
53//! let a = foo.bar
54//!            .baz()
55//!            .qux
56//! ```
57
58use std::borrow::Cow;
59use std::cmp::min;
60
61use rustc_ast::ast;
62use rustc_span::{BytePos, Span, symbol};
63use tracing::debug;
64
65use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment};
66use crate::config::{IndentStyle, StyleEdition};
67use crate::expr::rewrite_call;
68use crate::lists::extract_pre_comment;
69use crate::macros::convert_try_mac;
70use crate::rewrite::{
71    ExceedsMaxWidthError, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult,
72};
73use crate::shape::Shape;
74use crate::source_map::SpanUtils;
75use crate::utils::{
76    self, filtered_str_fits, first_line_width, last_line_extendable, last_line_width, mk_sp,
77    rewrite_ident, trimmed_last_line_width, wrap_str,
78};
79
80use thin_vec::ThinVec;
81
82/// Provides the original input contents from the span
83/// of a chain element with trailing spaces trimmed.
84fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> {
85    // TODO(ding-young): Currently returning None when the given span is out of the range
86    // covered by the snippet provider. If this is a common cause for internal
87    // rewrite failure, add a new enum variant and return RewriteError instead of None
88    context.snippet_provider.span_to_snippet(span).map(|s| {
89        s.lines()
90            .map(|l| l.trim_end())
91            .collect::<Vec<_>>()
92            .join("\n")
93    })
94}
95
96fn format_chain_item(
97    item: &ChainItem,
98    context: &RewriteContext<'_>,
99    rewrite_shape: Shape,
100    allow_overflow: bool,
101) -> RewriteResult {
102    if allow_overflow {
103        // TODO(ding-young): Consider calling format_overflow_style()
104        // only when item.rewrite_result() returns RewriteError::ExceedsMaxWidth.
105        // It may be inappropriate to call format_overflow_style on other RewriteError
106        // since the current approach retries formatting if allow_overflow is true
107        item.rewrite_result(context, rewrite_shape)
108            .or_else(|_| format_overflow_style(item.span, context).unknown_error())
109    } else {
110        item.rewrite_result(context, rewrite_shape)
111    }
112}
113
114fn get_block_child_shape(
115    prev_ends_with_block: bool,
116    context: &RewriteContext<'_>,
117    shape: Shape,
118) -> Shape {
119    if prev_ends_with_block {
120        shape.block_indent(0)
121    } else {
122        shape.block_indent(context.config.tab_spaces())
123    }
124    .with_max_width(context.config)
125}
126
127fn get_visual_style_child_shape(
128    context: &RewriteContext<'_>,
129    shape: Shape,
130    offset: usize,
131    parent_overflowing: bool,
132    span: Span,
133) -> Result<Shape, ExceedsMaxWidthError> {
134    if !parent_overflowing {
135        shape
136            .with_max_width(context.config)
137            .offset_left(offset, span)
138            .map(|s| s.visual_indent(0))
139    } else {
140        Ok(shape.visual_indent(offset))
141    }
142}
143
144pub(crate) fn rewrite_chain(
145    expr: &ast::Expr,
146    context: &RewriteContext<'_>,
147    shape: Shape,
148) -> RewriteResult {
149    let chain = Chain::from_ast(expr, context);
150    debug!("rewrite_chain {:?} {:?}", chain, shape);
151
152    // If this is just an expression with some `?`s, then format it trivially and
153    // return early.
154    if chain.children.is_empty() {
155        return chain.parent.rewrite_result(context, shape);
156    }
157
158    chain.rewrite_result(context, shape)
159}
160
161#[derive(Debug)]
162enum CommentPosition {
163    Back,
164    Top,
165}
166
167/// Information about an expression in a chain.
168struct SubExpr {
169    expr: ast::Expr,
170    is_postfix_receiver: bool,
171}
172
173/// An expression plus trailing `?`s to be formatted together.
174#[derive(Debug)]
175struct ChainItem {
176    kind: ChainItemKind,
177    tries: usize,
178    span: Span,
179}
180
181// FIXME: we can't use a reference here because to convert `try!` to `?` we
182// synthesise the AST node. However, I think we could use `Cow` and that
183// would remove a lot of cloning.
184#[derive(Debug)]
185enum ChainItemKind {
186    Parent {
187        expr: ast::Expr,
188        parens: bool,
189    },
190    MethodCall(
191        ast::PathSegment,
192        Vec<ast::GenericArg>,
193        ThinVec<Box<ast::Expr>>,
194    ),
195    StructField(symbol::Ident),
196    TupleField(symbol::Ident, bool),
197    Await,
198    Use,
199    Yield,
200    Comment(String, CommentPosition),
201}
202
203impl ChainItemKind {
204    fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool {
205        match self {
206            ChainItemKind::Parent { expr, .. } => utils::is_block_expr(context, expr, reps),
207            ChainItemKind::MethodCall(..)
208            | ChainItemKind::StructField(..)
209            | ChainItemKind::TupleField(..)
210            | ChainItemKind::Await
211            | ChainItemKind::Use
212            | ChainItemKind::Yield
213            | ChainItemKind::Comment(..) => false,
214        }
215    }
216
217    fn is_tup_field_access(expr: &ast::Expr) -> bool {
218        match expr.kind {
219            ast::ExprKind::Field(_, ref field) => {
220                field.name.as_str().chars().all(|c| c.is_digit(10))
221            }
222            _ => false,
223        }
224    }
225
226    fn from_ast(
227        context: &RewriteContext<'_>,
228        expr: &ast::Expr,
229        is_postfix_receiver: bool,
230    ) -> (ChainItemKind, Span) {
231        let (kind, span) = match expr.kind {
232            ast::ExprKind::MethodCall(ref call) => {
233                let types = if let Some(ref generic_args) = call.seg.args {
234                    if let ast::GenericArgs::AngleBracketed(ref data) = **generic_args {
235                        data.args
236                            .iter()
237                            .filter_map(|x| match x {
238                                ast::AngleBracketedArg::Arg(ref generic_arg) => {
239                                    Some(generic_arg.clone())
240                                }
241                                _ => None,
242                            })
243                            .collect::<Vec<_>>()
244                    } else {
245                        vec![]
246                    }
247                } else {
248                    vec![]
249                };
250                let span = mk_sp(call.receiver.span.hi(), expr.span.hi());
251                let kind = ChainItemKind::MethodCall(call.seg.clone(), types, call.args.clone());
252                (kind, span)
253            }
254            ast::ExprKind::Field(ref nested, field) => {
255                let kind = if Self::is_tup_field_access(expr) {
256                    ChainItemKind::TupleField(field, Self::is_tup_field_access(nested))
257                } else {
258                    ChainItemKind::StructField(field)
259                };
260                let span = mk_sp(nested.span.hi(), field.span.hi());
261                (kind, span)
262            }
263            ast::ExprKind::Await(ref nested, _) => {
264                let span = mk_sp(nested.span.hi(), expr.span.hi());
265                (ChainItemKind::Await, span)
266            }
267            ast::ExprKind::Use(ref nested, _) => {
268                let span = mk_sp(nested.span.hi(), expr.span.hi());
269                (ChainItemKind::Use, span)
270            }
271            ast::ExprKind::Yield(ast::YieldKind::Postfix(ref nested)) => {
272                let span = mk_sp(nested.span.hi(), expr.span.hi());
273                (ChainItemKind::Yield, span)
274            }
275            _ => {
276                return (
277                    ChainItemKind::Parent {
278                        expr: expr.clone(),
279                        parens: is_postfix_receiver && should_add_parens(expr, context),
280                    },
281                    expr.span,
282                );
283            }
284        };
285
286        // Remove comments from the span.
287        let lo = context.snippet_provider.span_before(span, ".");
288        (kind, mk_sp(lo, span.hi()))
289    }
290}
291
292impl Rewrite for ChainItem {
293    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
294        self.rewrite_result(context, shape).ok()
295    }
296
297    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
298        let shape = shape.sub_width(self.tries, self.span)?;
299        let rewrite = match self.kind {
300            ChainItemKind::Parent {
301                ref expr,
302                parens: true,
303            } => crate::expr::rewrite_paren(context, &expr, shape, expr.span)?,
304            ChainItemKind::Parent {
305                ref expr,
306                parens: false,
307            } => expr.rewrite_result(context, shape)?,
308            ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => {
309                Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)?
310            }
311            ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)),
312            ChainItemKind::TupleField(ident, nested) => format!(
313                "{}.{}",
314                if nested && context.config.style_edition() <= StyleEdition::Edition2021 {
315                    " "
316                } else {
317                    ""
318                },
319                rewrite_ident(context, ident)
320            ),
321            ChainItemKind::Await => ".await".to_owned(),
322            ChainItemKind::Use => ".use".to_owned(),
323            ChainItemKind::Yield => ".yield".to_owned(),
324            ChainItemKind::Comment(ref comment, _) => {
325                rewrite_comment(comment, false, shape, context.config)?
326            }
327        };
328        Ok(format!("{rewrite}{}", "?".repeat(self.tries)))
329    }
330}
331
332impl ChainItem {
333    fn new(context: &RewriteContext<'_>, expr: &SubExpr, tries: usize) -> ChainItem {
334        let (kind, span) = ChainItemKind::from_ast(context, &expr.expr, expr.is_postfix_receiver);
335        ChainItem { kind, tries, span }
336    }
337
338    fn comment(span: Span, comment: String, pos: CommentPosition) -> ChainItem {
339        ChainItem {
340            kind: ChainItemKind::Comment(comment, pos),
341            tries: 0,
342            span,
343        }
344    }
345
346    fn is_comment(&self) -> bool {
347        matches!(self.kind, ChainItemKind::Comment(..))
348    }
349
350    fn rewrite_method_call(
351        method_name: symbol::Ident,
352        types: &[ast::GenericArg],
353        args: &[Box<ast::Expr>],
354        span: Span,
355        context: &RewriteContext<'_>,
356        shape: Shape,
357    ) -> RewriteResult {
358        let type_str = if types.is_empty() {
359            String::new()
360        } else {
361            let type_list = types
362                .iter()
363                .map(|ty| ty.rewrite_result(context, shape))
364                .collect::<Result<Vec<_>, RewriteError>>()?;
365
366            format!("::<{}>", type_list.join(", "))
367        };
368        let callee_str = format!(".{}{}", rewrite_ident(context, method_name), type_str);
369        rewrite_call(context, &callee_str, &args, span, shape)
370    }
371}
372
373#[derive(Debug)]
374struct Chain {
375    parent: ChainItem,
376    children: Vec<ChainItem>,
377}
378
379impl Chain {
380    fn from_ast(expr: &ast::Expr, context: &RewriteContext<'_>) -> Chain {
381        let subexpr_list = Self::make_subexpr_list(expr, context);
382
383        // Un-parse the expression tree into ChainItems
384        let mut rev_children = vec![];
385        let mut sub_tries = 0;
386        for subexpr in &subexpr_list {
387            match subexpr.expr.kind {
388                ast::ExprKind::Try(_) => sub_tries += 1,
389                _ => {
390                    rev_children.push(ChainItem::new(context, subexpr, sub_tries));
391                    sub_tries = 0;
392                }
393            }
394        }
395
396        fn is_tries(s: &str) -> bool {
397            s.chars().all(|c| c == '?')
398        }
399
400        fn is_post_comment(s: &str) -> bool {
401            let comment_start_index = s.chars().position(|c| c == '/');
402            if comment_start_index.is_none() {
403                return false;
404            }
405
406            let newline_index = s.chars().position(|c| c == '\n');
407            if newline_index.is_none() {
408                return true;
409            }
410
411            comment_start_index.unwrap() < newline_index.unwrap()
412        }
413
414        fn handle_post_comment(
415            post_comment_span: Span,
416            post_comment_snippet: &str,
417            prev_span_end: &mut BytePos,
418            children: &mut Vec<ChainItem>,
419        ) {
420            let white_spaces: &[_] = &[' ', '\t'];
421            if post_comment_snippet
422                .trim_matches(white_spaces)
423                .starts_with('\n')
424            {
425                // No post comment.
426                return;
427            }
428            let trimmed_snippet = trim_tries(post_comment_snippet);
429            if is_post_comment(&trimmed_snippet) {
430                children.push(ChainItem::comment(
431                    post_comment_span,
432                    trimmed_snippet.trim().to_owned(),
433                    CommentPosition::Back,
434                ));
435                *prev_span_end = post_comment_span.hi();
436            }
437        }
438
439        let parent = rev_children.pop().unwrap();
440        let mut children = vec![];
441        let mut prev_span_end = parent.span.hi();
442        let mut iter = rev_children.into_iter().rev().peekable();
443        if let Some(first_chain_item) = iter.peek() {
444            let comment_span = mk_sp(prev_span_end, first_chain_item.span.lo());
445            let comment_snippet = context.snippet(comment_span);
446            if !is_tries(comment_snippet.trim()) {
447                handle_post_comment(
448                    comment_span,
449                    comment_snippet,
450                    &mut prev_span_end,
451                    &mut children,
452                );
453            }
454        }
455        while let Some(chain_item) = iter.next() {
456            let comment_snippet = context.snippet(chain_item.span);
457            // FIXME: Figure out the way to get a correct span when converting `try!` to `?`.
458            let handle_comment =
459                !(context.config.use_try_shorthand() || is_tries(comment_snippet.trim()));
460
461            // Pre-comment
462            if handle_comment {
463                let pre_comment_span = mk_sp(prev_span_end, chain_item.span.lo());
464                let pre_comment_snippet = trim_tries(context.snippet(pre_comment_span));
465                let (pre_comment, _) = extract_pre_comment(&pre_comment_snippet);
466                match pre_comment {
467                    Some(ref comment) if !comment.is_empty() => {
468                        children.push(ChainItem::comment(
469                            pre_comment_span,
470                            comment.to_owned(),
471                            CommentPosition::Top,
472                        ));
473                    }
474                    _ => (),
475                }
476            }
477
478            prev_span_end = chain_item.span.hi();
479            children.push(chain_item);
480
481            // Post-comment
482            if !handle_comment || iter.peek().is_none() {
483                continue;
484            }
485
486            let next_lo = iter.peek().unwrap().span.lo();
487            let post_comment_span = mk_sp(prev_span_end, next_lo);
488            let post_comment_snippet = context.snippet(post_comment_span);
489            handle_post_comment(
490                post_comment_span,
491                post_comment_snippet,
492                &mut prev_span_end,
493                &mut children,
494            );
495        }
496
497        Chain { parent, children }
498    }
499
500    // Returns a Vec of the prefixes of the chain.
501    // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a']
502    fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<SubExpr> {
503        let mut subexpr_list = vec![SubExpr {
504            expr: expr.clone(),
505            is_postfix_receiver: false,
506        }];
507
508        while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) {
509            subexpr_list.push(subexpr);
510        }
511
512        subexpr_list
513    }
514
515    // Returns the expression's subexpression, if it exists. When the subexpr
516    // is a try! macro, we'll convert it to shorthand when the option is set.
517    fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option<SubExpr> {
518        match expr.expr.kind {
519            ast::ExprKind::MethodCall(ref call) => Some(SubExpr {
520                expr: Self::convert_try(&call.receiver, context),
521                is_postfix_receiver: true,
522            }),
523            ast::ExprKind::Field(ref subexpr, _)
524            | ast::ExprKind::Await(ref subexpr, _)
525            | ast::ExprKind::Use(ref subexpr, _)
526            | ast::ExprKind::Yield(ast::YieldKind::Postfix(ref subexpr)) => Some(SubExpr {
527                expr: Self::convert_try(subexpr, context),
528                is_postfix_receiver: true,
529            }),
530            ast::ExprKind::Try(ref subexpr) => Some(SubExpr {
531                expr: Self::convert_try(subexpr, context),
532                is_postfix_receiver: false,
533            }),
534            _ => None,
535        }
536    }
537
538    fn convert_try(expr: &ast::Expr, context: &RewriteContext<'_>) -> ast::Expr {
539        match expr.kind {
540            ast::ExprKind::MacCall(ref mac) if context.config.use_try_shorthand() => {
541                if let Some(subexpr) = convert_try_mac(mac, context) {
542                    subexpr
543                } else {
544                    expr.clone()
545                }
546            }
547            _ => expr.clone(),
548        }
549    }
550}
551
552impl Rewrite for Chain {
553    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
554        self.rewrite_result(context, shape).ok()
555    }
556
557    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
558        debug!("rewrite chain {:?} {:?}", self, shape);
559
560        let mut formatter = match context.config.indent_style() {
561            IndentStyle::Block => {
562                Box::new(ChainFormatterBlock::new(self)) as Box<dyn ChainFormatter>
563            }
564            IndentStyle::Visual => {
565                Box::new(ChainFormatterVisual::new(self)) as Box<dyn ChainFormatter>
566            }
567        };
568
569        formatter.format_root(&self.parent, context, shape)?;
570        if let Some(result) = formatter.pure_root() {
571            return wrap_str(result, context.config.max_width(), shape)
572                .max_width_error(shape.width, self.parent.span);
573        }
574
575        let first = self.children.first().unwrap_or(&self.parent);
576        let last = self.children.last().unwrap_or(&self.parent);
577        let children_span = mk_sp(first.span.lo(), last.span.hi());
578        let full_span = self.parent.span.with_hi(children_span.hi());
579
580        // Decide how to layout the rest of the chain.
581        let child_shape = formatter.child_shape(context, shape, children_span)?;
582
583        formatter.format_children(context, child_shape)?;
584        formatter.format_last_child(context, shape, child_shape)?;
585
586        let result = formatter.join_rewrites(context, child_shape)?;
587        wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span)
588    }
589}
590
591// There are a few types for formatting chains. This is because there is a lot
592// in common between formatting with block vs visual indent, but they are
593// different enough that branching on the indent all over the place gets ugly.
594// Anything that can format a chain is a ChainFormatter.
595trait ChainFormatter {
596    // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
597    // Root is the parent plus any other chain items placed on the first line to
598    // avoid an orphan. E.g.,
599    // ```text
600    // foo.bar
601    //     .baz()
602    // ```
603    // If `bar` were not part of the root, then foo would be orphaned and 'float'.
604    fn format_root(
605        &mut self,
606        parent: &ChainItem,
607        context: &RewriteContext<'_>,
608        shape: Shape,
609    ) -> Result<(), RewriteError>;
610    fn child_shape(
611        &self,
612        context: &RewriteContext<'_>,
613        shape: Shape,
614        span: Span,
615    ) -> Result<Shape, ExceedsMaxWidthError>;
616    fn format_children(
617        &mut self,
618        context: &RewriteContext<'_>,
619        child_shape: Shape,
620    ) -> Result<(), RewriteError>;
621    fn format_last_child(
622        &mut self,
623        context: &RewriteContext<'_>,
624        shape: Shape,
625        child_shape: Shape,
626    ) -> Result<(), RewriteError>;
627    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult;
628    // Returns `Some` if the chain is only a root, None otherwise.
629    fn pure_root(&mut self) -> Option<String>;
630}
631
632// Data and behaviour that is shared by both chain formatters. The concrete
633// formatters can delegate much behaviour to `ChainFormatterShared`.
634struct ChainFormatterShared<'a> {
635    // The current working set of child items.
636    children: &'a [ChainItem],
637    // The current rewrites of items (includes trailing `?`s, but not any way to
638    // connect the rewrites together).
639    rewrites: Vec<String>,
640    // Whether the chain can fit on one line.
641    fits_single_line: bool,
642    // The number of children in the chain. This is not equal to `self.children.len()`
643    // because `self.children` will change size as we process the chain.
644    child_count: usize,
645    // Whether elements are allowed to overflow past the max_width limit
646    allow_overflow: bool,
647}
648
649impl<'a> ChainFormatterShared<'a> {
650    fn new(chain: &'a Chain) -> ChainFormatterShared<'a> {
651        ChainFormatterShared {
652            children: &chain.children,
653            rewrites: Vec::with_capacity(chain.children.len() + 1),
654            fits_single_line: false,
655            child_count: chain.children.len(),
656            // TODO(calebcartwright)
657            allow_overflow: false,
658        }
659    }
660
661    fn pure_root(&mut self) -> Option<String> {
662        if self.children.is_empty() {
663            assert_eq!(self.rewrites.len(), 1);
664            Some(self.rewrites.pop().unwrap())
665        } else {
666            None
667        }
668    }
669
670    fn format_children(
671        &mut self,
672        context: &RewriteContext<'_>,
673        child_shape: Shape,
674    ) -> Result<(), RewriteError> {
675        for item in &self.children[..self.children.len() - 1] {
676            let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?;
677            self.rewrites.push(rewrite);
678        }
679        Ok(())
680    }
681
682    // Rewrite the last child. The last child of a chain requires special treatment. We need to
683    // know whether 'overflowing' the last child make a better formatting:
684    //
685    // A chain with overflowing the last child:
686    // ```text
687    // parent.child1.child2.last_child(
688    //     a,
689    //     b,
690    //     c,
691    // )
692    // ```
693    //
694    // A chain without overflowing the last child (in vertical layout):
695    // ```text
696    // parent
697    //     .child1
698    //     .child2
699    //     .last_child(a, b, c)
700    // ```
701    //
702    // In particular, overflowing is effective when the last child is a method with a multi-lined
703    // block-like argument (e.g., closure):
704    // ```text
705    // parent.child1.child2.last_child(|a, b, c| {
706    //     let x = foo(a, b, c);
707    //     let y = bar(a, b, c);
708    //
709    //     // ...
710    //
711    //     result
712    // })
713    // ```
714    fn format_last_child(
715        &mut self,
716        may_extend: bool,
717        context: &RewriteContext<'_>,
718        shape: Shape,
719        child_shape: Shape,
720    ) -> Result<(), RewriteError> {
721        let last = self.children.last().unknown_error()?;
722        let extendable = may_extend && last_line_extendable(&self.rewrites[0]);
723        let prev_last_line_width = last_line_width(&self.rewrites[0]);
724
725        // Total of all items excluding the last.
726        let almost_total = if extendable {
727            prev_last_line_width
728        } else {
729            self.rewrites
730                .iter()
731                .map(|rw| utils::unicode_str_width(rw))
732                .sum()
733        } + last.tries;
734        let one_line_budget = if self.child_count == 1 {
735            shape.width
736        } else {
737            min(shape.width, context.config.chain_width())
738        }
739        .saturating_sub(almost_total);
740
741        let all_in_one_line = !self.children.iter().any(ChainItem::is_comment)
742            && self.rewrites.iter().all(|s| !s.contains('\n'))
743            && one_line_budget > 0;
744        let last_shape = if all_in_one_line {
745            shape.sub_width(last.tries, last.span)?
746        } else if extendable {
747            child_shape.sub_width(last.tries, last.span)?
748        } else {
749            child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries, last.span)?
750        };
751
752        let mut last_subexpr_str = None;
753        if all_in_one_line || extendable {
754            // First we try to 'overflow' the last child and see if it looks better than using
755            // vertical layout.
756            let one_line_shape = if context.use_block_indent() {
757                last_shape.offset_left_opt(almost_total)
758            } else {
759                last_shape
760                    .visual_indent(almost_total)
761                    .sub_width_opt(almost_total)
762            };
763
764            if let Some(one_line_shape) = one_line_shape {
765                if let Ok(rw) = last.rewrite_result(context, one_line_shape) {
766                    // We allow overflowing here only if both of the following conditions match:
767                    // 1. The entire chain fits in a single line except the last child.
768                    // 2. `last_child_str.lines().count() >= 5`.
769                    let line_count = rw.lines().count();
770                    let could_fit_single_line = first_line_width(&rw) <= one_line_budget;
771                    if could_fit_single_line && line_count >= 5 {
772                        last_subexpr_str = Some(rw);
773                        self.fits_single_line = all_in_one_line;
774                    } else {
775                        // We could not know whether overflowing is better than using vertical
776                        // layout, just by looking at the overflowed rewrite. Now we rewrite the
777                        // last child on its own line, and compare two rewrites to choose which is
778                        // better.
779                        let last_shape = child_shape.sub_width(
780                            shape.rhs_overhead(context.config) + last.tries,
781                            last.span,
782                        )?;
783                        match last.rewrite_result(context, last_shape) {
784                            Ok(ref new_rw) if !could_fit_single_line => {
785                                last_subexpr_str = Some(new_rw.clone());
786                            }
787                            Ok(ref new_rw) if new_rw.lines().count() >= line_count => {
788                                last_subexpr_str = Some(rw);
789                                self.fits_single_line = could_fit_single_line && all_in_one_line;
790                            }
791                            Ok(new_rw) => {
792                                last_subexpr_str = Some(new_rw);
793                            }
794                            _ => {
795                                last_subexpr_str = Some(rw);
796                                self.fits_single_line = could_fit_single_line && all_in_one_line;
797                            }
798                        }
799                    }
800                }
801            }
802        }
803
804        let last_shape = if context.use_block_indent() {
805            last_shape
806        } else {
807            child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries, last.span)?
808        };
809
810        let last_subexpr_str =
811            last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?);
812        self.rewrites.push(last_subexpr_str);
813        Ok(())
814    }
815
816    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
817        let connector = if self.fits_single_line {
818            // Yay, we can put everything on one line.
819            Cow::from("")
820        } else {
821            // Use new lines.
822            if context.force_one_line_chain.get() {
823                return Err(RewriteError::ExceedsMaxWidth {
824                    configured_width: child_shape.width,
825                    span: self.children.last().unknown_error()?.span,
826                });
827            }
828            child_shape.to_string_with_newline(context.config)
829        };
830
831        let mut rewrite_iter = self.rewrites.iter();
832        let mut result = rewrite_iter.next().unwrap().clone();
833        let children_iter = self.children.iter();
834        let iter = rewrite_iter.zip(children_iter);
835
836        for (rewrite, chain_item) in iter {
837            match chain_item.kind {
838                ChainItemKind::Comment(_, CommentPosition::Back) => result.push(' '),
839                ChainItemKind::Comment(_, CommentPosition::Top) => result.push_str(&connector),
840                _ => result.push_str(&connector),
841            }
842            result.push_str(rewrite);
843        }
844
845        Ok(result)
846    }
847}
848
849// Formats a chain using block indent.
850struct ChainFormatterBlock<'a> {
851    shared: ChainFormatterShared<'a>,
852    root_ends_with_block: bool,
853}
854
855impl<'a> ChainFormatterBlock<'a> {
856    fn new(chain: &'a Chain) -> ChainFormatterBlock<'a> {
857        ChainFormatterBlock {
858            shared: ChainFormatterShared::new(chain),
859            root_ends_with_block: false,
860        }
861    }
862}
863
864impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
865    fn format_root(
866        &mut self,
867        parent: &ChainItem,
868        context: &RewriteContext<'_>,
869        shape: Shape,
870    ) -> Result<(), RewriteError> {
871        let mut root_rewrite: String = parent.rewrite_result(context, shape)?;
872
873        let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
874        let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
875
876        while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') {
877            let item = &self.shared.children[0];
878            if let ChainItemKind::Comment(..) = item.kind {
879                break;
880            }
881            let shape = shape.offset_left(root_rewrite.len(), item.span)?;
882            match &item.rewrite_result(context, shape) {
883                Ok(rewrite) => root_rewrite.push_str(rewrite),
884                Err(_) => break,
885            }
886
887            root_ends_with_block = last_line_extendable(&root_rewrite);
888
889            self.shared.children = &self.shared.children[1..];
890            if self.shared.children.is_empty() {
891                break;
892            }
893        }
894        self.shared.rewrites.push(root_rewrite);
895        self.root_ends_with_block = root_ends_with_block;
896        Ok(())
897    }
898
899    fn child_shape(
900        &self,
901        context: &RewriteContext<'_>,
902        shape: Shape,
903        _span: Span,
904    ) -> Result<Shape, ExceedsMaxWidthError> {
905        let block_end = self.root_ends_with_block;
906        Ok(get_block_child_shape(block_end, context, shape))
907    }
908
909    fn format_children(
910        &mut self,
911        context: &RewriteContext<'_>,
912        child_shape: Shape,
913    ) -> Result<(), RewriteError> {
914        self.shared.format_children(context, child_shape)
915    }
916
917    fn format_last_child(
918        &mut self,
919        context: &RewriteContext<'_>,
920        shape: Shape,
921        child_shape: Shape,
922    ) -> Result<(), RewriteError> {
923        self.shared
924            .format_last_child(true, context, shape, child_shape)
925    }
926
927    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
928        self.shared.join_rewrites(context, child_shape)
929    }
930
931    fn pure_root(&mut self) -> Option<String> {
932        self.shared.pure_root()
933    }
934}
935
936// Format a chain using visual indent.
937struct ChainFormatterVisual<'a> {
938    shared: ChainFormatterShared<'a>,
939    // The extra offset from the chain's shape to the position of the `.`
940    offset: usize,
941}
942
943impl<'a> ChainFormatterVisual<'a> {
944    fn new(chain: &'a Chain) -> ChainFormatterVisual<'a> {
945        ChainFormatterVisual {
946            shared: ChainFormatterShared::new(chain),
947            offset: 0,
948        }
949    }
950}
951
952impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
953    fn format_root(
954        &mut self,
955        parent: &ChainItem,
956        context: &RewriteContext<'_>,
957        shape: Shape,
958    ) -> Result<(), RewriteError> {
959        let parent_shape = shape.visual_indent(0);
960        let mut root_rewrite = parent.rewrite_result(context, parent_shape)?;
961        let multiline = root_rewrite.contains('\n');
962        self.offset = if multiline {
963            last_line_width(&root_rewrite).saturating_sub(shape.used_width())
964        } else {
965            trimmed_last_line_width(&root_rewrite)
966        };
967
968        if !multiline || parent.kind.is_block_like(context, &root_rewrite) {
969            let item = &self.shared.children[0];
970            if let ChainItemKind::Comment(..) = item.kind {
971                self.shared.rewrites.push(root_rewrite);
972                return Ok(());
973            }
974            let child_shape = parent_shape
975                .visual_indent(self.offset)
976                .sub_width(self.offset, item.span)?;
977            let rewrite = item.rewrite_result(context, child_shape)?;
978            if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
979                root_rewrite.push_str(&rewrite);
980            } else {
981                // We couldn't fit in at the visual indent, try the last
982                // indent.
983                let rewrite = item.rewrite_result(context, parent_shape)?;
984                root_rewrite.push_str(&rewrite);
985                self.offset = 0;
986            }
987
988            self.shared.children = &self.shared.children[1..];
989        }
990
991        self.shared.rewrites.push(root_rewrite);
992        Ok(())
993    }
994
995    fn child_shape(
996        &self,
997        context: &RewriteContext<'_>,
998        shape: Shape,
999        span: Span,
1000    ) -> Result<Shape, ExceedsMaxWidthError> {
1001        get_visual_style_child_shape(
1002            context,
1003            shape,
1004            self.offset,
1005            // TODO(calebcartwright): self.shared.permissibly_overflowing_parent,
1006            false,
1007            span,
1008        )
1009    }
1010
1011    fn format_children(
1012        &mut self,
1013        context: &RewriteContext<'_>,
1014        child_shape: Shape,
1015    ) -> Result<(), RewriteError> {
1016        self.shared.format_children(context, child_shape)
1017    }
1018
1019    fn format_last_child(
1020        &mut self,
1021        context: &RewriteContext<'_>,
1022        shape: Shape,
1023        child_shape: Shape,
1024    ) -> Result<(), RewriteError> {
1025        self.shared
1026            .format_last_child(false, context, shape, child_shape)
1027    }
1028
1029    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
1030        self.shared.join_rewrites(context, child_shape)
1031    }
1032
1033    fn pure_root(&mut self) -> Option<String> {
1034        self.shared.pure_root()
1035    }
1036}
1037
1038/// Removes try operators (`?`s) that appear in the given string. If removing
1039/// them leaves an empty line, remove that line as well unless it is the first
1040/// line (we need the first newline for detecting pre/post comment).
1041fn trim_tries(s: &str) -> String {
1042    let mut result = String::with_capacity(s.len());
1043    let mut line_buffer = String::with_capacity(s.len());
1044    for (kind, rich_char) in CharClasses::new(s.chars()) {
1045        match rich_char.get_char() {
1046            '\n' => {
1047                if result.is_empty() || !line_buffer.trim().is_empty() {
1048                    result.push_str(&line_buffer);
1049                    result.push('\n')
1050                }
1051                line_buffer.clear();
1052            }
1053            '?' if kind == FullCodeCharKind::Normal => continue,
1054            c => line_buffer.push(c),
1055        }
1056    }
1057    if !line_buffer.trim().is_empty() {
1058        result.push_str(&line_buffer);
1059    }
1060    result
1061}
1062
1063/// Whether a method call's receiver needs parenthesis, like
1064/// ```rust,ignore
1065/// || .. .method();
1066/// || 1.. .method();
1067/// 1. .method();
1068/// ```
1069/// Which all need parenthesis or a space before `.method()`.
1070fn should_add_parens(expr: &ast::Expr, context: &RewriteContext<'_>) -> bool {
1071    match expr.kind {
1072        ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit, context),
1073        ast::ExprKind::Closure(ref cl) => match cl.body.kind {
1074            ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true,
1075            ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit, context),
1076            _ => false,
1077        },
1078        _ => false,
1079    }
1080}