]> git.lizzy.rs Git - rust.git/blob - src/overflow.rs
Avoid using to_expr as much as possible
[rust.git] / src / overflow.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Rewrite a list some items with overflow.
12
13 use config::lists::*;
14 use syntax::parse::token::DelimToken;
15 use syntax::source_map::Span;
16 use syntax::{ast, ptr};
17
18 use closures;
19 use expr::{
20     can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
21     maybe_get_args_offset,
22 };
23 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
24 use macros::MacroArg;
25 use patterns::{can_be_overflowed_pat, TuplePatField};
26 use rewrite::{Rewrite, RewriteContext};
27 use shape::Shape;
28 use source_map::SpanUtils;
29 use spanned::Spanned;
30 use types::{can_be_overflowed_type, SegmentParam};
31 use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
32
33 use std::cmp::min;
34
35 pub enum OverflowableItem<'a> {
36     Expr(&'a ast::Expr),
37     GenericParam(&'a ast::GenericParam),
38     MacroArg(&'a MacroArg),
39     SegmentParam(&'a SegmentParam<'a>),
40     StructField(&'a ast::StructField),
41     TuplePatField(&'a TuplePatField<'a>),
42     Ty(&'a ast::Ty),
43 }
44
45 impl<'a> Rewrite for OverflowableItem<'a> {
46     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
47         self.map(|item| item.rewrite(context, shape))
48     }
49 }
50
51 impl<'a> Spanned for OverflowableItem<'a> {
52     fn span(&self) -> Span {
53         self.map(|item| item.span())
54     }
55 }
56
57 impl<'a> OverflowableItem<'a> {
58     pub fn map<F, T>(&self, f: F) -> T
59     where
60         F: Fn(&IntoOverflowableItem<'a>) -> T,
61     {
62         match self {
63             OverflowableItem::Expr(expr) => f(*expr),
64             OverflowableItem::GenericParam(gp) => f(*gp),
65             OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
66             OverflowableItem::SegmentParam(sp) => f(*sp),
67             OverflowableItem::StructField(sf) => f(*sf),
68             OverflowableItem::TuplePatField(pat) => f(*pat),
69             OverflowableItem::Ty(ty) => f(*ty),
70         }
71     }
72
73     pub fn is_simple(&self) -> bool {
74         match self {
75             OverflowableItem::Expr(expr) => is_simple_expr(expr),
76             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
77             _ => false,
78         }
79     }
80
81     pub fn is_expr(&self) -> bool {
82         match self {
83             OverflowableItem::Expr(..) => true,
84             OverflowableItem::MacroArg(MacroArg::Expr(..)) => true,
85             _ => false,
86         }
87     }
88
89     pub fn is_nested_call(&self) -> bool {
90         match self {
91             OverflowableItem::Expr(expr) => is_nested_call(expr),
92             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
93             _ => false,
94         }
95     }
96
97     pub fn to_expr(&self) -> Option<&'a ast::Expr> {
98         match self {
99             OverflowableItem::Expr(expr) => Some(expr),
100             OverflowableItem::MacroArg(macro_arg) => match macro_arg {
101                 MacroArg::Expr(ref expr) => Some(expr),
102                 _ => None,
103             },
104             _ => None,
105         }
106     }
107
108     pub fn can_be_overflowed(&self, context: &RewriteContext, len: usize) -> bool {
109         match self {
110             OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
111             OverflowableItem::MacroArg(macro_arg) => match macro_arg {
112                 MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
113                 MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
114                 MacroArg::Pat(..) => false,
115                 MacroArg::Item(..) => len == 1,
116             },
117             OverflowableItem::SegmentParam(seg) => match seg {
118                 SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
119                 _ => false,
120             },
121             OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
122             OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
123             _ => false,
124         }
125     }
126 }
127
128 pub trait IntoOverflowableItem<'a>: Rewrite + Spanned {
129     fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
130 }
131
132 impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
133     fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
134         (**self).into_overflowable_item()
135     }
136 }
137
138 macro impl_into_overflowable_item_for_ast_node {
139     ($($ast_node:ident),*) => {
140         $(
141             impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
142                 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
143                     OverflowableItem::$ast_node(self)
144                 }
145             }
146         )*
147     }
148 }
149
150 macro impl_into_overflowable_item_for_rustfmt_types {
151     ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
152         $(
153             impl<'a> IntoOverflowableItem<'a> for $ty {
154                 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
155                     OverflowableItem::$ty(self)
156                 }
157             }
158         )*
159         $(
160             impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
161                 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
162                     OverflowableItem::$ty_with_lifetime(self)
163                 }
164             }
165         )*
166     }
167 }
168
169 impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, StructField, Ty);
170 impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
171
172 pub fn into_overflowable_list<'a, T>(
173     iter: impl Iterator<Item = &'a T>,
174 ) -> impl Iterator<Item = OverflowableItem<'a>>
175 where
176     T: 'a + IntoOverflowableItem<'a>,
177 {
178     iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
179 }
180
181 const SHORT_ITEM_THRESHOLD: usize = 10;
182
183 pub fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
184     context: &'a RewriteContext,
185     ident: &'a str,
186     items: impl Iterator<Item = &'a T>,
187     shape: Shape,
188     span: Span,
189     item_max_width: usize,
190     force_separator_tactic: Option<SeparatorTactic>,
191 ) -> Option<String> {
192     Context::new(
193         context,
194         items,
195         ident,
196         shape,
197         span,
198         "(",
199         ")",
200         item_max_width,
201         force_separator_tactic,
202         None,
203     )
204     .rewrite(shape)
205 }
206
207 pub fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
208     context: &'a RewriteContext,
209     ident: &'a str,
210     items: impl Iterator<Item = &'a T>,
211     shape: Shape,
212     span: Span,
213 ) -> Option<String> {
214     Context::new(
215         context,
216         items,
217         ident,
218         shape,
219         span,
220         "<",
221         ">",
222         context.config.max_width(),
223         None,
224         None,
225     )
226     .rewrite(shape)
227 }
228
229 pub fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
230     context: &'a RewriteContext,
231     name: &'a str,
232     items: impl Iterator<Item = &'a T>,
233     shape: Shape,
234     span: Span,
235     force_separator_tactic: Option<SeparatorTactic>,
236     delim_token: Option<DelimToken>,
237 ) -> Option<String> {
238     let (lhs, rhs) = match delim_token {
239         Some(DelimToken::Paren) => ("(", ")"),
240         Some(DelimToken::Brace) => ("{", "}"),
241         _ => ("[", "]"),
242     };
243     Context::new(
244         context,
245         items,
246         name,
247         shape,
248         span,
249         lhs,
250         rhs,
251         context.config.width_heuristics().array_width,
252         force_separator_tactic,
253         Some(("[", "]")),
254     )
255     .rewrite(shape)
256 }
257
258 struct Context<'a> {
259     context: &'a RewriteContext<'a>,
260     items: Vec<OverflowableItem<'a>>,
261     ident: &'a str,
262     prefix: &'static str,
263     suffix: &'static str,
264     one_line_shape: Shape,
265     nested_shape: Shape,
266     span: Span,
267     item_max_width: usize,
268     one_line_width: usize,
269     force_separator_tactic: Option<SeparatorTactic>,
270     custom_delims: Option<(&'a str, &'a str)>,
271 }
272
273 impl<'a> Context<'a> {
274     pub fn new<T: 'a + IntoOverflowableItem<'a>>(
275         context: &'a RewriteContext,
276         items: impl Iterator<Item = &'a T>,
277         ident: &'a str,
278         shape: Shape,
279         span: Span,
280         prefix: &'static str,
281         suffix: &'static str,
282         item_max_width: usize,
283         force_separator_tactic: Option<SeparatorTactic>,
284         custom_delims: Option<(&'a str, &'a str)>,
285     ) -> Context<'a> {
286         let used_width = extra_offset(ident, shape);
287         // 1 = `()`
288         let one_line_width = shape.width.saturating_sub(used_width + 2);
289
290         // 1 = "(" or ")"
291         let one_line_shape = shape
292             .offset_left(last_line_width(ident) + 1)
293             .and_then(|shape| shape.sub_width(1))
294             .unwrap_or(Shape { width: 0, ..shape });
295         let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
296         Context {
297             context,
298             items: into_overflowable_list(items).collect(),
299             ident,
300             one_line_shape,
301             nested_shape,
302             span,
303             prefix,
304             suffix,
305             item_max_width,
306             one_line_width,
307             force_separator_tactic,
308             custom_delims,
309         }
310     }
311
312     fn last_item(&self) -> Option<&OverflowableItem> {
313         self.items.last()
314     }
315
316     fn items_span(&self) -> Span {
317         let span_lo = self
318             .context
319             .snippet_provider
320             .span_after(self.span, self.prefix);
321         mk_sp(span_lo, self.span.hi())
322     }
323
324     fn rewrite_last_item_with_overflow(
325         &self,
326         last_list_item: &mut ListItem,
327         shape: Shape,
328     ) -> Option<String> {
329         let last_item = self.last_item()?;
330         let rewrite = match last_item {
331             OverflowableItem::Expr(ref expr) => {
332                 match expr.node {
333                     // When overflowing the closure which consists of a single control flow
334                     // expression, force to use block if its condition uses multi line.
335                     ast::ExprKind::Closure(..) => {
336                         // If the argument consists of multiple closures, we do not overflow
337                         // the last closure.
338                         if closures::args_have_many_closure(&self.items) {
339                             None
340                         } else {
341                             closures::rewrite_last_closure(self.context, expr, shape)
342                         }
343                     }
344                     _ => expr.rewrite(self.context, shape),
345                 }
346             }
347             item @ _ => item.rewrite(self.context, shape),
348         };
349
350         if let Some(rewrite) = rewrite {
351             let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
352             last_list_item.item = rewrite_first_line;
353             Some(rewrite)
354         } else {
355             None
356         }
357     }
358
359     fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
360         definitive_tactic(
361             list_items,
362             ListTactic::LimitedHorizontalVertical(self.item_max_width),
363             Separator::Comma,
364             self.one_line_width,
365         )
366     }
367
368     fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
369         // 1 = "("
370         let combine_arg_with_callee = self.items.len() == 1
371             && self.items[0].is_expr()
372             && self.ident.len() < self.context.config.tab_spaces();
373         let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
374
375         // Replace the last item with its first line to see if it fits with
376         // first arguments.
377         let placeholder = if overflow_last {
378             let old_value = *self.context.force_one_line_chain.borrow();
379             match self.last_item() {
380                 Some(OverflowableItem::Expr(expr))
381                     if !combine_arg_with_callee && is_method_call(expr) =>
382                 {
383                     self.context.force_one_line_chain.replace(true);
384                 }
385                 _ => (),
386             }
387             let result = last_item_shape(
388                 &self.items,
389                 list_items,
390                 self.one_line_shape,
391                 self.item_max_width,
392             )
393             .and_then(|arg_shape| {
394                 self.rewrite_last_item_with_overflow(
395                     &mut list_items[self.items.len() - 1],
396                     arg_shape,
397                 )
398             });
399             self.context.force_one_line_chain.replace(old_value);
400             result
401         } else {
402             None
403         };
404
405         let mut tactic = definitive_tactic(
406             &*list_items,
407             ListTactic::LimitedHorizontalVertical(self.item_max_width),
408             Separator::Comma,
409             self.one_line_width,
410         );
411
412         // Replace the stub with the full overflowing last argument if the rewrite
413         // succeeded and its first line fits with the other arguments.
414         match (overflow_last, tactic, placeholder) {
415             (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
416                 if self.items.len() == 1 =>
417             {
418                 // When we are rewriting a nested function call, we restrict the
419                 // budget for the inner function to avoid them being deeply nested.
420                 // However, when the inner function has a prefix or a suffix
421                 // (e.g. `foo() as u32`), this budget reduction may produce poorly
422                 // formatted code, where a prefix or a suffix being left on its own
423                 // line. Here we explicitlly check those cases.
424                 if count_newlines(overflowed) == 1 {
425                     let rw = self
426                         .items
427                         .last()
428                         .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
429                     let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
430                     if no_newline {
431                         list_items[self.items.len() - 1].item = rw;
432                     } else {
433                         list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
434                     }
435                 } else {
436                     list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
437                 }
438             }
439             (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
440                 list_items[self.items.len() - 1].item = placeholder;
441             }
442             _ if !self.items.is_empty() => {
443                 list_items[self.items.len() - 1].item = self
444                     .items
445                     .last()
446                     .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
447
448                 // Use horizontal layout for a function with a single argument as long as
449                 // everything fits in a single line.
450                 // `self.one_line_width == 0` means vertical layout is forced.
451                 if self.items.len() == 1
452                     && self.one_line_width != 0
453                     && !list_items[0].has_comment()
454                     && !list_items[0].inner_as_ref().contains('\n')
455                     && ::lists::total_item_width(&list_items[0]) <= self.one_line_width
456                 {
457                     tactic = DefinitiveListTactic::Horizontal;
458                 } else {
459                     tactic = self.default_tactic(list_items);
460
461                     if tactic == DefinitiveListTactic::Vertical {
462                         if let Some((all_simple, num_args_before)) =
463                             maybe_get_args_offset(self.ident, &self.items)
464                         {
465                             let one_line = all_simple
466                                 && definitive_tactic(
467                                     &list_items[..num_args_before],
468                                     ListTactic::HorizontalVertical,
469                                     Separator::Comma,
470                                     self.nested_shape.width,
471                                 ) == DefinitiveListTactic::Horizontal
472                                 && definitive_tactic(
473                                     &list_items[num_args_before + 1..],
474                                     ListTactic::HorizontalVertical,
475                                     Separator::Comma,
476                                     self.nested_shape.width,
477                                 ) == DefinitiveListTactic::Horizontal;
478
479                             if one_line {
480                                 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
481                             };
482                         } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
483                             tactic = DefinitiveListTactic::Mixed;
484                         }
485                     }
486                 }
487             }
488             _ => (),
489         }
490
491         tactic
492     }
493
494     fn rewrite_items(&self) -> Option<(bool, String)> {
495         let span = self.items_span();
496         let items = itemize_list(
497             self.context.snippet_provider,
498             self.items.iter(),
499             self.suffix,
500             ",",
501             |item| item.span().lo(),
502             |item| item.span().hi(),
503             |item| item.rewrite(self.context, self.nested_shape),
504             span.lo(),
505             span.hi(),
506             true,
507         );
508         let mut list_items: Vec<_> = items.collect();
509
510         // Try letting the last argument overflow to the next line with block
511         // indentation. If its first line fits on one line with the other arguments,
512         // we format the function arguments horizontally.
513         let tactic = self.try_overflow_last_item(&mut list_items);
514         let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
515             tactic
516         } else if !self.context.use_block_indent() {
517             SeparatorTactic::Never
518         } else if tactic == DefinitiveListTactic::Mixed {
519             // We are using mixed layout because everything did not fit within a single line.
520             SeparatorTactic::Always
521         } else {
522             self.context.config.trailing_comma()
523         };
524         let ends_with_newline = match tactic {
525             DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
526                 self.context.use_block_indent()
527             }
528             _ => false,
529         };
530
531         let fmt = ListFormatting::new(self.nested_shape, self.context.config)
532             .tactic(tactic)
533             .trailing_separator(trailing_separator)
534             .ends_with_newline(ends_with_newline);
535
536         write_list(&list_items, &fmt)
537             .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
538     }
539
540     fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
541         let shape = Shape {
542             width: shape.width.saturating_sub(last_line_width(self.ident)),
543             ..shape
544         };
545
546         let (prefix, suffix) = match self.custom_delims {
547             Some((lhs, rhs)) => (lhs, rhs),
548             _ => (self.prefix, self.suffix),
549         };
550
551         // 2 = `()`
552         let fits_one_line = items_str.len() + 2 <= shape.width;
553         let extend_width = if items_str.is_empty() {
554             2
555         } else {
556             first_line_width(items_str) + 1
557         };
558         let nested_indent_str = self
559             .nested_shape
560             .indent
561             .to_string_with_newline(self.context.config);
562         let indent_str = shape
563             .block()
564             .indent
565             .to_string_with_newline(self.context.config);
566         let mut result = String::with_capacity(
567             self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
568         );
569         result.push_str(self.ident);
570         result.push_str(prefix);
571         if !self.context.use_block_indent()
572             || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
573             || (is_extendable && extend_width <= shape.width)
574         {
575             result.push_str(items_str);
576         } else {
577             if !items_str.is_empty() {
578                 result.push_str(&nested_indent_str);
579                 result.push_str(items_str);
580             }
581             result.push_str(&indent_str);
582         }
583         result.push_str(suffix);
584         result
585     }
586
587     fn rewrite(&self, shape: Shape) -> Option<String> {
588         let (extendable, items_str) = self.rewrite_items()?;
589
590         // If we are using visual indent style and failed to format, retry with block indent.
591         if !self.context.use_block_indent()
592             && need_block_indent(&items_str, self.nested_shape)
593             && !extendable
594         {
595             self.context.use_block.replace(true);
596             let result = self.rewrite(shape);
597             self.context.use_block.replace(false);
598             return result;
599         }
600
601         Some(self.wrap_items(&items_str, shape, extendable))
602     }
603 }
604
605 fn need_block_indent(s: &str, shape: Shape) -> bool {
606     s.lines().skip(1).any(|s| {
607         s.find(|c| !char::is_whitespace(c))
608             .map_or(false, |w| w + 1 < shape.indent.width())
609     })
610 }
611
612 fn can_be_overflowed<'a>(context: &RewriteContext, items: &[OverflowableItem]) -> bool {
613     items
614         .last()
615         .map_or(false, |x| x.can_be_overflowed(context, items.len()))
616 }
617
618 /// Returns a shape for the last argument which is going to be overflowed.
619 fn last_item_shape(
620     lists: &[OverflowableItem],
621     items: &[ListItem],
622     shape: Shape,
623     args_max_width: usize,
624 ) -> Option<Shape> {
625     if items.len() == 1 && !lists.iter().next()?.is_nested_call() {
626         return Some(shape);
627     }
628     let offset = items.iter().rev().skip(1).fold(0, |acc, i| {
629         // 2 = ", "
630         acc + 2 + i.inner_as_ref().len()
631     });
632     Shape {
633         width: min(args_max_width, shape.width),
634         ..shape
635     }
636     .offset_left(offset)
637 }
638
639 fn shape_from_indent_style(
640     context: &RewriteContext,
641     shape: Shape,
642     overhead: usize,
643     offset: usize,
644 ) -> Shape {
645     let (shape, overhead) = if context.use_block_indent() {
646         let shape = shape
647             .block()
648             .block_indent(context.config.tab_spaces())
649             .with_max_width(context.config);
650         (shape, 1) // 1 = ","
651     } else {
652         (shape.visual_indent(offset), overhead)
653     };
654     Shape {
655         width: shape.width.saturating_sub(overhead),
656         ..shape
657     }
658 }
659
660 fn no_long_items(list: &[ListItem]) -> bool {
661     list.iter()
662         .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
663 }