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