]> git.lizzy.rs Git - rust.git/blobdiff - src/lib.rs
Cargo fmt
[rust.git] / src / lib.rs
index 596fa5b5396b65dae5ba5e72847304b7a98ed369..ec846de04dbd9dd6d298ea321155cf30d13f6f02 100644 (file)
@@ -8,8 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(match_default_bindings)]
 #![feature(rustc_private)]
+#![feature(type_ascription)]
 
+#[macro_use]
+extern crate derive_new;
 extern crate diff;
 #[macro_use]
 extern crate log;
@@ -19,7 +23,6 @@
 #[macro_use]
 extern crate serde_derive;
 extern crate serde_json;
-extern crate strings;
 extern crate syntax;
 extern crate term;
 extern crate unicode_segmentation;
 use std::fmt;
 use std::io::{self, stdout, Write};
 use std::iter::repeat;
-use std::ops::{Add, Sub};
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
 use std::rc::Rc;
+use std::time::Duration;
 
 use errors::{DiagnosticBuilder, Handler};
 use errors::emitter::{ColorConfig, EmitterWriter};
-use macros::MacroArg;
-use strings::string_buffer::StringBuffer;
 use syntax::ast;
-use syntax::codemap::{CodeMap, FilePathMapping, Span};
+use syntax::codemap::{CodeMap, FilePathMapping};
+pub use syntax::codemap::FileName;
 use syntax::parse::{self, ParseSess};
 
 use checkstyle::{output_footer, output_header};
-use config::Config;
+use comment::{CharClasses, FullCodeCharKind};
+pub use config::Config;
 use filemap::FileMap;
 use issues::{BadIssueSeeker, Issue};
-use utils::{isatty, mk_sp, outer_attributes};
-use visitor::FmtVisitor;
+use shape::Indent;
+use utils::use_colored_tty;
+use visitor::{FmtVisitor, SnippetProvider};
 
 pub use self::summary::Summary;
 
 #[macro_use]
 mod utils;
+mod shape;
+mod spanned;
 pub mod config;
 pub mod codemap;
 pub mod filemap;
 pub mod file_lines;
 pub mod visitor;
 mod checkstyle;
+mod closures;
 mod items;
 mod missed_spans;
 mod lists;
 mod summary;
 mod vertical;
 
