1 // Copyright 2015 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.
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.
11 // Formatting top-level items - functions, structs, enums, traits, impls.
13 use {ReturnIndent, BraceStyle};
14 use utils::{format_visibility, make_indent, contains_skip, span_after, end_typaram};
15 use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
16 use comment::FindUncommented;
17 use visitor::FmtVisitor;
19 use syntax::{ast, abi};
20 use syntax::codemap::{self, Span, BytePos};
21 use syntax::print::pprust;
22 use syntax::parse::token;
24 impl<'a> FmtVisitor<'a> {
25 pub fn rewrite_fn(&mut self,
29 explicit_self: Option<&ast::ExplicitSelf>,
30 generics: &ast::Generics,
31 unsafety: &ast::Unsafety,
32 constness: &ast::Constness,
38 let newline_brace = self.newline_for_brace(&generics.where_clause);
40 let mut result = self.rewrite_fn_base(indent,
52 // Prepare for the function body by possibly adding a newline and indent.
53 // FIXME we'll miss anything between the end of the signature and the start
54 // of the body, but we need more spans from the compiler to solve this.
57 result.push_str(&make_indent(indent));
65 pub fn rewrite_required_fn(&mut self,
72 // Drop semicolon or it will be interpreted as comment
73 let span = codemap::mk_sp(span.lo, span.hi - BytePos(1));
75 let mut result = self.rewrite_fn_base(indent,
78 Some(&sig.explicit_self),
83 ast::Visibility::Inherited,
87 // Re-attach semicolon
93 fn rewrite_fn_base(&mut self,
97 explicit_self: Option<&ast::ExplicitSelf>,
98 generics: &ast::Generics,
99 unsafety: &ast::Unsafety,
100 constness: &ast::Constness,
102 vis: ast::Visibility,
107 // FIXME we'll lose any comments in between parts of the function decl, but anyone
108 // who comments there probably deserves what they get.
110 let where_clause = &generics.where_clause;
112 let mut result = String::with_capacity(1024);
114 result.push_str(format_visibility(vis));
116 if let &ast::Unsafety::Unsafe = unsafety {
117 result.push_str("unsafe ");
119 if let &ast::Constness::Const = constness {
120 result.push_str("const ");
122 if *abi != abi::Rust {
123 result.push_str("extern ");
124 result.push_str(&abi.to_string());
129 result.push_str("fn ");
130 result.push_str(&token::get_ident(ident));
133 let generics_indent = indent + result.len();
134 result.push_str(&self.rewrite_generics(generics,
136 codemap::mk_sp(span.lo,
137 span_for_return(&fd.output).lo)));
139 let ret_str = self.rewrite_return(&fd.output);
142 let (one_line_budget, multi_line_budget, mut arg_indent) =
143 self.compute_budgets_for_args(&result, indent, ret_str.len(), newline_brace);
145 debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
146 one_line_budget, multi_line_budget, arg_indent);
148 // Check if vertical layout was forced by compute_budget_for_args.
149 if one_line_budget <= 0 {
150 if self.config.fn_args_paren_newline {
152 result.push_str(&make_indent(arg_indent));
153 arg_indent = arg_indent + 1; // extra space for `(`
156 result.push_str("(\n");
157 result.push_str(&make_indent(arg_indent));
163 // A conservative estimation, to goal is to be over all parens in generics
164 let args_start = generics.ty_params
166 .map(|tp| end_typaram(tp))
168 let args_span = codemap::mk_sp(
169 span_after(codemap::mk_sp(args_start, span.hi), "(", self.codemap),
170 span_for_return(&fd.output).lo);
171 result.push_str(&self.rewrite_args(&fd.inputs,
180 if ret_str.len() > 0 {
181 // If we've already gone multi-line, or the return type would push
182 // over the max width, then put the return type on a new line.
183 if result.contains("\n") ||
184 result.len() + indent + ret_str.len() > self.config.max_width {
185 let indent = match self.config.fn_return_indent {
186 ReturnIndent::WithWhereClause => indent + 4,
187 // TODO we might want to check that using the arg indent doesn't
188 // blow our budget, and if it does, then fallback to the where
194 result.push_str(&make_indent(indent));
198 result.push_str(&ret_str);
200 // Comment between return type and the end of the decl.
201 let snippet_lo = fd.output.span().hi;
202 if where_clause.predicates.len() == 0 {
203 let snippet_hi = span.hi;
204 let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
205 let snippet = snippet.trim();
206 if snippet.len() > 0 {
208 result.push_str(snippet);
211 // FIXME it would be nice to catch comments between the return type
212 // and the where clause, but we don't have a span for the where
218 result.push_str(&self.rewrite_where_clause(where_clause,
225 fn rewrite_args(&self,
227 explicit_self: Option<&ast::ExplicitSelf>,
228 one_line_budget: usize,
229 multi_line_budget: usize,
234 let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
235 // Account for sugary self.
236 let mut min_args = 1;
237 if let Some(explicit_self) = explicit_self {
238 match explicit_self.node {
239 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
240 let lt_str = match lt {
241 &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
242 &None => String::new(),
244 let mut_str = match m {
245 &ast::Mutability::MutMutable => "mut ".to_owned(),
246 &ast::Mutability::MutImmutable => String::new(),
248 arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
251 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
252 arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
254 ast::ExplicitSelf_::SelfValue(_) => {
255 assert!(args.len() >= 1, "&[ast::Arg] shouldn't be empty.");
257 // this hacky solution caused by absence of `Mutability` in `SelfValue`.
259 if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _)
262 ast::Mutability::MutMutable => "mut ",
263 ast::Mutability::MutImmutable => "",
266 panic!("there is a bug or change in structure of AST, aborting.");
270 arg_item_strs[0] = format!("{}self", mut_str);
277 // Comments between args
278 let mut arg_items = Vec::new();
280 arg_items.push(ListItem::from_str(""));
283 // TODO if there are no args, there might still be a comment, but without
284 // spans for the comment or parens, there is no chance of getting it right.
285 // You also don't get to put a comment on self, unless it is explicit.
286 if args.len() >= min_args {
287 let comment_span_start = if min_args == 2 {
288 span_after(span, ",", self.codemap)
293 arg_items = itemize_list(self.codemap,
295 args[min_args-1..].iter(),
298 |arg| arg.pat.span.lo,
299 |arg| arg.ty.span.hi,
305 assert_eq!(arg_item_strs.len(), arg_items.len());
307 for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
311 let fmt = ListFormatting {
312 tactic: ListTactic::HorizontalVertical,
314 trailing_separator: SeparatorTactic::Never,
316 h_width: one_line_budget,
317 v_width: multi_line_budget,
318 ends_with_newline: true,
321 write_list(&arg_items, &fmt)
324 fn compute_budgets_for_args(&self,
329 -> (usize, usize, usize)
331 let mut budgets = None;
333 // Try keeping everything on the same line
334 if !result.contains("\n") {
335 // 3 = `() `, space is before ret_string
336 let mut used_space = indent + result.len() + ret_str_len + 3;
340 let one_line_budget = if used_space > self.config.max_width {
343 self.config.max_width - used_space
347 let used_space = indent + result.len() + 2;
348 let max_space = self.config.ideal_width + self.config.leeway;
349 debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
350 used_space, max_space);
351 if used_space < max_space {
352 budgets = Some((one_line_budget,
353 max_space - used_space,
354 indent + result.len() + 1));
358 // Didn't work. we must force vertical layout and put args on a newline.
359 if let None = budgets {
360 let new_indent = indent + self.config.tab_spaces;
361 let used_space = new_indent + 2; // account for `(` and `)`
362 let max_space = self.config.ideal_width + self.config.leeway;
363 if used_space > max_space {
365 // TODO take evasive action, perhaps kill the indent or something.
367 budgets = Some((0, max_space - used_space, new_indent));
374 fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
375 match self.config.fn_brace_style {
376 BraceStyle::AlwaysNextLine => true,
377 BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
382 pub fn visit_enum(&mut self,
384 vis: ast::Visibility,
385 enum_def: &ast::EnumDef,
386 generics: &ast::Generics,
389 let header_str = self.format_header("enum ", ident, vis);
390 self.changes.push_str_span(span, &header_str);
392 let enum_snippet = self.snippet(span);
393 let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1);
394 let generics_str = self.format_generics(generics,
396 self.block_indent + self.config.tab_spaces,
397 codemap::mk_sp(span.lo,
399 self.changes.push_str_span(span, &generics_str);
401 self.last_pos = body_start;
402 self.block_indent += self.config.tab_spaces;
403 for (i, f) in enum_def.variants.iter().enumerate() {
404 let next_span_start: BytePos = if i == enum_def.variants.len() - 1 {
407 enum_def.variants[i + 1].span.lo
410 self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start);
412 self.block_indent -= self.config.tab_spaces;
414 self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32));
415 self.changes.push_str_span(span, "}");
418 // Variant of an enum
419 fn visit_variant(&mut self,
420 field: &ast::Variant,
422 next_span_start: BytePos)
424 if self.visit_attrs(&field.node.attrs) {
428 self.format_missing_with_indent(field.span.lo);
430 match field.node.kind {
431 ast::VariantKind::TupleVariantKind(ref types) => {
432 let vis = format_visibility(field.node.vis);
433 self.changes.push_str_span(field.span, vis);
434 let name = field.node.name.to_string();
435 self.changes.push_str_span(field.span, &name);
437 let mut result = String::new();
440 let items = itemize_list(self.codemap,
445 |arg| arg.ty.span.lo,
446 |arg| arg.ty.span.hi,
447 |arg| pprust::ty_to_string(&arg.ty),
448 span_after(field.span, "(", self.codemap),
453 let indent = self.block_indent
455 + field.node.name.to_string().len()
458 let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
459 let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
461 let fmt = ListFormatting {
462 tactic: ListTactic::HorizontalVertical,
464 trailing_separator: SeparatorTactic::Never,
468 ends_with_newline: false,
470 result.push_str(&write_list(&items, &fmt));
474 if let Some(ref expr) = field.node.disr_expr {
475 result.push_str(" = ");
476 let expr_snippet = self.snippet(expr.span);
477 result.push_str(&expr_snippet);
479 // Make sure we do not exceed column limit
481 assert!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
482 "Enum variant exceeded column limit");
485 self.changes.push_str_span(field.span, &result);
487 if !last_field || self.config.enum_trailing_comma {
488 self.changes.push_str_span(field.span, ",");
491 ast::VariantKind::StructVariantKind(ref struct_def) => {
492 let result = self.format_struct("",
500 self.changes.push_str_span(field.span, &result)
504 self.last_pos = field.span.hi + BytePos(1);
507 fn format_struct(&self,
510 vis: ast::Visibility,
511 struct_def: &ast::StructDef,
512 generics: Option<&ast::Generics>,
514 offset: usize) -> String
516 let mut result = String::with_capacity(1024);
518 let header_str = self.format_header(item_name, ident, vis);
519 result.push_str(&header_str);
521 if struct_def.fields.len() == 0 {
526 let is_tuple = match struct_def.fields[0].node.kind {
527 ast::StructFieldKind::NamedField(..) => false,
528 ast::StructFieldKind::UnnamedField(..) => true
531 let (opener, terminator) = if is_tuple { ("(", ")") } else { (" {", "}") };
533 let generics_str = match generics {
534 Some(g) => self.format_generics(g,
536 offset + header_str.len(),
537 codemap::mk_sp(span.lo,
538 struct_def.fields[0].span.lo)),
539 None => opener.to_owned()
541 result.push_str(&generics_str);
543 let items = itemize_list(self.codemap,
545 struct_def.fields.iter(),
549 // Include attributes and doc comments,
551 if field.node.attrs.len() > 0 {
552 field.node.attrs[0].span.lo
557 |field| field.node.ty.span.hi,
558 |field| self.format_field(field),
559 span_after(span, opener.trim(), self.codemap),
562 // 2 terminators and a semicolon
563 let used_budget = offset + header_str.len() + generics_str.len() + 3;
565 // Conservative approximation
566 let single_line_cost = (span.hi - struct_def.fields[0].span.lo).0;
567 let break_line = !is_tuple ||
568 generics_str.contains('\n') ||
569 single_line_cost as usize + used_budget > self.config.max_width;
572 let indentation = make_indent(offset + self.config.tab_spaces);
574 result.push_str(&indentation);
577 let tactic = if break_line { ListTactic::Vertical } else { ListTactic::Horizontal };
580 let budget = self.config.ideal_width - offset + self.config.tab_spaces - 1;
581 let fmt = ListFormatting {
584 trailing_separator: self.config.struct_trailing_comma,
585 indent: offset + self.config.tab_spaces,
586 h_width: self.config.max_width,
588 ends_with_newline: false,
591 result.push_str(&write_list(&items, &fmt));
595 result.push_str(&make_indent(offset));
598 result.push_str(terminator);
607 pub fn visit_struct(&mut self,
609 vis: ast::Visibility,
610 struct_def: &ast::StructDef,
611 generics: &ast::Generics,
614 let indent = self.block_indent;
615 let result = self.format_struct("struct ",
622 self.changes.push_str_span(span, &result);
623 self.last_pos = span.hi;
626 fn format_header(&self,
629 vis: ast::Visibility)
632 format!("{}{}{}", format_visibility(vis), item_name, &token::get_ident(ident))
635 fn format_generics(&self,
636 generics: &ast::Generics,
642 let mut result = self.rewrite_generics(generics, offset, span);
644 if generics.where_clause.predicates.len() > 0 || result.contains('\n') {
645 result.push_str(&self.rewrite_where_clause(&generics.where_clause,
648 result.push_str(&make_indent(self.block_indent));
650 result.push_str(opener.trim());
652 result.push_str(opener);
659 fn format_field(&self, field: &ast::StructField) -> String {
660 if contains_skip(&field.node.attrs) {
661 return self.snippet(codemap::mk_sp(field.node.attrs[0].span.lo, field.span.hi));
664 let name = match field.node.kind {
665 ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
666 ast::StructFieldKind::UnnamedField(_) => None,
668 let vis = match field.node.kind {
669 ast::StructFieldKind::NamedField(_, vis) |
670 ast::StructFieldKind::UnnamedField(vis) => format_visibility(vis)
672 let typ = pprust::ty_to_string(&field.node.ty);
674 let indent = self.block_indent + self.config.tab_spaces;
675 let mut attr_str = self.rewrite_attrs(&field.node.attrs, indent);
676 if attr_str.len() > 0 {
678 attr_str.push_str(&make_indent(indent));
682 Some(name) => format!("{}{}{}: {}", attr_str, vis, name, typ),
683 None => format!("{}{}{}", attr_str, vis, typ)
687 fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) -> String {
688 // FIXME convert bounds to where clauses where they get too big or if
689 // there is a where clause at all.
690 let mut result = String::new();
691 let lifetimes: &[_] = &generics.lifetimes;
692 let tys: &[_] = &generics.ty_params;
693 if lifetimes.len() + tys.len() == 0 {
697 let budget = self.config.max_width - offset - 2;
698 // TODO might need to insert a newline if the generics are really long
701 // Strings for the generics.
702 let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
703 let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
705 // Extract comments between generics.
706 let lt_spans = lifetimes.iter().map(|l| {
707 let hi = if l.bounds.len() == 0 {
710 l.bounds[l.bounds.len() - 1].span.hi
712 codemap::mk_sp(l.lifetime.span.lo, hi)
714 let ty_spans = tys.iter().map(span_for_ty_param);
716 let mut items = itemize_list(self.codemap,
718 lt_spans.chain(ty_spans),
724 span_after(span, "<", self.codemap),
727 for (item, ty) in items.iter_mut().zip(lt_strs.chain(ty_strs)) {
731 let fmt = ListFormatting {
732 tactic: ListTactic::HorizontalVertical,
734 trailing_separator: SeparatorTactic::Never,
738 ends_with_newline: true,
740 result.push_str(&write_list(&items, &fmt));
747 fn rewrite_where_clause(&self,
748 where_clause: &ast::WhereClause,
753 let mut result = String::new();
754 if where_clause.predicates.len() == 0 {
759 result.push_str(&make_indent(indent + 4));
760 result.push_str("where ");
762 let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
763 let items = itemize_list(self.codemap,
765 where_clause.predicates.iter(),
768 |pred| span_for_where_pred(pred).lo,
769 |pred| span_for_where_pred(pred).hi,
770 |pred| self.rewrite_pred(pred),
774 let budget = self.config.ideal_width + self.config.leeway - indent - 10;
775 let fmt = ListFormatting {
776 tactic: ListTactic::Vertical,
778 trailing_separator: SeparatorTactic::Never,
782 ends_with_newline: true,
784 result.push_str(&write_list(&items, &fmt));
789 fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
791 ast::FunctionRetTy::DefaultReturn(_) => String::new(),
792 ast::FunctionRetTy::NoReturn(_) => "-> !".to_owned(),
793 ast::FunctionRetTy::Return(ref ty) => "-> ".to_owned() + &pprust::ty_to_string(ty),
797 // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
798 fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
800 pprust::pat_to_string(&arg.pat),
801 pprust::ty_to_string(&arg.ty))
805 fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
807 ast::FunctionRetTy::NoReturn(ref span) |
808 ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
809 ast::FunctionRetTy::Return(ref ty) => ty.span,
813 fn span_for_ty_param(ty: &ast::TyParam) -> Span {
814 // Note that ty.span is the span for ty.ident, not the whole item.
816 if let Some(ref def) = ty.default {
817 return codemap::mk_sp(lo, def.span.hi);
819 if ty.bounds.len() == 0 {
822 let hi = match ty.bounds[ty.bounds.len() - 1] {
823 ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
824 ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
826 codemap::mk_sp(lo, hi)
829 fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
831 ast::WherePredicate::BoundPredicate(ref p) => p.span,
832 ast::WherePredicate::RegionPredicate(ref p) => p.span,
833 ast::WherePredicate::EqPredicate(ref p) => p.span,