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