-/// Spanned returns a span including attributes, if available.
-pub trait Spanned {
-    fn span(&self) -> Span;
-}
-
-macro_rules! span_with_attrs_lo_hi {
-    ($this:ident, $lo:expr, $hi:expr) => {
-        {
-            let attrs = outer_attributes(&$this.attrs);
-            if attrs.is_empty() {
-                mk_sp($lo, $hi)
-            } else {
-                mk_sp(attrs[0].span.lo(), $hi)
-            }
-        }
-    }
-}
-
-macro_rules! span_with_attrs {
-    ($this:ident) => {
-        span_with_attrs_lo_hi!($this, $this.span.lo(), $this.span.hi())
-    }
-}
-
-macro_rules! implement_spanned {
-    ($this:ty) => {
-        impl Spanned for $this {
-            fn span(&self) -> Span {
-                span_with_attrs!(self)
-            }
-        }
-    }
-}
-
-// Implement `Spanned` for structs with `attrs` field.
-implement_spanned!(ast::Expr);
-implement_spanned!(ast::Field);
-implement_spanned!(ast::ForeignItem);
-implement_spanned!(ast::Item);
-implement_spanned!(ast::Local);
-
-impl Spanned for ast::Stmt {
-    fn span(&self) -> Span {
-        match self.node {
-            ast::StmtKind::Local(ref local) => mk_sp(local.span().lo(), self.span.hi()),
-            ast::StmtKind::Item(ref item) => mk_sp(item.span().lo(), self.span.hi()),
-            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
-                mk_sp(expr.span().lo(), self.span.hi())
-            }
-            ast::StmtKind::Mac(ref mac) => {
-                let (_, _, ref attrs) = **mac;
-                if attrs.is_empty() {
-                    self.span
-                } else {
-                    mk_sp(attrs[0].span.lo(), self.span.hi())
-                }
-            }
-        }
-    }
-}
-
-impl Spanned for ast::Pat {
-    fn span(&self) -> Span {
-        self.span
-    }
-}
-
-impl Spanned for ast::Ty {
-    fn span(&self) -> Span {
-        self.span
-    }
-}
-
-impl Spanned for ast::Arm {
-    fn span(&self) -> Span {
-        span_with_attrs_lo_hi!(self, self.pats[0].span.lo(), self.body.span.hi())
-    }
-}
-
-impl Spanned for ast::Arg {
-    fn span(&self) -> Span {
-        if items::is_named_arg(self) {
-            utils::mk_sp(self.pat.span.lo(), self.ty.span.hi())
-        } else {
-            self.ty.span
-        }
-    }
-}
-
-impl Spanned for ast::StructField {
-    fn span(&self) -> Span {
-        span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span.hi())
-    }
-}
-
-impl Spanned for ast::WherePredicate {
-    fn span(&self) -> Span {
-        match *self {
-            ast::WherePredicate::BoundPredicate(ref p) => p.span,
-            ast::WherePredicate::RegionPredicate(ref p) => p.span,
-            ast::WherePredicate::EqPredicate(ref p) => p.span,
-        }
-    }
-}
-
-impl Spanned for ast::FunctionRetTy {
-    fn span(&self) -> Span {
-        match *self {
-            ast::FunctionRetTy::Default(span) => span,
-            ast::FunctionRetTy::Ty(ref ty) => ty.span,
-        }
-    }
-}
-
-impl Spanned for ast::TyParam {
-    fn span(&self) -> Span {
-        // Note that ty.span is the span for ty.ident, not the whole item.
-        let lo = if self.attrs.is_empty() {
-            self.span.lo()
-        } else {
-            self.attrs[0].span.lo()
-        };
-        if let Some(ref def) = self.default {
-            return mk_sp(lo, def.span.hi());
-        }
-        if self.bounds.is_empty() {
-            return mk_sp(lo, self.span.hi());
-        }
-        let hi = self.bounds[self.bounds.len() - 1].span().hi();
-        mk_sp(lo, hi)
-    }
-}
-
-impl Spanned for ast::TyParamBound {
-    fn span(&self) -> Span {
-        match *self {
-            ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span,
-            ast::TyParamBound::RegionTyParamBound(ref l) => l.span,
-        }
-    }
-}
-
-impl Spanned for MacroArg {
-    fn span(&self) -> Span {
-        match *self {
-            MacroArg::Expr(ref expr) => expr.span(),
-            MacroArg::Ty(ref ty) => ty.span(),
-            MacroArg::Pat(ref pat) => pat.span(),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct Indent {
-    // Width of the block indent, in characters. Must be a multiple of
-    // Config::tab_spaces.
-    pub block_indent: usize,
-    // Alignment in characters.
-    pub alignment: usize,
-}
-
-impl Indent {
-    pub fn new(block_indent: usize, alignment: usize) -> Indent {
-        Indent {
-            block_indent: block_indent,
-            alignment: alignment,
-        }
-    }
-
-    pub fn from_width(config: &Config, width: usize) -> Indent {
-        if config.hard_tabs() {
-            let tab_num = width / config.tab_spaces();
-            let alignment = width % config.tab_spaces();
-            Indent::new(config.tab_spaces() * tab_num, alignment)
-        } else {
-            Indent::new(width, 0)
-        }
-    }
-
-    pub fn empty() -> Indent {
-        Indent::new(0, 0)
-    }
-
-    pub fn block_only(&self) -> Indent {
-        Indent {
-            block_indent: self.block_indent,
-            alignment: 0,
-        }
-    }
-
-    pub fn block_indent(mut self, config: &Config) -> Indent {
-        self.block_indent += config.tab_spaces();
-        self
-    }
-
-    pub fn block_unindent(mut self, config: &Config) -> Indent {
-        if self.block_indent < config.tab_spaces() {
-            Indent::new(self.block_indent, 0)
-        } else {
-            self.block_indent -= config.tab_spaces();
-            self
-        }
-    }
-
-    pub fn width(&self) -> usize {
-        self.block_indent + self.alignment
-    }
-
-    pub fn to_string(&self, config: &Config) -> String {
-        let (num_tabs, num_spaces) = if config.hard_tabs() {
-            (self.block_indent / config.tab_spaces(), self.alignment)
-        } else {
-            (0, self.width())
-        };
-        let num_chars = num_tabs + num_spaces;
-        let mut indent = String::with_capacity(num_chars);
-        for _ in 0..num_tabs {
-            indent.push('\t')
-        }
-        for _ in 0..num_spaces {
-            indent.push(' ')
-        }
-        indent
-    }
-}
-
-impl Add for Indent {
-    type Output = Indent;
-
-    fn add(self, rhs: Indent) -> Indent {
-        Indent {
-            block_indent: self.block_indent + rhs.block_indent,
-            alignment: self.alignment + rhs.alignment,
-        }
-    }
-}
-
-impl Sub for Indent {
-    type Output = Indent;
-
-    fn sub(self, rhs: Indent) -> Indent {
-        Indent::new(
-            self.block_indent - rhs.block_indent,
-            self.alignment - rhs.alignment,
-        )
-    }
-}
-
-impl Add<usize> for Indent {
-    type Output = Indent;
-
-    fn add(self, rhs: usize) -> Indent {
-        Indent::new(self.block_indent, self.alignment + rhs)
-    }
-}
-
-impl Sub<usize> for Indent {
-    type Output = Indent;
-
-    fn sub(self, rhs: usize) -> Indent {
-        Indent::new(self.block_indent, self.alignment - rhs)
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct Shape {
-    pub width: usize,
-    // The current indentation of code.
-    pub indent: Indent,
-    // Indentation + any already emitted text on the first line of the current
-    // statement.
-    pub offset: usize,
-}
-
-impl Shape {
-    /// `indent` is the indentation of the first line. The next lines
-    /// should begin with at least `indent` spaces (except backwards
-    /// indentation). The first line should not begin with indentation.
-    /// `width` is the maximum number of characters on the last line
-    /// (excluding `indent`). The width of other lines is not limited by
-    /// `width`.
-    /// Note that in reality, we sometimes use width for lines other than the
-    /// last (i.e., we are conservative).
-    // .......*-------*
-    //        |       |
-    //        |     *-*
-    //        *-----|
-    // |<------------>|  max width
-    // |<---->|          indent
-    //        |<--->|    width
-    pub fn legacy(width: usize, indent: Indent) -> Shape {
-        Shape {
-            width: width,
-            indent: indent,
-            offset: indent.alignment,
-        }
-    }
-
-    pub fn indented(indent: Indent, config: &Config) -> Shape {
-        Shape {
-            width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
-            indent: indent,
-            offset: indent.alignment,
-        }
-    }
-
-    pub fn with_max_width(&self, config: &Config) -> Shape {
-        Shape {
-            width: config
-                .max_width()
-                .checked_sub(self.indent.width())
-                .unwrap_or(0),
-            ..*self
-        }
-    }
-
-    pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
-        Shape {
-            width: width,
-            indent: indent,
-            offset: offset,
-        }
-    }
-
-    pub fn visual_indent(&self, extra_width: usize) -> Shape {
-        let alignment = self.offset + extra_width;
-        Shape {
-            width: self.width,
-            indent: Indent::new(self.indent.block_indent, alignment),
-            offset: alignment,
-        }
-    }
-
-    pub fn block_indent(&self, extra_width: usize) -> Shape {
-        if self.indent.alignment == 0 {
-            Shape {
-                width: self.width,
-                indent: Indent::new(self.indent.block_indent + extra_width, 0),
-                offset: 0,
-            }
-        } else {
-            Shape {
-                width: self.width,
-                indent: self.indent + extra_width,
-                offset: self.indent.alignment + extra_width,
-            }
-        }
-    }
-
-    pub fn block_left(&self, width: usize) -> Option<Shape> {
-        self.block_indent(width).sub_width(width)
-    }
-
-    pub fn add_offset(&self, extra_width: usize) -> Shape {
-        Shape {
-            offset: self.offset + extra_width,
-            ..*self
-        }
-    }
-
-    pub fn block(&self) -> Shape {
-        Shape {
-            indent: self.indent.block_only(),
-            ..*self
-        }
-    }
-
-    pub fn sub_width(&self, width: usize) -> Option<Shape> {
-        Some(Shape {
-            width: try_opt!(self.width.checked_sub(width)),
-            ..*self
-        })
-    }
-
-    pub fn shrink_left(&self, width: usize) -> Option<Shape> {
-        Some(Shape {
-            width: try_opt!(self.width.checked_sub(width)),
-            indent: self.indent + width,
-            offset: self.offset + width,
-        })
-    }
-
-    pub fn offset_left(&self, width: usize) -> Option<Shape> {
-        self.add_offset(width).sub_width(width)
-    }
-
-    pub fn used_width(&self) -> usize {
-        self.indent.block_indent + self.offset
-    }
-
-    pub fn rhs_overhead(&self, config: &Config) -> usize {
-        config
-            .max_width()
-            .checked_sub(self.used_width() + self.width)
-            .unwrap_or(0)
-    }
-}
-
+#[derive(Clone, Copy)]
 pub enum ErrorKind {
     // Line has exceeded character limit (found, maximum)
     LineOverflow(usize, usize),
@@ -488,8 +98,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
             ErrorKind::LineOverflow(found, maximum) => write!(
                 fmt,
                 "line exceeded maximum width (maximum: {}, found: {})",
-                maximum,
-                found
+                maximum, found
             ),
             ErrorKind::TrailingWhitespace => write!(fmt, "left behind trailing whitespace"),
             ErrorKind::BadIssue(issue) => write!(fmt, "found {}", issue),
@@ -502,6 +111,7 @@ pub struct FormattingError {
     line: usize,
     kind: ErrorKind,
     is_comment: bool,
+    is_string: bool,
     line_buffer: String,
 }
 
@@ -513,13 +123,12 @@ fn msg_prefix(&self) -> &str {
         }
     }
 
-    fn msg_suffix(&self) -> String {
-        match self.kind {
-            ErrorKind::LineOverflow(..) if self.is_comment => format!(
-                "use `error_on_line_overflow_comments = false` to suppress \
-                 the warning against line comments\n",
-            ),
-            _ => String::from(""),
+    fn msg_suffix(&self) -> &str {
+        if self.is_comment || self.is_string {
+            "set `error_on_unformatted = false` to suppress \
+             the warning against comments or string literals\n"
+        } else {
+            ""
         }
     }
 
@@ -535,14 +144,14 @@ pub fn format_len(&self) -> (usize, usize) {
                     .count();
                 (self.line_buffer.len() - trailing_ws_len, trailing_ws_len)
             }
-            _ => (0, 0), // unreachable
+            _ => unreachable!(),
         }
     }
 }
 
 pub struct FormatReport {
     // Maps stringified file paths to their associated formatting errors.
-    file_error_map: HashMap<String, Vec<FormattingError>>,
+    file_error_map: HashMap<FileName, Vec<FormattingError>>,
 }
 
 impl FormatReport {
@@ -691,12 +300,12 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
 fn format_ast<F>(
     krate: &ast::Crate,
     parse_session: &mut ParseSess,
-    main_file: &Path,
+    main_file: &FileName,
     config: &Config,
     mut after_file: F,
 ) -> Result<(FileMap, bool), io::Error>
 where
-    F: FnMut(&str, &mut StringBuffer) -> Result<bool, io::Error>,
+    F: FnMut(&FileName, &mut String, &[(usize, usize)]) -> Result<bool, io::Error>,
 {
     let mut result = FileMap::new();
     // diff mode: check if any files are differing
@@ -705,36 +314,91 @@ fn format_ast<F>(
     // We always skip children for the "Plain" write mode, since there is
     // nothing to distinguish the nested module contents.
     let skip_children = config.skip_children() || config.write_mode() == config::WriteMode::Plain;
-    for (path, module) in modules::list_files(krate, parse_session.codemap()) {
-        if skip_children && path.as_path() != main_file {
+    for (path, module) in modules::list_files(krate, parse_session.codemap())? {
+        if skip_children && path != *main_file {
             continue;
         }
-        let path_str = path.to_str().unwrap();
         if config.verbose() {
-            println!("Formatting {}", path_str);
-        }
-        let mut visitor = FmtVisitor::from_codemap(parse_session, config);
-        let filemap = visitor.codemap.lookup_char_pos(module.inner.lo()).file;
+            println!("Formatting {}", path);
+        }
+        let filemap = parse_session
+            .codemap()
+            .lookup_char_pos(module.inner.lo())
+            .file;
+        let big_snippet = filemap.src.as_ref().unwrap();
+        let snippet_provider = SnippetProvider::new(filemap.start_pos, big_snippet);
+        let mut visitor = FmtVisitor::from_codemap(parse_session, config, &snippet_provider);
         // Format inner attributes if available.
-        if !krate.attrs.is_empty() && path == main_file {
-            visitor.visit_attrs(&krate.attrs, ast::AttrStyle::Inner);
+        if !krate.attrs.is_empty() && path == *main_file {
+            visitor.skip_empty_lines(filemap.end_pos);
+            if visitor.visit_attrs(&krate.attrs, ast::AttrStyle::Inner) {
+                visitor.push_rewrite(module.inner, None);
+            } else {
+                visitor.format_separate_mod(module, &*filemap);
+            }
         } else {
             visitor.last_pos = filemap.start_pos;
-        }
-        visitor.format_separate_mod(module, &*filemap);
+            visitor.skip_empty_lines(filemap.end_pos);
+            visitor.format_separate_mod(module, &*filemap);
+        };
 
-        has_diff |= after_file(path_str, &mut visitor.buffer)?;
+        assert_eq!(
+            visitor.line_number,
+            ::utils::count_newlines(&format!("{}", visitor.buffer))
+        );
+
+        let filename = path.clone();
+        has_diff |= match after_file(&filename, &mut visitor.buffer, &visitor.skipped_range) {
+            Ok(result) => result,
+            Err(e) => {
+                // Create a new error with path_str to help users see which files failed
+                let err_msg = format!("{}: {}", path, e);
+                return Err(io::Error::new(e.kind(), err_msg));
+            }
+        };
 
-        result.push((path_str.to_owned(), visitor.buffer));
+        result.push((filename, visitor.buffer));
     }
 
     Ok((result, has_diff))
 }
 
+/// Returns true if the line with the given line number was skipped by `#[rustfmt_skip]`.
+fn is_skipped_line(line_number: usize, skipped_range: &[(usize, usize)]) -> bool {
+    skipped_range
+        .iter()
+        .any(|&(lo, hi)| lo <= line_number && line_number <= hi)
+}
+
+fn should_report_error(
+    config: &Config,
+    char_kind: FullCodeCharKind,
+    is_string: bool,
+    error_kind: ErrorKind,
+) -> bool {
+    let allow_error_report = if char_kind.is_comment() || is_string {
+        config.error_on_unformatted()
+    } else {
+        true
+    };
+
+    match error_kind {
+        ErrorKind::LineOverflow(..) => config.error_on_line_overflow() && allow_error_report,
+        ErrorKind::TrailingWhitespace => allow_error_report,
+        _ => true,
+    }
+}
+
 // Formatting done on a char by char or line by line basis.
 // FIXME(#209) warn on bad license
 // FIXME(#20) other stuff for parity with make tidy
-fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &mut FormatReport) {
+fn format_lines(
+    text: &mut String,
+    name: &FileName,
+    skipped_range: &[(usize, usize)],
+    config: &Config,
+    report: &mut FormatReport,
+) {
     // Iterate over the chars in the file map.
     let mut trims = vec![];
     let mut last_wspace: Option<usize> = None;
@@ -743,17 +407,15 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
     let mut newline_count = 0;
     let mut errors = vec![];
     let mut issue_seeker = BadIssueSeeker::new(config.report_todo(), config.report_fixme());
-    let mut prev_char: Option<char> = None;
-    let mut is_comment = false;
     let mut line_buffer = String::with_capacity(config.max_width() * 2);
+    let mut is_string = false; // true if the current line contains a string literal.
+    let mut format_line = config.file_lines().contains_line(name, cur_line);
 
-    for (c, b) in text.chars() {
+    for (kind, (b, c)) in CharClasses::new(text.chars().enumerate()) {
         if c == '\r' {
             continue;
         }
 
-        let format_line = config.file_lines().contains_line(name, cur_line as usize);
-
         if format_line {
             // Add warnings for bad todos/ fixmes
             if let Some(issue) = issue_seeker.inspect(c) {
@@ -761,6 +423,7 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
                     line: cur_line,
                     kind: ErrorKind::BadIssue(issue),
                     is_comment: false,
+                    is_string: false,
                     line_buffer: String::new(),
                 });
             }
@@ -769,19 +432,23 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
         if c == '\n' {
             if format_line {
                 // Check for (and record) trailing whitespace.
-                if let Some(lw) = last_wspace {
-                    trims.push((cur_line, lw, b, line_buffer.clone()));
+                if let Some(..) = last_wspace {
+                    if should_report_error(config, kind, is_string, ErrorKind::TrailingWhitespace) {
+                        trims.push((cur_line, kind, line_buffer.clone()));
+                    }
                     line_len -= 1;
                 }
 
                 // Check for any line width errors we couldn't correct.
-                let report_error_on_line_overflow = config.error_on_line_overflow() &&
-                    (config.error_on_line_overflow_comments() || !is_comment);
-                if report_error_on_line_overflow && line_len > config.max_width() {
+                let error_kind = ErrorKind::LineOverflow(line_len, config.max_width());
+                if line_len > config.max_width() && !is_skipped_line(cur_line, skipped_range)
+                    && should_report_error(config, kind, is_string, error_kind)
+                {
                     errors.push(FormattingError {
                         line: cur_line,
-                        kind: ErrorKind::LineOverflow(line_len, config.max_width()),
-                        is_comment: is_comment,
+                        kind: error_kind,
+                        is_comment: kind.is_comment(),
+                        is_string,
                         line_buffer: line_buffer.clone(),
                     });
                 }
@@ -789,48 +456,47 @@ fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &m
 
             line_len = 0;
             cur_line += 1;
+            format_line = config.file_lines().contains_line(name, cur_line);
             newline_count += 1;
             last_wspace = None;
-            prev_char = None;
-            is_comment = false;
             line_buffer.clear();
+            is_string = false;
         } else {
             newline_count = 0;
-            line_len += 1;
+            line_len += if c == '\t' { config.tab_spaces() } else { 1 };
             if c.is_whitespace() {
                 if last_wspace.is_none() {
                     last_wspace = Some(b);
                 }
-            } else if c == '/' {
-                match prev_char {
-                    Some('/') => is_comment = true,
-                    _ => (),
-                }
-                last_wspace = None;
             } else {
                 last_wspace = None;
             }
-            prev_char = Some(c);
             line_buffer.push(c);
+            if kind.is_string() {
+                is_string = true;
+            }
         }
     }
 
     if newline_count > 1 {
-        debug!("track truncate: {} {}", text.len, newline_count);
-        let line = text.len - newline_count + 1;
+        debug!("track truncate: {} {}", text.len(), newline_count);
+        let line = text.len() - newline_count + 1;
         text.truncate(line);
     }
 
-    for &(l, _, _, ref b) in &trims {
-        errors.push(FormattingError {
-            line: l,
-            kind: ErrorKind::TrailingWhitespace,
-            is_comment: false,
-            line_buffer: b.clone(),
-        });
+    for &(l, kind, ref b) in &trims {
+        if !is_skipped_line(l, skipped_range) {
+            errors.push(FormattingError {
+                line: l,
+                kind: ErrorKind::TrailingWhitespace,
+                is_comment: kind.is_comment(),
+                is_string: kind.is_string(),
+                line_buffer: b.clone(),
+            });
+        }
     }
 
-    report.file_error_map.insert(name.to_owned(), errors);
+    report.file_error_map.insert(name.clone(), errors);
 }
 
 fn parse_input(
@@ -844,8 +510,11 @@ fn parse_input(
             parser.parse_crate_mod()
         }
         Input::Text(text) => {
-            let mut parser =
-                parse::new_parser_from_source_str(parse_session, "stdin".to_owned(), text);
+            let mut parser = parse::new_parser_from_source_str(
+                parse_session,
+                FileName::Custom("stdin".to_owned()),
+                text,
+            );
             parser.cfg_mods = false;
             parser.parse_crate_mod()
         }
@@ -864,24 +533,88 @@ fn parse_input(
     }
 }
 
+/// Format the given snippet. The snippet is expected to be *complete* code.
+/// When we cannot parse the given snippet, this function returns `None`.
+pub fn format_snippet(snippet: &str, config: &Config) -> Option<String> {
+    let mut out: Vec<u8> = Vec::with_capacity(snippet.len() * 2);
+    let input = Input::Text(snippet.into());
+    let mut config = config.clone();
+    config.set().write_mode(config::WriteMode::Plain);
+    config.set().hide_parse_errors(true);
+    match format_input(input, &config, Some(&mut out)) {
+        // `format_input()` returns an empty string on parsing error.
+        Ok(..) if out.is_empty() && !snippet.is_empty() => None,
+        Ok(..) => String::from_utf8(out).ok(),
+        Err(..) => None,
+    }
+}
+
+/// Format the given code block. Mainly targeted for code block in comment.
+/// The code block may be incomplete (i.e. parser may be unable to parse it).
+/// To avoid panic in parser, we wrap the code block with a dummy function.
+/// The returned code block does *not* end with newline.
+pub fn format_code_block(code_snippet: &str, config: &Config) -> Option<String> {
+    // Wrap the given code block with `fn main()` if it does not have one.
+    let fn_main_prefix = "fn main() {\n";
+    let snippet = fn_main_prefix.to_owned() + code_snippet + "\n}";
+
+    // Trim "fn main() {" on the first line and "}" on the last line,
+    // then unindent the whole code block.
+    format_snippet(&snippet, config).map(|s| {
+        // 2 = "}\n"
+        s[fn_main_prefix.len()..s.len().checked_sub(2).unwrap_or(0)]
+            .lines()
+            .map(|line| {
+                if line.len() > config.tab_spaces() {
+                    // Make sure that the line has leading whitespaces.
+                    let indent_str =
+                        Indent::from_width(config, config.tab_spaces()).to_string(config);
+                    if line.starts_with(indent_str.as_ref()) {
+                        &line[config.tab_spaces()..]
+                    } else {
+                        line
+                    }
+                } else {
+                    line
+                }
+            })
+            .collect::<Vec<_>>()
+            .join("\n")
+    })
+}
+
 pub fn format_input<T: Write>(
     input: Input,
     config: &Config,
     mut out: Option<&mut T>,
 ) -> Result<(Summary, FileMap, FormatReport), (io::Error, Summary)> {
-    let mut summary = Summary::new();
+    let mut summary = Summary::default();
     if config.disable_all_formatting() {
+        // When the input is from stdin, echo back the input.
+        if let Input::Text(ref buf) = input {
+            if let Err(e) = io::stdout().write_all(buf.as_bytes()) {
+                return Err((e, summary));
+            }
+        }
         return Ok((summary, FileMap::new(), FormatReport::new()));
     }
     let codemap = Rc::new(CodeMap::new(FilePathMapping::empty()));
 
-    let tty_handler =
-        Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone()));
+    let tty_handler = if config.hide_parse_errors() {
+        let silent_emitter = Box::new(EmitterWriter::new(
+            Box::new(Vec::new()),
+            Some(codemap.clone()),
+            false,
+        ));
+        Handler::with_emitter(true, false, silent_emitter)
+    } else {
+        Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone()))
+    };
     let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());
 
     let main_file = match input {
-        Input::File(ref file) => file.clone(),
-        Input::Text(..) => PathBuf::from("stdin"),
+        Input::File(ref file) => FileName::Real(file.clone()),
+        Input::Text(..) => FileName::Custom("stdin".to_owned()),
     };
 
     let krate = match parse_input(input, &parse_session) {
@@ -895,6 +628,8 @@ pub fn format_input<T: Write>(
         }
     };
 
+    summary.mark_parse_time();
+
     if parse_session.span_diagnostic.has_errors() {
         summary.add_parsing_error();
     }
@@ -903,29 +638,46 @@ pub fn format_input<T: Write>(
     let silent_emitter = Box::new(EmitterWriter::new(
         Box::new(Vec::new()),
         Some(codemap.clone()),
+        false,
     ));
     parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);
 
     let mut report = FormatReport::new();
 
-    match format_ast(
+    let format_result = format_ast(
         &krate,
         &mut parse_session,
         &main_file,
         config,
-        |file_name, file| {
+        |file_name, file, skipped_range| {
             // For some reason, the codemap does not include terminating
             // newlines so we must add one on for each file. This is sad.
             filemap::append_newline(file);
 
-            format_lines(file, file_name, config, &mut report);
+            format_lines(file, file_name, skipped_range, config, &mut report);
 
             if let Some(ref mut out) = out {
                 return filemap::write_file(file, file_name, out, config);
             }
             Ok(false)
         },
-    ) {
+    );
+
+    summary.mark_format_time();
+
+    if config.verbose() {
+        fn duration_to_f32(d: Duration) -> f32 {
+            d.as_secs() as f32 + d.subsec_nanos() as f32 / 1_000_000_000f32
+        }
+
+        println!(
+            "Spent {0:.3} secs in the parsing phase, and {1:.3} secs in the formatting phase",
+            duration_to_f32(summary.get_parse_time().unwrap()),
+            duration_to_f32(summary.get_format_time().unwrap()),
+        );
+    }
+
+    match format_result {
         Ok((file_map, has_diff)) => {
             if report.has_warnings() {
                 summary.add_formatting_error();
@@ -956,7 +708,10 @@ pub fn run(input: Input, config: &Config) -> Summary {
 
             if report.has_warnings() {
                 match term::stderr() {
-                    Some(ref t) if isatty() && t.supports_color() => {
+                    Some(ref t)
+                        if use_colored_tty(config.color()) && t.supports_color()
+                            && t.supports_attr(term::Attr::Bold) =>
+                    {
                         match report.print_warnings_fancy(term::stderr().unwrap()) {
                             Ok(..) => (),
                             Err(..) => panic!("Unable to write to stderr: {}", report),
@@ -978,85 +733,83 @@ pub fn run(input: Input, config: &Config) -> Summary {
 
 #[cfg(test)]
 mod test {
-    use super::*;
-
-    #[test]
-    fn indent_add_sub() {
-        let indent = Indent::new(4, 8) + Indent::new(8, 12);
-        assert_eq!(12, indent.block_indent);
-        assert_eq!(20, indent.alignment);
-
-        let indent = indent - Indent::new(4, 4);
-        assert_eq!(8, indent.block_indent);
-        assert_eq!(16, indent.alignment);
-    }
+    use super::{format_code_block, format_snippet, Config};
 
     #[test]
-    fn indent_add_sub_alignment() {
-        let indent = Indent::new(4, 8) + 4;
-        assert_eq!(4, indent.block_indent);
-        assert_eq!(12, indent.alignment);
-
-        let indent = indent - 4;
-        assert_eq!(4, indent.block_indent);
-        assert_eq!(8, indent.alignment);
+    fn test_no_panic_on_format_snippet_and_format_code_block() {
+        // `format_snippet()` and `format_code_block()` should not panic
+        // even when we cannot parse the given snippet.
+        let snippet = "let";
+        assert!(format_snippet(snippet, &Config::default()).is_none());
+        assert!(format_code_block(snippet, &Config::default()).is_none());
     }
 
-    #[test]
-    fn indent_to_string_spaces() {
-        let config = Config::default();
-        let indent = Indent::new(4, 8);
-
-        // 12 spaces
-        assert_eq!("            ", indent.to_string(&config));
-    }
-
-    #[test]
-    fn indent_to_string_hard_tabs() {
-        let mut config = Config::default();
-        config.set().hard_tabs(true);
-        let indent = Indent::new(8, 4);
-
-        // 2 tabs + 4 spaces
-        assert_eq!("\t\t    ", indent.to_string(&config));
-    }
-
-    #[test]
-    fn shape_visual_indent() {
-        let config = Config::default();
-        let indent = Indent::new(4, 8);
-        let shape = Shape::legacy(config.max_width(), indent);
-        let shape = shape.visual_indent(20);
-
-        assert_eq!(config.max_width(), shape.width);
-        assert_eq!(4, shape.indent.block_indent);
-        assert_eq!(28, shape.indent.alignment);
-        assert_eq!(28, shape.offset);
+    fn test_format_inner<F>(formatter: F, input: &str, expected: &str) -> bool
+    where
+        F: Fn(&str, &Config) -> Option<String>,
+    {
+        let output = formatter(input, &Config::default());
+        output.is_some() && output.unwrap() == expected
     }
 
     #[test]
-    fn shape_block_indent_without_alignment() {
-        let config = Config::default();
-        let indent = Indent::new(4, 0);
-        let shape = Shape::legacy(config.max_width(), indent);
-        let shape = shape.block_indent(20);
-
-        assert_eq!(config.max_width(), shape.width);
-        assert_eq!(24, shape.indent.block_indent);
-        assert_eq!(0, shape.indent.alignment);
-        assert_eq!(0, shape.offset);
+    fn test_format_snippet() {
+        let snippet = "fn main() { println!(\"hello, world\"); }";
+        let expected = "fn main() {\n    \
+                        println!(\"hello, world\");\n\
+                        }\n";
+        assert!(test_format_inner(format_snippet, snippet, expected));
     }
 
     #[test]
-    fn shape_block_indent_with_alignment() {
-        let config = Config::default();
-        let indent = Indent::new(4, 8);
-        let shape = Shape::legacy(config.max_width(), indent);
-        let shape = shape.block_indent(20);
-
-        assert_eq!(config.max_width(), shape.width);
-        assert_eq!(4, shape.indent.block_indent);
-        assert_eq!(28, shape.indent.alignment);
-        assert_eq!(28, shape.offset);
+    fn test_format_code_block() {
+        // simple code block
+        let code_block = "let x=3;";
+        let expected = "let x = 3;";
+        assert!(test_format_inner(format_code_block, code_block, expected));
+
+        // more complex code block, taken from chains.rs.
+        let code_block =
+"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
+(
+chain_indent(context, shape.add_offset(parent_rewrite.len())),
+context.config.indent_style() == IndentStyle::Visual || is_small_parent,
+)
+} else if is_block_expr(context, &parent, &parent_rewrite) {
+match context.config.indent_style() {
+// Try to put the first child on the same line with parent's last line
+IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true),
+// The parent is a block, so align the rest of the chain with the closing
+// brace.
+IndentStyle::Visual => (parent_shape, false),
+}
+} else {
+(
+chain_indent(context, shape.add_offset(parent_rewrite.len())),
+false,
+)
+};
+";
+        let expected =
+"let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
+    (
+        chain_indent(context, shape.add_offset(parent_rewrite.len())),
+        context.config.indent_style() == IndentStyle::Visual || is_small_parent,
+    )
+} else if is_block_expr(context, &parent, &parent_rewrite) {
+    match context.config.indent_style() {
+        // Try to put the first child on the same line with parent's last line
+        IndentStyle::Block => (parent_shape.block_indent(context.config.tab_spaces()), true),
+        // The parent is a block, so align the rest of the chain with the closing
+        // brace.
+        IndentStyle::Visual => (parent_shape, false),
+    }
+} else {
+    (
+        chain_indent(context, shape.add_offset(parent_rewrite.len())),
+        false,
+    )
+};";
+        assert!(test_format_inner(format_code_block, code_block, expected));
     }
 }