]> git.lizzy.rs Git - rust.git/blobdiff - src/items.rs
Remove wrap_str() from recover_comment_removed()
[rust.git] / src / items.rs
index fe4195e174cf012e16cfc6957bf2816326d6b84e..13c669d698f59437a0098b52e4acbf97c6740442 100644 (file)
 use syntax::codemap::{BytePos, Span};
 
 use {Indent, Shape, Spanned};
-use codemap::SpanUtils;
-use comment::{contains_comment, recover_comment_removed, rewrite_comment, FindUncommented};
+use codemap::{LineRangeUtils, SpanUtils};
+use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
+              recover_missing_comment_in_span, rewrite_missing_comment, FindUncommented};
 use config::{BraceStyle, Config, Density, IndentStyle, ReturnIndent, Style};
 use expr::{format_expr, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs,
            rewrite_call_inner, ExprType};
 use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
-            ListItem, ListTactic, SeparatorTactic};
+            ListItem, ListTactic, Separator, SeparatorPlace, SeparatorTactic};
 use rewrite::{Rewrite, RewriteContext};
 use types::join_bounds;
-use utils::{colon_spaces, contains_skip, end_typaram, format_defaultness, format_mutability,
-            format_unsafety, format_visibility, last_line_width, mk_sp, semicolon_for_expr,
-            stmt_expr, trim_newlines, trimmed_last_line_width, wrap_str};
+use utils::{colon_spaces, contains_skip, end_typaram, first_line_width, format_abi,
+            format_constness, format_defaultness, format_mutability, format_unsafety,
+            format_visibility, is_attributes_extendable, last_line_contains_single_line_comment,
+            last_line_used_width, last_line_width, mk_sp, semicolon_for_expr, stmt_expr,
+            trim_newlines, trimmed_last_line_width, wrap_str};
 use vertical::rewrite_with_alignment;
 use visitor::FmtVisitor;
 
@@ -39,7 +42,6 @@ fn type_annotation_separator(config: &Config) -> &str {
     )
 }
 
-
 // Statements of the form
 // let pat: ty = init;
 impl Rewrite for ast::Local {
@@ -50,18 +52,40 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
             shape.width,
             shape.indent
         );
-        let mut result = "let ".to_owned();
+
+        skip_out_of_file_lines_range!(context, self.span);
+
+        if contains_skip(&self.attrs) {
+            return None;
+        }
+
+        let attrs_str = try_opt!(self.attrs.rewrite(context, shape));
+        let mut result = if attrs_str.is_empty() {
+            "let ".to_owned()
+        } else {
+            try_opt!(combine_strs_with_missing_comments(
+                context,
+                &attrs_str,
+                "let ",
+                mk_sp(
+                    self.attrs.last().map(|a| a.span.hi()).unwrap(),
+                    self.span.lo(),
+                ),
+                shape,
+                false,
+            ))
+        };
 
         // 4 = "let ".len()
         let pat_shape = try_opt!(shape.offset_left(4));
         // 1 = ;
         let pat_shape = try_opt!(pat_shape.sub_width(1));
-        let pat_str = try_opt!(self.pat.rewrite(&context, pat_shape));
+        let pat_str = try_opt!(self.pat.rewrite(context, pat_shape));
         result.push_str(&pat_str);
 
         // String that is placed within the assignment pattern and expression.
         let infix = {
-            let mut infix = String::new();
+            let mut infix = String::with_capacity(32);
 
             if let Some(ref ty) = self.ty {
                 let separator = type_annotation_separator(context.config);
@@ -87,7 +111,7 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
             // 1 = trailing semicolon;
             let nested_shape = try_opt!(shape.sub_width(1));
 
-            result = try_opt!(rewrite_assign_rhs(&context, result, ex, nested_shape));
+            result = try_opt!(rewrite_assign_rhs(context, result, ex, nested_shape));
         }
 
         result.push(';');
@@ -146,11 +170,11 @@ fn format_item(&mut self, item: Item) {
         if !item.body.is_empty() || contains_comment(&snippet[brace_pos..]) {
             // FIXME: this skips comments between the extern keyword and the opening
             // brace.
-            self.last_pos = item.span.lo + BytePos(brace_pos as u32 + 1);
+            self.last_pos = item.span.lo() + BytePos(brace_pos as u32 + 1);
             self.block_indent = self.block_indent.block_indent(self.config);
 
             if item.body.is_empty() {
-                self.format_missing_no_indent(item.span.hi - BytePos(1));
+                self.format_missing_no_indent(item.span.hi() - BytePos(1));
                 self.block_indent = self.block_indent.block_unindent(self.config);
 
                 self.buffer
@@ -161,17 +185,17 @@ fn format_item(&mut self, item: Item) {
                 }
 
                 self.block_indent = self.block_indent.block_unindent(self.config);
-                self.format_missing_with_indent(item.span.hi - BytePos(1));
+                self.format_missing_with_indent(item.span.hi() - BytePos(1));
             }
         }
 
         self.buffer.push_str("}");
-        self.last_pos = item.span.hi;
+        self.last_pos = item.span.hi();
     }
 
     fn format_body_element(&mut self, element: &BodyElement) {
         match *element {
-            BodyElement::ForeignItem(ref item) => self.format_foreign_item(item),
+            BodyElement::ForeignItem(item) => self.format_foreign_item(item),
         }
     }
 
@@ -180,65 +204,11 @@ pub fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) {
         self.format_item(item);
     }
 
-    fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
-        self.format_missing_with_indent(item.span.lo);
-        // Drop semicolon or it will be interpreted as comment.
-        // FIXME: this may be a faulty span from libsyntax.
-        let span = mk_sp(item.span.lo, item.span.hi - BytePos(1));
 
