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_method_call_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_method_call_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_method_call_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) =
335            ChainItemKind::from_ast(context, &expr.expr, expr.is_method_call_receiver);
336        ChainItem { kind, tries, span }
337    }
338
339    fn comment(span: Span, comment: String, pos: CommentPosition) -> ChainItem {
340        ChainItem {
341            kind: ChainItemKind::Comment(comment, pos),
342            tries: 0,
343            span,
344        }
345    }
346
347    fn is_comment(&self) -> bool {
348        matches!(self.kind, ChainItemKind::Comment(..))
349    }
350
351    fn rewrite_method_call(
352        method_name: symbol::Ident,
353        types: &[ast::GenericArg],
354        args: &[Box<ast::Expr>],
355        span: Span,
356        context: &RewriteContext<'_>,
357        shape: Shape,
358    ) -> RewriteResult {
359        let type_str = if types.is_empty() {
360            String::new()
361        } else {
362            let type_list = types
363                .iter()
364                .map(|ty| ty.rewrite_result(context, shape))
365                .collect::<Result<Vec<_>, RewriteError>>()?;
366
367            format!("::<{}>", type_list.join(", "))
368        };
369        let callee_str = format!(".{}{}", rewrite_ident(context, method_name), type_str);
370        rewrite_call(context, &callee_str, &args, span, shape)
371    }
372}
373
374#[derive(Debug)]
375struct Chain {
376    parent: ChainItem,
377    children: Vec<ChainItem>,
378}
379
380impl Chain {
381    fn from_ast(expr: &ast::Expr, context: &RewriteContext<'_>) -> Chain {
382        let subexpr_list = Self::make_subexpr_list(expr, context);
383
384        // Un-parse the expression tree into ChainItems
385        let mut rev_children = vec![];
386        let mut sub_tries = 0;
387        for subexpr in &subexpr_list {
388            match subexpr.expr.kind {
389                ast::ExprKind::Try(_) => sub_tries += 1,
390                _ => {
391                    rev_children.push(ChainItem::new(context, subexpr, sub_tries));
392                    sub_tries = 0;
393                }
394            }
395        }
396
397        fn is_tries(s: &str) -> bool {
398            s.chars().all(|c| c == '?')
399        }
400
401        fn is_post_comment(s: &str) -> bool {
402            let comment_start_index = s.chars().position(|c| c == '/');
403            if comment_start_index.is_none() {
404                return false;
405            }
406
407            let newline_index = s.chars().position(|c| c == '\n');
408            if newline_index.is_none() {
409                return true;
410            }
411
412            comment_start_index.unwrap() < newline_index.unwrap()
413        }
414
415        fn handle_post_comment(
416            post_comment_span: Span,
417            post_comment_snippet: &str,
418            prev_span_end: &mut BytePos,
419            children: &mut Vec<ChainItem>,
420        ) {
421            let white_spaces: &[_] = &[' ', '\t'];
422            if post_comment_snippet
423                .trim_matches(white_spaces)
424                .starts_with('\n')
425            {
426                // No post comment.
427                return;
428            }
429            let trimmed_snippet = trim_tries(post_comment_snippet);
430            if is_post_comment(&trimmed_snippet) {
431                children.push(ChainItem::comment(
432                    post_comment_span,
433                    trimmed_snippet.trim().to_owned(),
434                    CommentPosition::Back,
435                ));
436                *prev_span_end = post_comment_span.hi();
437            }
438        }
439
440        let parent = rev_children.pop().unwrap();
441        let mut children = vec![];
442        let mut prev_span_end = parent.span.hi();
443        let mut iter = rev_children.into_iter().rev().peekable();
444        if let Some(first_chain_item) = iter.peek() {
445            let comment_span = mk_sp(prev_span_end, first_chain_item.span.lo());
446            let comment_snippet = context.snippet(comment_span);
447            if !is_tries(comment_snippet.trim()) {
448                handle_post_comment(
449                    comment_span,
450                    comment_snippet,
451                    &mut prev_span_end,
452                    &mut children,
453                );
454            }
455        }
456        while let Some(chain_item) = iter.next() {
457            let comment_snippet = context.snippet(chain_item.span);
458            // FIXME: Figure out the way to get a correct span when converting `try!` to `?`.
459            let handle_comment =
460                !(context.config.use_try_shorthand() || is_tries(comment_snippet.trim()));
461
462            // Pre-comment
463            if handle_comment {
464                let pre_comment_span = mk_sp(prev_span_end, chain_item.span.lo());
465                let pre_comment_snippet = trim_tries(context.snippet(pre_comment_span));
466                let (pre_comment, _) = extract_pre_comment(&pre_comment_snippet);
467                match pre_comment {
468                    Some(ref comment) if !comment.is_empty() => {
469                        children.push(ChainItem::comment(
470                            pre_comment_span,
471                            comment.to_owned(),
472                            CommentPosition::Top,
473                        ));
474                    }
475                    _ => (),
476                }
477            }
478
479            prev_span_end = chain_item.span.hi();
480            children.push(chain_item);
481
482            // Post-comment
483            if !handle_comment || iter.peek().is_none() {
484                continue;
485            }
486
487            let next_lo = iter.peek().unwrap().span.lo();
488            let post_comment_span = mk_sp(prev_span_end, next_lo);
489            let post_comment_snippet = context.snippet(post_comment_span);
490            handle_post_comment(
491                post_comment_span,
492                post_comment_snippet,
493                &mut prev_span_end,
494                &mut children,
495            );
496        }
497
498        Chain { parent, children }
499    }
500
501    // Returns a Vec of the prefixes of the chain.
502    // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a']
503    fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<SubExpr> {
504        let mut subexpr_list = vec![SubExpr {
505            expr: expr.clone(),
506            is_method_call_receiver: false,
507        }];
508
509        while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) {
510            subexpr_list.push(subexpr);
511        }
512
513        subexpr_list
514    }
515
516    // Returns the expression's subexpression, if it exists. When the subexpr
517    // is a try! macro, we'll convert it to shorthand when the option is set.
518    fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option<SubExpr> {
519        match expr.expr.kind {
520            ast::ExprKind::MethodCall(ref call) => Some(SubExpr {
521                expr: Self::convert_try(&call.receiver, context),
522                is_method_call_receiver: true,
523            }),
524            ast::ExprKind::Field(ref subexpr, _)
525            | ast::ExprKind::Try(ref subexpr)
526            | ast::ExprKind::Await(ref subexpr, _)
527            | ast::ExprKind::Use(ref subexpr, _)
528            | ast::ExprKind::Yield(ast::YieldKind::Postfix(ref subexpr)) => Some(SubExpr {
529                expr: Self::convert_try(subexpr, context),
530                is_method_call_receiver: false,
531            }),
532            _ => None,
533        }
534    }
535
536    fn convert_try(expr: &ast::Expr, context: &RewriteContext<'_>) -> ast::Expr {
537        match expr.kind {
538            ast::ExprKind::MacCall(ref mac) if context.config.use_try_shorthand() => {
539                if let Some(subexpr) = convert_try_mac(mac, context) {
540                    subexpr
541                } else {
542                    expr.clone()
543                }
544            }
545            _ => expr.clone(),
546        }
547    }
548}
549
550impl Rewrite for Chain {
551    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
552        self.rewrite_result(context, shape).ok()
553    }
554
555    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
556        debug!("rewrite chain {:?} {:?}", self, shape);
557
558        let mut formatter = match context.config.indent_style() {
559            IndentStyle::Block => {
560                Box::new(ChainFormatterBlock::new(self)) as Box<dyn ChainFormatter>
561            }
562            IndentStyle::Visual => {
563                Box::new(ChainFormatterVisual::new(self)) as Box<dyn ChainFormatter>
564            }
565        };
566
567        formatter.format_root(&self.parent, context, shape)?;
568        if let Some(result) = formatter.pure_root() {
569            return wrap_str(result, context.config.max_width(), shape)
570                .max_width_error(shape.width, self.parent.span);
571        }
572
573        let first = self.children.first().unwrap_or(&self.parent);
574        let last = self.children.last().unwrap_or(&self.parent);
575        let children_span = mk_sp(first.span.lo(), last.span.hi());
576        let full_span = self.parent.span.with_hi(children_span.hi());
577
578        // Decide how to layout the rest of the chain.
579        let child_shape = formatter.child_shape(context, shape, children_span)?;
580
581        formatter.format_children(context, child_shape)?;
582        formatter.format_last_child(context, shape, child_shape)?;
583
584        let result = formatter.join_rewrites(context, child_shape)?;
585        wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span)
586    }
587}
588
589// There are a few types for formatting chains. This is because there is a lot
590// in common between formatting with block vs visual indent, but they are
591// different enough that branching on the indent all over the place gets ugly.
592// Anything that can format a chain is a ChainFormatter.
593trait ChainFormatter {
594    // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
595    // Root is the parent plus any other chain items placed on the first line to
596    // avoid an orphan. E.g.,
597    // ```text
598    // foo.bar
599    //     .baz()
600    // ```
601    // If `bar` were not part of the root, then foo would be orphaned and 'float'.
602    fn format_root(
603        &mut self,
604        parent: &ChainItem,
605        context: &RewriteContext<'_>,
606        shape: Shape,
607    ) -> Result<(), RewriteError>;
608    fn child_shape(
609        &self,
610        context: &RewriteContext<'_>,
611        shape: Shape,
612        span: Span,
613    ) -> Result<Shape, ExceedsMaxWidthError>;
614    fn format_children(
615        &mut self,
616        context: &RewriteContext<'_>,
617        child_shape: Shape,
618    ) -> Result<(), RewriteError>;
619    fn format_last_child(
620        &mut self,
621        context: &RewriteContext<'_>,
622        shape: Shape,
623        child_shape: Shape,
624    ) -> Result<(), RewriteError>;
625    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult;
626    // Returns `Some` if the chain is only a root, None otherwise.
627    fn pure_root(&mut self) -> Option<String>;
628}
629
630// Data and behaviour that is shared by both chain formatters. The concrete
631// formatters can delegate much behaviour to `ChainFormatterShared`.
632struct ChainFormatterShared<'a> {
633    // The current working set of child items.
634    children: &'a [ChainItem],
635    // The current rewrites of items (includes trailing `?`s, but not any way to
636    // connect the rewrites together).
637    rewrites: Vec<String>,
638    // Whether the chain can fit on one line.
639    fits_single_line: bool,
640    // The number of children in the chain. This is not equal to `self.children.len()`
641    // because `self.children` will change size as we process the chain.
642    child_count: usize,
643    // Whether elements are allowed to overflow past the max_width limit
644    allow_overflow: bool,
645}
646
647impl<'a> ChainFormatterShared<'a> {
648    fn new(chain: &'a Chain) -> ChainFormatterShared<'a> {
649        ChainFormatterShared {
650            children: &chain.children,
651            rewrites: Vec::with_capacity(chain.children.len() + 1),
652            fits_single_line: false,
653            child_count: chain.children.len(),
654            // TODO(calebcartwright)
655            allow_overflow: false,
656        }
657    }
658
659    fn pure_root(&mut self) -> Option<String> {
660        if self.children.is_empty() {
661            assert_eq!(self.rewrites.len(), 1);
662            Some(self.rewrites.pop().unwrap())
663        } else {
664            None
665        }
666    }
667
668    fn format_children(
669        &mut self,
670        context: &RewriteContext<'_>,
671        child_shape: Shape,
672    ) -> Result<(), RewriteError> {
673        for item in &self.children[..self.children.len() - 1] {
674            let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?;
675            self.rewrites.push(rewrite);
676        }
677        Ok(())
678    }
679
680    // Rewrite the last child. The last child of a chain requires special treatment. We need to
681    // know whether 'overflowing' the last child make a better formatting:
682    //
683    // A chain with overflowing the last child:
684    // ```text
685    // parent.child1.child2.last_child(
686    //     a,
687    //     b,
688    //     c,
689    // )
690    // ```
691    //
692    // A chain without overflowing the last child (in vertical layout):
693    // ```text
694    // parent
695    //     .child1
696    //     .child2
697    //     .last_child(a, b, c)
698    // ```
699    //
700    // In particular, overflowing is effective when the last child is a method with a multi-lined
701    // block-like argument (e.g., closure):
702    // ```text
703    // parent.child1.child2.last_child(|a, b, c| {
704    //     let x = foo(a, b, c);
705    //     let y = bar(a, b, c);
706    //
707    //     // ...
708    //
709    //     result
710    // })
711    // ```
712    fn format_last_child(
713        &mut self,
714        may_extend: bool,
715        context: &RewriteContext<'_>,
716        shape: Shape,
717        child_shape: Shape,
718    ) -> Result<(), RewriteError> {
719        let last = self.children.last().unknown_error()?;
720        let extendable = may_extend && last_line_extendable(&self.rewrites[0]);
721        let prev_last_line_width = last_line_width(&self.rewrites[0]);
722
723        // Total of all items excluding the last.
724        let almost_total = if extendable {
725            prev_last_line_width
726        } else {
727            self.rewrites
728                .iter()
729                .map(|rw| utils::unicode_str_width(rw))
730                .sum()
731        } + last.tries;
732        let one_line_budget = if self.child_count == 1 {
733            shape.width
734        } else {
735            min(shape.width, context.config.chain_width())
736        }
737        .saturating_sub(almost_total);
738
739        let all_in_one_line = !self.children.iter().any(ChainItem::is_comment)
740            && self.rewrites.iter().all(|s| !s.contains('\n'))
741            && one_line_budget > 0;
742        let last_shape = if all_in_one_line {
743            shape.sub_width(last.tries, last.span)?
744        } else if extendable {
745            child_shape.sub_width(last.tries, last.span)?
746        } else {
747            child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries, last.span)?
748        };
749
750        let mut last_subexpr_str = None;
751        if all_in_one_line || extendable {
752            // First we try to 'overflow' the last child and see if it looks better than using
753            // vertical layout.
754            let one_line_shape = if context.use_block_indent() {
755                last_shape.offset_left_opt(almost_total)
756            } else {
757                last_shape
758                    .visual_indent(almost_total)
759                    .sub_width_opt(almost_total)
760            };
761
762            if let Some(one_line_shape) = one_line_shape {
763                if let Ok(rw) = last.rewrite_result(context, one_line_shape) {
764                    // We allow overflowing here only if both of the following conditions match:
765                    // 1. The entire chain fits in a single line except the last child.
766                    // 2. `last_child_str.lines().count() >= 5`.
767                    let line_count = rw.lines().count();
768                    let could_fit_single_line = first_line_width(&rw) <= one_line_budget;
769                    if could_fit_single_line && line_count >= 5 {
770                        last_subexpr_str = Some(rw);
771                        self.fits_single_line = all_in_one_line;
772                    } else {
773                        // We could not know whether overflowing is better than using vertical
774                        // layout, just by looking at the overflowed rewrite. Now we rewrite the
775                        // last child on its own line, and compare two rewrites to choose which is
776                        // better.
777                        let last_shape = child_shape.sub_width(
778                            shape.rhs_overhead(context.config) + last.tries,
779                            last.span,
780                        )?;
781                        match last.rewrite_result(context, last_shape) {
782                            Ok(ref new_rw) if !could_fit_single_line => {
783                                last_subexpr_str = Some(new_rw.clone());
784                            }
785                            Ok(ref new_rw) if new_rw.lines().count() >= line_count => {
786                                last_subexpr_str = Some(rw);
787                                self.fits_single_line = could_fit_single_line && all_in_one_line;
788                            }
789                            Ok(new_rw) => {
790                                last_subexpr_str = Some(new_rw);
791                            }
792                            _ => {
793                                last_subexpr_str = Some(rw);
794                                self.fits_single_line = could_fit_single_line && all_in_one_line;
795                            }
796                        }
797                    }
798                }
799            }
800        }
801
802        let last_shape = if context.use_block_indent() {
803            last_shape
804        } else {
805            child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries, last.span)?
806        };
807
808        let last_subexpr_str =
809            last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?);
810        self.rewrites.push(last_subexpr_str);
811        Ok(())
812    }
813
814    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
815        let connector = if self.fits_single_line {
816            // Yay, we can put everything on one line.
817            Cow::from("")
818        } else {
819            // Use new lines.
820            if context.force_one_line_chain.get() {
821                return Err(RewriteError::ExceedsMaxWidth {
822                    configured_width: child_shape.width,
823                    span: self.children.last().unknown_error()?.span,
824                });
825            }
826            child_shape.to_string_with_newline(context.config)
827        };
828
829        let mut rewrite_iter = self.rewrites.iter();
830        let mut result = rewrite_iter.next().unwrap().clone();
831        let children_iter = self.children.iter();
832        let iter = rewrite_iter.zip(children_iter);
833
834        for (rewrite, chain_item) in iter {
835            match chain_item.kind {
836                ChainItemKind::Comment(_, CommentPosition::Back) => result.push(' '),
837                ChainItemKind::Comment(_, CommentPosition::Top) => result.push_str(&connector),
838                _ => result.push_str(&connector),
839            }
840            result.push_str(rewrite);
841        }
842
843        Ok(result)
844    }
845}
846
847// Formats a chain using block indent.
848struct ChainFormatterBlock<'a> {
849    shared: ChainFormatterShared<'a>,
850    root_ends_with_block: bool,
851}
852
853impl<'a> ChainFormatterBlock<'a> {
854    fn new(chain: &'a Chain) -> ChainFormatterBlock<'a> {
855        ChainFormatterBlock {
856            shared: ChainFormatterShared::new(chain),
857            root_ends_with_block: false,
858        }
859    }
860}
861
862impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
863    fn format_root(
864        &mut self,
865        parent: &ChainItem,
866        context: &RewriteContext<'_>,
867        shape: Shape,
868    ) -> Result<(), RewriteError> {
869        let mut root_rewrite: String = parent.rewrite_result(context, shape)?;
870
871        let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
872        let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
873
874        while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') {
875            let item = &self.shared.children[0];
876            if let ChainItemKind::Comment(..) = item.kind {
877                break;
878            }
879            let shape = shape.offset_left(root_rewrite.len(), item.span)?;
880            match &item.rewrite_result(context, shape) {
881                Ok(rewrite) => root_rewrite.push_str(rewrite),
882                Err(_) => break,
883            }
884
885            root_ends_with_block = last_line_extendable(&root_rewrite);
886
887            self.shared.children = &self.shared.children[1..];
888            if self.shared.children.is_empty() {
889                break;
890            }
891        }
892        self.shared.rewrites.push(root_rewrite);
893        self.root_ends_with_block = root_ends_with_block;
894        Ok(())
895    }
896
897    fn child_shape(
898        &self,
899        context: &RewriteContext<'_>,
900        shape: Shape,
901        _span: Span,
902    ) -> Result<Shape, ExceedsMaxWidthError> {
903        let block_end = self.root_ends_with_block;
904        Ok(get_block_child_shape(block_end, context, shape))
905    }
906
907    fn format_children(
908        &mut self,
909        context: &RewriteContext<'_>,
910        child_shape: Shape,
911    ) -> Result<(), RewriteError> {
912        self.shared.format_children(context, child_shape)
913    }
914
915    fn format_last_child(
916        &mut self,
917        context: &RewriteContext<'_>,
918        shape: Shape,
919        child_shape: Shape,
920    ) -> Result<(), RewriteError> {
921        self.shared
922            .format_last_child(true, context, shape, child_shape)
923    }
924
925    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
926        self.shared.join_rewrites(context, child_shape)
927    }
928
929    fn pure_root(&mut self) -> Option<String> {
930        self.shared.pure_root()
931    }
932}
933
934// Format a chain using visual indent.
935struct ChainFormatterVisual<'a> {
936    shared: ChainFormatterShared<'a>,
937    // The extra offset from the chain's shape to the position of the `.`
938    offset: usize,
939}
940
941impl<'a> ChainFormatterVisual<'a> {
942    fn new(chain: &'a Chain) -> ChainFormatterVisual<'a> {
943        ChainFormatterVisual {
944            shared: ChainFormatterShared::new(chain),
945            offset: 0,
946        }
947    }
948}
949
950impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
951    fn format_root(
952        &mut self,
953        parent: &ChainItem,
954        context: &RewriteContext<'_>,
955        shape: Shape,
956    ) -> Result<(), RewriteError> {
957        let parent_shape = shape.visual_indent(0);
958        let mut root_rewrite = parent.rewrite_result(context, parent_shape)?;
959        let multiline = root_rewrite.contains('\n');
960        self.offset = if multiline {
961            last_line_width(&root_rewrite).saturating_sub(shape.used_width())
962        } else {
963            trimmed_last_line_width(&root_rewrite)
964        };
965
966        if !multiline || parent.kind.is_block_like(context, &root_rewrite) {
967            let item = &self.shared.children[0];
968            if let ChainItemKind::Comment(..) = item.kind {
969                self.shared.rewrites.push(root_rewrite);
970                return Ok(());
971            }
972            let child_shape = parent_shape
973                .visual_indent(self.offset)
974                .sub_width(self.offset, item.span)?;
975            let rewrite = item.rewrite_result(context, child_shape)?;
976            if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
977                root_rewrite.push_str(&rewrite);
978            } else {
979                // We couldn't fit in at the visual indent, try the last
980                // indent.
981                let rewrite = item.rewrite_result(context, parent_shape)?;
982                root_rewrite.push_str(&rewrite);
983                self.offset = 0;
984            }
985
986            self.shared.children = &self.shared.children[1..];
987        }
988
989        self.shared.rewrites.push(root_rewrite);
990        Ok(())
991    }
992
993    fn child_shape(
994        &self,
995        context: &RewriteContext<'_>,
996        shape: Shape,
997        span: Span,
998    ) -> Result<Shape, ExceedsMaxWidthError> {
999        get_visual_style_child_shape(
1000            context,
1001            shape,
1002            self.offset,
1003            // TODO(calebcartwright): self.shared.permissibly_overflowing_parent,
1004            false,
1005            span,
1006        )
1007    }
1008
1009    fn format_children(
1010        &mut self,
1011        context: &RewriteContext<'_>,
1012        child_shape: Shape,
1013    ) -> Result<(), RewriteError> {
1014        self.shared.format_children(context, child_shape)
1015    }
1016
1017    fn format_last_child(
1018        &mut self,
1019        context: &RewriteContext<'_>,
1020        shape: Shape,
1021        child_shape: Shape,
1022    ) -> Result<(), RewriteError> {
1023        self.shared
1024            .format_last_child(false, context, shape, child_shape)
1025    }
1026
1027    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
1028        self.shared.join_rewrites(context, child_shape)
1029    }
1030
1031    fn pure_root(&mut self) -> Option<String> {
1032        self.shared.pure_root()
1033    }
1034}
1035
1036/// Removes try operators (`?`s) that appear in the given string. If removing
1037/// them leaves an empty line, remove that line as well unless it is the first
1038/// line (we need the first newline for detecting pre/post comment).
1039fn trim_tries(s: &str) -> String {
1040    let mut result = String::with_capacity(s.len());
1041    let mut line_buffer = String::with_capacity(s.len());
1042    for (kind, rich_char) in CharClasses::new(s.chars()) {
1043        match rich_char.get_char() {
1044            '\n' => {
1045                if result.is_empty() || !line_buffer.trim().is_empty() {
1046                    result.push_str(&line_buffer);
1047                    result.push('\n')
1048                }
1049                line_buffer.clear();
1050            }
1051            '?' if kind == FullCodeCharKind::Normal => continue,
1052            c => line_buffer.push(c),
1053        }
1054    }
1055    if !line_buffer.trim().is_empty() {
1056        result.push_str(&line_buffer);
1057    }
1058    result
1059}
1060
1061/// Whether a method call's receiver needs parenthesis, like
1062/// ```rust,ignore
1063/// || .. .method();
1064/// || 1.. .method();
1065/// 1. .method();
1066/// ```
1067/// Which all need parenthesis or a space before `.method()`.
1068fn should_add_parens(expr: &ast::Expr, context: &RewriteContext<'_>) -> bool {
1069    match expr.kind {
1070        ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit, context),
1071        ast::ExprKind::Closure(ref cl) => match cl.body.kind {
1072            ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true,
1073            ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit, context),
1074            _ => false,
1075        },
1076        _ => false,
1077    }
1078}