]> git.lizzy.rs Git - rust.git/blob - src/lib.rs
Merge pull request #1930 from topecongiro/poor/item_brace_style
[rust.git] / src / lib.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![feature(rustc_private)]
12
13 extern crate diff;
14 #[macro_use]
15 extern crate log;
16 extern crate regex;
17 extern crate rustc_errors as errors;
18 extern crate serde;
19 #[macro_use]
20 extern crate serde_derive;
21 extern crate serde_json;
22 extern crate strings;
23 extern crate syntax;
24 extern crate term;
25 extern crate unicode_segmentation;
26
27 use std::collections::HashMap;
28 use std::fmt;
29 use std::io::{self, stdout, Write};
30 use std::iter::repeat;
31 use std::ops::{Add, Sub};
32 use std::path::{Path, PathBuf};
33 use std::rc::Rc;
34
35 use errors::{DiagnosticBuilder, Handler};
36 use errors::emitter::{ColorConfig, EmitterWriter};
37 use strings::string_buffer::StringBuffer;
38 use syntax::ast;
39 use syntax::codemap::{CodeMap, FilePathMapping, Span};
40 use syntax::parse::{self, ParseSess};
41
42 use checkstyle::{output_footer, output_header};
43 use config::Config;
44 use filemap::FileMap;
45 use issues::{BadIssueSeeker, Issue};
46 use utils::{isatty, mk_sp, outer_attributes};
47 use visitor::FmtVisitor;
48
49 pub use self::summary::Summary;
50
51 #[macro_use]
52 mod utils;
53 pub mod config;
54 pub mod codemap;
55 pub mod filemap;
56 pub mod file_lines;
57 pub mod visitor;
58 mod checkstyle;
59 mod items;
60 mod missed_spans;
61 mod lists;
62 mod types;
63 mod expr;
64 mod imports;
65 mod issues;
66 mod rewrite;
67 mod string;
68 mod comment;
69 pub mod modules;
70 pub mod rustfmt_diff;
71 mod chains;
72 mod macros;
73 mod patterns;
74 mod summary;
75 mod vertical;
76
77 /// Spanned returns a span including attributes, if available.
78 pub trait Spanned {
79     fn span(&self) -> Span;
80 }
81
82 macro_rules! span_with_attrs_lo_hi {
83     ($this:ident, $lo:expr, $hi:expr) => {
84         {
85             let attrs = outer_attributes(&$this.attrs);
86             if attrs.is_empty() {
87                 mk_sp($lo, $hi)
88             } else {
89                 mk_sp(attrs[0].span.lo(), $hi)
90             }
91         }
92     }
93 }
94
95 macro_rules! span_with_attrs {
96     ($this:ident) => {
97         span_with_attrs_lo_hi!($this, $this.span.lo(), $this.span.hi())
98     }
99 }
100
101 macro_rules! implement_spanned {
102     ($this:ty) => {
103         impl Spanned for $this {
104             fn span(&self) -> Span {
105                 span_with_attrs!(self)
106             }
107         }
108     }
109 }
110
111 // Implement `Spanned` for structs with `attrs` field.
112 implement_spanned!(ast::Expr);
113 implement_spanned!(ast::Field);
114 implement_spanned!(ast::ForeignItem);
115 implement_spanned!(ast::Item);
116 implement_spanned!(ast::Local);
117
118 impl Spanned for ast::Stmt {
119     fn span(&self) -> Span {
120         match self.node {
121             ast::StmtKind::Local(ref local) => mk_sp(local.span().lo(), self.span.hi()),
122             ast::StmtKind::Item(ref item) => mk_sp(item.span().lo(), self.span.hi()),
123             ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
124                 mk_sp(expr.span().lo(), self.span.hi())
125             }
126             ast::StmtKind::Mac(ref mac) => {
127                 let (_, _, ref attrs) = **mac;
128                 if attrs.is_empty() {
129                     self.span
130                 } else {
131                     mk_sp(attrs[0].span.lo(), self.span.hi())
132                 }
133             }
134         }
135     }
136 }
137
138 impl Spanned for ast::Pat {
139     fn span(&self) -> Span {
140         self.span
141     }
142 }
143
144 impl Spanned for ast::Ty {
145     fn span(&self) -> Span {
146         self.span
147     }
148 }
149
150 impl Spanned for ast::Arm {
151     fn span(&self) -> Span {
152         span_with_attrs_lo_hi!(self, self.pats[0].span.lo(), self.body.span.hi())
153     }
154 }
155
156 impl Spanned for ast::Arg {
157     fn span(&self) -> Span {
158         if items::is_named_arg(self) {
159             utils::mk_sp(self.pat.span.lo(), self.ty.span.hi())
160         } else {
161             self.ty.span
162         }
163     }
164 }
165
166 impl Spanned for ast::StructField {
167     fn span(&self) -> Span {
168         span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span.hi())
169     }
170 }
171
172 impl Spanned for ast::WherePredicate {
173     fn span(&self) -> Span {
174         match *self {
175             ast::WherePredicate::BoundPredicate(ref p) => p.span,
176             ast::WherePredicate::RegionPredicate(ref p) => p.span,
177             ast::WherePredicate::EqPredicate(ref p) => p.span,
178         }
179     }
180 }
181
182 impl Spanned for ast::FunctionRetTy {
183     fn span(&self) -> Span {
184         match *self {
185             ast::FunctionRetTy::Default(span) => span,
186             ast::FunctionRetTy::Ty(ref ty) => ty.span,
187         }
188     }
189 }
190
191 impl Spanned for ast::TyParam {
192     fn span(&self) -> Span {
193         // Note that ty.span is the span for ty.ident, not the whole item.
194         let lo = if self.attrs.is_empty() {
195             self.span.lo()
196         } else {
197             self.attrs[0].span.lo()
198         };
199         if let Some(ref def) = self.default {
200             return mk_sp(lo, def.span.hi());
201         }
202         if self.bounds.is_empty() {
203             return mk_sp(lo, self.span.hi());
204         }
205         let hi = self.bounds[self.bounds.len() - 1].span().hi();
206         mk_sp(lo, hi)
207     }
208 }
209
210 impl Spanned for ast::TyParamBound {
211     fn span(&self) -> Span {
212         match *self {
213             ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span,
214             ast::TyParamBound::RegionTyParamBound(ref l) => l.span,
215         }
216     }
217 }
218
219 #[derive(Copy, Clone, Debug)]
220 pub struct Indent {
221     // Width of the block indent, in characters. Must be a multiple of
222     // Config::tab_spaces.
223     pub block_indent: usize,
224     // Alignment in characters.
225     pub alignment: usize,
226 }
227
228 impl Indent {
229     pub fn new(block_indent: usize, alignment: usize) -> Indent {
230         Indent {
231             block_indent: block_indent,
232             alignment: alignment,
233         }
234     }
235
236     pub fn from_width(config: &Config, width: usize) -> Indent {
237         if config.hard_tabs() {
238             let tab_num = width / config.tab_spaces();
239             let alignment = width % config.tab_spaces();
240             Indent::new(config.tab_spaces() * tab_num, alignment)
241         } else {
242             Indent::new(width, 0)
243         }
244     }
245
246     pub fn empty() -> Indent {
247         Indent::new(0, 0)
248     }
249
250     pub fn block_only(&self) -> Indent {
251         Indent {
252             block_indent: self.block_indent,
253             alignment: 0,
254         }
255     }
256
257     pub fn block_indent(mut self, config: &Config) -> Indent {
258         self.block_indent += config.tab_spaces();
259         self
260     }
261
262     pub fn block_unindent(mut self, config: &Config) -> Indent {
263         if self.block_indent < config.tab_spaces() {
264             Indent::new(self.block_indent, 0)
265         } else {
266             self.block_indent -= config.tab_spaces();
267             self
268         }
269     }
270
271     pub fn width(&self) -> usize {
272         self.block_indent + self.alignment
273     }
274
275     pub fn to_string(&self, config: &Config) -> String {
276         let (num_tabs, num_spaces) = if config.hard_tabs() {
277             (self.block_indent / config.tab_spaces(), self.alignment)
278         } else {
279             (0, self.width())
280         };
281         let num_chars = num_tabs + num_spaces;
282         let mut indent = String::with_capacity(num_chars);
283         for _ in 0..num_tabs {
284             indent.push('\t')
285         }
286         for _ in 0..num_spaces {
287             indent.push(' ')
288         }
289         indent
290     }
291 }
292
293 impl Add for Indent {
294     type Output = Indent;
295
296     fn add(self, rhs: Indent) -> Indent {
297         Indent {
298             block_indent: self.block_indent + rhs.block_indent,
299             alignment: self.alignment + rhs.alignment,
300         }
301     }
302 }
303
304 impl Sub for Indent {
305     type Output = Indent;
306
307     fn sub(self, rhs: Indent) -> Indent {
308         Indent::new(
309             self.block_indent - rhs.block_indent,
310             self.alignment - rhs.alignment,
311         )
312     }
313 }
314
315 impl Add<usize> for Indent {
316     type Output = Indent;
317
318     fn add(self, rhs: usize) -> Indent {
319         Indent::new(self.block_indent, self.alignment + rhs)
320     }
321 }
322
323 impl Sub<usize> for Indent {
324     type Output = Indent;
325
326     fn sub(self, rhs: usize) -> Indent {
327         Indent::new(self.block_indent, self.alignment - rhs)
328     }
329 }
330
331 #[derive(Copy, Clone, Debug)]
332 pub struct Shape {
333     pub width: usize,
334     // The current indentation of code.
335     pub indent: Indent,
336     // Indentation + any already emitted text on the first line of the current
337     // statement.
338     pub offset: usize,
339 }
340
341 impl Shape {
342     /// `indent` is the indentation of the first line. The next lines
343     /// should begin with at least `indent` spaces (except backwards
344     /// indentation). The first line should not begin with indentation.
345     /// `width` is the maximum number of characters on the last line
346     /// (excluding `indent`). The width of other lines is not limited by
347     /// `width`.
348     /// Note that in reality, we sometimes use width for lines other than the
349     /// last (i.e., we are conservative).
350     // .......*-------*
351     //        |       |
352     //        |     *-*
353     //        *-----|
354     // |<------------>|  max width
355     // |<---->|          indent
356     //        |<--->|    width
357     pub fn legacy(width: usize, indent: Indent) -> Shape {
358         Shape {
359             width: width,
360             indent: indent,
361             offset: indent.alignment,
362         }
363     }
364
365     pub fn indented(indent: Indent, config: &Config) -> Shape {
366         Shape {
367             width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
368             indent: indent,
369             offset: indent.alignment,
370         }
371     }
372
373     pub fn with_max_width(&self, config: &Config) -> Shape {
374         Shape {
375             width: config
376                 .max_width()
377                 .checked_sub(self.indent.width())
378                 .unwrap_or(0),
379             ..*self
380         }
381     }
382
383     pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
384         Shape {
385             width: width,
386             indent: indent,
387             offset: offset,
388         }
389     }
390
391     pub fn visual_indent(&self, extra_width: usize) -> Shape {
392         let alignment = self.offset + extra_width;
393         Shape {
394             width: self.width,
395             indent: Indent::new(self.indent.block_indent, alignment),
396             offset: alignment,
397         }
398     }
399
400     pub fn block_indent(&self, extra_width: usize) -> Shape {
401         if self.indent.alignment == 0 {
402             Shape {
403                 width: self.width,
404                 indent: Indent::new(self.indent.block_indent + extra_width, 0),
405                 offset: 0,
406             }
407         } else {
408             Shape {
409                 width: self.width,
410                 indent: self.indent + extra_width,
411                 offset: self.indent.alignment + extra_width,
412             }
413         }
414     }
415
416     pub fn block_left(&self, width: usize) -> Option<Shape> {
417         self.block_indent(width).sub_width(width)
418     }
419
420     pub fn add_offset(&self, extra_width: usize) -> Shape {
421         Shape {
422             offset: self.offset + extra_width,
423             ..*self
424         }
425     }
426
427     pub fn block(&self) -> Shape {
428         Shape {
429             indent: self.indent.block_only(),
430             ..*self
431         }
432     }
433
434     pub fn sub_width(&self, width: usize) -> Option<Shape> {
435         Some(Shape {
436             width: try_opt!(self.width.checked_sub(width)),
437             ..*self
438         })
439     }
440
441     pub fn shrink_left(&self, width: usize) -> Option<Shape> {
442         Some(Shape {
443             width: try_opt!(self.width.checked_sub(width)),
444             indent: self.indent + width,
445             offset: self.offset + width,
446         })
447     }
448
449     pub fn offset_left(&self, width: usize) -> Option<Shape> {
450         self.add_offset(width).sub_width(width)
451     }
452
453     pub fn used_width(&self) -> usize {
454         self.indent.block_indent + self.offset
455     }
456
457     pub fn rhs_overhead(&self, config: &Config) -> usize {
458         config
459             .max_width()
460             .checked_sub(self.used_width() + self.width)
461             .unwrap_or(0)
462     }
463 }
464
465 pub enum ErrorKind {
466     // Line has exceeded character limit (found, maximum)
467     LineOverflow(usize, usize),
468     // Line ends in whitespace
469     TrailingWhitespace,
470     // TO-DO or FIX-ME item without an issue number
471     BadIssue(Issue),
472 }
473
474 impl fmt::Display for ErrorKind {
475     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
476         match *self {
477             ErrorKind::LineOverflow(found, maximum) => write!(
478                 fmt,
479                 "line exceeded maximum width (maximum: {}, found: {})",
480                 maximum,
481                 found
482             ),
483             ErrorKind::TrailingWhitespace => write!(fmt, "left behind trailing whitespace"),
484             ErrorKind::BadIssue(issue) => write!(fmt, "found {}", issue),
485         }
486     }
487 }
488
489 // Formatting errors that are identified *after* rustfmt has run.
490 pub struct FormattingError {
491     line: usize,
492     kind: ErrorKind,
493     is_comment: bool,
494     line_buffer: String,
495 }
496
497 impl FormattingError {
498     fn msg_prefix(&self) -> &str {
499         match self.kind {
500             ErrorKind::LineOverflow(..) | ErrorKind::TrailingWhitespace => "error:",
501             ErrorKind::BadIssue(_) => "WARNING:",
502         }
503     }
504
505     fn msg_suffix(&self) -> String {
506         match self.kind {
507             ErrorKind::LineOverflow(..) if self.is_comment => format!(
508                 "use `error_on_line_overflow_comments = false` to suppress \
509                  the warning against line comments\n",
510             ),
511             _ => String::from(""),
512         }
513     }
514
515     // (space, target)
516     pub fn format_len(&self) -> (usize, usize) {
517         match self.kind {
518             ErrorKind::LineOverflow(found, max) => (max, found - max),
519             ErrorKind::TrailingWhitespace => {
520                 let trailing_ws_len = self.line_buffer
521                     .chars()
522                     .rev()
523                     .take_while(|c| c.is_whitespace())
524                     .count();
525                 (self.line_buffer.len() - trailing_ws_len, trailing_ws_len)
526             }
527             _ => (0, 0), // unreachable
528         }
529     }
530 }
531
532 pub struct FormatReport {
533     // Maps stringified file paths to their associated formatting errors.
534     file_error_map: HashMap<String, Vec<FormattingError>>,
535 }
536
537 impl FormatReport {
538     fn new() -> FormatReport {
539         FormatReport {
540             file_error_map: HashMap::new(),
541         }
542     }
543
544     pub fn warning_count(&self) -> usize {
545         self.file_error_map
546             .iter()
547             .map(|(_, errors)| errors.len())
548             .fold(0, |acc, x| acc + x)
549     }
550
551     pub fn has_warnings(&self) -> bool {
552         self.warning_count() > 0
553     }
554
555     pub fn print_warnings_fancy(
556         &self,
557         mut t: Box<term::Terminal<Output = io::Stderr>>,
558     ) -> Result<(), term::Error> {
559         for (file, errors) in &self.file_error_map {
560             for error in errors {
561                 let prefix_space_len = error.line.to_string().len();
562                 let prefix_spaces: String = repeat(" ").take(1 + prefix_space_len).collect();
563
564                 // First line: the overview of error
565                 t.fg(term::color::RED)?;
566                 t.attr(term::Attr::Bold)?;
567                 write!(t, "{} ", error.msg_prefix())?;
568                 t.reset()?;
569                 t.attr(term::Attr::Bold)?;
570                 write!(t, "{}\n", error.kind)?;
571
572                 // Second line: file info
573                 write!(t, "{}--> ", &prefix_spaces[1..])?;
574                 t.reset()?;
575                 write!(t, "{}:{}\n", file, error.line)?;
576
577                 // Third to fifth lines: show the line which triggered error, if available.
578                 if !error.line_buffer.is_empty() {
579                     let (space_len, target_len) = error.format_len();
580                     t.attr(term::Attr::Bold)?;
581                     write!(t, "{}|\n{} | ", prefix_spaces, error.line)?;
582                     t.reset()?;
583                     write!(t, "{}\n", error.line_buffer)?;
584                     t.attr(term::Attr::Bold)?;
585                     write!(t, "{}| ", prefix_spaces)?;
586                     t.fg(term::color::RED)?;
587                     write!(t, "{}\n", target_str(space_len, target_len))?;
588                     t.reset()?;
589                 }
590
591                 // The last line: show note if available.
592                 let msg_suffix = error.msg_suffix();
593                 if !msg_suffix.is_empty() {
594                     t.attr(term::Attr::Bold)?;
595                     write!(t, "{}= note: ", prefix_spaces)?;
596                     t.reset()?;
597                     write!(t, "{}\n", error.msg_suffix())?;
598                 } else {
599                     write!(t, "\n")?;
600                 }
601                 t.reset()?;
602             }
603         }
604
605         if !self.file_error_map.is_empty() {
606             t.attr(term::Attr::Bold)?;
607             write!(t, "warning: ")?;
608             t.reset()?;
609             write!(
610                 t,
611                 "rustfmt may have failed to format. See previous {} errors.\n\n",
612                 self.warning_count(),
613             )?;
614         }
615
616         Ok(())
617     }
618 }
619
620 fn target_str(space_len: usize, target_len: usize) -> String {
621     let empty_line: String = repeat(" ").take(space_len).collect();
622     let overflowed: String = repeat("^").take(target_len).collect();
623     empty_line + &overflowed
624 }
625
626 impl fmt::Display for FormatReport {
627     // Prints all the formatting errors.
628     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
629         for (file, errors) in &self.file_error_map {
630             for error in errors {
631                 let prefix_space_len = error.line.to_string().len();
632                 let prefix_spaces: String = repeat(" ").take(1 + prefix_space_len).collect();
633
634                 let error_line_buffer = if error.line_buffer.is_empty() {
635                     String::from(" ")
636                 } else {
637                     let (space_len, target_len) = error.format_len();
638                     format!(
639                         "{}|\n{} | {}\n{}| {}",
640                         prefix_spaces,
641                         error.line,
642                         error.line_buffer,
643                         prefix_spaces,
644                         target_str(space_len, target_len)
645                     )
646                 };
647
648                 let error_info = format!("{} {}", error.msg_prefix(), error.kind);
649                 let file_info = format!("{}--> {}:{}", &prefix_spaces[1..], file, error.line);
650                 let msg_suffix = error.msg_suffix();
651                 let note = if msg_suffix.is_empty() {
652                     String::new()
653                 } else {
654                     format!("{}note= ", prefix_spaces)
655                 };
656
657                 write!(
658                     fmt,
659                     "{}\n{}\n{}\n{}{}\n",
660                     error_info,
661                     file_info,
662                     error_line_buffer,
663                     note,
664                     error.msg_suffix()
665                 )?;
666             }
667         }
668         if !self.file_error_map.is_empty() {
669             write!(
670                 fmt,
671                 "warning: rustfmt may have failed to format. See previous {} errors.\n",
672                 self.warning_count(),
673             )?;
674         }
675         Ok(())
676     }
677 }
678
679 // Formatting which depends on the AST.
680 fn format_ast<F>(
681     krate: &ast::Crate,
682     parse_session: &mut ParseSess,
683     main_file: &Path,
684     config: &Config,
685     codemap: &Rc<CodeMap>,
686     mut after_file: F,
687 ) -> Result<(FileMap, bool), io::Error>
688 where
689     F: FnMut(&str, &mut StringBuffer) -> Result<bool, io::Error>,
690 {
691     let mut result = FileMap::new();
692     // diff mode: check if any files are differing
693     let mut has_diff = false;
694
695     // We always skip children for the "Plain" write mode, since there is
696     // nothing to distinguish the nested module contents.
697     let skip_children = config.skip_children() || config.write_mode() == config::WriteMode::Plain;
698     for (path, module) in modules::list_files(krate, parse_session.codemap()) {
699         if skip_children && path.as_path() != main_file {
700             continue;
701         }
702         let path_str = path.to_str().unwrap();
703         if config.verbose() {
704             println!("Formatting {}", path_str);
705         }
706         {
707             let mut visitor = FmtVisitor::from_codemap(parse_session, config);
708             let filemap = visitor.codemap.lookup_char_pos(module.inner.lo()).file;
709             // Format inner attributes if available.
710             if !krate.attrs.is_empty() && path == main_file {
711                 visitor.visit_attrs(&krate.attrs, ast::AttrStyle::Inner);
712             } else {
713                 visitor.last_pos = filemap.start_pos;
714             }
715             visitor.format_separate_mod(module, &*filemap);
716
717             has_diff |= after_file(path_str, &mut visitor.buffer)?;
718
719             result.push((path_str.to_owned(), visitor.buffer));
720         }
721         // Reset the error count.
722         if parse_session.span_diagnostic.has_errors() {
723             let silent_emitter = Box::new(EmitterWriter::new(
724                 Box::new(Vec::new()),
725                 Some(codemap.clone()),
726             ));
727             parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);
728         }
729     }
730
731     Ok((result, has_diff))
732 }
733
734 // Formatting done on a char by char or line by line basis.
735 // FIXME(#209) warn on bad license
736 // FIXME(#20) other stuff for parity with make tidy
737 fn format_lines(text: &mut StringBuffer, name: &str, config: &Config, report: &mut FormatReport) {
738     // Iterate over the chars in the file map.
739     let mut trims = vec![];
740     let mut last_wspace: Option<usize> = None;
741     let mut line_len = 0;
742     let mut cur_line = 1;
743     let mut newline_count = 0;
744     let mut errors = vec![];
745     let mut issue_seeker = BadIssueSeeker::new(config.report_todo(), config.report_fixme());
746     let mut prev_char: Option<char> = None;
747     let mut is_comment = false;
748     let mut line_buffer = String::with_capacity(config.max_width() * 2);
749
750     for (c, b) in text.chars() {
751         if c == '\r' {
752             continue;
753         }
754
755         let format_line = config.file_lines().contains_line(name, cur_line as usize);
756
757         if format_line {
758             // Add warnings for bad todos/ fixmes
759             if let Some(issue) = issue_seeker.inspect(c) {
760                 errors.push(FormattingError {
761                     line: cur_line,
762                     kind: ErrorKind::BadIssue(issue),
763                     is_comment: false,
764                     line_buffer: String::new(),
765                 });
766             }
767         }
768
769         if c == '\n' {
770             if format_line {
771                 // Check for (and record) trailing whitespace.
772                 if let Some(lw) = last_wspace {
773                     trims.push((cur_line, lw, b, line_buffer.clone()));
774                     line_len -= 1;
775                 }
776
777                 // Check for any line width errors we couldn't correct.
778                 let report_error_on_line_overflow = config.error_on_line_overflow() &&
779                     (config.error_on_line_overflow_comments() || !is_comment);
780                 if report_error_on_line_overflow && line_len > config.max_width() {
781                     errors.push(FormattingError {
782                         line: cur_line,
783                         kind: ErrorKind::LineOverflow(line_len, config.max_width()),
784                         is_comment: is_comment,
785                         line_buffer: line_buffer.clone(),
786                     });
787                 }
788             }
789
790             line_len = 0;
791             cur_line += 1;
792             newline_count += 1;
793             last_wspace = None;
794             prev_char = None;
795             is_comment = false;
796             line_buffer.clear();
797         } else {
798             newline_count = 0;
799             line_len += 1;
800             if c.is_whitespace() {
801                 if last_wspace.is_none() {
802                     last_wspace = Some(b);
803                 }
804             } else if c == '/' {
805                 match prev_char {
806                     Some('/') => is_comment = true,
807                     _ => (),
808                 }
809                 last_wspace = None;
810             } else {
811                 last_wspace = None;
812             }
813             prev_char = Some(c);
814             line_buffer.push(c);
815         }
816     }
817
818     if newline_count > 1 {
819         debug!("track truncate: {} {}", text.len, newline_count);
820         let line = text.len - newline_count + 1;
821         text.truncate(line);
822     }
823
824     for &(l, _, _, ref b) in &trims {
825         errors.push(FormattingError {
826             line: l,
827             kind: ErrorKind::TrailingWhitespace,
828             is_comment: false,
829             line_buffer: b.clone(),
830         });
831     }
832
833     report.file_error_map.insert(name.to_owned(), errors);
834 }
835
836 fn parse_input(
837     input: Input,
838     parse_session: &ParseSess,
839 ) -> Result<ast::Crate, Option<DiagnosticBuilder>> {
840     let result = match input {
841         Input::File(file) => {
842             let mut parser = parse::new_parser_from_file(parse_session, &file);
843             parser.cfg_mods = false;
844             parser.parse_crate_mod()
845         }
846         Input::Text(text) => {
847             let mut parser =
848                 parse::new_parser_from_source_str(parse_session, "stdin".to_owned(), text);
849             parser.cfg_mods = false;
850             parser.parse_crate_mod()
851         }
852     };
853
854     match result {
855         Ok(c) => {
856             if parse_session.span_diagnostic.has_errors() {
857                 // Bail out if the parser recovered from an error.
858                 Err(None)
859             } else {
860                 Ok(c)
861             }
862         }
863         Err(e) => Err(Some(e)),
864     }
865 }
866
867 pub fn format_input<T: Write>(
868     input: Input,
869     config: &Config,
870     mut out: Option<&mut T>,
871 ) -> Result<(Summary, FileMap, FormatReport), (io::Error, Summary)> {
872     let mut summary = Summary::new();
873     if config.disable_all_formatting() {
874         return Ok((summary, FileMap::new(), FormatReport::new()));
875     }
876     let codemap = Rc::new(CodeMap::new(FilePathMapping::empty()));
877
878     let tty_handler =
879         Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone()));
880     let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());
881
882     let main_file = match input {
883         Input::File(ref file) => file.clone(),
884         Input::Text(..) => PathBuf::from("stdin"),
885     };
886
887     let krate = match parse_input(input, &parse_session) {
888         Ok(krate) => krate,
889         Err(diagnostic) => {
890             if let Some(mut diagnostic) = diagnostic {
891                 diagnostic.emit();
892             }
893             summary.add_parsing_error();
894             return Ok((summary, FileMap::new(), FormatReport::new()));
895         }
896     };
897
898     if parse_session.span_diagnostic.has_errors() {
899         summary.add_parsing_error();
900     }
901
902     // Suppress error output after parsing.
903     let silent_emitter = Box::new(EmitterWriter::new(
904         Box::new(Vec::new()),
905         Some(codemap.clone()),
906     ));
907     parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);
908
909     let mut report = FormatReport::new();
910
911     match format_ast(
912         &krate,
913         &mut parse_session,
914         &main_file,
915         config,
916         &codemap,
917         |file_name, file| {
918             // For some reason, the codemap does not include terminating
919             // newlines so we must add one on for each file. This is sad.
920             filemap::append_newline(file);
921
922             format_lines(file, file_name, config, &mut report);
923
924             if let Some(ref mut out) = out {
925                 return filemap::write_file(file, file_name, out, config);
926             }
927             Ok(false)
928         },
929     ) {
930         Ok((file_map, has_diff)) => {
931             if report.has_warnings() {
932                 summary.add_formatting_error();
933             }
934
935             if has_diff {
936                 summary.add_diff();
937             }
938
939             Ok((summary, file_map, report))
940         }
941         Err(e) => Err((e, summary)),
942     }
943 }
944
945 #[derive(Debug)]
946 pub enum Input {
947     File(PathBuf),
948     Text(String),
949 }
950
951 pub fn run(input: Input, config: &Config) -> Summary {
952     let out = &mut stdout();
953     output_header(out, config.write_mode()).ok();
954     match format_input(input, config, Some(out)) {
955         Ok((summary, _, report)) => {
956             output_footer(out, config.write_mode()).ok();
957
958             if report.has_warnings() {
959                 match term::stderr() {
960                     Some(ref t) if isatty() && t.supports_color() => {
961                         match report.print_warnings_fancy(term::stderr().unwrap()) {
962                             Ok(..) => (),
963                             Err(..) => panic!("Unable to write to stderr: {}", report),
964                         }
965                     }
966                     _ => msg!("{}", report),
967                 }
968             }
969
970             summary
971         }
972         Err((msg, mut summary)) => {
973             msg!("Error writing files: {}", msg);
974             summary.add_operational_error();
975             summary
976         }
977     }
978 }
979
980 #[cfg(test)]
981 mod test {
982     use super::*;
983
984     #[test]
985     fn indent_add_sub() {
986         let indent = Indent::new(4, 8) + Indent::new(8, 12);
987         assert_eq!(12, indent.block_indent);
988         assert_eq!(20, indent.alignment);
989
990         let indent = indent - Indent::new(4, 4);
991         assert_eq!(8, indent.block_indent);
992         assert_eq!(16, indent.alignment);
993     }
994
995     #[test]
996     fn indent_add_sub_alignment() {
997         let indent = Indent::new(4, 8) + 4;
998         assert_eq!(4, indent.block_indent);
999         assert_eq!(12, indent.alignment);
1000
1001         let indent = indent - 4;
1002         assert_eq!(4, indent.block_indent);
1003         assert_eq!(8, indent.alignment);
1004     }
1005
1006     #[test]
1007     fn indent_to_string_spaces() {
1008         let config = Config::default();
1009         let indent = Indent::new(4, 8);
1010
1011         // 12 spaces
1012         assert_eq!("            ", indent.to_string(&config));
1013     }
1014
1015     #[test]
1016     fn indent_to_string_hard_tabs() {
1017         let mut config = Config::default();
1018         config.set().hard_tabs(true);
1019         let indent = Indent::new(8, 4);
1020
1021         // 2 tabs + 4 spaces
1022         assert_eq!("\t\t    ", indent.to_string(&config));
1023     }
1024
1025     #[test]
1026     fn shape_visual_indent() {
1027         let config = Config::default();
1028         let indent = Indent::new(4, 8);
1029         let shape = Shape::legacy(config.max_width(), indent);
1030         let shape = shape.visual_indent(20);
1031
1032         assert_eq!(config.max_width(), shape.width);
1033         assert_eq!(4, shape.indent.block_indent);
1034         assert_eq!(28, shape.indent.alignment);
1035         assert_eq!(28, shape.offset);
1036     }
1037
1038     #[test]
1039     fn shape_block_indent_without_alignment() {
1040         let config = Config::default();
1041         let indent = Indent::new(4, 0);
1042         let shape = Shape::legacy(config.max_width(), indent);
1043         let shape = shape.block_indent(20);
1044
1045         assert_eq!(config.max_width(), shape.width);
1046         assert_eq!(24, shape.indent.block_indent);
1047         assert_eq!(0, shape.indent.alignment);
1048         assert_eq!(0, shape.offset);
1049     }
1050
1051     #[test]
1052     fn shape_block_indent_with_alignment() {
1053         let config = Config::default();
1054         let indent = Indent::new(4, 8);
1055         let shape = Shape::legacy(config.max_width(), indent);
1056         let shape = shape.block_indent(20);
1057
1058         assert_eq!(config.max_width(), shape.width);
1059         assert_eq!(4, shape.indent.block_indent);
1060         assert_eq!(28, shape.indent.alignment);
1061         assert_eq!(28, shape.offset);
1062     }
1063 }