-        match item.node {
-            ast::ForeignItemKind::Fn(ref fn_decl, ref generics) => {
-                let indent = self.block_indent;
-                let rewrite = rewrite_fn_base(
-                    &self.get_context(),
-                    indent,
-                    item.ident,
-                    fn_decl,
-                    generics,
-                    ast::Unsafety::Normal,
-                    ast::Constness::NotConst,
-                    ast::Defaultness::Final,
-                    // These are not actually rust functions,
-                    // but we format them as such.
-                    abi::Abi::Rust,
-                    &item.vis,
-                    span,
-                    false,
-                    false,
-                    false,
-                );
-
-                match rewrite {
-                    Some((new_fn, _)) => {
-                        self.buffer.push_str(&new_fn);
-                        self.buffer.push_str(";");
-                    }
-                    None => self.format_missing(item.span.hi),
-                }
-            }
-            ast::ForeignItemKind::Static(ref ty, is_mutable) => {
-                // FIXME(#21): we're dropping potential comments in between the
-                // function keywords here.
-                let vis = format_visibility(&item.vis);
-                let mut_str = if is_mutable { "mut " } else { "" };
-                let prefix = format!("{}static {}{}: ", vis, mut_str, item.ident);
-                let offset = self.block_indent + prefix.len();
-                // 1 = ;
-                let shape = Shape::indented(offset, self.config).sub_width(1).unwrap();
-                let rewrite = ty.rewrite(&self.get_context(), shape);
-
-                match rewrite {
-                    Some(result) => {
-                        self.buffer.push_str(&prefix);
-                        self.buffer.push_str(&result);
-                        self.buffer.push_str(";");
-                    }
-                    None => self.format_missing(item.span.hi),
-                }
-            }
-        }
-
-        self.last_pos = item.span.hi;
+    fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
+        let rewrite = item.rewrite(&self.get_context(), self.shape());
+        self.push_rewrite(item.span(), rewrite);
+        self.last_pos = item.span.hi();
     }
 
     pub fn rewrite_fn(
@@ -255,12 +225,12 @@ pub fn rewrite_fn(
         span: Span,
         block: &ast::Block,
     ) -> Option<String> {
-        let mut newline_brace = newline_for_brace(self.config, &generics.where_clause);
         let context = self.get_context();
 
-        let block_snippet = self.snippet(mk_sp(block.span.lo, block.span.hi));
-        let has_body = !block_snippet[1..block_snippet.len() - 1].trim().is_empty() ||
-            !context.config.fn_empty_single_line();
+        let block_snippet = self.snippet(mk_sp(block.span.lo(), block.span.hi()));
+        let has_body = !block_snippet[1..block_snippet.len() - 1].trim().is_empty()
+            || !context.config.fn_empty_single_line();
+        let mut newline_brace = newline_for_brace(self.config, &generics.where_clause, has_body);
 
         let (mut result, force_newline_brace) = try_opt!(rewrite_fn_base(
             &context,
@@ -281,8 +251,8 @@ pub fn rewrite_fn(
 
         if force_newline_brace {
             newline_brace = true;
-        } else if self.config.fn_brace_style() != BraceStyle::AlwaysNextLine &&
-            !result.contains('\n')
+        } else if self.config.fn_brace_style() != BraceStyle::AlwaysNextLine
+            && !result.contains('\n')
         {
             newline_brace = false;
         }
@@ -310,7 +280,7 @@ pub fn rewrite_required_fn(
         span: Span,
     ) -> Option<String> {
         // Drop semicolon or it will be interpreted as comment.
-        let span = mk_sp(span.lo, span.hi - BytePos(1));
+        let span = mk_sp(span.lo(), span.hi() - BytePos(1));
         let context = self.get_context();
 
         let (mut result, _) = try_opt!(rewrite_fn_base(
@@ -343,15 +313,15 @@ fn single_line_fn(&self, fn_str: &str, block: &ast::Block) -> Option<String> {
 
         let codemap = self.get_context().codemap;
 
-        if self.config.fn_empty_single_line() && is_empty_block(block, codemap) &&
-            self.block_indent.width() + fn_str.len() + 2 <= self.config.max_width()
+        if self.config.fn_empty_single_line() && is_empty_block(block, codemap)
+            && self.block_indent.width() + fn_str.len() + 2 <= self.config.max_width()
         {
             return Some(format!("{}{{}}", fn_str));
         }
 
         if self.config.fn_single_line() && is_simple_block_stmt(block, codemap) {
             let rewrite = {
-                if let Some(ref stmt) = block.stmts.first() {
+                if let Some(stmt) = block.stmts.first() {
                     match stmt_expr(stmt) {
                         Some(e) => {
                             let suffix = if semicolon_for_expr(&self.get_context(), e) {
@@ -360,18 +330,11 @@ fn single_line_fn(&self, fn_str: &str, block: &ast::Block) -> Option<String> {
                                 ""
                             };
 
-                            format_expr(
-                                &e,
-                                ExprType::Statement,
-                                &self.get_context(),
-                                Shape::indented(self.block_indent, self.config),
-                            ).map(|s| s + suffix)
+                            format_expr(e, ExprType::Statement, &self.get_context(), self.shape())
+                                .map(|s| s + suffix)
                                 .or_else(|| Some(self.snippet(e.span)))
                         }
-                        None => stmt.rewrite(
-                            &self.get_context(),
-                            Shape::indented(self.block_indent, self.config),
-                        ),
+                        None => stmt.rewrite(&self.get_context(), self.shape()),
                     }
                 } else {
                     None
@@ -402,7 +365,7 @@ pub fn visit_enum(
 
         let enum_snippet = self.snippet(span);
         let brace_pos = enum_snippet.find_uncommented("{").unwrap();
-        let body_start = span.lo + BytePos(brace_pos as u32 + 1);
+        let body_start = span.lo() + BytePos(brace_pos as u32 + 1);
         let generics_str = format_generics(
             &self.get_context(),
             generics,
@@ -411,7 +374,7 @@ pub fn visit_enum(
             self.config.item_brace_style(),
             enum_def.variants.is_empty(),
             self.block_indent,
-            mk_sp(span.lo, body_start),
+            mk_sp(span.lo(), body_start),
             last_line_width(&enum_header),
         ).unwrap();
         self.buffer.push_str(&generics_str);
@@ -419,13 +382,11 @@ pub fn visit_enum(
         self.last_pos = body_start;
 
         self.block_indent = self.block_indent.block_indent(self.config);
-        let variant_list = self.format_variant_list(enum_def, body_start, span.hi - BytePos(1));
+        let variant_list = self.format_variant_list(enum_def, body_start, span.hi() - BytePos(1));
         match variant_list {
             Some(ref body_str) => self.buffer.push_str(body_str),
             None => if contains_comment(&enum_snippet[brace_pos..]) {
-                self.format_missing_no_indent(span.hi - BytePos(1))
-            } else {
-                self.format_missing(span.hi - BytePos(1))
+                self.format_missing_no_indent(span.hi() - BytePos(1))
             },
         }
         self.block_indent = self.block_indent.block_unindent(self.config);
@@ -435,7 +396,7 @@ pub fn visit_enum(
                 .push_str(&self.block_indent.to_string(self.config));
         }
         self.buffer.push_str("}");
-        self.last_pos = span.hi;
+        self.last_pos = span.hi();
     }
 
     // Format the body of an enum definition
@@ -458,23 +419,23 @@ fn format_variant_list(
             enum_def.variants.iter(),
             "}",
             |f| if !f.node.attrs.is_empty() {
-                f.node.attrs[0].span.lo
+                f.node.attrs[0].span.lo()
             } else {
-                f.span.lo
+                f.span.lo()
             },
-            |f| f.span.hi,
+            |f| f.span.hi(),
             |f| self.format_variant(f),
             body_lo,
             body_hi,
+            false,
         );
 
-        let shape = Shape::indented(self.block_indent, self.config)
-            .sub_width(2)
-            .unwrap();
+        let shape = self.shape().sub_width(2).unwrap();
         let fmt = ListFormatting {
             tactic: DefinitiveListTactic::Vertical,
             separator: ",",
             trailing_separator: self.config.trailing_comma(),
+            separator_place: SeparatorPlace::Back,
             shape: shape,
             ends_with_newline: true,
             preserve_newline: true,
@@ -490,39 +451,26 @@ fn format_variant_list(
     // Variant of an enum.
     fn format_variant(&self, field: &ast::Variant) -> Option<String> {
         if contains_skip(&field.node.attrs) {
-            let lo = field.node.attrs[0].span.lo;
-            let span = mk_sp(lo, field.span.hi);
+            let lo = field.node.attrs[0].span.lo();
+            let span = mk_sp(lo, field.span.hi());
             return Some(self.snippet(span));
         }
 
         let context = self.get_context();
         let indent = self.block_indent;
-        let mut result = try_opt!(
-            field
-                .node
-                .attrs
-                .rewrite(&context, Shape::indented(indent, self.config))
-        );
-        if !result.is_empty() {
-            let shape = Shape {
-                width: context.config.max_width(),
-                indent: self.block_indent,
-                offset: self.block_indent.alignment,
-            };
-            let missing_comment = rewrite_missing_comment_on_field(
-                &context,
-                shape,
-                field.node.attrs[field.node.attrs.len() - 1].span.hi,
-                field.span.lo,
-                &mut result,
-            ).unwrap_or(String::new());
-            result.push_str(&missing_comment);
-        }
+        let shape = self.shape();
+        let attrs_str = try_opt!(field.node.attrs.rewrite(&context, shape));
+        let lo = field
+            .node
+            .attrs
+            .last()
+            .map_or(field.span.lo(), |attr| attr.span.hi());
+        let span = mk_sp(lo, field.span.lo());
 
         let variant_body = match field.node.data {
             ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => {
                 // FIXME: Should limit the width, as we have a trailing comma
-                format_struct(
+                try_opt!(format_struct(
                     &context,
                     "",
                     field.node.name,
@@ -532,29 +480,40 @@ fn format_variant(&self, field: &ast::Variant) -> Option<String> {
                     field.span,
                     indent,
                     Some(self.config.struct_variant_width()),
-                )
+                ))
             }
-            ast::VariantData::Unit(..) => {
-                let tag = if let Some(ref expr) = field.node.disr_expr {
+            ast::VariantData::Unit(..) => if let Some(ref expr) = field.node.disr_expr {
+                let one_line_width =
+                    field.node.name.to_string().len() + self.snippet(expr.span).len() + 3;
+                if one_line_width <= shape.width {
                     format!("{} = {}", field.node.name, self.snippet(expr.span))
                 } else {
-                    field.node.name.to_string()
-                };
-
-                wrap_str(
-                    tag,
-                    self.config.max_width(),
-                    Shape::indented(indent, self.config),
-                )
-            }
+                    format!(
+                        "{}\n{}{}",
+                        field.node.name,
+                        shape
+                            .indent
+                            .block_indent(self.config)
+                            .to_string(self.config),
+                        self.snippet(expr.span)
+                    )
+                }
+            } else {
+                String::from(field.node.name.to_string())
+            },
         };
 
-        if let Some(variant_str) = variant_body {
-            result.push_str(&variant_str);
-            Some(result)
-        } else {
-            None
-        }
+        let attrs_extendable = attrs_str.is_empty()
+            || (context.config.attributes_on_same_line_as_variant()
+                && is_attributes_extendable(&attrs_str));
+        combine_strs_with_missing_comments(
+            &context,
+            &attrs_str,
+            &variant_body,
+            span,
+            shape,
+            attrs_extendable,
+        )
     }
 }
 
@@ -565,19 +524,18 @@ pub fn format_impl(
     where_span_end: Option<BytePos>,
 ) -> Option<String> {
     if let ast::ItemKind::Impl(_, _, _, ref generics, _, ref self_ty, ref items) = item.node {
-        let mut result = String::new();
+        let mut result = String::with_capacity(128);
         let ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset));
+        let indent_str = offset.to_string(context.config);
+        let sep = format!("\n{}", &indent_str);
         result.push_str(&ref_and_type);
 
         let where_budget = if result.contains('\n') {
             context.config.max_width()
         } else {
-            context
-                .config
-                .max_width()
-                .checked_sub(last_line_width(&result))
-                .unwrap_or(0)
+            context.budget(last_line_width(&result))
         };
+        let option = WhereClauseOption::snuggled(&ref_and_type);
         let where_clause_str = try_opt!(rewrite_where_clause(
             context,
             &generics.where_clause,
@@ -585,24 +543,39 @@ pub fn format_impl(
             Shape::legacy(where_budget, offset.block_only()),
             context.config.where_density(),
             "{",
-            false,
-            last_line_width(&ref_and_type) == 1,
             where_span_end,
-            item.span,
-            self_ty.span.hi,
+            self_ty.span.hi(),
+            option,
         ));
 
+        // If there is no where clause, we may have missing comments between the trait name and
+        // the opening brace.
+        if generics.where_clause.predicates.is_empty() {
+            if let Some(hi) = where_span_end {
+                match recover_missing_comment_in_span(
+                    mk_sp(self_ty.span.hi(), hi),
+                    Shape::indented(offset, context.config),
+                    context,
+                    last_line_width(&result),
+                ) {
+                    Some(ref missing_comment) if !missing_comment.is_empty() => {
+                        result.push_str(missing_comment);
+                    }
+                    _ => (),
+                }
+            }
+        }
+
         if try_opt!(is_impl_single_line(
             context,
-            &items,
+            items,
             &result,
             &where_clause_str,
-            &item,
+            item,
         )) {
             result.push_str(&where_clause_str);
-            if where_clause_str.contains('\n') {
-                let white_space = offset.to_string(context.config);
-                result.push_str(&format!("\n{}{{\n{}}}", &white_space, &white_space));
+            if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) {
+                result.push_str(&format!("{}{{{}}}", &sep, &sep));
             } else {
                 result.push_str(" {}");
             }
@@ -618,14 +591,11 @@ pub fn format_impl(
         result.push_str(&where_clause_str);
 
         match context.config.item_brace_style() {
-            BraceStyle::AlwaysNextLine => {
-                result.push('\n');
-                result.push_str(&offset.to_string(context.config));
-            }
+            _ if last_line_contains_single_line_comment(&result) => result.push_str(&sep),
+            BraceStyle::AlwaysNextLine => result.push_str(&sep),
             BraceStyle::PreferSameLine => result.push(' '),
             BraceStyle::SameLineWhere => if !where_clause_str.is_empty() {
-                result.push('\n');
-                result.push_str(&offset.to_string(context.config));
+                result.push_str(&sep);
             } else {
                 result.push(' ');
             },
@@ -639,14 +609,14 @@ pub fn format_impl(
         if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
             let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
             visitor.block_indent = offset.block_only().block_indent(context.config);
-            visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
+            visitor.last_pos = item.span.lo() + BytePos(open_pos as u32);
 
             visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner);
             for item in items {
                 visitor.visit_impl_item(item);
             }
 
-            visitor.format_missing(item.span.hi - BytePos(1));
+            visitor.format_missing(item.span.hi() - BytePos(1));
 
             let inner_indent_str = visitor.block_indent.to_string(context.config);
             let outer_indent_str = offset.block_only().to_string(context.config);
@@ -659,8 +629,7 @@ pub fn format_impl(
         }
 
         if result.chars().last().unwrap() == '{' {
-            result.push('\n');
-            result.push_str(&offset.to_string(context.config));
+            result.push_str(&sep);
         }
         result.push('}');
 
@@ -681,9 +650,9 @@ fn is_impl_single_line(
     let open_pos = try_opt!(snippet.find_uncommented("{")) + 1;
 
     Some(
-        context.config.impl_empty_single_line() && items.is_empty() &&
-            result.len() + where_clause_str.len() <= context.config.max_width() &&
-            !contains_comment(&snippet[open_pos..]),
+        context.config.impl_empty_single_line() && items.is_empty() && !result.contains('\n')
+            && result.len() + where_clause_str.len() <= context.config.max_width()
+            && !contains_comment(&snippet[open_pos..]),
     )
 }
 
@@ -702,17 +671,17 @@ fn format_impl_ref_and_type(
         _,
     ) = item.node
     {
-        let mut result = String::new();
+        let mut result = String::with_capacity(128);
 
         result.push_str(&format_visibility(&item.vis));
-        result.push_str(&format_defaultness(defaultness));
+        result.push_str(format_defaultness(defaultness));
         result.push_str(format_unsafety(unsafety));
         result.push_str("impl");
 
         let lo = context.codemap.span_after(item.span, "impl");
         let hi = match *trait_ref {
-            Some(ref tr) => tr.path.span.lo,
-            None => self_ty.span.lo,
+            Some(ref tr) => tr.path.span.lo(),
+            None => self_ty.span.lo(),
         };
         let shape = try_opt!(generics_shape_from_config(
             context.config,
@@ -738,7 +707,7 @@ fn format_impl_ref_and_type(
             let result_len = result.len();
             if let Some(trait_ref_str) = rewrite_trait_ref(
                 context,
-                &trait_ref,
+                trait_ref,
                 offset,
                 &generics_str,
                 true,
@@ -756,7 +725,7 @@ fn format_impl_ref_and_type(
                 ));
                 result.push_str(&try_opt!(rewrite_trait_ref(
                     context,
-                    &trait_ref,
+                    trait_ref,
                     offset,
                     &generics_str,
                     false,
@@ -783,11 +752,7 @@ fn format_impl_ref_and_type(
         };
         let used_space = last_line_width(&result) + trait_ref_overhead + curly_brace_overhead;
         // 1 = space before the type.
-        let budget = context
-            .config
-            .max_width()
-            .checked_sub(used_space + 1)
-            .unwrap_or(0);
+        let budget = context.budget(used_space + 1);
         if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) {
             if !self_ty_str.contains('\n') {
                 if trait_ref.is_some() {
@@ -808,7 +773,7 @@ fn format_impl_ref_and_type(
         if trait_ref.is_some() {
             result.push_str("for ");
         }
-        let budget = context.config.max_width() - last_line_width(&result);
+        let budget = context.budget(last_line_width(&result));
         let type_offset = match context.config.where_style() {
             Style::Legacy => new_line_offset + trait_ref_overhead,
             Style::Rfc => new_line_offset,
@@ -832,11 +797,7 @@ fn rewrite_trait_ref(
     result_len: usize,
 ) -> Option<String> {
     // 1 = space between generics and trait_ref
-    let used_space = 1 + polarity_str.len() + if generics_str.contains('\n') {
-        last_line_width(&generics_str)
-    } else {
-        result_len + generics_str.len()
-    };
+    let used_space = 1 + polarity_str.len() + last_line_used_width(generics_str, result_len);
     let shape = Shape::indented(offset + used_space, context.config);
     if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) {
         if !(retry && trait_ref_str.contains('\n')) {
@@ -906,7 +867,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
     if let ast::ItemKind::Trait(unsafety, ref generics, ref type_param_bounds, ref trait_items) =
         item.node
     {
-        let mut result = String::new();
+        let mut result = String::with_capacity(128);
         let header = format!(
             "{}{}trait {}",
             format_visibility(&item.vis),
@@ -923,7 +884,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
             context,
             generics,
             shape,
-            mk_sp(item.span.lo, body_lo),
+            mk_sp(item.span.lo(), body_lo),
         ));
         result.push_str(&generics_str);
 
@@ -934,8 +895,8 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
         ));
         // If the trait, generics, and trait bound cannot fit on the same line,
         // put the trait bounds on an indented new line
-        if offset.width() + last_line_width(&result) + trait_bound_str.len() >
-            context.config.comment_width()
+        if offset.width() + last_line_width(&result) + trait_bound_str.len()
+            context.config.comment_width()
         {
             result.push('\n');
             let trait_indent = offset.block_only().block_indent(context.config);
@@ -945,35 +906,24 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
 
         let has_body = !trait_items.is_empty();
 
-        let where_density = if (context.config.where_density() == Density::Compressed &&
-            (!result.contains('\n') || context.config.fn_args_layout() == IndentStyle::Block)) ||
-            (context.config.fn_args_layout() == IndentStyle::Block && result.is_empty()) ||
-            (context.config.where_density() == Density::CompressedIfEmpty && !has_body &&
-                !result.contains('\n'))
+        let where_density = if (context.config.where_density() == Density::Compressed
+            && (!result.contains('\n') || context.config.fn_args_layout() == IndentStyle::Block))
+            || (context.config.fn_args_layout() == IndentStyle::Block && result.is_empty())
+            || (context.config.where_density() == Density::CompressedIfEmpty && !has_body
+                && !result.contains('\n'))
         {
             Density::Compressed
         } else {
             Density::Tall
         };
 
-        let where_budget = try_opt!(
-            context
-                .config
-                .max_width()
-                .checked_sub(last_line_width(&result))
-        );
+        let where_budget = context.budget(last_line_width(&result));
         let pos_before_where = if type_param_bounds.is_empty() {
-            if generics.where_clause.predicates.is_empty() {
-                // We do not use this, so it does not matter
-                item.span.lo
-            } else {
-                let snippet = context.snippet(item.span);
-                let where_pos = snippet.find_uncommented("where");
-                item.span.lo + where_pos.map_or(BytePos(0), |p| BytePos(p as u32))
-            }
+            generics.where_clause.span.lo()
         } else {
-            type_param_bounds[type_param_bounds.len() - 1].span().hi
+            type_param_bounds[type_param_bounds.len() - 1].span().hi()
         };
+        let option = WhereClauseOption::snuggled(&generics_str);
         let where_clause_str = try_opt!(rewrite_where_clause(
             context,
             &generics.where_clause,
@@ -981,17 +931,15 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
             Shape::legacy(where_budget, offset.block_only()),
             where_density,
             "{",
-            false,
-            trait_bound_str.is_empty() && last_line_width(&generics_str) == 1,
             None,
-            item.span,
             pos_before_where,
+            option,
         ));
         // If the where clause cannot fit on the same line,
         // put the where clause on a new line
-        if !where_clause_str.contains('\n') &&
-            last_line_width(&result) + where_clause_str.len() + offset.width() >
-                context.config.comment_width()
+        if !where_clause_str.contains('\n')
+            && last_line_width(&result) + where_clause_str.len() + offset.width()
+                context.config.comment_width()
         {
             result.push('\n');
             let width = offset.block_indent + context.config.tab_spaces() - 1;
@@ -1000,14 +948,40 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
         }
         result.push_str(&where_clause_str);
 
+        if generics.where_clause.predicates.is_empty() {
+            let item_snippet = context.snippet(item.span);
+            if let Some(lo) = item_snippet.chars().position(|c| c == '/') {
+                // 1 = `{`
+                let comment_hi = body_lo - BytePos(1);
+                let comment_lo = item.span.lo() + BytePos(lo as u32);
+                if comment_lo < comment_hi {
+                    match recover_missing_comment_in_span(
+                        mk_sp(comment_lo, comment_hi),
+                        Shape::indented(offset, context.config),
+                        context,
+                        last_line_width(&result),
+                    ) {
+                        Some(ref missing_comment) if !missing_comment.is_empty() => {
+                            result.push_str(missing_comment);
+                        }
+                        _ => (),
+                    }
+                }
+            }
+        }
+
         match context.config.item_brace_style() {
+            _ if last_line_contains_single_line_comment(&result) => {
+                result.push('\n');
+                result.push_str(&offset.to_string(context.config));
+            }
             BraceStyle::AlwaysNextLine => {
                 result.push('\n');
                 result.push_str(&offset.to_string(context.config));
             }
             BraceStyle::PreferSameLine => result.push(' '),
-            BraceStyle::SameLineWhere => if !where_clause_str.is_empty() &&
-                (!trait_items.is_empty() || result.contains('\n'))
+            BraceStyle::SameLineWhere => if !where_clause_str.is_empty()
+                && (!trait_items.is_empty() || result.contains('\n'))
             {
                 result.push('\n');
                 result.push_str(&offset.to_string(context.config));
@@ -1023,13 +997,13 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
         if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) {
             let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
             visitor.block_indent = offset.block_only().block_indent(context.config);
-            visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
+            visitor.last_pos = item.span.lo() + BytePos(open_pos as u32);
 
             for item in trait_items {
                 visitor.visit_trait_item(item);
             }
 
-            visitor.format_missing(item.span.hi - BytePos(1));
+            visitor.format_missing(item.span.hi() - BytePos(1));
 
             let inner_indent_str = visitor.block_indent.to_string(context.config);
             let outer_indent_str = offset.block_only().to_string(context.config);
@@ -1081,19 +1055,15 @@ pub fn format_struct_struct(
             context.config.item_brace_style(),
             fields.is_empty(),
             offset,
-            mk_sp(span.lo, body_lo),
+            mk_sp(span.lo(), body_lo),
             last_line_width(&result),
         )),
         None => {
             // 3 = ` {}`, 2 = ` {`.
             let overhead = if fields.is_empty() { 3 } else { 2 };
-            if (context.config.item_brace_style() == BraceStyle::AlwaysNextLine &&
-                !fields.is_empty()) ||
-                context
-                    .config
-                    .max_width()
-                    .checked_sub(result.len())
-                    .unwrap_or(0) < overhead
+            if (context.config.item_brace_style() == BraceStyle::AlwaysNextLine
+                && !fields.is_empty())
+                || context.config.max_width() < overhead + result.len()
             {
                 format!("\n{}{{", offset.block_only().to_string(context.config))
             } else {
@@ -1103,26 +1073,24 @@ pub fn format_struct_struct(
     };
     // 1 = `}`
     let overhead = if fields.is_empty() { 1 } else { 0 };
-    let max_len = context
-        .config
-        .max_width()
-        .checked_sub(offset.width())
-        .unwrap_or(0);
-    if !generics_str.contains('\n') && result.len() + generics_str.len() + overhead > max_len {
+    let total_width = result.len() + generics_str.len() + overhead;
+    if !generics_str.is_empty() && !generics_str.contains('\n')
+        && total_width > context.config.max_width()
+    {
         result.push('\n');
         result.push_str(&offset.to_string(context.config));
-        result.push_str(&generics_str.trim_left());
+        result.push_str(generics_str.trim_left());
     } else {
         result.push_str(&generics_str);
     }
 
     if fields.is_empty() {
-        let snippet = context.snippet(mk_sp(body_lo, span.hi - BytePos(1)));
+        let snippet = context.snippet(mk_sp(body_lo, span.hi() - BytePos(1)));
         if snippet.trim().is_empty() {
             // `struct S {}`
         } else if snippet.trim_right_matches(&[' ', '\t'][..]).ends_with('\n') {
             // fix indent
-            result.push_str(&snippet.trim_right());
+            result.push_str(snippet.trim_right());
             result.push('\n');
             result.push_str(&offset.to_string(context.config));
         } else {
@@ -1133,11 +1101,7 @@ pub fn format_struct_struct(
     }
 
     // 3 = ` ` and ` }`
-    let one_line_budget = context
-        .config
-        .max_width()
-        .checked_sub(result.len() + 3 + offset.width())
-        .unwrap_or(0);
+    let one_line_budget = context.budget(result.len() + 3 + offset.width());
     let one_line_budget =
         one_line_width.map_or(0, |one_line_width| min(one_line_width, one_line_budget));
 
@@ -1145,7 +1109,7 @@ pub fn format_struct_struct(
         fields,
         context,
         Shape::indented(offset, context.config),
-        mk_sp(body_lo, span.hi),
+        mk_sp(body_lo, span.hi()),
         one_line_budget,
     ));
 
@@ -1182,19 +1146,19 @@ fn format_tuple_struct(
     let body_lo = if fields.is_empty() {
         context.codemap.span_after(span, "(")
     } else {
-        fields[0].span.lo
+        fields[0].span.lo()
     };
     let body_hi = if fields.is_empty() {
         context.codemap.span_after(span, ")")
     } else {
         // This is a dirty hack to work around a missing `)` from the span of the last field.
         let last_arg_span = fields[fields.len() - 1].span;
-        if context.snippet(last_arg_span).ends_with(")") {
-            last_arg_span.hi
+        if context.snippet(last_arg_span).ends_with(')') {
+            last_arg_span.hi()
         } else {
             context
                 .codemap
-                .span_after(mk_sp(last_arg_span.hi, span.hi), ")")
+                .span_after(mk_sp(last_arg_span.hi(), span.hi()), ")")
         }
     };
 
@@ -1202,11 +1166,12 @@ fn format_tuple_struct(
         Some(generics) => {
             let budget = context.budget(last_line_width(&header_str));
             let shape = Shape::legacy(budget, offset);
-            let g_span = mk_sp(span.lo, body_lo);
+            let g_span = mk_sp(span.lo(), body_lo);
             let generics_str = try_opt!(rewrite_generics(context, generics, shape, g_span));
             result.push_str(&generics_str);
 
             let where_budget = context.budget(last_line_width(&result));
+            let option = WhereClauseOption::new(true, false);
             try_opt!(rewrite_where_clause(
                 context,
                 &generics.where_clause,
@@ -1214,11 +1179,9 @@ fn format_tuple_struct(
                 Shape::legacy(where_budget, offset.block_only()),
                 Density::Compressed,
                 ";",
-                true,
-                false,
                 None,
-                span,
                 body_hi,
+                option,
             ))
         }
         None => "".to_owned(),
@@ -1226,11 +1189,7 @@ fn format_tuple_struct(
 
     if fields.is_empty() {
         // 3 = `();`
-        let used_width = if result.contains('\n') {
-            last_line_width(&result) + 3
-        } else {
-            offset.width() + result.len() + 3
-        };
+        let used_width = last_line_used_width(&result, offset.width()) + 3;
         if used_width > context.config.max_width() {
             result.push('\n');
             result.push_str(&offset
@@ -1242,7 +1201,7 @@ fn format_tuple_struct(
         if snippet.is_empty() {
             // `struct S ()`
         } else if snippet.trim_right_matches(&[' ', '\t'][..]).ends_with('\n') {
-            result.push_str(&snippet.trim_right());
+            result.push_str(snippet.trim_right());
             result.push('\n');
             result.push_str(&offset.to_string(context.config));
         } else {
@@ -1265,10 +1224,10 @@ fn format_tuple_struct(
         result.push_str(&body);
     }
 
-    if !where_clause_str.is_empty() && !where_clause_str.contains('\n') &&
-        (result.contains('\n') ||
-            offset.block_indent + result.len() + where_clause_str.len() + 1 >
-                context.config.max_width())
+    if !where_clause_str.is_empty() && !where_clause_str.contains('\n')
+        && (result.contains('\n')
+            || offset.block_indent + result.len() + where_clause_str.len() + 1
+                context.config.max_width())
     {
         // We need to put the where clause on a new line, but we didn't
         // know that earlier, so the where clause will not be indented properly.
@@ -1290,7 +1249,7 @@ pub fn rewrite_type_alias(
     vis: &ast::Visibility,
     span: Span,
 ) -> Option<String> {
-    let mut result = String::new();
+    let mut result = String::with_capacity(128);
 
     result.push_str(&format_visibility(vis));
     result.push_str("type ");
@@ -1298,16 +1257,12 @@ pub fn rewrite_type_alias(
 
     // 2 = `= `
     let shape = try_opt!(Shape::indented(indent + result.len(), context.config).sub_width(2));
-    let g_span = mk_sp(context.codemap.span_after(span, "type"), ty.span.lo);
+    let g_span = mk_sp(context.codemap.span_after(span, "type"), ty.span.lo());
     let generics_str = try_opt!(rewrite_generics(context, generics, shape, g_span));
     result.push_str(&generics_str);
 
-    let where_budget = try_opt!(
-        context
-            .config
-            .max_width()
-            .checked_sub(last_line_width(&result))
-    );
+    let where_budget = context.budget(last_line_width(&result));
+    let option = WhereClauseOption::snuggled(&result);
     let where_clause_str = try_opt!(rewrite_where_clause(
         context,
         &generics.where_clause,
@@ -1315,23 +1270,21 @@ pub fn rewrite_type_alias(
         Shape::legacy(where_budget, indent),
         context.config.where_density(),
         "=",
-        true,
-        true,
-        Some(span.hi),
-        span,
-        generics.span.hi,
+        Some(span.hi()),
+        generics.span.hi(),
+        option,
     ));
     result.push_str(&where_clause_str);
-    result.push_str(" = ");
+    if where_clause_str.is_empty() {
+        result.push_str(" = ");
+    } else {
+        result.push_str(&format!("\n{}= ", indent.to_string(context.config)));
+    }
 
     let line_width = last_line_width(&result);
     // This checked_sub may fail as the extra space after '=' is not taken into account
     // In that case the budget is set to 0 which will make ty.rewrite retry on a new line
-    let budget = context
-        .config
-        .max_width()
-        .checked_sub(indent.width() + line_width + ";".len())
-        .unwrap_or(0);
+    let budget = context.budget(indent.width() + line_width + ";".len());
     let type_indent = indent + line_width;
     // Try to fit the type on the same line
     let ty_str = try_opt!(
@@ -1344,12 +1297,7 @@ pub fn rewrite_type_alias(
                 let type_indent = indent.block_indent(context.config);
                 result.push('\n');
                 result.push_str(&type_indent.to_string(context.config));
-                let budget = try_opt!(
-                    context
-                        .config
-                        .max_width()
-                        .checked_sub(type_indent.width() + ";".len())
-                );
+                let budget = context.budget(type_indent.width() + ";".len());
                 ty.rewrite(context, Shape::legacy(budget, type_indent))
             })
     );
@@ -1373,68 +1321,15 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) {
     )
 }
 
-fn rewrite_missing_comment_on_field(
-    context: &RewriteContext,
-    shape: Shape,
-    lo: BytePos,
-    hi: BytePos,
-    result: &mut String,
-) -> Option<String> {
-    let possibly_comment_snippet = context.snippet(mk_sp(lo, hi));
-    let newline_index = possibly_comment_snippet.find('\n');
-    let comment_index = possibly_comment_snippet.find('/');
-    match (newline_index, comment_index) {
-        (Some(i), Some(j)) if i > j => result.push(' '),
-        _ => {
-            result.push('\n');
-            result.push_str(&shape.indent.to_string(context.config));
-        }
-    }
-    let trimmed = possibly_comment_snippet.trim();
-    if trimmed.is_empty() {
-        None
-    } else {
-        rewrite_comment(trimmed, false, shape, context.config).map(|s| {
-            format!("{}\n{}", s, shape.indent.to_string(context.config))
-        })
-    }
-}
-
 pub fn rewrite_struct_field_prefix(
     context: &RewriteContext,
     field: &ast::StructField,
-    shape: Shape,
 ) -> Option<String> {
     let vis = format_visibility(&field.vis);
-    let mut attr_str = try_opt!(
-        field
-            .attrs
-            .rewrite(context, Shape::indented(shape.indent, context.config))
-    );
-    // Try format missing comments after attributes
-    let missing_comment = if !field.attrs.is_empty() {
-        rewrite_missing_comment_on_field(
-            context,
-            shape,
-            field.attrs[field.attrs.len() - 1].span.hi,
-            field.span.lo,
-            &mut attr_str,
-        ).unwrap_or(String::new())
-    } else {
-        String::new()
-    };
-
     let type_annotation_spacing = type_annotation_spacing(context.config);
     Some(match field.ident {
-        Some(name) => format!(
-            "{}{}{}{}{}:",
-            attr_str,
-            missing_comment,
-            vis,
-            name,
-            type_annotation_spacing.0
-        ),
-        None => format!("{}{}{}", attr_str, missing_comment, vis),
+        Some(name) => format!("{}{}{}:", vis, name, type_annotation_spacing.0),
+        None => format!("{}", vis),
     })
 }
 
@@ -1465,32 +1360,56 @@ pub fn rewrite_struct_field(
     lhs_max_width: usize,
 ) -> Option<String> {
     if contains_skip(&field.attrs) {
-        let span = context.snippet(mk_sp(field.attrs[0].span.lo, field.span.hi));
+        let span = context.snippet(mk_sp(field.attrs[0].span.lo(), field.span.hi()));
         return wrap_str(span, context.config.max_width(), shape);
     }
 
     let type_annotation_spacing = type_annotation_spacing(context.config);
-    let prefix = try_opt!(rewrite_struct_field_prefix(context, field, shape));
-
-    // Try to put everything on a single line.
-    let last_line_width = last_line_width(&prefix);
+    let prefix = try_opt!(rewrite_struct_field_prefix(context, field));
+
+    let attrs_str = try_opt!(field.attrs.rewrite(context, shape));
+    let attrs_extendable = attrs_str.is_empty()
+        || (context.config.attributes_on_same_line_as_field()
+            && is_attributes_extendable(&attrs_str));
+    let missing_span = if field.attrs.is_empty() {
+        mk_sp(field.span.lo(), field.span.lo())
+    } else {
+        mk_sp(field.attrs.last().unwrap().span.hi(), field.span.lo())
+    };
     let mut spacing = String::from(if field.ident.is_some() {
         type_annotation_spacing.1
     } else {
         ""
     });
-    let lhs_offset = lhs_max_width.checked_sub(last_line_width).unwrap_or(0);
+    // Try to put everything on a single line.
+    let attr_prefix = try_opt!(combine_strs_with_missing_comments(
+        context,
+        &attrs_str,
+        &prefix,
+        missing_span,
+        shape,
+        attrs_extendable,
+    ));
+    let overhead = last_line_width(&attr_prefix);
+    let lhs_offset = lhs_max_width.checked_sub(overhead).unwrap_or(0);
     for _ in 0..lhs_offset {
         spacing.push(' ');
     }
-    let ty_rewritten = rewrite_struct_field_type(context, last_line_width, field, &spacing, shape);
+    // In this extreme case we will be missing a space betweeen an attribute and a field.
+    if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() {
+        spacing.push(' ');
+    }
+    let ty_rewritten = rewrite_struct_field_type(context, overhead, field, &spacing, shape);
     if let Some(ref ty) = ty_rewritten {
         if !ty.contains('\n') {
-            return Some(prefix + &ty);
+            return Some(attr_prefix + ty);
         }
     }
 
     // We must use multiline.
+    let last_line_width = last_line_width(&prefix);
+    let ty_rewritten = rewrite_struct_field_type(context, last_line_width, field, &spacing, shape);
+
     let type_offset = shape.indent.block_indent(context.config);
     let rewrite_type_in_next_line = || {
         field
@@ -1498,27 +1417,35 @@ pub fn rewrite_struct_field(
             .rewrite(context, Shape::indented(type_offset, context.config))
     };
 
-    match ty_rewritten {
+    let field_str = match ty_rewritten {
         // If we start from the next line and type fits in a single line, then do so.
         Some(ref ty) => match rewrite_type_in_next_line() {
-            Some(ref new_ty) if !new_ty.contains('\n') => Some(format!(
+            Some(ref new_ty) if !new_ty.contains('\n') => format!(
                 "{}\n{}{}",
                 prefix,
-                type_offset.to_string(&context.config),
+                type_offset.to_string(context.config),
                 &new_ty
-            )),
-            _ => Some(prefix + &ty),
+            ),
+            _ => prefix + ty,
         },
         _ => {
             let ty = try_opt!(rewrite_type_in_next_line());
-            Some(format!(
+            format!(
                 "{}\n{}{}",
                 prefix,
-                type_offset.to_string(&context.config),
+                type_offset.to_string(context.config),
                 &ty
-            ))
+            )
         }
-    }
+    };
+    combine_strs_with_missing_comments(
+        context,
+        &attrs_str,
+        &field_str,
+        missing_span,
+        shape,
+        attrs_extendable,
+    )
 }
 
 pub fn rewrite_static(
@@ -1555,15 +1482,13 @@ pub fn rewrite_static(
     if let Some(expr) = expr_opt {
         let lhs = format!("{}{} =", prefix, ty_str);
         // 1 = ;
-        let remaining_width = context.config.max_width() - offset.block_indent - 1;
+        let remaining_width = context.budget(offset.block_indent + 1);
         rewrite_assign_rhs(
             context,
             lhs,
             expr,
             Shape::legacy(remaining_width, offset.block_only()),
-        ).and_then(|res| {
-            recover_comment_removed(res, span, context, Shape::indented(offset, context.config))
-        })
+        ).and_then(|res| recover_comment_removed(res, span, context))
             .map(|s| if s.ends_with(';') { s } else { s + ";" })
     } else {
         Some(format!("{}{};", prefix, ty_str))
@@ -1580,7 +1505,8 @@ pub fn rewrite_associated_type(
     let prefix = format!("type {}", ident);
 
     let type_bounds_str = if let Some(ty_param_bounds) = ty_param_bounds_opt {
-        let shape = Shape::indented(indent, context.config);
+        // 2 = ": ".len()
+        let shape = try_opt!(Shape::indented(indent, context.config).offset_left(prefix.len() + 2));
         let bounds: &[_] = ty_param_bounds;
         let bound_str = try_opt!(
             bounds
@@ -1588,7 +1514,7 @@ pub fn rewrite_associated_type(
                 .map(|ty_bound| ty_bound.rewrite(context, shape))
                 .collect::<Option<Vec<_>>>()
         );
-        if bounds.len() > 0 {
+        if !bounds.is_empty() {
             format!(": {}", join_bounds(context, shape, &bound_str))
         } else {
             String::new()
@@ -1601,11 +1527,11 @@ pub fn rewrite_associated_type(
         let ty_str = try_opt!(ty.rewrite(
             context,
             Shape::legacy(
-                context.config.max_width() - indent.block_indent - prefix.len() - 2,
+                context.budget(indent.block_indent + prefix.len() + 2),
                 indent.block_only(),
             ),
         ));
-        Some(format!("{} = {};", prefix, ty_str))
+        Some(format!("{}{} = {};", prefix, type_bounds_str, ty_str))
     } else {
         Some(format!("{}{};", prefix, type_bounds_str))
     }
@@ -1743,17 +1669,17 @@ fn explicit_self_mutability(arg: &ast::Arg) -> ast::Mutability {
 
 pub fn span_lo_for_arg(arg: &ast::Arg) -> BytePos {
     if is_named_arg(arg) {
-        arg.pat.span.lo
+        arg.pat.span.lo()
     } else {
-        arg.ty.span.lo
+        arg.ty.span.lo()
     }
 }
 
 pub fn span_hi_for_arg(context: &RewriteContext, arg: &ast::Arg) -> BytePos {
     match arg.ty.node {
-        ast::TyKind::Infer if context.snippet(arg.ty.span) == "_" => arg.ty.span.hi,
-        ast::TyKind::Infer if is_named_arg(arg) => arg.pat.span.hi,
-        _ => arg.ty.span.hi,
+        ast::TyKind::Infer if context.snippet(arg.ty.span) == "_" => arg.ty.span.hi(),
+        ast::TyKind::Infer if is_named_arg(arg) => arg.pat.span.hi(),
+        _ => arg.ty.span.hi(),
     }
 }
 
@@ -1787,24 +1713,13 @@ fn rewrite_fn_base(
     let where_clause = &generics.where_clause;
 
     let mut result = String::with_capacity(1024);
-    // Vis unsafety abi.
+    // Vis defaultness constness unsafety abi.
     result.push_str(&*format_visibility(vis));
-
-    if let ast::Defaultness::Default = defaultness {
-        result.push_str("default ");
-    }
-
-    if let ast::Constness::Const = constness {
-        result.push_str("const ");
-    }
-
-    result.push_str(::utils::format_unsafety(unsafety));
-
+    result.push_str(format_defaultness(defaultness));
+    result.push_str(format_constness(constness));
+    result.push_str(format_unsafety(unsafety));
     if abi != abi::Abi::Rust {
-        result.push_str(&::utils::format_abi(
-            abi,
-            context.config.force_explicit_abi(),
-        ));
+        result.push_str(&format_abi(abi, context.config.force_explicit_abi()));
     }
 
     // fn foo
@@ -1819,10 +1734,14 @@ fn rewrite_fn_base(
         // 2 = `()`
         2
     };
-    let shape = try_opt!(
-        Shape::indented(indent + last_line_width(&result), context.config).sub_width(overhead)
-    );
-    let g_span = mk_sp(span.lo, fd.output.span().lo);
+    let used_width = last_line_used_width(&result, indent.width());
+    let one_line_budget = context.budget(used_width + overhead);
+    let shape = Shape {
+        width: one_line_budget,
+        indent: indent,
+        offset: used_width,
+    };
+    let g_span = mk_sp(span.lo(), fd.output.span().lo());
     let generics_str = try_opt!(rewrite_generics(context, generics, shape, g_span));
     result.push_str(&generics_str);
 
@@ -1835,28 +1754,22 @@ fn rewrite_fn_base(
     // return type later anyway.
     let ret_str = try_opt!(
         fd.output
-            .rewrite(&context, Shape::indented(indent, context.config))
+            .rewrite(context, Shape::indented(indent, context.config))
     );
 
     let multi_line_ret_str = ret_str.contains('\n');
     let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() };
 
     // Args.
-    let (mut one_line_budget, mut multi_line_budget, mut arg_indent) =
-        try_opt!(compute_budgets_for_args(
-            context,
-            &result,
-            indent,
-            ret_str_len,
-            newline_brace,
-            has_braces,
-        ));
-
-    if context.config.fn_args_layout() == IndentStyle::Block {
-        arg_indent = indent.block_indent(context.config);
-        // 1 = ","
-        multi_line_budget = context.config.max_width() - (arg_indent.width() + 1);
-    }
+    let (one_line_budget, multi_line_budget, mut arg_indent) = try_opt!(compute_budgets_for_args(
+        context,
+        &result,
+        indent,
+        ret_str_len,
+        newline_brace,
+        has_braces,
+        multi_line_ret_str,
+    ));
 
     debug!(
         "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}",
@@ -1869,46 +1782,44 @@ fn rewrite_fn_base(
     if one_line_budget == 0 {
         if snuggle_angle_bracket {
             result.push('(');
+        } else if context.config.fn_args_paren_newline() {
+            result.push('\n');
+            result.push_str(&arg_indent.to_string(context.config));
+            if context.config.fn_args_layout() == IndentStyle::Visual {
+                arg_indent = arg_indent + 1; // extra space for `(`
+            }
+            result.push('(');
         } else {
-            if context.config.fn_args_paren_newline() {
+            result.push_str("(");
+            if context.config.fn_args_layout() == IndentStyle::Visual {
                 result.push('\n');
                 result.push_str(&arg_indent.to_string(context.config));
-                if context.config.fn_args_layout() == IndentStyle::Visual {
-                    arg_indent = arg_indent + 1; // extra space for `(`
-                }
-                result.push('(');
-            } else {
-                result.push_str("(");
-                if context.config.fn_args_layout() == IndentStyle::Visual {
-                    result.push('\n');
-                    result.push_str(&arg_indent.to_string(context.config));
-                }
             }
         }
     } else {
         result.push('(');
     }
-    if context.config.spaces_within_parens() && fd.inputs.len() > 0 && result.ends_with('(') {
+    if context.config.spaces_within_parens() && !fd.inputs.is_empty() && result.ends_with('(') {
         result.push(' ')
     }
 
-    if multi_line_ret_str {
-        one_line_budget = 0;
-    }
-
     // A conservative estimation, to goal is to be over all parens in generics
     let args_start = generics
         .ty_params
         .last()
-        .map_or(span.lo, |tp| end_typaram(tp));
+        .map_or(span.lo(), |tp| end_typaram(tp));
     let args_end = if fd.inputs.is_empty() {
-        context.codemap.span_after(mk_sp(args_start, span.hi), ")")
+        context
+            .codemap
+            .span_after(mk_sp(args_start, span.hi()), ")")
     } else {
-        let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi, span.hi);
+        let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi(), span.hi());
         context.codemap.span_after(last_span, ")")
     };
     let args_span = mk_sp(
-        context.codemap.span_after(mk_sp(args_start, span.hi), "("),
+        context
+            .codemap
+            .span_after(mk_sp(args_start, span.hi()), "("),
         args_end,
     );
     let arg_str = try_opt!(rewrite_args(
@@ -1924,11 +1835,8 @@ fn rewrite_fn_base(
         generics_str.contains('\n'),
     ));
 
-    let multi_line_arg_str =
-        arg_str.contains('\n') || arg_str.chars().last().map_or(false, |c| c == ',');
-
     let put_args_in_block = match context.config.fn_args_layout() {
-        IndentStyle::Block => multi_line_arg_str || generics_str.contains('\n'),
+        IndentStyle::Block => arg_str.contains('\n') || arg_str.len() > one_line_budget,
         _ => false,
     } && !fd.inputs.is_empty();
 
@@ -1943,7 +1851,13 @@ fn rewrite_fn_base(
         result.push(')');
     } else {
         result.push_str(&arg_str);
-        if context.config.spaces_within_parens() && fd.inputs.len() > 0 {
+        let used_width = last_line_used_width(&result, indent.width()) + first_line_width(&ret_str);
+        // Put the closing brace on the next line if it overflows the max width.
+        // 1 = `)`
+        if fd.inputs.is_empty() && used_width + 1 > context.config.max_width() {
+            result.push('\n');
+        }
+        if context.config.spaces_within_parens() && !fd.inputs.is_empty() {
             result.push(' ')
         }
         // If the last line of args contains comment, we cannot put the closing paren
@@ -1961,15 +1875,16 @@ fn rewrite_fn_base(
     }
 
     // Return type.
-    if !ret_str.is_empty() {
+    if let ast::FunctionRetTy::Ty(..) = fd.output {
         let ret_should_indent = match context.config.fn_args_layout() {
             // If our args are block layout then we surely must have space.
-            IndentStyle::Block if put_args_in_block => false,
+            IndentStyle::Block if put_args_in_block || fd.inputs.is_empty() => false,
+            _ if args_last_line_contains_comment => false,
+            _ if result.contains('\n') || multi_line_ret_str => true,
             _ => {
-                // If we've already gone multi-line, or the return type would push over the max
-                // width, then put the return type on a new line. With the +1 for the signature
-                // length an additional space between the closing parenthesis of the argument and
-                // the arrow '->' is considered.
+                // If the return type would push over the max width, then put the return type on
+                // a new line. With the +1 for the signature length an additional space between
+                // the closing parenthesis of the argument and the arrow '->' is considered.
                 let mut sig_length = result.len() + indent.width() + ret_str_len + 1;
 
                 // If there is no where clause, take into account the space after the return type
@@ -1978,10 +1893,7 @@ fn rewrite_fn_base(
                     sig_length += 2;
                 }
 
-                let overlong_sig = sig_length > context.config.max_width();
-
-                (!args_last_line_contains_comment) &&
-                    (result.contains('\n') || multi_line_ret_str || overlong_sig)
+                sig_length > context.config.max_width()
             }
         };
         let ret_indent = if ret_should_indent {
@@ -2019,9 +1931,9 @@ fn rewrite_fn_base(
         }
 
         // Comment between return type and the end of the decl.
-        let snippet_lo = fd.output.span().hi;
+        let snippet_lo = fd.output.span().hi();
         if where_clause.predicates.is_empty() {
-            let snippet_hi = span.hi;
+            let snippet_hi = span.hi();
             let snippet = context.snippet(mk_sp(snippet_lo, snippet_hi));
             // Try to preserve the layout of the original snippet.
             let original_starts_with_newline = snippet
@@ -2042,30 +1954,22 @@ fn rewrite_fn_base(
                     force_new_line_for_brace = true;
                 }
             }
-        } else {
-            // FIXME it would be nice to catch comments between the return type
-            // and the where clause, but we don't have a span for the where
-            // clause.
         }
     }
 
     let should_compress_where = match context.config.where_density() {
-        Density::Compressed => !result.contains('\n') || put_args_in_block,
+        Density::Compressed => !result.contains('\n'),
         Density::CompressedIfEmpty => !has_body && !result.contains('\n'),
         _ => false,
-    } || (put_args_in_block && ret_str.is_empty());
+    };
 
     let pos_before_where = match fd.output {
-        ast::FunctionRetTy::Default(..) => args_span.hi,
-        ast::FunctionRetTy::Ty(ref ty) => ty.span.hi,
+        ast::FunctionRetTy::Default(..) => args_span.hi(),
+        ast::FunctionRetTy::Ty(ref ty) => ty.span.hi(),
     };
+
     if where_clause.predicates.len() == 1 && should_compress_where {
-        let budget = try_opt!(
-            context
-                .config
-                .max_width()
-                .checked_sub(last_line_width(&result))
-        );
+        let budget = context.budget(last_line_used_width(&result, indent.width()));
         if let Some(where_clause_str) = rewrite_where_clause(
             context,
             where_clause,
@@ -2073,25 +1977,17 @@ fn rewrite_fn_base(
             Shape::legacy(budget, indent),
             Density::Compressed,
             "{",
-            !has_braces,
-            put_args_in_block && ret_str.is_empty(),
-            Some(span.hi),
-            span,
+            Some(span.hi()),
             pos_before_where,
+            WhereClauseOption::compressed(),
         ) {
-            if !where_clause_str.contains('\n') {
-                if last_line_width(&result) + where_clause_str.len() > context.config.max_width() {
-                    result.push('\n');
-                }
-
-                result.push_str(&where_clause_str);
-
-                force_new_line_for_brace |= last_line_contains_single_line_comment(&result);
-                return Some((result, force_new_line_for_brace));
-            }
+            result.push_str(&where_clause_str);
+            force_new_line_for_brace |= last_line_contains_single_line_comment(&result);
+            return Some((result, force_new_line_for_brace));
         }
     }
 
+    let option = WhereClauseOption::new(!has_braces, put_args_in_block && ret_str.is_empty());
     let where_clause_str = try_opt!(rewrite_where_clause(
         context,
         where_clause,
@@ -2099,21 +1995,66 @@ fn rewrite_fn_base(
         Shape::indented(indent, context.config),
         Density::Tall,
         "{",
-        !has_braces,
-        put_args_in_block && ret_str.is_empty(),
-        Some(span.hi),
-        span,
+        Some(span.hi()),
         pos_before_where,
+        option,
     ));
+    // If there are neither where clause nor return type, we may be missing comments between
+    // args and `{`.
+    if where_clause_str.is_empty() {
+        if let ast::FunctionRetTy::Default(ret_span) = fd.output {
+            match recover_missing_comment_in_span(
+                mk_sp(args_span.hi(), ret_span.hi()),
+                shape,
+                context,
+                last_line_width(&result),
+            ) {
+                Some(ref missing_comment) if !missing_comment.is_empty() => {
+                    result.push_str(missing_comment);
+                    force_new_line_for_brace = true;
+                }
+                _ => (),
+            }
+        }
+    }
 
     result.push_str(&where_clause_str);
 
     force_new_line_for_brace |= last_line_contains_single_line_comment(&result);
-    return Some((result, force_new_line_for_brace));
+    Some((result, force_new_line_for_brace))
 }
 
-fn last_line_contains_single_line_comment(s: &str) -> bool {
-    s.lines().last().map_or(false, |l| l.contains("//"))
+#[derive(Copy, Clone)]
+struct WhereClauseOption {
+    suppress_comma: bool, // Force no trailing comma
+    snuggle: bool,        // Do not insert newline before `where`
+    compress_where: bool, // Try single line where clause instead of vertical layout
+}
+
+impl WhereClauseOption {
+    pub fn new(suppress_comma: bool, snuggle: bool) -> WhereClauseOption {
+        WhereClauseOption {
+            suppress_comma: suppress_comma,
+            snuggle: snuggle,
+            compress_where: false,
+        }
+    }
+
+    pub fn compressed() -> WhereClauseOption {
+        WhereClauseOption {
+            suppress_comma: true,
+            snuggle: false,
+            compress_where: true,
+        }
+    }
+
+    pub fn snuggled(current: &str) -> WhereClauseOption {
+        WhereClauseOption {
+            suppress_comma: false,
+            snuggle: trimmed_last_line_width(current) == 1,
+            compress_where: false,
+        }
+    }
 }
 
 fn rewrite_args(
@@ -2131,7 +2072,7 @@ fn rewrite_args(
     let mut arg_item_strs = try_opt!(
         args.iter()
             .map(|arg| {
-                arg.rewrite(&context, Shape::legacy(multi_line_budget, arg_indent))
+                arg.rewrite(context, Shape::legacy(multi_line_budget, arg_indent))
             })
             .collect::<Option<Vec<_>>>()
     );
@@ -2161,15 +2102,15 @@ fn rewrite_args(
     if args.len() >= min_args || variadic {
         let comment_span_start = if min_args == 2 {
             let second_arg_start = if arg_has_pattern(&args[1]) {
-                args[1].pat.span.lo
+                args[1].pat.span.lo()
             } else {
-                args[1].ty.span.lo
+                args[1].ty.span.lo()
             };
-            let reduced_span = mk_sp(span.lo, second_arg_start);
+            let reduced_span = mk_sp(span.lo(), second_arg_start);
 
             context.codemap.span_after_last(reduced_span, ",")
         } else {
-            span.lo
+            span.lo()
         };
 
         enum ArgumentKind<'a> {
@@ -2178,7 +2119,7 @@ enum ArgumentKind<'a> {
         }
 
         let variadic_arg = if variadic {
-            let variadic_span = mk_sp(args.last().unwrap().ty.span.hi, span.hi);
+            let variadic_span = mk_sp(args.last().unwrap().ty.span.hi(), span.hi());
             let variadic_start = context.codemap.span_after(variadic_span, "...") - BytePos(3);
             Some(ArgumentKind::Variadic(variadic_start))
         } else {
@@ -2197,7 +2138,7 @@ enum ArgumentKind<'a> {
                 ArgumentKind::Variadic(start) => start,
             },
             |arg| match *arg {
-                ArgumentKind::Regular(arg) => arg.ty.span.hi,
+                ArgumentKind::Regular(arg) => arg.ty.span.hi(),
                 ArgumentKind::Variadic(start) => start + BytePos(3),
             },
             |arg| match *arg {
@@ -2205,14 +2146,16 @@ enum ArgumentKind<'a> {
                 ArgumentKind::Variadic(..) => Some("...".to_owned()),
             },
             comment_span_start,
-            span.hi,
+            span.hi(),
+            false,
         );
 
         arg_items.extend(more_items);
     }
 
-    let fits_in_one_line = !generics_str_contains_newline &&
-        (arg_items.len() == 0 || arg_items.len() == 1 && arg_item_strs[0].len() <= one_line_budget);
+    let fits_in_one_line = !generics_str_contains_newline
+        && (arg_items.is_empty()
+            || arg_items.len() == 1 && arg_item_strs[0].len() <= one_line_budget);
 
     for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
         item.item = Some(arg);
@@ -2241,6 +2184,7 @@ enum ArgumentKind<'a> {
     let tactic = definitive_tactic(
         &arg_items,
         context.config.fn_args_density().to_list_tactic(),
+        Separator::Comma,
         one_line_budget,
     );
     let budget = match tactic {
@@ -2258,6 +2202,7 @@ enum ArgumentKind<'a> {
         } else {
             trailing_comma
         },
+        separator_place: SeparatorPlace::Back,
         shape: Shape::legacy(budget, indent),
         ends_with_newline: tactic.ends_with_newline(context.config.fn_args_layout()),
         preserve_newline: true,
@@ -2282,6 +2227,7 @@ fn compute_budgets_for_args(
     ret_str_len: usize,
     newline_brace: bool,
     has_braces: bool,
+    force_vertical_layout: bool,
 ) -> Option<((usize, usize, Indent))> {
     debug!(
         "compute_budgets_for_args {} {:?}, {}, {}",
@@ -2291,7 +2237,7 @@ fn compute_budgets_for_args(
         newline_brace
     );
     // Try keeping everything on the same line.
-    if !result.contains('\n') {
+    if !result.contains('\n') && !force_vertical_layout {
         // 2 = `()`, 3 = `() `, space is before ret_string.
         let overhead = if ret_str_len == 0 { 2 } else { 3 };
         let mut used_space = indent.width() + result.len() + ret_str_len + overhead;
@@ -2304,39 +2250,43 @@ fn compute_budgets_for_args(
             // 1 = `;`
             used_space += 1;
         }
-        let one_line_budget = context
-            .config
-            .max_width()
-            .checked_sub(used_space)
-            .unwrap_or(0);
+        let one_line_budget = context.budget(used_space);
 
         if one_line_budget > 0 {
             // 4 = "() {".len()
-            let multi_line_overhead =
-                indent.width() + result.len() + if newline_brace { 2 } else { 4 };
-            let multi_line_budget =
-                try_opt!(context.config.max_width().checked_sub(multi_line_overhead));
-
-            return Some((
-                one_line_budget,
-                multi_line_budget,
-                indent + result.len() + 1,
-            ));
+            let (indent, multi_line_budget) = match context.config.fn_args_layout() {
+                IndentStyle::Block => {
+                    let indent = indent.block_indent(context.config);
+                    (indent, context.budget(indent.width() + 1))
+                }
+                IndentStyle::Visual => {
+                    let indent = indent + result.len() + 1;
+                    let multi_line_overhead = indent.width() + if newline_brace { 2 } else { 4 };
+                    (indent, context.budget(multi_line_overhead))
+                }
+            };
+
+            return Some((one_line_budget, multi_line_budget, indent));
         }
     }
 
     // Didn't work. we must force vertical layout and put args on a newline.
     let new_indent = indent.block_indent(context.config);
-    // Account for `)` and possibly ` {`.
-    let used_space = new_indent.width() + if ret_str_len == 0 { 1 } else { 3 };
-    let max_space = try_opt!(context.config.max_width().checked_sub(used_space));
-    Some((0, max_space, new_indent))
+    let used_space = match context.config.fn_args_layout() {
+        // 1 = `,`
+        IndentStyle::Block => new_indent.width() + 1,
+        // Account for `)` and possibly ` {`.
+        IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 },
+    };
+    Some((0, context.budget(used_space), new_indent))
 }
 
-fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> bool {
-    match config.fn_brace_style() {
-        BraceStyle::AlwaysNextLine => true,
-        BraceStyle::SameLineWhere if !where_clause.predicates.is_empty() => true,
+fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause, has_body: bool) -> bool {
+    match (config.fn_brace_style(), config.where_density()) {
+        (BraceStyle::AlwaysNextLine, _) => true,
+        (_, Density::Compressed) if where_clause.predicates.len() == 1 => false,
+        (_, Density::CompressedIfEmpty) if where_clause.predicates.len() == 1 && !has_body => false,
+        (BraceStyle::SameLineWhere, _) if !where_clause.predicates.is_empty() => true,
         _ => false,
     }
 }
@@ -2363,37 +2313,48 @@ fn rewrite_generics_inner(
 ) -> Option<String> {
     // FIXME: convert bounds to where clauses where they get too big or if
     // there is a where clause at all.
-    let lifetimes: &[_] = &generics.lifetimes;
-    let tys: &[_] = &generics.ty_params;
-    if lifetimes.is_empty() && tys.is_empty() {
-        return Some(String::new());
-    }
 
-    // Strings for the generics.
-    let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(context, shape));
-    let ty_strs = tys.iter().map(|ty_param| ty_param.rewrite(context, shape));
+    // Wrapper type
+    enum GenericsArg<'a> {
+        Lifetime(&'a ast::LifetimeDef),
+        TyParam(&'a ast::TyParam),
+    }
+    impl<'a> Rewrite for GenericsArg<'a> {
+        fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+            match *self {
+                GenericsArg::Lifetime(ref lifetime) => lifetime.rewrite(context, shape),
+                GenericsArg::TyParam(ref ty) => ty.rewrite(context, shape),
+            }
+        }
+    }
+    impl<'a> Spanned for GenericsArg<'a> {
+        fn span(&self) -> Span {
+            match *self {
+                GenericsArg::Lifetime(ref lifetime) => lifetime.span(),
+                GenericsArg::TyParam(ref ty) => ty.span(),
+            }
+        }
+    }
 
-    // Extract comments between generics.
-    let lt_spans = lifetimes.iter().map(|l| {
-        let hi = if l.bounds.is_empty() {
-            l.lifetime.span.hi
-        } else {
-            l.bounds[l.bounds.len() - 1].span.hi
-        };
-        mk_sp(l.lifetime.span.lo, hi)
-    });
-    let ty_spans = tys.iter().map(|ty| ty.span());
+    if generics.lifetimes.is_empty() && generics.ty_params.is_empty() {
+        return Some(String::new());
+    }
 
+    let generics_args = generics
+        .lifetimes
+        .iter()
+        .map(|lt| GenericsArg::Lifetime(lt))
+        .chain(generics.ty_params.iter().map(|ty| GenericsArg::TyParam(ty)));
     let items = itemize_list(
         context.codemap,
-        lt_spans.chain(ty_spans).zip(lt_strs.chain(ty_strs)),
+        generics_args,
         ">",
-        |&(sp, _)| sp.lo,
-        |&(sp, _)| sp.hi,
-        // FIXME: don't clone
-        |&(_, ref str)| str.clone(),
+        |arg| arg.span().lo(),
+        |arg| arg.span().hi(),
+        |arg| arg.rewrite(context, shape),
         context.codemap.span_after(span, "<"),
-        span.hi,
+        span.hi(),
+        false,
     );
     format_generics_item_list(context, items, shape, one_line_width)
 }
@@ -2423,7 +2384,12 @@ pub fn format_generics_item_list<I>(
 {
     let item_vec = items.collect::<Vec<_>>();
 
-    let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, one_line_budget);
+    let tactic = definitive_tactic(
+        &item_vec,
+        ListTactic::HorizontalVertical,
+        Separator::Comma,
+        one_line_budget,
+    );
     let fmt = ListFormatting {
         tactic: tactic,
         separator: ",",
@@ -2432,6 +2398,7 @@ pub fn format_generics_item_list<I>(
         } else {
             context.config.trailing_comma()
         },
+        separator_place: SeparatorPlace::Back,
         shape: shape,
         ends_with_newline: tactic.ends_with_newline(context.config.generics_indent()),
         preserve_newline: true,
@@ -2452,8 +2419,8 @@ pub fn wrap_generics_with_angle_brackets(
     list_str: &str,
     list_offset: Indent,
 ) -> String {
-    if context.config.generics_indent() == IndentStyle::Block &&
-        (list_str.contains('\n') || list_str.ends_with(','))
+    if context.config.generics_indent() == IndentStyle::Block
+        && (list_str.contains('\n') || list_str.ends_with(','))
     {
         format!(
             "<\n{}{}\n{}>",
@@ -2483,7 +2450,7 @@ fn rewrite_trait_bounds(
     let bound_str = try_opt!(
         bounds
             .iter()
-            .map(|ty_bound| ty_bound.rewrite(&context, shape))
+            .map(|ty_bound| ty_bound.rewrite(context, shape))
             .collect::<Option<Vec<_>>>()
     );
     Some(format!(": {}", join_bounds(context, shape, &bound_str)))
@@ -2494,17 +2461,14 @@ fn rewrite_where_clause_rfc_style(
     where_clause: &ast::WhereClause,
     shape: Shape,
     terminator: &str,
-    suppress_comma: bool,
-    // where clause can be kept on the current line.
-    snuggle: bool,
     span_end: Option<BytePos>,
-    span: Span,
     span_end_before_where: BytePos,
+    where_clause_option: WhereClauseOption,
 ) -> Option<String> {
     let block_shape = shape.block().with_max_width(context.config);
 
     let (span_before, span_after) =
-        missing_span_before_after_where(context, span.hi, span_end_before_where, where_clause);
+        missing_span_before_after_where(span_end_before_where, where_clause);
     let (comment_before, comment_after) = try_opt!(rewrite_comments_before_after_where(
         context,
         span_before,
@@ -2512,7 +2476,7 @@ fn rewrite_where_clause_rfc_style(
         shape,
     ));
 
-    let starting_newline = if snuggle && comment_before.is_empty() {
+    let starting_newline = if where_clause_option.snuggle && comment_before.is_empty() {
         " ".to_owned()
     } else {
         "\n".to_owned() + &block_shape.indent.to_string(context.config)
@@ -2520,23 +2484,24 @@ fn rewrite_where_clause_rfc_style(
 
     let clause_shape = block_shape.block_indent(context.config.tab_spaces());
     // each clause on one line, trailing comma (except if suppress_comma)
-    let span_start = where_clause.predicates[0].span().lo;
+    let span_start = where_clause.predicates[0].span().lo();
     // If we don't have the start of the next span, then use the end of the
     // predicates, but that means we miss comments.
     let len = where_clause.predicates.len();
-    let end_of_preds = where_clause.predicates[len - 1].span().hi;
+    let end_of_preds = where_clause.predicates[len - 1].span().hi();
     let span_end = span_end.unwrap_or(end_of_preds);
     let items = itemize_list(
         context.codemap,
         where_clause.predicates.iter(),
         terminator,
-        |pred| pred.span().lo,
-        |pred| pred.span().hi,
+        |pred| pred.span().lo(),
+        |pred| pred.span().hi(),
         |pred| pred.rewrite(context, block_shape),
         span_start,
         span_end,
+        false,
     );
-    let comma_tactic = if suppress_comma {
+    let comma_tactic = if where_clause_option.suppress_comma {
         SeparatorTactic::Never
     } else {
         context.config.trailing_comma()
@@ -2546,6 +2511,7 @@ fn rewrite_where_clause_rfc_style(
         tactic: DefinitiveListTactic::Vertical,
         separator: ",",
         trailing_separator: comma_tactic,
+        separator_place: SeparatorPlace::Back,
         shape: clause_shape,
         ends_with_newline: true,
         preserve_newline: true,
@@ -2553,24 +2519,31 @@ fn rewrite_where_clause_rfc_style(
     };
     let preds_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
 
-    let newline_before_where = if comment_before.is_empty() || comment_before.ends_with('\n') {
+    let comment_separator = |comment: &str, shape: Shape| if comment.is_empty() {
         String::new()
     } else {
-        "\n".to_owned() + &shape.indent.to_string(context.config)
+        format!("\n{}", shape.indent.to_string(context.config))
     };
-    let newline_after_where = if comment_after.is_empty() {
-        String::new()
+    let newline_before_where = comment_separator(&comment_before, shape);
+    let newline_after_where = comment_separator(&comment_after, clause_shape);
+
+    // 6 = `where `
+    let clause_sep = if where_clause_option.compress_where && comment_before.is_empty()
+        && comment_after.is_empty() && !preds_str.contains('\n')
+        && 6 + preds_str.len() <= shape.width
+    {
+        String::from(" ")
     } else {
-        "\n".to_owned() + &clause_shape.indent.to_string(context.config)
+        format!("\n{}", clause_shape.indent.to_string(context.config))
     };
     Some(format!(
-        "{}{}{}where{}{}\n{}{}",
+        "{}{}{}where{}{}{}{}",
         starting_newline,
         comment_before,
         newline_before_where,
         newline_after_where,
         comment_after,
-        clause_shape.indent.to_string(context.config),
+        clause_sep,
         preds_str
     ))
 }
@@ -2582,11 +2555,9 @@ fn rewrite_where_clause(
     shape: Shape,
     density: Density,
     terminator: &str,
-    suppress_comma: bool,
-    snuggle: bool,
     span_end: Option<BytePos>,
-    span: Span,
     span_end_before_where: BytePos,
+    where_clause_option: WhereClauseOption,
 ) -> Option<String> {
     if where_clause.predicates.is_empty() {
         return Some(String::new());
@@ -2598,11 +2569,9 @@ fn rewrite_where_clause(
             where_clause,
             shape,
             terminator,
-            suppress_comma,
-            snuggle,
             span_end,
-            span,
             span_end_before_where,
+            where_clause_option,
         );
     }
 
@@ -2617,30 +2586,36 @@ fn rewrite_where_clause(
     // be out by a char or two.
 
     let budget = context.config.max_width() - offset.width();
-    let span_start = where_clause.predicates[0].span().lo;
+    let span_start = where_clause.predicates[0].span().lo();
     // If we don't have the start of the next span, then use the end of the
     // predicates, but that means we miss comments.
     let len = where_clause.predicates.len();
-    let end_of_preds = where_clause.predicates[len - 1].span().hi;
+    let end_of_preds = where_clause.predicates[len - 1].span().hi();
     let span_end = span_end.unwrap_or(end_of_preds);
     let items = itemize_list(
         context.codemap,
         where_clause.predicates.iter(),
         terminator,
-        |pred| pred.span().lo,
-        |pred| pred.span().hi,
+        |pred| pred.span().lo(),
+        |pred| pred.span().hi(),
         |pred| pred.rewrite(context, Shape::legacy(budget, offset)),
         span_start,
         span_end,
+        false,
     );
     let item_vec = items.collect::<Vec<_>>();
     // FIXME: we don't need to collect here if the where_layout isn't
     // HorizontalVertical.
-    let tactic = definitive_tactic(&item_vec, context.config.where_layout(), budget);
+    let tactic = definitive_tactic(
+        &item_vec,
+        context.config.where_layout(),
+        Separator::Comma,
+        budget,
+    );
 
     let mut comma_tactic = context.config.trailing_comma();
     // Kind of a hack because we don't usually have trailing commas in where clauses.
-    if comma_tactic == SeparatorTactic::Vertical || suppress_comma {
+    if comma_tactic == SeparatorTactic::Vertical || where_clause_option.suppress_comma {
         comma_tactic = SeparatorTactic::Never;
     }
 
@@ -2648,6 +2623,7 @@ fn rewrite_where_clause(
         tactic: tactic,
         separator: ",",
         trailing_separator: comma_tactic,
+        separator_place: SeparatorPlace::Back,
         shape: Shape::legacy(budget, offset),
         ends_with_newline: tactic.ends_with_newline(context.config.where_pred_indent()),
         preserve_newline: true,
@@ -2667,8 +2643,8 @@ fn rewrite_where_clause(
     } else {
         terminator.len()
     };
-    if density == Density::Tall || preds_str.contains('\n') ||
-        shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width
+    if density == Density::Tall || preds_str.contains('\n')
+        || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width
     {
         Some(format!(
             "\n{}where {}",
@@ -2681,49 +2657,27 @@ fn rewrite_where_clause(
 }
 
 fn missing_span_before_after_where(
-    context: &RewriteContext,
-    item_end: BytePos,
     before_item_span_end: BytePos,
     where_clause: &ast::WhereClause,
 ) -> (Span, Span) {
-    let snippet = context.snippet(mk_sp(before_item_span_end, item_end));
-    let pos_before_where =
-        before_item_span_end + BytePos(snippet.find_uncommented("where").unwrap() as u32);
-    let missing_span_before = mk_sp(before_item_span_end, pos_before_where);
+    let missing_span_before = mk_sp(before_item_span_end, where_clause.span.lo());
     // 5 = `where`
-    let pos_after_where = pos_before_where + BytePos(5);
-    let missing_span_after = mk_sp(pos_after_where, where_clause.predicates[0].span().lo);
+    let pos_after_where = where_clause.span.lo() + BytePos(5);
+    let missing_span_after = mk_sp(pos_after_where, where_clause.predicates[0].span().lo());
     (missing_span_before, missing_span_after)
 }
 
-fn rewrite_missing_comment_in_where(
-    context: &RewriteContext,
-    comment: &str,
-    shape: Shape,
-) -> Option<String> {
-    let comment = comment.trim();
-    if comment.is_empty() {
-        Some(String::new())
-    } else {
-        rewrite_comment(comment, false, shape, context.config)
-    }
-}
-
 fn rewrite_comments_before_after_where(
     context: &RewriteContext,
     span_before_where: Span,
     span_after_where: Span,
     shape: Shape,
 ) -> Option<(String, String)> {
-    let before_comment = try_opt!(rewrite_missing_comment_in_where(
-        context,
-        &context.snippet(span_before_where),
-        shape,
-    ));
-    let after_comment = try_opt!(rewrite_missing_comment_in_where(
-        context,
-        &context.snippet(span_after_where),
+    let before_comment = try_opt!(rewrite_missing_comment(span_before_where, shape, context));
+    let after_comment = try_opt!(rewrite_missing_comment(
+        span_after_where,
         shape.block_indent(context.config.tab_spaces()),
+        context,
     ));
     Some((before_comment, after_comment))
 }
@@ -2747,11 +2701,8 @@ fn format_generics(
     let mut result = try_opt!(rewrite_generics(context, generics, shape, span));
 
     let same_line_brace = if !generics.where_clause.predicates.is_empty() || result.contains('\n') {
-        let budget = context
-            .config
-            .max_width()
-            .checked_sub(last_line_width(&result))
-            .unwrap_or(0);
+        let budget = context.budget(last_line_used_width(&result, offset.width()));
+        let option = WhereClauseOption::snuggled(&result);
         let where_clause_str = try_opt!(rewrite_where_clause(
             context,
             &generics.where_clause,
@@ -2759,29 +2710,20 @@ fn format_generics(
             Shape::legacy(budget, offset.block_only()),
             Density::Tall,
             terminator,
-            false,
-            trimmed_last_line_width(&result) == 1,
-            Some(span.hi),
-            span,
-            generics.span.hi,
+            Some(span.hi()),
+            generics.span.hi(),
+            option,
         ));
         result.push_str(&where_clause_str);
-        force_same_line_brace || brace_style == BraceStyle::PreferSameLine ||
-            (generics.where_clause.predicates.is_empty() && trimmed_last_line_width(&result) == 1)
+        force_same_line_brace || brace_style == BraceStyle::PreferSameLine
+            || (generics.where_clause.predicates.is_empty()
+                && trimmed_last_line_width(&result) == 1)
     } else {
-        force_same_line_brace || trimmed_last_line_width(&result) == 1 ||
-            brace_style != BraceStyle::AlwaysNextLine
+        force_same_line_brace || trimmed_last_line_width(&result) == 1
+            || brace_style != BraceStyle::AlwaysNextLine
     };
-    let total_used_width = if result.contains('\n') {
-        last_line_width(&result)
-    } else {
-        used_width + result.len()
-    };
-    let remaining_budget = context
-        .config
-        .max_width()
-        .checked_sub(total_used_width)
-        .unwrap_or(0);
+    let total_used_width = last_line_used_width(&result, used_width);
+    let remaining_budget = context.budget(total_used_width);
     // If the same line brace if forced, it indicates that we are rewriting an item with empty body,
     // and hence we take the closer into account as well for one line budget.
     // We assume that the closer has the same length as the opener.
@@ -2801,3 +2743,68 @@ fn format_generics(
 
     Some(result)
 }
+
+impl Rewrite for ast::ForeignItem {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        let attrs_str = try_opt!(self.attrs.rewrite(context, shape));
+        // Drop semicolon or it will be interpreted as comment.
+        // FIXME: this may be a faulty span from libsyntax.
+        let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1));
+
+        let item_str = try_opt!(match self.node {
+            ast::ForeignItemKind::Fn(ref fn_decl, ref generics) => {
+                rewrite_fn_base(
+                    context,
+                    shape.indent,
+                    self.ident,
+                    fn_decl,
+                    generics,
+                    ast::Unsafety::Normal,
+                    ast::Constness::NotConst,
+                    ast::Defaultness::Final,
+                    // These are not actually rust functions,
+                    // but we format them as such.
+                    abi::Abi::Rust,
+                    &self.vis,
+                    span,
+                    false,
+                    false,
+                    false,
+                ).map(|(s, _)| format!("{};", s))
+            }
+            ast::ForeignItemKind::Static(ref ty, is_mutable) => {
+                // FIXME(#21): we're dropping potential comments in between the
+                // function keywords here.
+                let vis = format_visibility(&self.vis);
+                let mut_str = if is_mutable { "mut " } else { "" };
+                let prefix = format!("{}static {}{}:", vis, mut_str, self.ident);
+                // 1 = ;
+                let shape = try_opt!(shape.sub_width(1));
+                ty.rewrite(context, shape).map(|ty_str| {
+                    // 1 = space between prefix and type.
+                    let sep = if prefix.len() + ty_str.len() + 1 <= shape.width {
+                        String::from(" ")
+                    } else {
+                        let nested_indent = shape.indent.block_indent(context.config);
+                        format!("\n{}", nested_indent.to_string(context.config))
+                    };
+                    format!("{}{}{};", prefix, sep, ty_str)
+                })
+            }
+        });
+
+        let missing_span = if self.attrs.is_empty() {
+            mk_sp(self.span.lo(), self.span.lo())
+        } else {
+            mk_sp(self.attrs[self.attrs.len() - 1].span.hi(), self.span.lo())
+        };
+        combine_strs_with_missing_comments(
+            context,
+            &attrs_str,
+            &item_str,
+            missing_span,
+            shape,
+            false,
+        )
+    }
+}