1 // Formatting top-level items - functions, structs, enums, traits, impls.
4 use std::cmp::{max, min, Ordering};
8 use rustc_ast::{ast, ptr};
9 use rustc_span::{symbol, BytePos, Span, DUMMY_SP};
11 use crate::attr::filter_inline_attrs;
13 combine_strs_with_missing_comments, contains_comment, is_last_comment_block,
14 recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment,
17 use crate::config::lists::*;
18 use crate::config::{BraceStyle, Config, IndentStyle, Version};
20 is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with,
21 rewrite_assign_rhs_with_comments, RhsTactics,
23 use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
24 use crate::macros::{rewrite_macro, MacroPosition};
26 use crate::rewrite::{Rewrite, RewriteContext};
27 use crate::shape::{Indent, Shape};
28 use crate::source_map::{LineRangeUtils, SpanUtils};
29 use crate::spanned::Spanned;
30 use crate::stmt::Stmt;
32 use crate::vertical::rewrite_with_alignment;
33 use crate::visitor::FmtVisitor;
35 const DEFAULT_VISIBILITY: ast::Visibility = ast::Visibility {
36 kind: ast::VisibilityKind::Inherited,
41 fn type_annotation_separator(config: &Config) -> &str {
45 // Statements of the form
46 // let pat: ty = init;
47 impl Rewrite for ast::Local {
48 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
50 "Local::rewrite {:?} {} {:?}",
51 self, shape.width, shape.indent
54 skip_out_of_file_lines_range!(context, self.span);
56 if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) {
60 let attrs_str = self.attrs.rewrite(context, shape)?;
61 let mut result = if attrs_str.is_empty() {
64 combine_strs_with_missing_comments(
69 self.attrs.last().map(|a| a.span.hi()).unwrap(),
78 let pat_shape = shape.offset_left(4)?;
80 let pat_shape = pat_shape.sub_width(1)?;
81 let pat_str = self.pat.rewrite(context, pat_shape)?;
82 result.push_str(&pat_str);
84 // String that is placed within the assignment pattern and expression.
86 let mut infix = String::with_capacity(32);
88 if let Some(ref ty) = self.ty {
89 let separator = type_annotation_separator(context.config);
90 let ty_shape = if pat_str.contains('\n') {
91 shape.with_max_width(context.config)
95 .offset_left(last_line_width(&result) + separator.len())?
99 let rewrite = ty.rewrite(context, ty_shape)?;
101 infix.push_str(separator);
102 infix.push_str(&rewrite);
105 if self.kind.init().is_some() {
106 infix.push_str(" =");
112 result.push_str(&infix);
114 if let Some((init, _els)) = self.kind.init_else_opt() {
115 // 1 = trailing semicolon;
116 let nested_shape = shape.sub_width(1)?;
118 result = rewrite_assign_rhs(context, result, init, nested_shape)?;
127 // FIXME convert to using rewrite style rather than visitor
128 // FIXME format modules in this style
132 unsafety: ast::Unsafe,
133 abi: Cow<'static, str>,
134 vis: Option<&'a ast::Visibility>,
135 body: Vec<BodyElement<'a>>,
140 fn from_foreign_mod(fm: &'a ast::ForeignMod, span: Span, config: &Config) -> Item<'a> {
142 unsafety: fm.unsafety,
144 ast::Extern::from_abi(fm.abi),
145 config.force_explicit_abi(),
152 .map(|i| BodyElement::ForeignItem(i))
160 enum BodyElement<'a> {
161 // Stmt(&'a ast::Stmt),
162 // Field(&'a ast::ExprField),
163 // Variant(&'a ast::Variant),
164 // Item(&'a ast::Item),
165 ForeignItem(&'a ast::ForeignItem),
168 /// Represents a fn's signature.
169 pub(crate) struct FnSig<'a> {
170 decl: &'a ast::FnDecl,
171 generics: &'a ast::Generics,
173 is_async: Cow<'a, ast::Async>,
174 constness: ast::Const,
175 defaultness: ast::Defaultness,
176 unsafety: ast::Unsafe,
177 visibility: &'a ast::Visibility,
181 pub(crate) fn from_method_sig(
182 method_sig: &'a ast::FnSig,
183 generics: &'a ast::Generics,
184 visibility: &'a ast::Visibility,
187 unsafety: method_sig.header.unsafety,
188 is_async: Cow::Borrowed(&method_sig.header.asyncness),
189 constness: method_sig.header.constness,
190 defaultness: ast::Defaultness::Final,
191 ext: method_sig.header.ext,
192 decl: &*method_sig.decl,
198 pub(crate) fn from_fn_kind(
199 fn_kind: &'a visit::FnKind<'_>,
200 generics: &'a ast::Generics,
201 decl: &'a ast::FnDecl,
202 defaultness: ast::Defaultness,
205 visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, _) => match fn_ctxt {
206 visit::FnCtxt::Assoc(..) => {
207 let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis);
208 fn_sig.defaultness = defaultness;
214 ext: fn_sig.header.ext,
215 constness: fn_sig.header.constness,
216 is_async: Cow::Borrowed(&fn_sig.header.asyncness),
218 unsafety: fn_sig.header.unsafety,
226 fn to_str(&self, context: &RewriteContext<'_>) -> String {
227 let mut result = String::with_capacity(128);
228 // Vis defaultness constness unsafety abi.
229 result.push_str(&*format_visibility(context, self.visibility));
230 result.push_str(format_defaultness(self.defaultness));
231 result.push_str(format_constness(self.constness));
232 result.push_str(format_async(&self.is_async));
233 result.push_str(format_unsafety(self.unsafety));
234 result.push_str(&format_extern(
236 context.config.force_explicit_abi(),
243 impl<'a> FmtVisitor<'a> {
244 fn format_item(&mut self, item: &Item<'_>) {
245 self.buffer.push_str(format_unsafety(item.unsafety));
246 self.buffer.push_str(&item.abi);
248 let snippet = self.snippet(item.span);
249 let brace_pos = snippet.find_uncommented("{").unwrap();
252 if !item.body.is_empty() || contains_comment(&snippet[brace_pos..]) {
253 // FIXME: this skips comments between the extern keyword and the opening
255 self.last_pos = item.span.lo() + BytePos(brace_pos as u32 + 1);
256 self.block_indent = self.block_indent.block_indent(self.config);
258 if !item.body.is_empty() {
259 for item in &item.body {
260 self.format_body_element(item);
264 self.format_missing_no_indent(item.span.hi() - BytePos(1));
265 self.block_indent = self.block_indent.block_unindent(self.config);
266 let indent_str = self.block_indent.to_string(self.config);
267 self.push_str(&indent_str);
271 self.last_pos = item.span.hi();
274 fn format_body_element(&mut self, element: &BodyElement<'_>) {
276 BodyElement::ForeignItem(item) => self.format_foreign_item(item),
280 pub(crate) fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) {
281 let item = Item::from_foreign_mod(fm, span, self.config);
282 self.format_item(&item);
285 fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
286 let rewrite = item.rewrite(&self.get_context(), self.shape());
287 let hi = item.span.hi();
288 let span = if item.attrs.is_empty() {
291 mk_sp(item.attrs[0].span.lo(), hi)
293 self.push_rewrite(span, rewrite);
297 pub(crate) fn rewrite_fn_before_block(
300 ident: symbol::Ident,
303 ) -> Option<(String, FnBraceStyle)> {
304 let context = self.get_context();
306 let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause);
307 let (result, _, force_newline_brace) =
308 rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?;
311 if self.config.brace_style() == BraceStyle::AlwaysNextLine
312 || force_newline_brace
313 || last_line_width(&result) + 2 > self.shape().width
315 fn_brace_style = FnBraceStyle::NextLine
318 Some((result, fn_brace_style))
321 pub(crate) fn rewrite_required_fn(
324 ident: symbol::Ident,
326 vis: &ast::Visibility,
327 generics: &ast::Generics,
329 ) -> Option<String> {
330 // Drop semicolon or it will be interpreted as comment.
331 let span = mk_sp(span.lo(), span.hi() - BytePos(1));
332 let context = self.get_context();
334 let (mut result, ends_with_comment, _) = rewrite_fn_base(
338 &FnSig::from_method_sig(sig, generics, vis),
343 // If `result` ends with a comment, then remember to add a newline
344 if ends_with_comment {
345 result.push_str(&indent.to_string_with_newline(context.config));
348 // Re-attach semicolon
354 pub(crate) fn single_line_fn(
358 inner_attrs: Option<&[ast::Attribute]>,
359 ) -> Option<String> {
360 if fn_str.contains('\n') || inner_attrs.map_or(false, |a| !a.is_empty()) {
364 let context = self.get_context();
366 if self.config.empty_item_single_line()
367 && is_empty_block(&context, block, None)
368 && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width()
369 && !last_line_contains_single_line_comment(fn_str)
371 return Some(format!("{} {{}}", fn_str));
374 if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) {
378 let res = Stmt::from_ast_node(block.stmts.first()?, true)
379 .rewrite(&self.get_context(), self.shape())?;
381 let width = self.block_indent.width() + fn_str.len() + res.len() + 5;
382 if !res.contains('\n') && width <= self.config.max_width() {
383 Some(format!("{} {{ {} }}", fn_str, res))
389 pub(crate) fn visit_static(&mut self, static_parts: &StaticParts<'_>) {
390 let rewrite = rewrite_static(&self.get_context(), static_parts, self.block_indent);
391 self.push_rewrite(static_parts.span, rewrite);
394 pub(crate) fn visit_struct(&mut self, struct_parts: &StructParts<'_>) {
395 let is_tuple = match struct_parts.def {
396 ast::VariantData::Tuple(..) => true,
399 let rewrite = format_struct(&self.get_context(), struct_parts, self.block_indent, None)
400 .map(|s| if is_tuple { s + ";" } else { s });
401 self.push_rewrite(struct_parts.span, rewrite);
404 pub(crate) fn visit_enum(
406 ident: symbol::Ident,
407 vis: &ast::Visibility,
408 enum_def: &ast::EnumDef,
409 generics: &ast::Generics,
413 format_header(&self.get_context(), "enum ", ident, vis, self.block_indent);
414 self.push_str(&enum_header);
416 let enum_snippet = self.snippet(span);
417 let brace_pos = enum_snippet.find_uncommented("{").unwrap();
418 let body_start = span.lo() + BytePos(brace_pos as u32 + 1);
419 let generics_str = format_generics(
422 self.config.brace_style(),
423 if enum_def.variants.is_empty() {
424 BracePos::ForceSameLine
429 // make a span that starts right after `enum Foo`
430 mk_sp(ident.span.hi(), body_start),
431 last_line_width(&enum_header),
434 self.push_str(&generics_str);
436 self.last_pos = body_start;
438 match self.format_variant_list(enum_def, body_start, span.hi()) {
439 Some(ref s) if enum_def.variants.is_empty() => self.push_str(s),
441 self.push_rewrite(mk_sp(body_start, span.hi()), rw);
442 self.block_indent = self.block_indent.block_unindent(self.config);
447 // Format the body of an enum definition
448 fn format_variant_list(
450 enum_def: &ast::EnumDef,
453 ) -> Option<String> {
454 if enum_def.variants.is_empty() {
455 let mut buffer = String::with_capacity(128);
457 let span = mk_sp(body_lo, body_hi - BytePos(1));
458 format_empty_struct_or_tuple(
468 let mut result = String::with_capacity(1024);
469 let original_offset = self.block_indent;
470 self.block_indent = self.block_indent.block_indent(self.config);
472 // If enum variants have discriminants, try to vertically align those,
473 // provided the discrims are not shifted too much to the right
474 let align_threshold: usize = self.config.enum_discrim_align_threshold();
475 let discr_ident_lens: Vec<usize> = enum_def
478 .filter(|var| var.disr_expr.is_some())
479 .map(|var| rewrite_ident(&self.get_context(), var.ident).len())
481 // cut the list at the point of longest discrim shorter than the threshold
482 // All of the discrims under the threshold will get padded, and all above - left as is.
483 let pad_discrim_ident_to = *discr_ident_lens
485 .filter(|&l| *l <= align_threshold)
489 let itemize_list_with = |one_line_width: usize| {
491 self.snippet_provider,
492 enum_def.variants.iter(),
496 if !f.attrs.is_empty() {
503 |f| self.format_variant(f, one_line_width, pad_discrim_ident_to),
510 let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width());
512 // If one of the variants use multiple lines, use multi-lined formatting for all variants.
513 let has_multiline_variant = items.iter().any(|item| item.inner_as_ref().contains('\n'));
514 let has_single_line_variant = items.iter().any(|item| !item.inner_as_ref().contains('\n'));
515 if has_multiline_variant && has_single_line_variant {
516 items = itemize_list_with(0);
519 let shape = self.shape().sub_width(2)?;
520 let fmt = ListFormatting::new(shape, self.config)
521 .trailing_separator(self.config.trailing_comma())
522 .preserve_newline(true);
524 let list = write_list(&items, &fmt)?;
525 result.push_str(&list);
526 result.push_str(&original_offset.to_string_with_newline(self.config));
531 // Variant of an enum.
534 field: &ast::Variant,
535 one_line_width: usize,
536 pad_discrim_ident_to: usize,
537 ) -> Option<String> {
538 if contains_skip(&field.attrs) {
539 let lo = field.attrs[0].span.lo();
540 let span = mk_sp(lo, field.span.hi());
541 return Some(self.snippet(span).to_owned());
544 let context = self.get_context();
546 let shape = self.shape().sub_width(1)?;
547 let attrs_str = field.attrs.rewrite(&context, shape)?;
551 .map_or(field.span.lo(), |attr| attr.span.hi());
552 let span = mk_sp(lo, field.span.lo());
554 let variant_body = match field.data {
555 ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => format_struct(
557 &StructParts::from_variant(field),
559 Some(one_line_width),
561 ast::VariantData::Unit(..) => rewrite_ident(&context, field.ident).to_owned(),
564 let variant_body = if let Some(ref expr) = field.disr_expr {
565 let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to);
566 rewrite_assign_rhs_with(
571 RhsTactics::AllowOverflow,
577 combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false)
580 fn visit_impl_items(&mut self, items: &[ptr::P<ast::AssocItem>]) {
581 if self.get_context().config.reorder_impl_items() {
582 // Create visitor for each items, then reorder them.
583 let mut buffer = vec![];
585 self.visit_impl_item(item);
586 buffer.push((self.buffer.clone(), item.clone()));
590 fn is_type(ty: &Option<rustc_ast::ptr::P<ast::Ty>>) -> bool {
591 if let Some(lty) = ty {
592 if let ast::TyKind::ImplTrait(..) = lty.kind {
599 fn is_opaque(ty: &Option<rustc_ast::ptr::P<ast::Ty>>) -> bool {
604 a: &Option<rustc_ast::ptr::P<ast::Ty>>,
605 b: &Option<rustc_ast::ptr::P<ast::Ty>>,
607 is_type(a) && is_type(b)
611 a: &Option<rustc_ast::ptr::P<ast::Ty>>,
612 b: &Option<rustc_ast::ptr::P<ast::Ty>>,
614 is_opaque(a) && is_opaque(b)
617 // In rustc-ap-v638 the `OpaqueTy` AssocItemKind variant was removed but
618 // we still need to differentiate to maintain sorting order.
620 // type -> opaque -> const -> macro -> method
621 use crate::ast::AssocItemKind::*;
622 fn need_empty_line(a: &ast::AssocItemKind, b: &ast::AssocItemKind) -> bool {
624 (TyAlias(lty), TyAlias(rty))
625 if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) =>
629 (Const(..), Const(..)) => false,
634 buffer.sort_by(|(_, a), (_, b)| match (&a.kind, &b.kind) {
635 (TyAlias(lty), TyAlias(rty))
636 if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) =>
638 a.ident.as_str().cmp(&b.ident.as_str())
640 (Const(..), Const(..)) | (MacCall(..), MacCall(..)) => {
641 a.ident.as_str().cmp(&b.ident.as_str())
643 (Fn(..), Fn(..)) => a.span.lo().cmp(&b.span.lo()),
644 (TyAlias(ty), _) if is_type(&ty.ty) => Ordering::Less,
645 (_, TyAlias(ty)) if is_type(&ty.ty) => Ordering::Greater,
646 (TyAlias(..), _) => Ordering::Less,
647 (_, TyAlias(..)) => Ordering::Greater,
648 (Const(..), _) => Ordering::Less,
649 (_, Const(..)) => Ordering::Greater,
650 (MacCall(..), _) => Ordering::Less,
651 (_, MacCall(..)) => Ordering::Greater,
653 let mut prev_kind = None;
654 for (buf, item) in buffer {
655 // Make sure that there are at least a single empty line between
656 // different impl items.
659 .map_or(false, |prev_kind| need_empty_line(prev_kind, &item.kind))
663 let indent_str = self.block_indent.to_string_with_newline(self.config);
664 self.push_str(&indent_str);
665 self.push_str(buf.trim());
666 prev_kind = Some(item.kind.clone());
670 self.visit_impl_item(item);
676 pub(crate) fn format_impl(
677 context: &RewriteContext<'_>,
680 ) -> Option<String> {
681 if let ast::ItemKind::Impl(impl_kind) = &item.kind {
688 let mut result = String::with_capacity(128);
689 let ref_and_type = format_impl_ref_and_type(context, item, offset)?;
690 let sep = offset.to_string_with_newline(context.config);
691 result.push_str(&ref_and_type);
693 let where_budget = if result.contains('\n') {
694 context.config.max_width()
696 context.budget(last_line_width(&result))
699 let mut option = WhereClauseOption::snuggled(&ref_and_type);
700 let snippet = context.snippet(item.span);
701 let open_pos = snippet.find_uncommented("{")? + 1;
702 if !contains_comment(&snippet[open_pos..])
704 && generics.where_clause.predicates.len() == 1
705 && !result.contains('\n')
707 option.suppress_comma();
709 option.allow_single_line();
712 let missing_span = mk_sp(self_ty.span.hi(), item.span.hi());
713 let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{");
714 let where_clause_str = rewrite_where_clause(
716 &generics.where_clause,
717 context.config.brace_style(),
718 Shape::legacy(where_budget, offset.block_only()),
726 // If there is no where-clause, we may have missing comments between the trait name and
727 // the opening brace.
728 if generics.where_clause.predicates.is_empty() {
729 if let Some(hi) = where_span_end {
730 match recover_missing_comment_in_span(
731 mk_sp(self_ty.span.hi(), hi),
732 Shape::indented(offset, context.config),
734 last_line_width(&result),
736 Some(ref missing_comment) if !missing_comment.is_empty() => {
737 result.push_str(missing_comment);
744 if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? {
745 result.push_str(&where_clause_str);
746 if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) {
747 // if the where_clause contains extra comments AND
748 // there is only one where-clause predicate
749 // recover the suppressed comma in single line where_clause formatting
750 if generics.where_clause.predicates.len() == 1 {
753 result.push_str(&format!("{}{{{}}}", sep, sep));
755 result.push_str(" {}");
760 result.push_str(&where_clause_str);
762 let need_newline = last_line_contains_single_line_comment(&result) || result.contains('\n');
763 match context.config.brace_style() {
764 _ if need_newline => result.push_str(&sep),
765 BraceStyle::AlwaysNextLine => result.push_str(&sep),
766 BraceStyle::PreferSameLine => result.push(' '),
767 BraceStyle::SameLineWhere => {
768 if !where_clause_str.is_empty() {
769 result.push_str(&sep);
777 // this is an impl body snippet(impl SampleImpl { /* here */ })
778 let lo = max(self_ty.span.hi(), generics.where_clause.span.hi());
779 let snippet = context.snippet(mk_sp(lo, item.span.hi()));
780 let open_pos = snippet.find_uncommented("{")? + 1;
782 if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
783 let mut visitor = FmtVisitor::from_context(context);
784 let item_indent = offset.block_only().block_indent(context.config);
785 visitor.block_indent = item_indent;
786 visitor.last_pos = lo + BytePos(open_pos as u32);
788 visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner);
789 visitor.visit_impl_items(items);
791 visitor.format_missing(item.span.hi() - BytePos(1));
793 let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config);
794 let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
796 result.push_str(&inner_indent_str);
797 result.push_str(visitor.buffer.trim());
798 result.push_str(&outer_indent_str);
799 } else if need_newline || !context.config.empty_item_single_line() {
800 result.push_str(&sep);
811 fn is_impl_single_line(
812 context: &RewriteContext<'_>,
813 items: &[ptr::P<ast::AssocItem>],
815 where_clause_str: &str,
818 let snippet = context.snippet(item.span);
819 let open_pos = snippet.find_uncommented("{")? + 1;
822 context.config.empty_item_single_line()
824 && !result.contains('\n')
825 && result.len() + where_clause_str.len() <= context.config.max_width()
826 && !contains_comment(&snippet[open_pos..]),
830 fn format_impl_ref_and_type(
831 context: &RewriteContext<'_>,
834 ) -> Option<String> {
835 if let ast::ItemKind::Impl(impl_kind) = &item.kind {
842 of_trait: ref trait_ref,
846 let mut result = String::with_capacity(128);
848 result.push_str(&format_visibility(context, &item.vis));
849 result.push_str(format_defaultness(defaultness));
850 result.push_str(format_unsafety(unsafety));
852 let shape = if context.config.version() == Version::Two {
853 Shape::indented(offset + last_line_width(&result), context.config)
855 generics_shape_from_config(
857 Shape::indented(offset + last_line_width(&result), context.config),
861 let generics_str = rewrite_generics(context, "impl", generics, shape)?;
862 result.push_str(&generics_str);
863 result.push_str(format_constness_right(constness));
865 let polarity_str = match polarity {
866 ast::ImplPolarity::Negative(_) => "!",
867 ast::ImplPolarity::Positive => "",
870 let polarity_overhead;
871 let trait_ref_overhead;
872 if let Some(ref trait_ref) = *trait_ref {
873 let result_len = last_line_width(&result);
874 result.push_str(&rewrite_trait_ref(
881 polarity_overhead = 0; // already written
882 trait_ref_overhead = " for".len();
884 polarity_overhead = polarity_str.len();
885 trait_ref_overhead = 0;
888 // Try to put the self type in a single line.
889 let curly_brace_overhead = if generics.where_clause.predicates.is_empty() {
890 // If there is no where-clause adapt budget for type formatting to take space and curly
891 // brace into account.
892 match context.config.brace_style() {
893 BraceStyle::AlwaysNextLine => 0,
899 let used_space = last_line_width(&result)
902 + curly_brace_overhead;
903 // 1 = space before the type.
904 let budget = context.budget(used_space + 1);
905 if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) {
906 if !self_ty_str.contains('\n') {
907 if trait_ref.is_some() {
908 result.push_str(" for ");
911 result.push_str(polarity_str);
913 result.push_str(&self_ty_str);
918 // Couldn't fit the self type on a single line, put it on a new line.
920 // Add indentation of one additional tab.
921 let new_line_offset = offset.block_indent(context.config);
922 result.push_str(&new_line_offset.to_string(context.config));
923 if trait_ref.is_some() {
924 result.push_str("for ");
926 result.push_str(polarity_str);
928 let budget = context.budget(last_line_width(&result) + polarity_overhead);
929 let type_offset = match context.config.indent_style() {
930 IndentStyle::Visual => new_line_offset + trait_ref_overhead,
931 IndentStyle::Block => new_line_offset,
933 result.push_str(&*self_ty.rewrite(context, Shape::legacy(budget, type_offset))?);
940 fn rewrite_trait_ref(
941 context: &RewriteContext<'_>,
942 trait_ref: &ast::TraitRef,
946 ) -> Option<String> {
947 // 1 = space between generics and trait_ref
948 let used_space = 1 + polarity_str.len() + result_len;
949 let shape = Shape::indented(offset + used_space, context.config);
950 if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) {
951 if !trait_ref_str.contains('\n') {
952 return Some(format!(" {}{}", polarity_str, trait_ref_str));
955 // We could not make enough space for trait_ref, so put it on new line.
956 let offset = offset.block_indent(context.config);
957 let shape = Shape::indented(offset, context.config);
958 let trait_ref_str = trait_ref.rewrite(context, shape)?;
961 offset.to_string_with_newline(context.config),
967 pub(crate) struct StructParts<'a> {
969 ident: symbol::Ident,
970 vis: &'a ast::Visibility,
971 def: &'a ast::VariantData,
972 generics: Option<&'a ast::Generics>,
976 impl<'a> StructParts<'a> {
977 fn format_header(&self, context: &RewriteContext<'_>, offset: Indent) -> String {
978 format_header(context, self.prefix, self.ident, self.vis, offset)
981 fn from_variant(variant: &'a ast::Variant) -> Self {
984 ident: variant.ident,
985 vis: &DEFAULT_VISIBILITY,
992 pub(crate) fn from_item(item: &'a ast::Item) -> Self {
993 let (prefix, def, generics) = match item.kind {
994 ast::ItemKind::Struct(ref def, ref generics) => ("struct ", def, generics),
995 ast::ItemKind::Union(ref def, ref generics) => ("union ", def, generics),
1003 generics: Some(generics),
1010 context: &RewriteContext<'_>,
1011 struct_parts: &StructParts<'_>,
1013 one_line_width: Option<usize>,
1014 ) -> Option<String> {
1015 match *struct_parts.def {
1016 ast::VariantData::Unit(..) => format_unit_struct(context, struct_parts, offset),
1017 ast::VariantData::Tuple(ref fields, _) => {
1018 format_tuple_struct(context, struct_parts, fields, offset)
1020 ast::VariantData::Struct(ref fields, _) => {
1021 format_struct_struct(context, struct_parts, fields, offset, one_line_width)
1026 pub(crate) fn format_trait(
1027 context: &RewriteContext<'_>,
1030 ) -> Option<String> {
1031 if let ast::ItemKind::Trait(trait_kind) = &item.kind {
1039 let mut result = String::with_capacity(128);
1040 let header = format!(
1042 format_visibility(context, &item.vis),
1043 format_unsafety(unsafety),
1044 format_auto(is_auto),
1046 result.push_str(&header);
1048 let body_lo = context.snippet_provider.span_after(item.span, "{");
1050 let shape = Shape::indented(offset, context.config).offset_left(result.len())?;
1052 rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?;
1053 result.push_str(&generics_str);
1055 // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds.
1056 if !bounds.is_empty() {
1057 let ident_hi = context
1059 .span_after(item.span, &item.ident.as_str());
1060 let bound_hi = bounds.last().unwrap().span().hi();
1061 let snippet = context.snippet(mk_sp(ident_hi, bound_hi));
1062 if contains_comment(snippet) {
1066 result = rewrite_assign_rhs_with(
1071 RhsTactics::ForceNextLineWithoutIndent,
1075 // Rewrite where-clause.
1076 if !generics.where_clause.predicates.is_empty() {
1077 let where_on_new_line = context.config.indent_style() != IndentStyle::Block;
1079 let where_budget = context.budget(last_line_width(&result));
1080 let pos_before_where = if bounds.is_empty() {
1081 generics.where_clause.span.lo()
1083 bounds[bounds.len() - 1].span().hi()
1085 let option = WhereClauseOption::snuggled(&generics_str);
1086 let where_clause_str = rewrite_where_clause(
1088 &generics.where_clause,
1089 context.config.brace_style(),
1090 Shape::legacy(where_budget, offset.block_only()),
1097 // If the where-clause cannot fit on the same line,
1098 // put the where-clause on a new line
1099 if !where_clause_str.contains('\n')
1100 && last_line_width(&result) + where_clause_str.len() + offset.width()
1101 > context.config.comment_width()
1103 let width = offset.block_indent + context.config.tab_spaces() - 1;
1104 let where_indent = Indent::new(0, width);
1105 result.push_str(&where_indent.to_string_with_newline(context.config));
1107 result.push_str(&where_clause_str);
1109 let item_snippet = context.snippet(item.span);
1110 if let Some(lo) = item_snippet.find('/') {
1112 let comment_hi = body_lo - BytePos(1);
1113 let comment_lo = item.span.lo() + BytePos(lo as u32);
1114 if comment_lo < comment_hi {
1115 match recover_missing_comment_in_span(
1116 mk_sp(comment_lo, comment_hi),
1117 Shape::indented(offset, context.config),
1119 last_line_width(&result),
1121 Some(ref missing_comment) if !missing_comment.is_empty() => {
1122 result.push_str(missing_comment);
1130 let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi());
1131 let snippet = context.snippet(block_span);
1132 let open_pos = snippet.find_uncommented("{")? + 1;
1134 match context.config.brace_style() {
1135 _ if last_line_contains_single_line_comment(&result)
1136 || last_line_width(&result) + 2 > context.budget(offset.width()) =>
1138 result.push_str(&offset.to_string_with_newline(context.config));
1140 _ if context.config.empty_item_single_line()
1142 && !result.contains('\n')
1143 && !contains_comment(&snippet[open_pos..]) =>
1145 result.push_str(" {}");
1146 return Some(result);
1148 BraceStyle::AlwaysNextLine => {
1149 result.push_str(&offset.to_string_with_newline(context.config));
1151 BraceStyle::PreferSameLine => result.push(' '),
1152 BraceStyle::SameLineWhere => {
1153 if result.contains('\n')
1154 || (!generics.where_clause.predicates.is_empty() && !items.is_empty())
1156 result.push_str(&offset.to_string_with_newline(context.config));
1164 let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
1166 if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
1167 let mut visitor = FmtVisitor::from_context(context);
1168 visitor.block_indent = offset.block_only().block_indent(context.config);
1169 visitor.last_pos = block_span.lo() + BytePos(open_pos as u32);
1172 visitor.visit_trait_item(item);
1175 visitor.format_missing(item.span.hi() - BytePos(1));
1177 let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config);
1179 result.push_str(&inner_indent_str);
1180 result.push_str(visitor.buffer.trim());
1181 result.push_str(&outer_indent_str);
1182 } else if result.contains('\n') {
1183 result.push_str(&outer_indent_str);
1193 pub(crate) struct TraitAliasBounds<'a> {
1194 generic_bounds: &'a ast::GenericBounds,
1195 generics: &'a ast::Generics,
1198 impl<'a> Rewrite for TraitAliasBounds<'a> {
1199 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1200 let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?;
1202 let mut option = WhereClauseOption::new(true, WhereClauseSpace::None);
1203 option.allow_single_line();
1205 let where_str = rewrite_where_clause(
1207 &self.generics.where_clause,
1208 context.config.brace_style(),
1213 self.generics.where_clause.span.lo(),
1217 let fits_single_line = !generic_bounds_str.contains('\n')
1218 && !where_str.contains('\n')
1219 && generic_bounds_str.len() + where_str.len() < shape.width;
1220 let space = if generic_bounds_str.is_empty() || where_str.is_empty() {
1222 } else if fits_single_line {
1225 shape.indent.to_string_with_newline(context.config)
1228 Some(format!("{}{}{}", generic_bounds_str, space, where_str))
1232 pub(crate) fn format_trait_alias(
1233 context: &RewriteContext<'_>,
1234 ident: symbol::Ident,
1235 vis: &ast::Visibility,
1236 generics: &ast::Generics,
1237 generic_bounds: &ast::GenericBounds,
1239 ) -> Option<String> {
1240 let alias = rewrite_ident(context, ident);
1241 // 6 = "trait ", 2 = " ="
1242 let g_shape = shape.offset_left(6)?.sub_width(2)?;
1243 let generics_str = rewrite_generics(context, alias, generics, g_shape)?;
1244 let vis_str = format_visibility(context, vis);
1245 let lhs = format!("{}trait {} =", vis_str, generics_str);
1247 let trait_alias_bounds = TraitAliasBounds {
1251 rewrite_assign_rhs(context, lhs, &trait_alias_bounds, shape.sub_width(1)?).map(|s| s + ";")
1254 fn format_unit_struct(
1255 context: &RewriteContext<'_>,
1256 p: &StructParts<'_>,
1258 ) -> Option<String> {
1259 let header_str = format_header(context, p.prefix, p.ident, p.vis, offset);
1260 let generics_str = if let Some(generics) = p.generics {
1261 let hi = context.snippet_provider.span_before(p.span, ";");
1265 context.config.brace_style(),
1268 // make a span that starts right after `struct Foo`
1269 mk_sp(p.ident.span.hi(), hi),
1270 last_line_width(&header_str),
1275 Some(format!("{}{};", header_str, generics_str))
1278 pub(crate) fn format_struct_struct(
1279 context: &RewriteContext<'_>,
1280 struct_parts: &StructParts<'_>,
1281 fields: &[ast::FieldDef],
1283 one_line_width: Option<usize>,
1284 ) -> Option<String> {
1285 let mut result = String::with_capacity(1024);
1286 let span = struct_parts.span;
1288 let header_str = struct_parts.format_header(context, offset);
1289 result.push_str(&header_str);
1291 let header_hi = struct_parts.ident.span.hi();
1292 let body_lo = context.snippet_provider.span_after(span, "{");
1294 let generics_str = match struct_parts.generics {
1295 Some(g) => format_generics(
1298 context.config.brace_style(),
1299 if fields.is_empty() {
1300 BracePos::ForceSameLine
1305 // make a span that starts right after `struct Foo`
1306 mk_sp(header_hi, body_lo),
1307 last_line_width(&result),
1310 // 3 = ` {}`, 2 = ` {`.
1311 let overhead = if fields.is_empty() { 3 } else { 2 };
1312 if (context.config.brace_style() == BraceStyle::AlwaysNextLine && !fields.is_empty())
1313 || context.config.max_width() < overhead + result.len()
1315 format!("\n{}{{", offset.block_only().to_string(context.config))
1322 let overhead = if fields.is_empty() { 1 } else { 0 };
1323 let total_width = result.len() + generics_str.len() + overhead;
1324 if !generics_str.is_empty()
1325 && !generics_str.contains('\n')
1326 && total_width > context.config.max_width()
1329 result.push_str(&offset.to_string(context.config));
1330 result.push_str(generics_str.trim_start());
1332 result.push_str(&generics_str);
1335 if fields.is_empty() {
1336 let inner_span = mk_sp(body_lo, span.hi() - BytePos(1));
1337 format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "", "}");
1338 return Some(result);
1342 let one_line_budget = context.budget(result.len() + 3 + offset.width());
1343 let one_line_budget =
1344 one_line_width.map_or(0, |one_line_width| min(one_line_width, one_line_budget));
1346 let items_str = rewrite_with_alignment(
1349 Shape::indented(offset.block_indent(context.config), context.config).sub_width(1)?,
1350 mk_sp(body_lo, span.hi()),
1354 if !items_str.contains('\n')
1355 && !result.contains('\n')
1356 && items_str.len() <= one_line_budget
1357 && !last_line_contains_single_line_comment(&items_str)
1359 Some(format!("{} {} }}", result, items_str))
1365 .block_indent(context.config)
1366 .to_string(context.config),
1368 offset.to_string(context.config)
1373 fn get_bytepos_after_visibility(vis: &ast::Visibility, default_span: Span) -> BytePos {
1375 ast::VisibilityKind::Crate(..) | ast::VisibilityKind::Restricted { .. } => vis.span.hi(),
1376 _ => default_span.lo(),
1380 // Format tuple or struct without any fields. We need to make sure that the comments
1381 // inside the delimiters are preserved.
1382 fn format_empty_struct_or_tuple(
1383 context: &RewriteContext<'_>,
1386 result: &mut String,
1390 // 3 = " {}" or "();"
1391 let used_width = last_line_used_width(result, offset.width()) + 3;
1392 if used_width > context.config.max_width() {
1393 result.push_str(&offset.to_string_with_newline(context.config))
1395 result.push_str(opener);
1396 match rewrite_missing_comment(span, Shape::indented(offset, context.config), context) {
1397 Some(ref s) if s.is_empty() => (),
1399 if !is_single_line(s) || first_line_contains_single_line_comment(s) {
1400 let nested_indent_str = offset
1401 .block_indent(context.config)
1402 .to_string_with_newline(context.config);
1403 result.push_str(&nested_indent_str);
1406 if last_line_contains_single_line_comment(s) {
1407 result.push_str(&offset.to_string_with_newline(context.config));
1410 None => result.push_str(context.snippet(span)),
1412 result.push_str(closer);
1415 fn format_tuple_struct(
1416 context: &RewriteContext<'_>,
1417 struct_parts: &StructParts<'_>,
1418 fields: &[ast::FieldDef],
1420 ) -> Option<String> {
1421 let mut result = String::with_capacity(1024);
1422 let span = struct_parts.span;
1424 let header_str = struct_parts.format_header(context, offset);
1425 result.push_str(&header_str);
1427 let body_lo = if fields.is_empty() {
1428 let lo = get_bytepos_after_visibility(struct_parts.vis, span);
1431 .span_after(mk_sp(lo, span.hi()), "(")
1435 let body_hi = if fields.is_empty() {
1438 .span_after(mk_sp(body_lo, span.hi()), ")")
1440 // This is a dirty hack to work around a missing `)` from the span of the last field.
1441 let last_arg_span = fields[fields.len() - 1].span;
1444 .opt_span_after(mk_sp(last_arg_span.hi(), span.hi()), ")")
1445 .unwrap_or_else(|| last_arg_span.hi())
1448 let where_clause_str = match struct_parts.generics {
1450 let budget = context.budget(last_line_width(&header_str));
1451 let shape = Shape::legacy(budget, offset);
1452 let generics_str = rewrite_generics(context, "", generics, shape)?;
1453 result.push_str(&generics_str);
1455 let where_budget = context.budget(last_line_width(&result));
1456 let option = WhereClauseOption::new(true, WhereClauseSpace::Newline);
1457 rewrite_where_clause(
1459 &generics.where_clause,
1460 context.config.brace_style(),
1461 Shape::legacy(where_budget, offset.block_only()),
1469 None => "".to_owned(),
1472 if fields.is_empty() {
1473 let body_hi = context
1475 .span_before(mk_sp(body_lo, span.hi()), ")");
1476 let inner_span = mk_sp(body_lo, body_hi);
1477 format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "(", ")");
1479 let shape = Shape::indented(offset, context.config).sub_width(1)?;
1480 let lo = if let Some(generics) = struct_parts.generics {
1483 struct_parts.ident.span.hi()
1485 result = overflow::rewrite_with_parens(
1490 mk_sp(lo, span.hi()),
1491 context.config.fn_call_width(),
1496 if !where_clause_str.is_empty()
1497 && !where_clause_str.contains('\n')
1498 && (result.contains('\n')
1499 || offset.block_indent + result.len() + where_clause_str.len() + 1
1500 > context.config.max_width())
1502 // We need to put the where-clause on a new line, but we didn't
1503 // know that earlier, so the where-clause will not be indented properly.
1506 &(offset.block_only() + (context.config.tab_spaces() - 1)).to_string(context.config),
1509 result.push_str(&where_clause_str);
1514 pub(crate) enum ItemVisitorKind<'a> {
1515 Item(&'a ast::Item),
1516 AssocTraitItem(&'a ast::AssocItem),
1517 AssocImplItem(&'a ast::AssocItem),
1518 ForeignItem(&'a ast::ForeignItem),
1521 struct TyAliasRewriteInfo<'c, 'g>(
1522 &'c RewriteContext<'c>,
1529 pub(crate) fn rewrite_type_alias<'a, 'b>(
1530 ty_alias_kind: &ast::TyAlias,
1531 context: &RewriteContext<'a>,
1533 visitor_kind: &ItemVisitorKind<'b>,
1535 ) -> Option<String> {
1536 use ItemVisitorKind::*;
1544 let ty_opt = ty.as_ref().map(|t| &**t);
1545 let (ident, vis) = match visitor_kind {
1546 Item(i) => (i.ident, &i.vis),
1547 AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis),
1548 ForeignItem(i) => (i.ident, &i.vis),
1550 let rw_info = &TyAliasRewriteInfo(context, indent, generics, ident, span);
1552 // Type Aliases are formatted slightly differently depending on the context
1553 // in which they appear, whether they are opaque, and whether they are associated.
1554 // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html
1555 // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases
1556 match (visitor_kind, ty_opt) {
1557 (Item(_), None) => {
1558 let op_ty = OpaqueType { bounds };
1559 rewrite_ty(rw_info, Some(bounds), Some(&op_ty), vis)
1561 (Item(_), Some(ty)) => rewrite_ty(rw_info, Some(bounds), Some(&*ty), vis),
1562 (AssocImplItem(_), _) => {
1563 let result = if let Some(ast::Ty {
1564 kind: ast::TyKind::ImplTrait(_, ref bounds),
1568 let op_ty = OpaqueType { bounds };
1569 rewrite_ty(rw_info, None, Some(&op_ty), &DEFAULT_VISIBILITY)
1571 rewrite_ty(rw_info, None, ty.as_ref(), vis)
1574 ast::Defaultness::Default(..) => Some(format!("default {}", result)),
1578 (AssocTraitItem(_), _) | (ForeignItem(_), _) => {
1579 rewrite_ty(rw_info, Some(bounds), ty.as_ref(), vis)
1584 fn rewrite_ty<R: Rewrite>(
1585 rw_info: &TyAliasRewriteInfo<'_, '_>,
1586 generic_bounds_opt: Option<&ast::GenericBounds>,
1588 vis: &ast::Visibility,
1589 ) -> Option<String> {
1590 let mut result = String::with_capacity(128);
1591 let TyAliasRewriteInfo(context, indent, generics, ident, span) = *rw_info;
1592 result.push_str(&format!("{}type ", format_visibility(context, vis)));
1593 let ident_str = rewrite_ident(context, ident);
1595 if generics.params.is_empty() {
1596 result.push_str(ident_str)
1599 let g_shape = Shape::indented(indent, context.config)
1600 .offset_left(result.len())?
1602 let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?;
1603 result.push_str(&generics_str);
1606 if let Some(bounds) = generic_bounds_opt {
1607 if !bounds.is_empty() {
1609 let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?;
1610 let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?;
1611 result.push_str(&type_bounds);
1615 let where_budget = context.budget(last_line_width(&result));
1616 let mut option = WhereClauseOption::snuggled(&result);
1618 option.suppress_comma();
1620 let where_clause_str = rewrite_where_clause(
1622 &generics.where_clause,
1623 context.config.brace_style(),
1624 Shape::legacy(where_budget, indent),
1631 result.push_str(&where_clause_str);
1633 if let Some(ty) = rhs {
1634 // If there's a where clause, add a newline before the assignment. Otherwise just add a
1636 let has_where = !generics.where_clause.predicates.is_empty();
1638 result.push_str(&indent.to_string_with_newline(context.config));
1643 let comment_span = context
1645 .opt_span_before(span, "=")
1646 .map(|op_lo| mk_sp(generics.where_clause.span.hi(), op_lo));
1648 let lhs = match comment_span {
1650 if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) =>
1652 let comment_shape = if has_where {
1653 Shape::indented(indent, context.config)
1655 Shape::indented(indent, context.config)
1656 .block_left(context.config.tab_spaces())?
1659 combine_strs_with_missing_comments(
1668 _ => format!("{}=", result),
1672 let shape = Shape::indented(indent, context.config).sub_width(1)?;
1673 rewrite_assign_rhs(context, lhs, &*ty, shape).map(|s| s + ";")
1675 Some(format!("{};", result))
1679 fn type_annotation_spacing(config: &Config) -> (&str, &str) {
1681 if config.space_before_colon() { " " } else { "" },
1682 if config.space_after_colon() { " " } else { "" },
1686 pub(crate) fn rewrite_struct_field_prefix(
1687 context: &RewriteContext<'_>,
1688 field: &ast::FieldDef,
1689 ) -> Option<String> {
1690 let vis = format_visibility(context, &field.vis);
1691 let type_annotation_spacing = type_annotation_spacing(context.config);
1692 Some(match field.ident {
1693 Some(name) => format!(
1696 rewrite_ident(context, name),
1697 type_annotation_spacing.0
1699 None => vis.to_string(),
1703 impl Rewrite for ast::FieldDef {
1704 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1705 rewrite_struct_field(context, self, shape, 0)
1709 pub(crate) fn rewrite_struct_field(
1710 context: &RewriteContext<'_>,
1711 field: &ast::FieldDef,
1713 lhs_max_width: usize,
1714 ) -> Option<String> {
1715 if contains_skip(&field.attrs) {
1716 return Some(context.snippet(field.span()).to_owned());
1719 let type_annotation_spacing = type_annotation_spacing(context.config);
1720 let prefix = rewrite_struct_field_prefix(context, field)?;
1722 let attrs_str = field.attrs.rewrite(context, shape)?;
1723 let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str);
1724 let missing_span = if field.attrs.is_empty() {
1725 mk_sp(field.span.lo(), field.span.lo())
1727 mk_sp(field.attrs.last().unwrap().span.hi(), field.span.lo())
1729 let mut spacing = String::from(if field.ident.is_some() {
1730 type_annotation_spacing.1
1734 // Try to put everything on a single line.
1735 let attr_prefix = combine_strs_with_missing_comments(
1743 let overhead = trimmed_last_line_width(&attr_prefix);
1744 let lhs_offset = lhs_max_width.saturating_sub(overhead);
1745 for _ in 0..lhs_offset {
1748 // In this extreme case we will be missing a space between an attribute and a field.
1749 if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() {
1753 .offset_left(overhead + spacing.len())
1754 .and_then(|ty_shape| field.ty.rewrite(context, ty_shape));
1755 if let Some(ref ty) = orig_ty {
1756 if !ty.contains('\n') {
1757 return Some(attr_prefix + &spacing + ty);
1761 let is_prefix_empty = prefix.is_empty();
1762 // We must use multiline. We are going to put attributes and a field on different lines.
1763 let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, shape)?;
1764 // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct.
1765 let field_str = if is_prefix_empty {
1766 field_str.trim_start()
1770 combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false)
1773 pub(crate) struct StaticParts<'a> {
1775 vis: &'a ast::Visibility,
1776 ident: symbol::Ident,
1778 mutability: ast::Mutability,
1779 expr_opt: Option<&'a ptr::P<ast::Expr>>,
1780 defaultness: Option<ast::Defaultness>,
1784 impl<'a> StaticParts<'a> {
1785 pub(crate) fn from_item(item: &'a ast::Item) -> Self {
1786 let (defaultness, prefix, ty, mutability, expr) = match item.kind {
1787 ast::ItemKind::Static(ref ty, mutability, ref expr) => {
1788 (None, "static", ty, mutability, expr)
1790 ast::ItemKind::Const(defaultness, ref ty, ref expr) => {
1791 (Some(defaultness), "const", ty, ast::Mutability::Not, expr)
1793 _ => unreachable!(),
1801 expr_opt: expr.as_ref(),
1807 pub(crate) fn from_trait_item(ti: &'a ast::AssocItem) -> Self {
1808 let (defaultness, ty, expr_opt) = match ti.kind {
1809 ast::AssocItemKind::Const(defaultness, ref ty, ref expr_opt) => {
1810 (defaultness, ty, expr_opt)
1812 _ => unreachable!(),
1819 mutability: ast::Mutability::Not,
1820 expr_opt: expr_opt.as_ref(),
1821 defaultness: Some(defaultness),
1826 pub(crate) fn from_impl_item(ii: &'a ast::AssocItem) -> Self {
1827 let (defaultness, ty, expr) = match ii.kind {
1828 ast::AssocItemKind::Const(defaultness, ref ty, ref expr) => (defaultness, ty, expr),
1829 _ => unreachable!(),
1836 mutability: ast::Mutability::Not,
1837 expr_opt: expr.as_ref(),
1838 defaultness: Some(defaultness),
1845 context: &RewriteContext<'_>,
1846 static_parts: &StaticParts<'_>,
1848 ) -> Option<String> {
1849 let colon = colon_spaces(context.config);
1850 let mut prefix = format!(
1852 format_visibility(context, static_parts.vis),
1853 static_parts.defaultness.map_or("", format_defaultness),
1854 static_parts.prefix,
1855 format_mutability(static_parts.mutability),
1856 rewrite_ident(context, static_parts.ident),
1861 Shape::indented(offset.block_only(), context.config).offset_left(prefix.len() + 2)?;
1862 let ty_str = match static_parts.ty.rewrite(context, ty_shape) {
1863 Some(ty_str) => ty_str,
1865 if prefix.ends_with(' ') {
1868 let nested_indent = offset.block_indent(context.config);
1869 let nested_shape = Shape::indented(nested_indent, context.config);
1870 let ty_str = static_parts.ty.rewrite(context, nested_shape)?;
1873 nested_indent.to_string_with_newline(context.config),
1879 if let Some(expr) = static_parts.expr_opt {
1880 let comments_lo = context.snippet_provider.span_after(static_parts.span, "=");
1881 let expr_lo = expr.span.lo();
1882 let comments_span = mk_sp(comments_lo, expr_lo);
1884 let lhs = format!("{}{} =", prefix, ty_str);
1887 let remaining_width = context.budget(offset.block_indent + 1);
1888 rewrite_assign_rhs_with_comments(
1892 Shape::legacy(remaining_width, offset.block_only()),
1893 RhsTactics::Default,
1897 .and_then(|res| recover_comment_removed(res, static_parts.span, context))
1898 .map(|s| if s.ends_with(';') { s } else { s + ";" })
1900 Some(format!("{}{};", prefix, ty_str))
1903 struct OpaqueType<'a> {
1904 bounds: &'a ast::GenericBounds,
1907 impl<'a> Rewrite for OpaqueType<'a> {
1908 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1909 let shape = shape.offset_left(5)?; // `impl `
1911 .rewrite(context, shape)
1912 .map(|s| format!("impl {}", s))
1916 impl Rewrite for ast::FnRetTy {
1917 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1919 ast::FnRetTy::Default(_) => Some(String::new()),
1920 ast::FnRetTy::Ty(ref ty) => {
1921 if context.config.version() == Version::One
1922 || context.config.indent_style() == IndentStyle::Visual
1924 let inner_width = shape.width.checked_sub(3)?;
1926 .rewrite(context, Shape::legacy(inner_width, shape.indent + 3))
1927 .map(|r| format!("-> {}", r));
1930 ty.rewrite(context, shape.offset_left(3)?)
1931 .map(|s| format!("-> {}", s))
1937 fn is_empty_infer(ty: &ast::Ty, pat_span: Span) -> bool {
1939 ast::TyKind::Infer => ty.span.hi() == pat_span.hi(),
1944 /// Recover any missing comments between the param and the type.
1948 /// A 2-len tuple with the comment before the colon in first position, and the comment after the
1949 /// colon in second position.
1950 fn get_missing_param_comments(
1951 context: &RewriteContext<'_>,
1955 ) -> (String, String) {
1956 let missing_comment_span = mk_sp(pat_span.hi(), ty_span.lo());
1958 let span_before_colon = {
1959 let missing_comment_span_hi = context
1961 .span_before(missing_comment_span, ":");
1962 mk_sp(pat_span.hi(), missing_comment_span_hi)
1964 let span_after_colon = {
1965 let missing_comment_span_lo = context
1967 .span_after(missing_comment_span, ":");
1968 mk_sp(missing_comment_span_lo, ty_span.lo())
1971 let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context)
1972 .filter(|comment| !comment.is_empty())
1973 .map_or(String::new(), |comment| format!(" {}", comment));
1974 let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context)
1975 .filter(|comment| !comment.is_empty())
1976 .map_or(String::new(), |comment| format!("{} ", comment));
1977 (comment_before_colon, comment_after_colon)
1980 impl Rewrite for ast::Param {
1981 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
1982 let param_attrs_result = self
1984 .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
1985 // N.B. Doc comments aren't typically valid syntax, but could appear
1986 // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936
1987 let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() {
1988 let num_attrs = self.attrs.len();
1990 mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()),
1991 param_attrs_result.contains('\n'),
1992 self.attrs.iter().any(|a| a.is_doc_comment()),
1995 (mk_sp(self.span.lo(), self.span.lo()), false, false)
1998 if let Some(ref explicit_self) = self.to_self() {
1999 rewrite_explicit_self(
2002 ¶m_attrs_result,
2005 has_multiple_attr_lines,
2007 } else if is_named_param(self) {
2008 let param_name = &self
2010 .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
2011 let mut result = combine_strs_with_missing_comments(
2013 ¶m_attrs_result,
2017 !has_multiple_attr_lines && !has_doc_comments,
2020 if !is_empty_infer(&*self.ty, self.pat.span) {
2021 let (before_comment, after_comment) =
2022 get_missing_param_comments(context, self.pat.span, self.ty.span, shape);
2023 result.push_str(&before_comment);
2024 result.push_str(colon_spaces(context.config));
2025 result.push_str(&after_comment);
2026 let overhead = last_line_width(&result);
2027 let max_width = shape.width.checked_sub(overhead)?;
2028 if let Some(ty_str) = self
2030 .rewrite(context, Shape::legacy(max_width, shape.indent))
2032 result.push_str(&ty_str);
2034 result = combine_strs_with_missing_comments(
2036 &(param_attrs_result + &shape.to_string_with_newline(context.config)),
2040 !has_multiple_attr_lines,
2042 result.push_str(&before_comment);
2043 result.push_str(colon_spaces(context.config));
2044 result.push_str(&after_comment);
2045 let overhead = last_line_width(&result);
2046 let max_width = shape.width.checked_sub(overhead)?;
2049 .rewrite(context, Shape::legacy(max_width, shape.indent))?;
2050 result.push_str(&ty_str);
2056 self.ty.rewrite(context, shape)
2061 fn rewrite_explicit_self(
2062 context: &RewriteContext<'_>,
2063 explicit_self: &ast::ExplicitSelf,
2067 has_multiple_attr_lines: bool,
2068 ) -> Option<String> {
2069 match explicit_self.node {
2070 ast::SelfKind::Region(lt, m) => {
2071 let mut_str = format_mutability(m);
2074 let lifetime_str = l.rewrite(
2076 Shape::legacy(context.config.max_width(), Indent::empty()),
2078 Some(combine_strs_with_missing_comments(
2081 &format!("&{} {}self", lifetime_str, mut_str),
2084 !has_multiple_attr_lines,
2087 None => Some(combine_strs_with_missing_comments(
2090 &format!("&{}self", mut_str),
2093 !has_multiple_attr_lines,
2097 ast::SelfKind::Explicit(ref ty, mutability) => {
2098 let type_str = ty.rewrite(
2100 Shape::legacy(context.config.max_width(), Indent::empty()),
2103 Some(combine_strs_with_missing_comments(
2106 &format!("{}self: {}", format_mutability(mutability), type_str),
2109 !has_multiple_attr_lines,
2112 ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments(
2115 &format!("{}self", format_mutability(mutability)),
2118 !has_multiple_attr_lines,
2123 pub(crate) fn span_lo_for_param(param: &ast::Param) -> BytePos {
2124 if param.attrs.is_empty() {
2125 if is_named_param(param) {
2131 param.attrs[0].span.lo()
2135 pub(crate) fn span_hi_for_param(context: &RewriteContext<'_>, param: &ast::Param) -> BytePos {
2136 match param.ty.kind {
2137 ast::TyKind::Infer if context.snippet(param.ty.span) == "_" => param.ty.span.hi(),
2138 ast::TyKind::Infer if is_named_param(param) => param.pat.span.hi(),
2139 _ => param.ty.span.hi(),
2143 pub(crate) fn is_named_param(param: &ast::Param) -> bool {
2144 if let ast::PatKind::Ident(_, ident, _) = param.pat.kind {
2145 ident.name != symbol::kw::Empty
2151 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
2152 pub(crate) enum FnBraceStyle {
2158 // Return type is (result, force_new_line_for_brace)
2160 context: &RewriteContext<'_>,
2162 ident: symbol::Ident,
2165 fn_brace_style: FnBraceStyle,
2166 ) -> Option<(String, bool, bool)> {
2167 let mut force_new_line_for_brace = false;
2169 let where_clause = &fn_sig.generics.where_clause;
2171 let mut result = String::with_capacity(1024);
2172 result.push_str(&fn_sig.to_str(context));
2175 result.push_str("fn ");
2178 let overhead = if let FnBraceStyle::SameLine = fn_brace_style {
2185 let used_width = last_line_used_width(&result, indent.width());
2186 let one_line_budget = context.budget(used_width + overhead);
2188 width: one_line_budget,
2192 let fd = fn_sig.decl;
2193 let generics_str = rewrite_generics(
2195 rewrite_ident(context, ident),
2199 result.push_str(&generics_str);
2201 let snuggle_angle_bracket = generics_str
2204 .map_or(false, |l| l.trim_start().len() == 1);
2206 // Note that the width and indent don't really matter, we'll re-layout the
2207 // return type later anyway.
2210 .rewrite(context, Shape::indented(indent, context.config))?;
2212 let multi_line_ret_str = ret_str.contains('\n');
2213 let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() };
2216 let (one_line_budget, multi_line_budget, mut param_indent) = compute_budgets_for_params(
2226 "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}",
2227 one_line_budget, multi_line_budget, param_indent
2231 // Check if vertical layout was forced.
2232 if one_line_budget == 0
2233 && !snuggle_angle_bracket
2234 && context.config.indent_style() == IndentStyle::Visual
2236 result.push_str(¶m_indent.to_string_with_newline(context.config));
2239 let params_end = if fd.inputs.is_empty() {
2242 .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), ")")
2244 let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi(), span.hi());
2245 context.snippet_provider.span_after(last_span, ")")
2247 let params_span = mk_sp(
2250 .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), "("),
2253 let param_str = rewrite_params(
2264 let put_params_in_block = match context.config.indent_style() {
2265 IndentStyle::Block => param_str.contains('\n') || param_str.len() > one_line_budget,
2267 } && !fd.inputs.is_empty();
2269 let mut params_last_line_contains_comment = false;
2270 let mut no_params_and_over_max_width = false;
2272 if put_params_in_block {
2273 param_indent = indent.block_indent(context.config);
2274 result.push_str(¶m_indent.to_string_with_newline(context.config));
2275 result.push_str(¶m_str);
2276 result.push_str(&indent.to_string_with_newline(context.config));
2279 result.push_str(¶m_str);
2280 let used_width = last_line_used_width(&result, indent.width()) + first_line_width(&ret_str);
2281 // Put the closing brace on the next line if it overflows the max width.
2283 let closing_paren_overflow_max_width =
2284 fd.inputs.is_empty() && used_width + 1 > context.config.max_width();
2285 // If the last line of params contains comment, we cannot put the closing paren
2286 // on the same line.
2287 params_last_line_contains_comment = param_str
2290 .map_or(false, |last_line| last_line.contains("//"));
2292 if context.config.version() == Version::Two {
2293 if closing_paren_overflow_max_width {
2295 result.push_str(&indent.to_string_with_newline(context.config));
2296 no_params_and_over_max_width = true;
2297 } else if params_last_line_contains_comment {
2298 result.push_str(&indent.to_string_with_newline(context.config));
2300 no_params_and_over_max_width = true;
2305 if closing_paren_overflow_max_width || params_last_line_contains_comment {
2306 result.push_str(&indent.to_string_with_newline(context.config));
2313 if let ast::FnRetTy::Ty(..) = fd.output {
2314 let ret_should_indent = match context.config.indent_style() {
2315 // If our params are block layout then we surely must have space.
2316 IndentStyle::Block if put_params_in_block || fd.inputs.is_empty() => false,
2317 _ if params_last_line_contains_comment => false,
2318 _ if result.contains('\n') || multi_line_ret_str => true,
2320 // If the return type would push over the max width, then put the return type on
2321 // a new line. With the +1 for the signature length an additional space between
2322 // the closing parenthesis of the param and the arrow '->' is considered.
2323 let mut sig_length = result.len() + indent.width() + ret_str_len + 1;
2325 // If there is no where-clause, take into account the space after the return type
2327 if where_clause.predicates.is_empty() {
2331 sig_length > context.config.max_width()
2334 let ret_shape = if ret_should_indent {
2335 if context.config.version() == Version::One
2336 || context.config.indent_style() == IndentStyle::Visual
2338 let indent = if param_str.is_empty() {
2339 // Aligning with non-existent params looks silly.
2340 force_new_line_for_brace = true;
2343 // FIXME: we might want to check that using the param indent
2344 // doesn't blow our budget, and if it does, then fallback to
2345 // the where-clause indent.
2349 result.push_str(&indent.to_string_with_newline(context.config));
2350 Shape::indented(indent, context.config)
2352 let mut ret_shape = Shape::indented(indent, context.config);
2353 if param_str.is_empty() {
2354 // Aligning with non-existent params looks silly.
2355 force_new_line_for_brace = true;
2356 ret_shape = if context.use_block_indent() {
2357 ret_shape.offset_left(4).unwrap_or(ret_shape)
2359 ret_shape.indent = ret_shape.indent + 4;
2364 result.push_str(&ret_shape.indent.to_string_with_newline(context.config));
2368 if context.config.version() == Version::Two {
2369 if !param_str.is_empty() || !no_params_and_over_max_width {
2376 let ret_shape = Shape::indented(indent, context.config);
2378 .offset_left(last_line_width(&result))
2379 .unwrap_or(ret_shape)
2382 if multi_line_ret_str || ret_should_indent {
2383 // Now that we know the proper indent and width, we need to
2384 // re-layout the return type.
2385 let ret_str = fd.output.rewrite(context, ret_shape)?;
2386 result.push_str(&ret_str);
2388 result.push_str(&ret_str);
2391 // Comment between return type and the end of the decl.
2392 let snippet_lo = fd.output.span().hi();
2393 if where_clause.predicates.is_empty() {
2394 let snippet_hi = span.hi();
2395 let snippet = context.snippet(mk_sp(snippet_lo, snippet_hi));
2396 // Try to preserve the layout of the original snippet.
2397 let original_starts_with_newline = snippet
2399 .map_or(false, |i| starts_with_newline(&snippet[i..]));
2400 let original_ends_with_newline = snippet
2401 .rfind(|c| c != ' ')
2402 .map_or(false, |i| snippet[i..].ends_with('\n'));
2403 let snippet = snippet.trim();
2404 if !snippet.is_empty() {
2405 result.push(if original_starts_with_newline {
2410 result.push_str(snippet);
2411 if original_ends_with_newline {
2412 force_new_line_for_brace = true;
2418 let pos_before_where = match fd.output {
2419 ast::FnRetTy::Default(..) => params_span.hi(),
2420 ast::FnRetTy::Ty(ref ty) => ty.span.hi(),
2423 let is_params_multi_lined = param_str.contains('\n');
2425 let space = if put_params_in_block && ret_str.is_empty() {
2426 WhereClauseSpace::Space
2428 WhereClauseSpace::Newline
2430 let mut option = WhereClauseOption::new(fn_brace_style == FnBraceStyle::None, space);
2431 if is_params_multi_lined {
2432 option.veto_single_line();
2434 let where_clause_str = rewrite_where_clause(
2437 context.config.brace_style(),
2438 Shape::indented(indent, context.config),
2445 // If there are neither where-clause nor return type, we may be missing comments between
2447 if where_clause_str.is_empty() {
2448 if let ast::FnRetTy::Default(ret_span) = fd.output {
2449 match recover_missing_comment_in_span(
2450 mk_sp(params_span.hi(), ret_span.hi()),
2453 last_line_width(&result),
2455 Some(ref missing_comment) if !missing_comment.is_empty() => {
2456 result.push_str(missing_comment);
2457 force_new_line_for_brace = true;
2464 result.push_str(&where_clause_str);
2466 let ends_with_comment = last_line_contains_single_line_comment(&result);
2467 force_new_line_for_brace |= ends_with_comment;
2468 force_new_line_for_brace |=
2469 is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty();
2470 Some((result, ends_with_comment, force_new_line_for_brace))
2473 /// Kind of spaces to put before `where`.
2474 #[derive(Copy, Clone)]
2475 enum WhereClauseSpace {
2484 #[derive(Copy, Clone)]
2485 struct WhereClauseOption {
2486 suppress_comma: bool, // Force no trailing comma
2487 snuggle: WhereClauseSpace,
2488 allow_single_line: bool, // Try single line where-clause instead of vertical layout
2489 veto_single_line: bool, // Disallow a single-line where-clause.
2492 impl WhereClauseOption {
2493 fn new(suppress_comma: bool, snuggle: WhereClauseSpace) -> WhereClauseOption {
2497 allow_single_line: false,
2498 veto_single_line: false,
2502 fn snuggled(current: &str) -> WhereClauseOption {
2504 suppress_comma: false,
2505 snuggle: if last_line_width(current) == 1 {
2506 WhereClauseSpace::Space
2508 WhereClauseSpace::Newline
2510 allow_single_line: false,
2511 veto_single_line: false,
2515 fn suppress_comma(&mut self) {
2516 self.suppress_comma = true
2519 fn allow_single_line(&mut self) {
2520 self.allow_single_line = true
2523 fn snuggle(&mut self) {
2524 self.snuggle = WhereClauseSpace::Space
2527 fn veto_single_line(&mut self) {
2528 self.veto_single_line = true;
2533 context: &RewriteContext<'_>,
2534 params: &[ast::Param],
2535 one_line_budget: usize,
2536 multi_line_budget: usize,
2538 param_indent: Indent,
2541 ) -> Option<String> {
2542 if params.is_empty() {
2543 let comment = context
2547 span.hi() - BytePos(1),
2550 return Some(comment.to_owned());
2552 let param_items: Vec<_> = itemize_list(
2553 context.snippet_provider,
2557 |param| span_lo_for_param(param),
2558 |param| param.ty.span.hi(),
2561 .rewrite(context, Shape::legacy(multi_line_budget, param_indent))
2562 .or_else(|| Some(context.snippet(param.span()).to_owned()))
2570 let tactic = definitive_tactic(
2575 .to_list_tactic(param_items.len()),
2579 let budget = match tactic {
2580 DefinitiveListTactic::Horizontal => one_line_budget,
2581 _ => multi_line_budget,
2583 let indent = match context.config.indent_style() {
2584 IndentStyle::Block => indent.block_indent(context.config),
2585 IndentStyle::Visual => param_indent,
2587 let trailing_separator = if variadic {
2588 SeparatorTactic::Never
2590 match context.config.indent_style() {
2591 IndentStyle::Block => context.config.trailing_comma(),
2592 IndentStyle::Visual => SeparatorTactic::Never,
2595 let fmt = ListFormatting::new(Shape::legacy(budget, indent), context.config)
2597 .trailing_separator(trailing_separator)
2598 .ends_with_newline(tactic.ends_with_newline(context.config.indent_style()))
2599 .preserve_newline(true);
2600 write_list(¶m_items, &fmt)
2603 fn compute_budgets_for_params(
2604 context: &RewriteContext<'_>,
2608 fn_brace_style: FnBraceStyle,
2609 force_vertical_layout: bool,
2610 ) -> Option<(usize, usize, Indent)> {
2612 "compute_budgets_for_params {} {:?}, {}, {:?}",
2618 // Try keeping everything on the same line.
2619 if !result.contains('\n') && !force_vertical_layout {
2620 // 2 = `()`, 3 = `() `, space is before ret_string.
2621 let overhead = if ret_str_len == 0 { 2 } else { 3 };
2622 let mut used_space = indent.width() + result.len() + ret_str_len + overhead;
2623 match fn_brace_style {
2624 FnBraceStyle::None => used_space += 1, // 1 = `;`
2625 FnBraceStyle::SameLine => used_space += 2, // 2 = `{}`
2626 FnBraceStyle::NextLine => (),
2628 let one_line_budget = context.budget(used_space);
2630 if one_line_budget > 0 {
2632 let (indent, multi_line_budget) = match context.config.indent_style() {
2633 IndentStyle::Block => {
2634 let indent = indent.block_indent(context.config);
2635 (indent, context.budget(indent.width() + 1))
2637 IndentStyle::Visual => {
2638 let indent = indent + result.len() + 1;
2639 let multi_line_overhead = match fn_brace_style {
2640 FnBraceStyle::SameLine => 4,
2643 (indent, context.budget(multi_line_overhead))
2647 return Some((one_line_budget, multi_line_budget, indent));
2651 // Didn't work. we must force vertical layout and put params on a newline.
2652 let new_indent = indent.block_indent(context.config);
2653 let used_space = match context.config.indent_style() {
2655 IndentStyle::Block => new_indent.width() + 1,
2656 // Account for `)` and possibly ` {`.
2657 IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 },
2659 Some((0, context.budget(used_space), new_indent))
2662 fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle {
2663 let predicate_count = where_clause.predicates.len();
2665 if config.where_single_line() && predicate_count == 1 {
2666 return FnBraceStyle::SameLine;
2668 let brace_style = config.brace_style();
2670 let use_next_line = brace_style == BraceStyle::AlwaysNextLine
2671 || (brace_style == BraceStyle::SameLineWhere && predicate_count > 0);
2673 FnBraceStyle::NextLine
2675 FnBraceStyle::SameLine
2679 fn rewrite_generics(
2680 context: &RewriteContext<'_>,
2682 generics: &ast::Generics,
2684 ) -> Option<String> {
2685 // FIXME: convert bounds to where-clauses where they get too big or if
2686 // there is a where-clause at all.
2688 if generics.params.is_empty() {
2689 return Some(ident.to_owned());
2692 let params = generics.params.iter();
2693 overflow::rewrite_with_angle_brackets(context, ident, params, shape, generics.span)
2696 fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> Option<Shape> {
2697 match config.indent_style() {
2698 IndentStyle::Visual => shape.visual_indent(1 + offset).sub_width(offset + 2),
2699 IndentStyle::Block => {
2703 .block_indent(config.tab_spaces())
2704 .with_max_width(config)
2710 fn rewrite_where_clause_rfc_style(
2711 context: &RewriteContext<'_>,
2712 where_clause: &ast::WhereClause,
2715 span_end: Option<BytePos>,
2716 span_end_before_where: BytePos,
2717 where_clause_option: WhereClauseOption,
2718 ) -> Option<String> {
2719 let (where_keyword, allow_single_line) = rewrite_where_keyword(
2723 span_end_before_where,
2724 where_clause_option,
2728 let clause_shape = shape
2730 .with_max_width(context.config)
2731 .block_left(context.config.tab_spaces())?
2733 let force_single_line = context.config.where_single_line()
2734 && where_clause.predicates.len() == 1
2735 && !where_clause_option.veto_single_line;
2737 let preds_str = rewrite_bounds_on_where_clause(
2743 where_clause_option,
2749 if allow_single_line && !preds_str.contains('\n') && 6 + preds_str.len() <= shape.width
2750 || force_single_line
2754 clause_shape.indent.to_string_with_newline(context.config)
2757 Some(format!("{}{}{}", where_keyword, clause_sep, preds_str))
2760 /// Rewrite `where` and comment around it.
2761 fn rewrite_where_keyword(
2762 context: &RewriteContext<'_>,
2763 where_clause: &ast::WhereClause,
2765 span_end_before_where: BytePos,
2766 where_clause_option: WhereClauseOption,
2767 ) -> Option<(String, bool)> {
2768 let block_shape = shape.block().with_max_width(context.config);
2770 let clause_shape = block_shape
2771 .block_left(context.config.tab_spaces())?
2774 let comment_separator = |comment: &str, shape: Shape| {
2775 if comment.is_empty() {
2778 shape.indent.to_string_with_newline(context.config)
2782 let (span_before, span_after) =
2783 missing_span_before_after_where(span_end_before_where, where_clause);
2784 let (comment_before, comment_after) =
2785 rewrite_comments_before_after_where(context, span_before, span_after, shape)?;
2787 let starting_newline = match where_clause_option.snuggle {
2788 WhereClauseSpace::Space if comment_before.is_empty() => Cow::from(" "),
2789 WhereClauseSpace::None => Cow::from(""),
2790 _ => block_shape.indent.to_string_with_newline(context.config),
2793 let newline_before_where = comment_separator(&comment_before, shape);
2794 let newline_after_where = comment_separator(&comment_after, clause_shape);
2795 let result = format!(
2797 starting_newline, comment_before, newline_before_where, newline_after_where, comment_after
2799 let allow_single_line = where_clause_option.allow_single_line
2800 && comment_before.is_empty()
2801 && comment_after.is_empty();
2803 Some((result, allow_single_line))
2806 /// Rewrite bounds on a where clause.
2807 fn rewrite_bounds_on_where_clause(
2808 context: &RewriteContext<'_>,
2809 where_clause: &ast::WhereClause,
2812 span_end: Option<BytePos>,
2813 where_clause_option: WhereClauseOption,
2814 force_single_line: bool,
2815 ) -> Option<String> {
2816 let span_start = where_clause.predicates[0].span().lo();
2817 // If we don't have the start of the next span, then use the end of the
2818 // predicates, but that means we miss comments.
2819 let len = where_clause.predicates.len();
2820 let end_of_preds = where_clause.predicates[len - 1].span().hi();
2821 let span_end = span_end.unwrap_or(end_of_preds);
2822 let items = itemize_list(
2823 context.snippet_provider,
2824 where_clause.predicates.iter(),
2827 |pred| pred.span().lo(),
2828 |pred| pred.span().hi(),
2829 |pred| pred.rewrite(context, shape),
2834 let comma_tactic = if where_clause_option.suppress_comma || force_single_line {
2835 SeparatorTactic::Never
2837 context.config.trailing_comma()
2840 // shape should be vertical only and only if we have `force_single_line` option enabled
2841 // and the number of items of the where-clause is equal to 1
2842 let shape_tactic = if force_single_line {
2843 DefinitiveListTactic::Horizontal
2845 DefinitiveListTactic::Vertical
2848 let fmt = ListFormatting::new(shape, context.config)
2849 .tactic(shape_tactic)
2850 .trailing_separator(comma_tactic)
2851 .preserve_newline(true);
2852 write_list(&items.collect::<Vec<_>>(), &fmt)
2855 fn rewrite_where_clause(
2856 context: &RewriteContext<'_>,
2857 where_clause: &ast::WhereClause,
2858 brace_style: BraceStyle,
2862 span_end: Option<BytePos>,
2863 span_end_before_where: BytePos,
2864 where_clause_option: WhereClauseOption,
2865 ) -> Option<String> {
2866 if where_clause.predicates.is_empty() {
2867 return Some(String::new());
2870 if context.config.indent_style() == IndentStyle::Block {
2871 return rewrite_where_clause_rfc_style(
2877 span_end_before_where,
2878 where_clause_option,
2882 let extra_indent = Indent::new(context.config.tab_spaces(), 0);
2884 let offset = match context.config.indent_style() {
2885 IndentStyle::Block => shape.indent + extra_indent.block_indent(context.config),
2886 // 6 = "where ".len()
2887 IndentStyle::Visual => shape.indent + extra_indent + 6,
2889 // FIXME: if indent_style != Visual, then the budgets below might
2890 // be out by a char or two.
2892 let budget = context.config.max_width() - offset.width();
2893 let span_start = where_clause.predicates[0].span().lo();
2894 // If we don't have the start of the next span, then use the end of the
2895 // predicates, but that means we miss comments.
2896 let len = where_clause.predicates.len();
2897 let end_of_preds = where_clause.predicates[len - 1].span().hi();
2898 let span_end = span_end.unwrap_or(end_of_preds);
2899 let items = itemize_list(
2900 context.snippet_provider,
2901 where_clause.predicates.iter(),
2904 |pred| pred.span().lo(),
2905 |pred| pred.span().hi(),
2906 |pred| pred.rewrite(context, Shape::legacy(budget, offset)),
2911 let item_vec = items.collect::<Vec<_>>();
2912 // FIXME: we don't need to collect here
2913 let tactic = definitive_tactic(&item_vec, ListTactic::Vertical, Separator::Comma, budget);
2915 let mut comma_tactic = context.config.trailing_comma();
2916 // Kind of a hack because we don't usually have trailing commas in where-clauses.
2917 if comma_tactic == SeparatorTactic::Vertical || where_clause_option.suppress_comma {
2918 comma_tactic = SeparatorTactic::Never;
2921 let fmt = ListFormatting::new(Shape::legacy(budget, offset), context.config)
2923 .trailing_separator(comma_tactic)
2924 .ends_with_newline(tactic.ends_with_newline(context.config.indent_style()))
2925 .preserve_newline(true);
2926 let preds_str = write_list(&item_vec, &fmt)?;
2928 let end_length = if terminator == "{" {
2929 // If the brace is on the next line we don't need to count it otherwise it needs two
2932 BraceStyle::AlwaysNextLine | BraceStyle::SameLineWhere => 0,
2933 BraceStyle::PreferSameLine => 2,
2935 } else if terminator == "=" {
2941 || preds_str.contains('\n')
2942 || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width
2946 (shape.indent + extra_indent).to_string(context.config),
2950 Some(format!(" where {}", preds_str))
2954 fn missing_span_before_after_where(
2955 before_item_span_end: BytePos,
2956 where_clause: &ast::WhereClause,
2958 let missing_span_before = mk_sp(before_item_span_end, where_clause.span.lo());
2960 let pos_after_where = where_clause.span.lo() + BytePos(5);
2961 let missing_span_after = mk_sp(pos_after_where, where_clause.predicates[0].span().lo());
2962 (missing_span_before, missing_span_after)
2965 fn rewrite_comments_before_after_where(
2966 context: &RewriteContext<'_>,
2967 span_before_where: Span,
2968 span_after_where: Span,
2970 ) -> Option<(String, String)> {
2971 let before_comment = rewrite_missing_comment(span_before_where, shape, context)?;
2972 let after_comment = rewrite_missing_comment(
2974 shape.block_indent(context.config.tab_spaces()),
2977 Some((before_comment, after_comment))
2981 context: &RewriteContext<'_>,
2983 ident: symbol::Ident,
2984 vis: &ast::Visibility,
2987 let mut result = String::with_capacity(128);
2988 let shape = Shape::indented(offset, context.config);
2990 result.push_str(format_visibility(context, vis).trim());
2992 // Check for a missing comment between the visibility and the item name.
2993 let after_vis = vis.span.hi();
2994 if let Some(before_item_name) = context
2996 .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim())
2998 let missing_span = mk_sp(after_vis, before_item_name);
2999 if let Some(result_with_comment) = combine_strs_with_missing_comments(
3005 /* allow_extend */ true,
3007 result = result_with_comment;
3011 result.push_str(rewrite_ident(context, ident));
3016 #[derive(PartialEq, Eq, Clone, Copy)]
3024 context: &RewriteContext<'_>,
3025 generics: &ast::Generics,
3026 brace_style: BraceStyle,
3027 brace_pos: BracePos,
3031 ) -> Option<String> {
3032 let shape = Shape::legacy(context.budget(used_width + offset.width()), offset);
3033 let mut result = rewrite_generics(context, "", generics, shape)?;
3035 // If the generics are not parameterized then generics.span.hi() == 0,
3036 // so we use span.lo(), which is the position after `struct Foo`.
3037 let span_end_before_where = if !generics.params.is_empty() {
3042 let (same_line_brace, missed_comments) = if !generics.where_clause.predicates.is_empty() {
3043 let budget = context.budget(last_line_used_width(&result, offset.width()));
3044 let mut option = WhereClauseOption::snuggled(&result);
3045 if brace_pos == BracePos::None {
3046 option.suppress_comma = true;
3048 let where_clause_str = rewrite_where_clause(
3050 &generics.where_clause,
3052 Shape::legacy(budget, offset.block_only()),
3056 span_end_before_where,
3059 result.push_str(&where_clause_str);
3061 brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine,
3062 // missed comments are taken care of in #rewrite_where_clause
3067 brace_pos == BracePos::ForceSameLine
3068 || (result.contains('\n') && brace_style == BraceStyle::PreferSameLine
3069 || brace_style != BraceStyle::AlwaysNextLine)
3070 || trimmed_last_line_width(&result) == 1,
3071 rewrite_missing_comment(
3073 span_end_before_where,
3074 if brace_pos == BracePos::None {
3077 context.snippet_provider.span_before(span, "{")
3085 // add missing comments
3086 let missed_line_comments = missed_comments
3087 .filter(|missed_comments| !missed_comments.is_empty())
3088 .map_or(false, |missed_comments| {
3089 let is_block = is_last_comment_block(&missed_comments);
3090 let sep = if is_block { " " } else { "\n" };
3091 result.push_str(sep);
3092 result.push_str(&missed_comments);
3095 if brace_pos == BracePos::None {
3096 return Some(result);
3098 let total_used_width = last_line_used_width(&result, used_width);
3099 let remaining_budget = context.budget(total_used_width);
3100 // If the same line brace if forced, it indicates that we are rewriting an item with empty body,
3101 // and hence we take the closer into account as well for one line budget.
3102 // We assume that the closer has the same length as the opener.
3103 let overhead = if brace_pos == BracePos::ForceSameLine {
3110 let forbid_same_line_brace = missed_line_comments || overhead > remaining_budget;
3111 if !forbid_same_line_brace && same_line_brace {
3115 result.push_str(&offset.block_only().to_string(context.config));
3122 impl Rewrite for ast::ForeignItem {
3123 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
3124 let attrs_str = self.attrs.rewrite(context, shape)?;
3125 // Drop semicolon or it will be interpreted as comment.
3126 // FIXME: this may be a faulty span from libsyntax.
3127 let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1));
3129 let item_str = match self.kind {
3130 ast::ForeignItemKind::Fn(ref fn_kind) => {
3137 if let Some(ref body) = body {
3138 let mut visitor = FmtVisitor::from_context(context);
3139 visitor.block_indent = shape.indent;
3140 visitor.last_pos = self.span.lo();
3141 let inner_attrs = inner_attributes(&self.attrs);
3142 let fn_ctxt = visit::FnCtxt::Foreign;
3144 visit::FnKind::Fn(fn_ctxt, self.ident, &sig, &self.vis, Some(body)),
3151 Some(visitor.buffer.to_owned())
3157 &FnSig::from_method_sig(&sig, generics, &self.vis),
3161 .map(|(s, _, _)| format!("{};", s))
3164 ast::ForeignItemKind::Static(ref ty, mutability, _) => {
3165 // FIXME(#21): we're dropping potential comments in between the
3166 // function kw here.
3167 let vis = format_visibility(context, &self.vis);
3168 let mut_str = format_mutability(mutability);
3169 let prefix = format!(
3173 rewrite_ident(context, self.ident)
3176 rewrite_assign_rhs(context, prefix, &**ty, shape.sub_width(1)?).map(|s| s + ";")
3178 ast::ForeignItemKind::TyAlias(ref ty_alias) => {
3179 let (kind, span) = (&ItemVisitorKind::ForeignItem(&self), self.span);
3180 rewrite_type_alias(ty_alias, context, shape.indent, kind, span)
3182 ast::ForeignItemKind::MacCall(ref mac) => {
3183 rewrite_macro(mac, None, context, shape, MacroPosition::Item)
3187 let missing_span = if self.attrs.is_empty() {
3188 mk_sp(self.span.lo(), self.span.lo())
3190 mk_sp(self.attrs[self.attrs.len() - 1].span.hi(), self.span.lo())
3192 combine_strs_with_missing_comments(
3203 /// Rewrite the attributes of an item.
3205 context: &RewriteContext<'_>,
3209 ) -> Option<String> {
3210 let attrs = filter_inline_attrs(&item.attrs, item.span());
3211 let attrs_str = attrs.rewrite(context, shape)?;
3213 let missed_span = if attrs.is_empty() {
3214 mk_sp(item.span.lo(), item.span.lo())
3216 mk_sp(attrs[attrs.len() - 1].span.hi(), item.span.lo())
3219 let allow_extend = if attrs.len() == 1 {
3220 let line_len = attrs_str.len() + 1 + item_str.len();
3221 !attrs.first().unwrap().is_doc_comment()
3222 && context.config.inline_attribute_width() >= line_len
3227 combine_strs_with_missing_comments(
3237 /// Rewrite an inline mod.
3238 /// The given shape is used to format the mod's attributes.
3239 pub(crate) fn rewrite_mod(
3240 context: &RewriteContext<'_>,
3243 ) -> Option<String> {
3244 let mut result = String::with_capacity(32);
3245 result.push_str(&*format_visibility(context, &item.vis));
3246 result.push_str("mod ");
3247 result.push_str(rewrite_ident(context, item.ident));
3249 rewrite_attrs(context, item, &result, attrs_shape)
3252 /// Rewrite `extern crate foo;`.
3253 /// The given shape is used to format the extern crate's attributes.
3254 pub(crate) fn rewrite_extern_crate(
3255 context: &RewriteContext<'_>,
3258 ) -> Option<String> {
3259 assert!(is_extern_crate(item));
3260 let new_str = context.snippet(item.span);
3261 let item_str = if contains_comment(new_str) {
3264 let no_whitespace = &new_str.split_whitespace().collect::<Vec<&str>>().join(" ");
3265 String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";"))
3267 rewrite_attrs(context, item, &item_str, attrs_shape)
3270 /// Returns `true` for `mod foo;`, false for `mod foo { .. }`.
3271 pub(crate) fn is_mod_decl(item: &ast::Item) -> bool {
3274 ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _))
3278 pub(crate) fn is_use_item(item: &ast::Item) -> bool {
3279 matches!(item.kind, ast::ItemKind::Use(_))
3282 pub(crate) fn is_extern_crate(item: &ast::Item) -> bool {
3283 matches!(item.kind, ast::ItemKind::ExternCrate(..))